Silicon Labs documentation on OpenThread version 3.1.0.

Documentation source: https://docs.silabs.com/openthread/3.1.0

# OpenThread

## Developing Silicon Labs OpenThread Applications

OpenThread released by Google is:

- **An open-source implementation of the Thread® networking protocol.** Google 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.
- **OS and platform agnostic**, with a narrow platform abstraction layer and a small memory footprint, making it highly portable. It supports both system-on-chip (SoC) and co-processor designs.
- **A Thread Certified Component**, implementing all features defined in the Thread 1.4.1 specification, including all Thread networking layers (IPv6, 6LoWPAN, IEEE 802.15.4 with MAC security, Mesh Link Establishment, Mesh Routing) and device roles, as well as Border Router support.

![Thread stack](/openthread-start/0.2/images/thread-stack-overview.png)

Silicon Labs has implemented an OpenThread-based protocol tailored to work with Silicon Labs hardware. This protocol is available on GitHub and also as a software development kit (SDK) installed with Simplicity Studio. The SDK is a fully tested snapshot of the GitHub
source. It supports a broader range of hardware than does the GitHub version and includes documentation and example applications not available on GitHub. The content on these pages is intended for those who want to experiment with, or are already developing, an OpenThread application using the Silicon Labs OpenThread SDK.

**For details about this release**: Links to release notes are available on the [silabs.com Gecko SDK page](https://www.silabs.com/developers/gecko-software-development-kit).

**For Silicon Labs' OpenThread product information**: See the [product pages on silabs.com](https://www.silabs.com/wireless/thread).

**For background about the OpenThread protocol and other wireless networking topics**: The [Fundamentals section](/openthread/3.1.0/openthread-fundamentals-overview) is a good place to start. This section also contains information and resources for the open source OpenThread protocol.

**To get started with development**: See the [Getting Started section](/openthread/3.1.0/openthread-getting-started-overview) to get started working with example applications.

**If you are already in development**: See the [Developer's Guide](/openthread/3.1.0/openthread-developers-guide-overview) for details or go directly to the [API Reference](/openthread/3.1.0/openthread-api).

## Release Notes

### OpenThread Version 3.1.0 - Release Notes (Jun 23, 2026)

The OpenThread SDK is an enhanced version of the open-source OpenThread implementation released by Google that has been tailored to work with Silicon Labs hardware.

Click [here](https://github.com/SiliconLabs/simplicity_sdk/tags) for earlier releases.

#### Release Summary

<table>
    <thead>
        <tr>
            <th>Release Item</th>
            <th>Version</th>
            <th>Release Date</th>
            <th>Release Notes</th>
            <th>Key Features</th>
            <th>API Changes</th>
            <th>Bug Fixes</th>
            <th>Chip Enablement</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>OpenThread SDK</p>
            </td>
            <td>
                <p>3.1.0</p>
            </td>
            <td>
                <p>Jun 23, 2026</p>
            </td>
            <td>
                <p><a href="sisdk-ot-sdk-release-notes">Release Notes</a></p>
            </td>
            <td>
                <ul>
                    <li>Added Thread 1.4 qualification support for OTBR in NCP mode</li>
                    <li>Continued Thread 1.4 qualification support for SoC and OTBR in RCP mode</li>
                    <li>Added Thread - SoC Empty sample applications</li>
                    <li>Added NAT64 and TREL support for Host / NCP configurations</li>
                    <li>OTBR support on Android 15 with CPC spinel proxy support</li>
                    <li>Standardized Simplicity SDK example app titles and descriptions</li>
                    <li>Silicon Labs standard logger integration</li>
                </ul>
            </td>
            <td>Refer to API changes as documented <a href="https://openthread.io/reference/api-updates">here</a> starting with the changes on November 11, 2025 and ending with the changes on April 29, 2026</td>
            <td>Targeted quality improvements and bug fixes.</td>
            <td>None</td>
        </tr>
        <tr>
            <td>
                <p>802.15.4 Mesh + BLE Multiprotocol</p>
            </td>
            <td>
                <p>3.1.0</p>
            </td>
            <td>
                <p>Jun 23, 2026</p>
            </td>
            <td>
                <p><a href="sisdk-ot-multiprotocol-release-notes">Release Notes</a></p>
            </td>
            <td>
                <ul>
                    <li>Added CMP co-processor sample applications running Zigbee NCP, OpenThread RCP, and Bluetooth RCP.</li>
                    <li>Added Alpha Android 15 host development support with ZigbeeD libraries and source to build ZigbeeD and OTBR host applications.</li>
                    <li>Standardized Multiprotocol sample application names, project layout, and Simplicity Studio display titles.</li>
                    <li>Reorganized multiprotocol package file paths for a more cohesive structure.</li>
                </ul>
            </td>
            <td>
                <p>Removed <code>sl_802154_radio_set_scheduler_priorities()</code>.</p>
            </td>
            <td>
                <p>Targeted quality improvements and bug fixes.</p>
            </td>
            <td>
                <p>Added Alpha software support for xG2B devices.</p>
            </td>
        </tr>
    </tbody>
</table>

#### Impact of Release Changes

[Impact Statements](#impact-statements) | [Migration Guide](#migration-guide)

##### Impact Statements

<table>
    <thead>
        <tr>
            <th>Change</th>
            <th>Impact</th>
            <th>Affected Software Variants if applicable</th>
            <th>Affected Modes</th>
            <th>Affected OPNs / Boards / OPN Combinations</th>
            <th>Affected Host Interfaces</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Multi-instance upgrades</p>
            </td>
            <td>
                <p>Existing multi-instance applications may require manual updates.</p>
            </td>
            <td>
                <p>Multi-instance</p>
            </td>
            <td>
                <p>All</p>
            </td>
            <td>
                <p>All</p>
            </td>
            <td>
                <p>N/A</p>
            </td>
        </tr>
    </tbody>
</table>

##### Migration Guide

Multi-instance upgrades:

- Multi-instance applications require an `app.c` update only when app-level logging with instance-aware logging is enabled, in which case, implement `otPlatLogOutput()` and add the `#include <stdint.h>` and `#include <openthread/instance.h>` directives.
- The 25Q4 OpenThread BLE DMP samples were moved to the multiprotocol example set in 26Q2. No manual `app.c` changes are required for the single-instance sample. The multi-instance sample requires an `app.c` update only when instance-aware logging is enabled. In that case, implement `otPlatLogOutput()` and add the required `#include <stdint.h>` and `#include <openthread/instance.h>` directives.

#### Using This Release

[What's in the Release?](#what-s-in-the-release) | [Compatible Software](#compatible-software) | [Installation and Use](#installation-and-use) | [Help and Feedback](#help-and-feedback)

##### What's in the Release?

- Silicon Labs OpenThread stack
- Silicon Labs OpenThread sample applications
- Silicon Labs OpenThread border router

##### Compatible Software

<table>
    <thead>
        <tr>
            <th>Software</th>
            <th>Compatible Version or Variant</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Simplicity SDK</td>
            <td>2026.6.0</td>
        </tr>
        <tr>
            <td>GCC (The GNU Compiler Collection)</td>
            <td>14.2 Rel1 (provided with Simplicity Studio)</td>
        </tr>
    </tbody>
</table>

##### Installation and Use

The OpenThread SDK is part of the Simplicity SDK, the suite of Silicon Labs SDKs. To quickly get started with OpenThread and the Simplicity SDK, install [Simplicity Studio](https://www.silabs.com/developer-tools/simplicity-studio), which will set up your development environment and walk you through Simplicity SDK installation. Simplicity Studio includes everything needed for IoT product development with Silicon Labs devices, including a resource and project launcher, software configuration tools, full IDE with GNU toolchain, and analysis tools.

Alternatively, Simplicity SDK may be installed manually by downloading or cloning the latest from GitHub. See [https://github.com/SiliconLabsSoftware/sisdk-release](https://github.com/SiliconLabsSoftware/sisdk-release) for more information.

Documentation specific to the SDK version is installed with the SDK. API references and other information about this release are available
on [https://docs.silabs.com/openthread/latest/](https://docs.silabs.com/openthread/latest/). Select your SDK version in the upper right.

For more information about the OpenThread SDK see [Silicon Labs OpenThread QuickStart Guide](https://docs.silabs.com/openthread/latest/openthread-quick-start-guide/).

If you are new to Thread see [Thread Fundamentals](https://docs.silabs.com/openthread/latest/thread-fundamentals/).

To review Security and Software Advisory notifications and manage your notification preferences:

1. Go to [https://community.silabs.com/](https://community.silabs.com/).
2. Log in with your account credentials.
3. Click your profile icon in the upper-right corner of the page.
4. Select **Notifications** from the dropdown menu.
5. In the Notifications section, go to the **My Product Notifications** tab to review historical Security and Software Advisory notifications
6. To manage your preferences, use the **Manage Notifications** tab to customize which product updates and advisories you receive.

To learn more about the software in this release, dive into our [online documentation](https://docs.silabs.com/openthread/latest/openthread-start/).

###### Additional Information

- **OpenThread GitHub Repository**  
  The Silicon Labs OpenThread SDK is based on the upstream OpenThread repo ([https://github.com/openthread/openthread](https://github.com/openthread/openthread)) through ancestor commit **fb274efe6**. Silicon Labs' fork is available on GitHub: [openthread_stack/util/third_party/openthread](https://github.com/SiliconLabsSoftware/sisdk-release/tree/sisdk-2026.6/openthread_stack/util/third_party/openthread).
- **OpenThread Border Router GitHub Repository**  
  The Silicon Labs OpenThread SDK is based on the upstream OpenThread Border Router repo ([https://github.com/openthread/ot-br-posix](https://github.com/openthread/ot-br-posix)) through ancestor commit **717abf0dc**. Silicon Labs' fork is available on GitHub: [openthread_stack/util/third_party/ot-br-posix](https://github.com/SiliconLabsSoftware/sisdk-release/tree/sisdk-2026.6/openthread_stack/util/third_party/ot-br-posix).
- **SDK Fork Patches**  
  Silicon Labs ships patch files that document fork changes relative to each upstream repository. See [`openthread_stack/README.md`](https://github.com/SiliconLabsSoftware/sisdk-release/blob/sisdk-2026.6/openthread_stack/README.md) for the file listing, what's patched in this release, and usage instructions.
- **Secure Vault Integration**  
  When deployed to Secure Vault High devices, sensitive keys are protected using the Secure Vault Key Management functionality. The following table shows the protected keys and their storage protection characteristics.  
  Wrapped keys that are marked as “Non-Exportable” can be used but cannot be viewed or shared at runtime.  
  Wrapped keys that are marked as “Exportable” can be used or shared at runtime but remain encrypted while stored in flash.  
  For more information on Secure Vault Key Management functionality, see [Secure Key Storage](https://docs.silabs.com/openthread/latest/efr32-secure-key-storage/).

<table>
    <thead>
        <tr>
            <th>Wrapped Key</th>
            <th>Exportable / Non-Exportable</th>
            <th>Notes</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                Thread Master Key
            </td>
            <td>
                Exportable
            </td>
            <td>
                Must be exportable to form the TLVs
            </td>
        </tr>
        <tr>
            <td>
                PSKc
            </td>
            <td>
                Exportable
            </td>
            <td>
                Must be exportable to form the TLVs
            </td>
        </tr>
        <tr>
            <td>
                Key Encryption Key
            </td>
            <td>
                Exportable
            </td>
            <td>
                Must be exportable to form the TLVs
            </td>
        </tr>
        <tr>
            <td>
                MLE Key
            </td>
            <td>
                Non-Exportable
            </td>
            <td>
            </td>
        </tr>
        <tr>
            <td>
                Temporary MLE Key
            </td>
            <td>
                Non-Exportable
            </td>
            <td>
            </td>
        </tr>
        <tr>
            <td>
                MAC Previous Key
            </td>
            <td>
                Non-Exportable
            </td>
            <td>
            </td>
        </tr>
        <tr>
            <td>
                MAC Current Key
            </td>
            <td>
                Non-Exportable
            </td>
            <td>
            </td>
        </tr>
        <tr>
            <td>
                MAC Next Key
            </td>
            <td>
                Non-Exportable
            </td>
            <td>
            </td>
        </tr>
    </tbody>
</table>

- **Thread Certification**  
  This release has been qualified for Thread 1.4 compliance for the SoC and Border Router using the Thread Test Harness v64.0, the most recent Member Release available. Border Router qualification includes validation in both RCP and NCP operating modes. Silicon Labs recommends using Thread Test Harness v64.0 for all qualification efforts associated with this release. Additionally, this release includes OpenThread stack libraries that may be leveraged for certification by inheritance.

##### Help and Feedback

- Contact [Silicon Labs Support](https://www.silabs.com/support).
- To use our **Ask AI** tool to get answers, see the search field at the top of [this page](https://docs.silabs.com/).  
  > **Note:** **Ask AI** is experimental.
- Get help from our [developer community](https://community.silabs.com/s/?language=en_US).

#### Feature Matrix

[Supported Features](#supported-features) | [Unsupported Features](#unsupported-features)

##### Supported Features

<table>
    <thead>
        <tr>
            <th>Feature Name</th>
            <th>Description</th>
            <th>Quality</th>
            <th>Related API Names</th>
            <th>Supported Software Variants, Hardware, Modes, Host Interfaces</th>
            <th>Related Example Names</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Thread 1.4</td>
            <td>All features as defined by the Thread Specification up to and including version 1.4.</td>
            <td>Production</td>
            <td><a href="https://docs.silabs.com/openthread/3.1.0/openthread-api/">OpenThread API</a></td>
            <td>None</td>
            <td>None</td>
        </tr>
    </tbody>
</table>

##### Unsupported Features

This release includes preliminary support for selected Thread 2.0 features that have already been defined while the overall Thread 2.0 specification continues to evolve.

#### SDK Release and Maintenance Policy

See our [SDK Release and Maintenance Policy](https://www.silabs.com/developer-tools/sdk-release-and-maintenance-policy).

### OpenThread SDK Version 3.1.0 - Release Notes (Jun 23, 2026)

[**OpenThread Version 3.1.0**](.)

Thread is a secure, reliable, scalable, and upgradeable wireless IPv6 mesh networking protocol. It provides low-cost bridging to other IP networks while optimized for low-power/battery-backed operation. The Thread stack is designed specifically for Connected Home applications where IP-based networking is desired and a variety of application layers may be required.

OpenThread, released by Google, is an open-source implementation of Thread. Google has released OpenThread to accelerate the development of products for the connected home and commercial buildings. With a narrow platform abstraction layer and a small memory footprint, OpenThread is highly portable. It supports system-on-chip (SoC), network co-processor (NCP), and radio co-processor (RCP) designs.

Silicon Labs has developed an OpenThread-based SDK tailored to work with Silicon Labs hardware. The Silicon Labs OpenThread SDK is a fully tested enhanced version of the GitHub source. It supports a broader range of hardware than does the GitHub version and includes documentation and example applications not available on GitHub.

Click [here](https://github.com/SiliconLabs/simplicity_sdk/tags) for earlier releases.

#### Release Summary

[Key Features](#key-features) | [API Changes](#api-changes) | [Bug Fixes](#bug-fixes) | [Chip Enablement](#chip-enablement)

##### Key Features

- Added Thread 1.4 qualification support for OTBR in NCP mode
- Continued Thread 1.4 qualification support for SoC and OTBR in RCP mode
- Added Thread - SoC Empty sample applications
- Added NAT64 and TREL support for Host / NCP configurations
- Added OTBR Android 15 support with CPC spinel proxy support
- Standardized Simplicity SDK example app titles and descriptions
- Silicon Labs standard logger integration

##### API Changes

Refer to API changes as documented [here](https://openthread.io/reference/api-updates), starting with the changes on November 11, 2025 and ending with the changes on April 29, 2026.

##### Bug Fixes

Targeted quality improvements and bug fixes

##### Chip Enablement

None.

#### Key Features

[New Features](#new-features) | [Enhancements](#enhancements) | [Removed Features](#removed-features) | [Deprecated Features](#deprecated-features)

##### New Features

###### Thread - SoC Empty sample applications

This release adds two new OpenThread SoC Empty sample applications:

- `ot-soc-empty` - `OpenThread - SoC Empty FTD`
- `ot-soc-empty-freertos` - `OpenThread - SoC Empty FTD FreeRTOS`

These sample applications provide minimal Full Thread Device (FTD) reference projects for baremetal and FreeRTOS environments. They are intended as starting points for developing Thread applications and include configurable default dataset values for form / join workflows.

OpenThread - SoC Empty FTD is a bare-metal application compatible with series 2 devices, while OpenThread - SoC Empty FTD FreeRTOS utilizes RTOS and is compatible with both series 2 and series 3 devices. Both applications contain only the essential elements required for a functional Thread application, and thus can serve as a base from which to build out OpenThread applications. The pre-made application code in SoC Empty provides basic out-of-the-box functionality to form a Thread network or join an existing one. It does so using dataset parameters that can be configured after project generation using the Simplicity Studio UI to configure the default Dataset Values component, or by modifying the application code directly.

##### Enhancements

- Thread 1.4 qualification coverage: This release extends Border Router qualification to support Thread 1.4 compliance in both RCP and NCP operating modes, while continuing Thread 1.4 compliance support for SoC applications.
- Host / NCP NAT64 support: This release adds NAT64 support for OpenThread Border Router deployments that use a Host / NCP architecture.
- Host / NCP TREL support: This release adds TREL peer-discovery and forwarding support for Host / NCP configurations.
- Android CPC spinel proxy: This release adds the `cpc-spinel-proxy` application and related CPC transport refactoring to support Android Thread HAL integrations that communicate through a Unix socket, including OTBR deployments on Android 15.
- Example metadata standardization: Standardized Simplicity SDK OpenThread example titles and descriptions to align naming and presentation across the example catalog.
- Toolchain and platform integration updates: Added integration with the Silicon Labs standard logger.
- Runtime initialization of IPv6 external address pools: OpenThread now supports runtime initialization of IPv6 external address pools via `otIp6Init()`, with CLI support through `ifconfig init <ucast-addr-count> <mcast-addr-count>`. This is not yet supported in NCP configurations.  
  Impacted users: certification library configurations (non-NCP) in this release default to runtime external address pool initialization.  
  - For non-NCP certification library users, update application flow to call `otIp6Init()` before `otIp6SetEnabled()` (or run `ifconfig init ...` before `ifconfig up` in CLI workflows).  
  - For (non-NCP) certification source users, runtime behavior is optional: enable `OPENTHREAD_CONFIG_IP6_INIT_EXT_ADDR_POOL_ENABLE` (and `OPENTHREAD_CONFIG_CLI_IFCONFIG_INIT_ENABLE` for CLI) to use this API/CLI flow; otherwise continue using compile-time `OPENTHREAD_CONFIG_IP6_MAX_EXT_UCAST_ADDRS` / `OPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS`.
- Upgrade rules: Added project upgrade rules to preserve required NCP CLI support and serial-task stack sizing when upgrading existing projects.
- Configurable ECDSA dependencies: Added OpenThread crypto component options to allow projects to choose deterministic or non-deterministic ECDSA behavior.
- Default Dataset Values Component for testing purposes: This release adds a new component for OpenThread called Default Dataset Values. This component provides defines for the basic elements of an OpenThread dataset, those being Channel, PAN ID, and Network Key. Each of these is configurable via the component’s configuration menu in the Simplicity Studio UI. This component should only be used for testing, as utilizing a static default dataset in production applications poses a security risk.
- OpenThread TrustZone applications: OpenThread TrustZone applications now utilize all available flash and RAM memory.
- Packaging Updates: The filepaths of .slcc files and code classification headers inside openthread_stack have moved: component to component_stack and include to include/code_classification (but the SLC component interface to use this content in a project is unchanged).
- Logging improvements: Added a new `ot_sl_log` component which, when included in an application, utilizes the standard Silicon Labs debug logger.

##### Removed Features

None.

##### Deprecated Features

None.

#### API Changes

Refer to API changes as documented [here](https://openthread.io/reference/api-updates), starting with the changes on November 11, 2025 and ending with the changes on April 29, 2026.

#### Impact of Release Changes

[Impact Statements](#impact-statements) | [Migration Guide](#migration-guide)

##### Impact Statements

Multi-instance application upgrades may require manual updates to `app.c` when migrating from 25Q4 to 26Q2.

##### Migration Guide

Multi-instance upgrades:

- Multi-instance applications require an `app.c` update only when app-level logging with instance-aware logging is enabled, in which case, implement `otPlatLogOutput()` and add the `#include <stdint.h>` and `#include <openthread/instance.h>` directives.
- The 25Q4 OpenThread BLE DMP samples were moved to the multiprotocol example set in 26Q2. No manual `app.c` changes are required for the single-instance sample. The multi-instance sample requires an `app.c` update only when instance-aware logging is enabled. In that case, implement `otPlatLogOutput()` and add the required `#include <stdint.h>` and `#include <openthread/instance.h>` directives.

#### Bug Fixes

<table>
    <thead>
        <tr>
            <th>ID</th>
            <th>Issue Description</th>
            <th>GitHub / Salesforce Reference (if any)</th>
            <th>Affected Software Variants, Hardware, Modes, Host Interfaces</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>1224581, 1224586</td>
            <td>Updated the Silicon Labs OpenThread platform layer to avoid configuration-dependent Mbed TLS / PSA Crypto ABI mismatches when using precompiled libraries with customized Mbed TLS settings.</td>
            <td>None</td>
            <td>Applications using precompiled OpenThread libraries with customized Mbed TLS / PSA Crypto configurations</td>
        </tr>
        <tr>
            <td>1485392, 1582132</td>
            <td>Fixed issue in which the RTOS stack task would unexpectedly process serial data.</td>
            <td>None</td>
            <td>RTOS OpenThread applications using the serial task</td>
        </tr>
        <tr>
            <td>1566197</td>
            <td>Fixed an issue where certain Thread communications could be disrupted between a pair of devices in which a ble connection is open.</td>
            <td>None</td>
            <td>Thread Multiprotocol applications with BLE</td>
        </tr>
        <tr>
            <td>1569156</td>
            <td>Linking with the “OpenThread certification library” and enabling both “SHA-384 and SHA-512” components will no longer result in a hard fault.</td>
            <td>00336033</td>
            <td>Applications using the OpenThread certification libraries and customized PSA / MBEDTLS configurations</td>
        </tr>
        <tr>
            <td>1589205</td>
            <td>Fixed otPlatRadioEnergyScan to return OT_ERROR_BUSY when radio is already performing energy scan, as required by OpenThread platform API contract.</td>
            <td>None</td>
            <td>Multi-instance applications</td>
        </tr>
        <tr>
            <td>1590627</td>
            <td>OTBR Debian packages now install NAT64 and other default OTBR services correctly.</td>
            <td>None</td>
            <td>Debian OTBR installations</td>
        </tr>
        <tr>
            <td>1569103</td>
            <td>Fixed an issue where a disconnected CPC endpoint during frame transmission was incorrectly treated as a successful send, leading to undetected frame loss.</td>
            <td>None</td>
            <td>OTBR / NCP CPC configurations</td>
        </tr>
        <tr>
            <td>1602474</td>
            <td>During the join process for a Synchronized Sleepy End Device (SSED), the NCP support no longer errors out in the MAC when processing enhanced ACKs. This occurred only during the CSL negotiation and not in other cases.</td>
            <td>None</td>
            <td>Synchronized Sleepy End Devices (SSEDs)</td>
        </tr>
        <tr>
            <td>1605124</td>
            <td>Fixed issue in which an assert is occasionally caused by starting an alarm while another alarm is active.</td>
            <td>00335311</td>
            <td>All OPNs, boards, modes and interfaces</td>
        </tr>
        <tr>
            <td>1621998</td>
            <td>Corrected the value returned by <code>otPlatRadioGetTransmitPower()</code> so transmit power is reported in dBm.</td>
            <td>00340226</td>
            <td>All OPNs, boards, modes and interfaces</td>
        </tr>
        <tr>
            <td>1635295</td>
            <td>Fixed a busfault / hardfault that could occur when building or running OpenThread applications with <code>NDEBUG=1</code>.</td>
            <td>00341017</td>
            <td>All OPNs, boards, modes and interfaces</td>
        </tr>
        <tr>
            <td>1640055</td>
            <td>Fixed an issue where openthread was reinitializing the sl_iostream_debug stream causing the system default stream to become sl_iostream_debug.</td>
            <td>00341123</td>
            <td>All OPNs, boards, modes and interfaces</td>
        </tr>
        <tr>
            <td>1633284</td>
            <td>Fixed a CSL issue with otbr + rcp by increasing OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US on host.</td>
            <td>None</td>
            <td>Host / OTBR configurations using sleepy end devices on Series 3</td>
        </tr>
    </tbody>
</table>

#### Chip Enablement

None.

#### Application Example Changes

[New Examples](#new-examples) | [Modified Examples](#modified-examples) | [Removed Examples](#removed-examples) | [Deprecated Examples](#deprecated-examples)

##### New Examples

<table>
    <thead>
        <tr>
            <th>Example Name</th>
            <th>Description</th>
            <th>Supported Software Variants (if applicable)</th>
            <th>Supported Modes</th>
            <th>Supported OPNs / Boards / OPN Combinations</th>
            <th>Supported Host Interfaces</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>ot-soc-empty</p>
                <p>See <a href="https://github.com/SiliconLabsSoftware/sisdk-release/tree/sisdk-2026.6/openthread_app/ot-soc-empty/baremetal/README.md">README</a>.</p>
            </td>
            <td>This is a minimal OpenThread SoC application on a Full Thread Device (FTD), intended as a starting point for developing Thread applications in a baremetal environment.</td>
            <td>Full Thread Device</td>
            <td>SoC</td>
            <td>Series 2 devices</td>
            <td>None</td>
        </tr>
        <tr>
            <td>
                <p>ot-soc-empty-freertos</p>
                <p>See <a href="https://github.com/SiliconLabsSoftware/sisdk-release/tree/sisdk-2026.6/openthread_app/ot-soc-empty/freertos/README.md">README</a>.</p>
            </td>
            <td>This is a minimal OpenThread SoC application on a Full Thread Device (FTD), intended as a starting point for developing Thread applications in a FreeRTOS environment.</td>
            <td>Full Thread Device</td>
            <td>SoC</td>
            <td>All OPNs and boards that support Thread</td>
            <td>None</td>
        </tr>
    </tbody>
</table>

##### Modified Examples

- OpenThread example application titles and descriptions were standardized across the Simplicity SDK example catalog.
- The OpenThread BLE DMP samples were moved to multiprotocol.
- Watchdog support has been added to all sample apps.

##### Removed Examples

None.

##### Deprecated Examples

None.

#### Known Issues and Limitations

Issues in bold were added since the previous release.

<table>
    <thead>
        <tr>
            <th>ID</th>
            <th>Issue or Limitation Description</th>
            <th>GitHub / Salesforce Reference (if any)</th>
            <th>Workaround (if any)</th>
            <th>Affected Software Variants, Hardware, Modes, Host Interfaces</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>1559163</td>
            <td>When running in a Host / NCP architecture and repeatedly sending factory resets to the NCP, after approximately 15 times the host may stop sending messages to the NCP. This is intended behavior and the correct procedure when factory resetting the NCP is to also reset the host at the same time. This will be removed as a known issue in the next release.</td>
            <td>None</td>
            <td>The correct procedure when factory resetting the NCP is to also reset the host at the same time.</td>
            <td>Host / NCP architectures</td>
        </tr>
        <tr>
            <td>1625968</td>
            <td>Radio may not return to IDLE when CSL is in use in multi-protocol environments.</td>
            <td>00339903</td>
            <td>Contact customer support for workaround.</td>
            <td>Multi-protocol environments with CSL enabled</td>
        </tr>
        <tr>
            <td>1629296</td>
            <td><code>RxFrame::ProcessReceiveAesCcm()</code> can take more than 5 ms to process on Series 2 platforms.</td>
            <td>00340682</td>
            <td>Contact customer support for workaround.</td>
            <td>All Series 2 devices</td>
        </tr>
        <tr>
            <td><b>1634898</b></td>
            <td><b>Intermittent Ping Failures when using ot-ble-multi-instance app.</b></td>
            <td><b>None</b></td>
            <td><b>No known workaround.</b></td>
            <td><b>All devices</b></td>
        </tr>
        <tr>
            <td><b>1673927</b></td>
            <td><b>An intermittent Usage Fault may occur on Series 3 devices during in-band commissioning on the commissioner node when the application is built using the LLVM compiler.</b></td>
            <td><b>None</b></td>
            <td><b>No known workaround.</b></td>
            <td><b>Series 3 devices using LLVM-built</b></td>
        </tr>
        <tr>
            <td><b>1677527</b></td>
            <td><b>When OpenThread runs on the NCP (network co-processor), the border router does not forward DNS queries from Thread devices to upstream resolvers on the infrastructure link, so upstream DNS forwarding is not supported in NCP mode. Product flows where the border router must resolve arbitrary Internet names for Thread clients are out of scope for this release. If you need assistance implementing upstream DNS forwarding or integrating a host-side DNS forwarder into an NCP OTBR platform image, please contact Silicon Labs support.</b></td>
            <td><b>None</b></td>
            <td><b>Implement upstream DNS forwarding or integrate a host-side DNS forwarder.</b></td>
            <td><b>OTBR / NCP configurations</b></td>
        </tr>
    </tbody>
</table>

### OpenThread - 802.15.4 Mesh + BLE Multiprotocol Version 3.1.0 - Release Notes (Jun 23, 2026)

[**OpenThread Version 3.1.0**](.)

[**Simplicity SDK Version 2026.6.0**](/sisdk-release-notes/2026.6.0/sisdk-release-notes-overview)

This document provides release information for the 802.15.4 Mesh + BLE Multiprotocol SDK.

Click [here](https://github.com/SiliconLabs/simplicity_sdk/tags) for earlier releases.

#### Release Summary

[Key Features](#key-features) | [API Changes](#api-changes) | [Bug Fixes](#bug-fixes) | [Chip Enablement](#chip-enablement)

##### Key Features

###### Added in 3.1.0

- Concurrent Multiprotocol (CMP) co-processor sample applications running Zigbee NCP, OpenThread RCP, and Bluetooth RCP
- ZigbeeD + OTBR support on Android 15 – Alpha
- Standardized Multiprotocol sample application names, project layout, and Simplicity Studio display titles
- Reorganization of multiprotocol packages: file paths of components and applications have been re-organized to give multiprotocol packages a more cohesive file structure

##### API Changes

`sl_802154_radio_set_scheduler_priorities()` has been removed.

##### Bug Fixes

###### Fixed in 3.1.0

Targeted quality improvements and bug fixes across Zigbee + OpenThread + BLE Dynamic Multiprotocol and Concurrent Multiprotocol applications on Series 2 and Series 3 devices.

##### Chip Enablement

###### Added in 3.1.0

xG2B: Added Alpha software support for xG2B devices in Multiprotocol.

#### Key Features

[New Features](#new-features) | [Enhancements](#enhancements) | [Removed Features](#removed-features) | [Deprecated Features](#deprecated-features)

##### New Features

###### Added in 3.1.0

###### CMP + BLE-RCP sample applications (Zigbee NCP + OpenThread RCP + Bluetooth RCP) (heading level 7)

Added new Concurrent Multiprotocol (CMP) co-processor sample projects that run Zigbee NCP, OpenThread RCP, and Bluetooth RCP on one EFR device. The Bluetooth side exposes a BLE controller over CPC so a Linux host can use the Silicon Labs RCP with CPCd and a standard Bluetooth stack (i.e. BlueZ). Zigbee and OpenThread host applications use the same CPC multiplexer model as existing NCP / RCP designs.

**Variants**:

|Project|Host link|
|---|---|
|`mp-zb-ncp-ot-rcp-ble-rcp-uart`|UART (VCOM-style link; configurable baud / flow control)|
|`mp-zb-ncp-ot-rcp-ble-rcp-spi`|SPI|

**Details**:

- CPC multiplexes Zigbee NCP, OpenThread RCP, and BLE HCI over a single physical interface (UART or SPI) to the host.
- BLE is included as Bluetooth controller + HCI over CPC (`bluetooth_hci_cpc`), suitable for pairing with host-side CPC and Bluetooth middleware.
- Zigbee NCP behavior aligns with existing `zigbee_ncp_cpc`-based samples; optional NCP extensions remain supported as described in the project description.
- OpenThread is provided as RCP with `ot_ncp_cpc` for Spinel / CPC toward the host.
- Bluetooth Controller component configuration was added and updated for RCP applications, including the new `SL_BTCTRL_RTOS_LINK_LAYER_TASK_STACK_SIZE` parameter in the `.slcp` files (minimum value 1100 on Series 2, default 1400 on Series 3) to allow fine-tuning the BLE link-layer task stack size based on application requirements.

###### ZigbeeD + OTBR on Android 15 — Alpha (heading level 7)

Added Alpha support for Android 15 host development with Multiprotocol. The release includes ZigbeeD libraries and the source code needed to build the ZigbeeD and OpenThread Border Router (OTBR) host applications. The Android-15 host build uses NDK r27d and ships through dedicated container and packaging scripts.

##### Enhancements

###### Changed in 3.1.0

###### Standardized Multiprotocol sample application names (heading level 7)

Standardized Multiprotocol sample application names across Simplicity Studio labels, SLCP `project_name` values, filenames, and directory layout under `multiprotocol_app/`. The coordinated scheme groups examples by protocol combination and architecture, uses a common `mp-` prefix for all project names, and aligns display titles with the SiSDK / GSDK example-app naming effort.

Directory structure — examples are organized under `multiprotocol_app/` by protocol combination (`ot-ble/`, `zb-ble/`, `zb-ot/`, `zb-ot-ble/`), then by architecture (`soc/`, `ncp/`, `ncp-rcp/`, `rcp/`). UART / SPI transport variants share a subdirectory; minimal variants share a subdirectory with their base sample.

`project_name` / path renames (21 applications):

- **OpenThread + BLE (`ot-ble/`):** `ot-ble-dmp → mp-ot-ble-soc`; `ot-ble-dmp-no-buttons → mp-ot-ble-soc-minimal`; `ot-ble-dmp-tz-ns → mp-ot-ble-soc-tz`; `ot-ble-dmp-multi-instance → mp-ot-ble-soc-multi-instance`; `ot-ncp-ftd-ble-rcp → mp-ot-ncp-ble-rcp`.
- **Zigbee + BLE (`zb-ble/`):** `zigbee_ble_dynamic_multiprotocol_light → mp-zb-ble-soc-light` (plus `-minimal`, `-sed`, `-sed-minimal` variants); `zigbee_ble_direct_device_light → mp-zb-ble-soc-zdd-light`; `zigbee_ncp-ble_ncp-uart/spi → mp-zb-ble-ncp-uart/spi`.
- **Zigbee + OpenThread (`zb-ot/`):** `z3-light_ot-ftd_soc → mp-zb-ot-soc-light`; `zigbee_ncp-ot_rcp-uart/spi → mp-zb-ncp-ot-rcp-uart/spi`; `zigbee_ncp_ot_rcp_uart_gp_multi_rail → mp-zb-ncp-ot-rcp-uart-gp`.
- **Zigbee + OpenThread + BLE (`zb-ot-ble/`):** `zigbee_ncp-ot_rcp-ble_rcp-uart/spi → mp-zb-ncp-ot-rcp-ble-rcp-uart/spi`; `rcp-uart/spi-802154-blehci → mp-zb-ot-ble-rcp-uart/spi`.

Label updates — display titles now follow a consistent pattern: protocol names spelled out, architecture and transport where applicable, and explicit protocol-role coupling for mixed-role co-processor apps (e.g. "Zigbee NCP + OpenThread RCP - UART (CMP)" instead of legacy titles such as "Zigbee - NCP + OpenThread - RCP …" or "Multiprotocol (OpenThread+Zigbee+BLE) - RCP (SPI)"). DMP (Dynamic Multiprotocol) and CMP (Concurrent Multiprotocol) are noted in labels or descriptions where relevant.

Naming convention — all project names use the `mp-{protocols}-{arch}[-{transport}][-{usecase}][-{variant}]` pattern with hyphens throughout; protocols are ordered Zigbee, OpenThread, then BLE; mixed-role co-processor apps couple each protocol to its role (e.g. `mp-zb-ncp-ot-rcp-uart`).

###### Multiprotocol package reorganization (heading level 7)

The file paths of components and applications inside multiprotocol packages have been re-organized to give those packages a more cohesive file structure. This should not affect how the content is displayed in Simplicity Studio.

##### Removed Features

None.

##### Deprecated Features

None.

#### API Changes

[New APIs](#new-ap-is) | [Modified APIs](#modified-ap-is) | [Removed APIs](#removed-ap-is) | [Deprecated APIs](#deprecated-ap-is)

##### New APIs

None.

##### Modified APIs

None.

##### Removed APIs

###### Removed in 3.1.0

|Removed API Name|Was Deprecated?|
|---|---|
|`sl_802154_radio_set_scheduler_priorities`|Yes|

##### Deprecated APIs

None.

#### Bug Fixes

###### Fixed in 3.1.0

<table>
    <thead>
        <tr>
            <th>ID</th>
            <th>Issue Description</th>
            <th>GitHub / Salesforce Reference (if any)</th>
            <th>Affected Software Variants, Hardware, Modes, Host Interfaces</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>1569320</td>
            <td>Fixed an issue where CPCd could crash after a BLE scan with an active OT network in the <code>mp-ot-ncp-ble-rcp</code> application. Updated <code>SL_BTCTRL_RTOS_LINK_LAYER_TASK_STACK_SIZE</code> to 1100 for Series 2 devices. Note: RTOS stack sizes require tuning as needed.</td>
            <td>N/A</td>
            <td><code>mp-ot-ncp-ble-rcp</code> app on Series 2 devices</td>
        </tr>
        <tr>
            <td>1602054</td>
            <td>Disabled the serial task on the OT-NCP + BLE-RCP application so that <code>otbr-agent</code> can start correctly with the <code>mp-ot-ncp-ble-rcp</code> application.</td>
            <td>N/A</td>
            <td><code>mp-ot-ncp-ble-rcp</code> app</td>
        </tr>
        <tr>
            <td>1613155</td>
            <td>Fixed an issue where projects using the <code>zigbee_stack_unix</code> component and built with the "Copy all sources" option were missing certain header files, causing ZigbeeD <code>make</code> to fail with <code>sl_token_manager_api.h: No such file or directory</code>.</td>
            <td>SF: 00340128</td>
            <td>ZigbeeD host builds with Copy All Sources</td>
        </tr>
        <tr>
            <td>1619616</td>
            <td>Fixed an intermittent failure when performing Update Trust Center Link Key in multi-hop scenarios, where the Trust Center could fail to handle the first Request Key (Trust Center Link Key) of the joining devices.</td>
            <td>N/A</td>
            <td>Zigbee + BLE DMP large-topology deployments</td>
        </tr>
        <tr>
            <td>1628216</td>
            <td>Fixed an issue where the Zigbee libraries for Android 12 were not being included in projects. All host architectures that the Zigbee libraries are built for currently use the <code>toolchain_gcc</code> SLC component.</td>
            <td>N/A</td>
            <td>ZigbeeD sample on Android 12</td>
        </tr>
        <tr>
            <td>1639647</td>
            <td>Fixed a CPC issue where the HCI Bridge could fail to start properly after a host reconnection to the remote device when using the <code>mp-zb-ncp-ot-rcp-ble-rcp</code> application.</td>
            <td>N/A</td>
            <td>CMP <code>mp-zb-ncp-ot-rcp-ble-rcp</code> app on MG24</td>
        </tr>
        <tr>
            <td>1652601</td>
            <td>Fixed an IPC issue affecting throughput testing with large packets (127 bytes) on the Zigbee Direct Device Light (ZDDLight) sample application.</td>
            <td>N/A</td>
            <td>Zigbee Direct Device Light app on DMP / CMP configurations</td>
        </tr>
    </tbody>
</table>

#### Chip Enablement

###### Added in 3.1.0

xG2B: Added Alpha software support for xG2B devices in Multiprotocol.

#### Application Example Changes

[New Examples](#new-examples) | [Modified Examples](#modified-examples) | [Removed Examples](#removed-examples) | [Deprecated Examples](#deprecated-examples)

##### New Examples

###### Added in 3.1.0

<table>
    <thead>
        <tr>
            <th>Example Name</th>
            <th>Description</th>
            <th>Supported Software Variants (if applicable)</th>
            <th>Supported Modes</th>
            <th>Supported OPNs / Boards / OPN Combinations</th>
            <th>Supported Host Interfaces</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p><code>mp-zb-ncp-ot-rcp-ble-rcp-uart</code></p>
                <p><code>mp-zb-ncp-ot-rcp-ble-rcp-spi</code></p>
                <p>See readme in <code>multiprotocol_app/zb-ot-ble/ncp-rcp/</code></p>
            </td>
            <td>New Concurrent Multiprotocol (CMP) co-processor sample applications. Zigbee NCP, OpenThread RCP, and Bluetooth RCP run on one EFR device, multiplexed over CPC to a Linux host. Bluetooth is exposed as a BLE controller over CPC via <code>bluetooth_hci_cpc</code>, suitable for pairing with host-side CPC and a standard Bluetooth stack (i.e. BlueZ).</td>
            <td>N/A</td>
            <td>
                <p>NCP / RCP</p>
            </td>
            <td>
                <p>All supported parts with sufficient RAM (≥ 128 KB)</p>
            </td>
            <td>
                <ul>
                    <li>CPC over UART</li>
                    <li>CPC over SPI</li>
                </ul>
            </td>
        </tr>
    </tbody>
</table>

##### Modified Examples

###### Modified in 3.1.0

All Multiprotocol sample application project names, paths, and Simplicity Studio display titles have been standardized as part of the SiSDK / GSDK example-app naming effort. See the [Standardized Multiprotocol sample application names](#standardized-multiprotocol-sample-application-names) enhancement above for the full list of renames, the new directory layout under `multiprotocol_app/`, and the unified `mp-{protocols}-{arch}[-{transport}][-{usecase}][-{variant}]` naming convention. Existing projects upgrading to 3.1.0 must be regenerated to pick up the new paths and project names.

##### Removed Examples

None.

##### Deprecated Examples

None.

#### Known Issues and Limitations

<table>
    <thead>
        <tr>
            <th>ID</th>
            <th>Issue or Limitation Description</th>
            <th>GitHub / Salesforce Reference (if any)</th>
            <th>Workaround (if any)</th>
            <th>Affected Software Variants, Hardware, Modes, Host Interfaces</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>1209958</td>
            <td>The Zigbee + OpenThread + BLE RCP may stop working in endurance testing (~2 hours) on MG24 and MG21 devices with constant and concurrent traffic on all three stacks.</td>
            <td>N/A</td>
            <td>None</td>
            <td>Zigbee + OpenThread + BLE RCP app</td>
        </tr>
        <tr>
            <td>1385052</td>
            <td>Coex-enabled RCP may still occasionally transmit TX ACK after losing the Grant even when Acking is disabled and TX Abort is enabled.</td>
            <td>N/A</td>
            <td>This will be fixed in future releases.</td>
            <td>Coex-enabled RCP</td>
        </tr>
        <tr>
            <td>1385486</td>
            <td>TX from coex-enabled RCP may infrequently happen without the request after turning on the non-802.15.4 compliant MAC Holdoff coex option.</td>
            <td>N/A</td>
            <td>This will be fixed in future releases.</td>
            <td>Coex-enabled RCP</td>
        </tr>
        <tr>
            <td>1457086</td>
            <td>Coex-enabled EFR32MG24 RCP may sometimes output higher than expected grant denied counts in the PTA counters.</td>
            <td>N/A</td>
            <td>This will be fixed in future releases.</td>
            <td>Coex-enabled RCP</td>
        </tr>
        <tr>
            <td>1514786</td>
            <td>Wi-Fi coexistence on SiXG301 is currently not supported.<br>Other references: 1514794, 1515385.</td>
            <td>N/A</td>
            <td>This will be supported in future releases.</td>
            <td>SiXG301 devices</td>
        </tr>
        <tr>
            <td>1619497</td>
            <td>The sample app <code>mp-zb-ncp-ot-rcp-spi</code> over SPI may fail to form an OT network after a Zigbee connection is established.</td>
            <td>N/A</td>
            <td>None</td>
            <td>Series-3 xG301 devices with <code>mp-zb-ncp-ot-rcp-spi</code> app over SPI</td>
        </tr>
        <tr>
            <td>1639512</td>
            <td>In the Z3GatewayCPC host with the <code>mp-zb-ncp-ot-rcp-ble-rcp</code> use case, Z3GatewayCPC may crash if Zigbee and BLE protocols are under heavy load and OTBR is restarted.</td>
            <td>N/A</td>
            <td>This will be fixed in a future release.</td>
            <td>CMP <code>mp-zb-ncp-ot-rcp-ble-rcp</code> app on MG24 (BRD4187C / BRD4186C)</td>
        </tr>
        <tr>
            <td>1651620</td>
            <td>In Simplicity Studio 6, the Zigbee ARM64 component, along with other Zigbee and Platform components for building with ARM Linux host architectures, may not appear in the Software Components tab when generating zigbeed and other host projects. This may also prevent these projects from compiling at initial generation time, since these components contain the Zigbee libraries for a given architecture.</td>
            <td>N/A</td>
            <td>Check which target part the host project is using; if it says <code>None</code>, change it to an actual host target like <code>LINUX_32</code> or <code>LINUX_64</code> and the relevant components should become selectable.</td>
            <td>Simplicity Studio 6 ZigbeeD and other ARM Linux host projects</td>
        </tr>
        <tr>
            <td>1674274</td>
            <td>The <code>mp-zb-ot-soc-light</code> CMP application may fail to start Zigbee on a different channel after an OpenThread network is already running when built with HDR PHY, fast channel switching (FCS), and RX duty cycling (DC). This is a specific corner case; same-channel operation and the reverse startup order are not affected.</td>
            <td>N/A</td>
            <td>Start Zigbee first, or use the same channel for Zigbee and OpenThread.</td>
            <td>CMP <code>mp-zb-ot-soc-light</code> app with HDR PHY + FCS + DC on BRD4407A / SiXG301</td>
        </tr>
    </tbody>
</table>

## Getting Started

### Getting Started with Silicon Labs OpenThread Development

To get started with Silicon Labs OpenThread development, download the Simplicity Studio Development environment
as described in the Simplicity Studio User's Guide.

This will also prompt you to install the Simplicity SDK, which contains the OpenThread SDK. The Simplicity SDK combines Silicon Labs wireless software development kits (SDKs) and Platform into a single, integrated package. The Simplicity SDK is your primary tool for developing in the Silicon Labs IoT Software ecosystem. All of Silicon Labs' stacks are written in-house to provide a seamless experience from silicon to tools, allowing you to unlock powerful features with ease, including:

- Abstraction of complex requirements like multiprotocol and pre-certification
- Industry-leading ability to support a large number of nodes
- Ultra-low power consumption
- Strong network reliability

Silicon Labs also helps future-proof your devices with over-the-air software and security updates, helping to minimize maintenance cost and improve your end user product experience.

Once you have downloaded Simplicity Studio and the Simplicity SDK, detailed instructions for using the OpenThread examples and configuration tools are provided in the [**OpenThread SDK Quick-Start Guide**](/openthread/3.1.0/openthread-quick-start-guide).

Note: The recommended method to get started with the Simplicity SDK is to first install Simplicity Studio, which will set up your development environment and walk you through the Simplicity SDK installation. Alternatively, Simplicity SDK and other required tools may be installed manually from the [GitHub Simplicity SDK site](https://github.com/SiliconLabsSoftware/sisdk-release).

### Silicon Labs OpenThread Quick Start Guide

#### Silicon Labs OpenThread Quick-Start Guide

> **NOTE: This section replaces _QSG170: Silicon Labs OpenThread Quick-Start Guide_. Further updates to this quick start guide will be provided here**.

This document describes how to get started with OpenThread development using the Silicon Labs OpenThread software development kit (SDK) and Simplicity Studio with a compatible wireless starter kit.

##### Key Points

- About the Silicon Labs OpenThread SDK
- About Example Applications and Demos
- Getting started with development
- Next steps
- Development tools

#### Introduction

Google’s OpenThread is an open-source implementation of Thread. Google has released OpenThread to make the networking technology used in Google Nest products more broadly available to developers in order to accelerate the development of products for the connected home and commercial buildings.

With a narrow platform abstraction layer and a small memory footprint, OpenThread is highly portable. It supports system-on-chip (SoC), network co-processor (NCP), and radio co-processor (RCP) designs. OpenThread implements all features defined up to the Thread 1.4.1 specification. This specification defines an IPv6-based reliable, secure, and low-power wireless device-to-device communication protocol for home and commercial building applications.

This guide describes how to get started developing OpenThread applications using the Silicon Labs OpenThread SDK, delivered with the Simplicity SDK, Simplicity Studio (SSv6) and Simplicity Studio Extension for VS Code. SSv6 and Simplicity Studio Extension for VS Code include everything needed for IoT product development with Silicon Labs devices, including a resource and project launcher, software configuration tools, full IDE with GNU toolchain, and analysis tools. This document focuses on use and development in the Simplicity Studio environment. Alternatively, the Gecko SDK may be installed manually by downloading or cloning the latest from GitHub. See [https://github.com/SiliconLabs/gecko_sdk](https://github.com/SiliconLabs/gecko_sdk) for more information.

Our newest embedded software platform, the Simplicity Software Development Kit (SDK), is designed specifically for our Series 2 and Series 3 devices. The Simplicity SDK is a significant advancement in IoT development, providing our customers with a cohesive development environment for wireless innovation. It enables wireless developers to utilize advanced features and capabilities with the most recent Silicon Labs IoT devices. See [https://github.com/SiliconLabsSoftware/sisdk-release](https://github.com/SiliconLabsSoftware/sisdk-release) for more information.

Meanwhile, our Gecko Software Development Kit (GSDK) will continue to be available for users of our Series 0 and Series 1 devices. For additional information on Series 0 and Series 1 devices reference: Series 0 and series 1 EFM32/EZR32/EFR32 device (silabs.com).

##### About the Silicon Labs OpenThread SDK

The Silicon Labs OpenThread SDK is based on the Gecko Platform component-based design, where each component provides a specific function. Components are made up of a collection of source files and properties. The component-based design enables customization by adding, configuring, and removing components. The application developer can use SSv6’s Project Configurator and Component Editor to easily assemble the desired features by including those components that match the required functionality and by configuring the various properties associated with those components.

The Silicon Labs OpenThread SDK is based on the OpenThread stack available in GitHub but with a number of enhancements, including support of additional platforms and functionality, additional example applications, and additional metadata to allow for the seamless integration into SSv6.

**Platforms and functionality**: Silicon Labs has enhanced the OpenThread source code and the platform abstraction layer supporting the EFR32 platform in order to offer additional features provided by the Gecko Platform, such as:

- Wi-Fi coexistence
- Antenna diversity
- FreeRTOS support
- Non-Volatile Memory storage support (NVM3)
- Hardware acceleration with MbedTLS
- Multi-PAN Radio Co-Processor (RCP)
- Co-Processor Communication support (CPC)
- PSA Vault support
- Proprietary Sub-GHz
- Power manager support
- TrustZone support

**Example applications**: The example applications from GitHub are included in the SDK, along with new example applications showcasing additional functionality, such as OpenThread/Bluetooth Low Energy Dynamic Multiprotocol support or Sleepy Device behavior (see the sleepy device section in [Developing and Debugging Silicon Labs OpenThread Applications](https://docs.silabs.com/openthread/latest/openthread-developing-debugging-overview/) for more information). The examples are described in section [About Example Applications and Demos](02-about-example-applications-and-demos#about-example-applications-and-demos).

The Silicon Labs OpenThread SDK contains the complete OpenThread GitHub directory structure but does not use the makefile build system provided by the GitHub solution. Instead, the Silicon Labs OpenThread SDK uses the build system provided by SSv6. The GitHub makefile build options have been made available as component configuration options and are discussed in [Configuring the Project](03-getting-started-with-development#configuring-the-project). When using the Silicon Labs OpenThread SDK to build a Host-RCP or Host-NCP application (such as an OpenThread Border Router), make sure to use compatible co-processor and host code from the same SDK.

See the release notes for a description of the special features and the OpenThread stack version used for the release.

###### Fork Transparency

Silicon Labs maintains `openthread` and `ot-br-posix` as forks of their respective upstream projects. The changes Silicon Labs applies on top of the upstream commits are shipped as patch files with the `openthread_stack` package:

- `openthread_stack/README.md` — overview of the patch files and how to inspect or apply them
- `openthread_stack/openthread.patch` — diff between the upstream `openthread` base commit and the Silicon Labs fork
- `openthread_stack/ot-br-posix.patch` — diff between the upstream `ot-br-posix` base commit and the Silicon Labs fork
- `openthread_stack/upstream-commits` — the upstream base commits for both repositories
- `openthread_stack/CHANGELOG.md` — per-release summary of fork changes relative to upstream

The upstream commit SHAs are also listed in the Silicon Labs OpenThread Release Notes. These patch files are primarily relevant when building the OpenThread Border Router from source or maintaining an internal fork — see the [Border Router Host build guide](sld1037-using-sl-coprocessors-with-openthread-border-router-04-build-and-installation-instructions-for-the-border-router-host) for operational details.

###### Simplicity Studio (SSv6) and Simplicity Studio Extension for VS Code

SSv6 and Simplicity Studio Extension for VS Code are the core development environment designed to support the Silicon Labs IoT portfolio of system-on-chips (SoCs) and modules. They provide access to target device-specific web and SDK resources; software and hardware configuration tools; an integrated development environment (IDE) featuring industry-standard code editors, compilers and debuggers; and advanced, value-add tools for network analysis and code-correlated energy profiling.

SSv6 is designed to simplify developer workflow. It intelligently recognizes all Silicon Labs evaluation and development kit parts and, based on the selected development target, presents appropriate software development kits (SDKs) and other development resources.

The Silicon Labs OpenThread SDK is downloaded through SSv6. The GNU Compiler Collection (GCC) is provided with Simplicity Studio Extension. Other important development tools provided with SSv6 are introduced in section [Development Tools](05-development-tools#development-tools).

###### Gecko Bootloader

A bootloader is a program stored in reserved flash memory that can initialize a device, update firmware images, and possibly perform some integrity checks. Silicon Labs networking devices use bootloaders that perform firmware updates in two different modes: standalone (also called standalone bootloaders) and application (also called application bootloaders). An application bootloader performs a firmware image update by reprogramming the flash with an update image stored in internal or external memory. Silicon Labs recommends that you always flash a bootloader image along with your application, so that flash memory usage is appropriately allocated from the beginning. For more information about bootloaders see [Bootloader Fundamentals](/openthread/{build-docspace-version}/bootloader-fundamentals).

###### Gecko Platform

The Gecko Platform is a set of drivers and other lower layer features that interact directly with Silicon Labs chips and modules. Gecko Platform components include EMLIB, EMDRV, RAIL Library, NVM3, and MbedTLS. For more information about Gecko Platform, see release notes that can be found in SSv6’s Documentation tab, as well as online API documentation in [https://docs.silabs.com/](https://docs.silabs.com/).

##### Prerequisites

Before following the procedures in this guide you must have:

- Purchased one of the Wireless Gecko (EFR32) Portfolio Wireless Kits.
- Downloaded SSv6 and the Gecko SDK, which includes the Silicon Labs OpenThread SDK, and be generally familiar with the SSv6 Launcher perspective. SSv6 installation and getting started instructions along with a set of detailed references can be found in the online _Simplicity Studio 6 User’s Guide_, available on [https://docs.silabs.com/](https://docs.silabs.com/) and in the Learn & Support section on the Home page of SSv6.

For a compiler, use the version of the GNU ARM toolchain installed with the Silicon Labs OpenThread SDK. The IAR Embedded Workbench for ARM (IAR EWARM) Compiler is currently not supported with OpenThread.

##### Support

Access the Silicon Labs support portal at [https://www.silabs.com/support](https://www.silabs.com/support) through SSv6’s Welcome view under Learn and Support. Use the support portal to contact Customer Support for any questions you might have during the development process.

![Support](/openthread-quick-start-guide/0.2/images/sld867-image1.png)

##### Documentation

In addition to this site, documentation is also available through Simplicity Studio. It is filtered based on your selected part. Hardware-specific documentation can be accessed through links on the part OVERVIEW tab.

![Documentation](/openthread-quick-start-guide/0.2/images/sld867-image2.png)

SDK documentation and other references are available through the DOCUMENTATION tab. Filter with the Thread Technology Type checkbox to see documentation most closely related to the OpenThread SDK.

![Documentation](/openthread-quick-start-guide/0.2/images/sld867-image3.png)

Simplicity Studio v6 and its tools are documented in the online [Silicon Labs Developer Tools](https://docs.silabs.com/dev-tools).

![Documentation](/openthread-quick-start-guide/0.2/images/sld867-image4.png)

#### About Example Applications and Demos

Because starting application development from scratch is difficult, the Silicon Labs OpenThread SDK comes with a number of built-in example applications and demos covering the most frequent use cases and are preconfigured code designed to illustrate common application functions. Silicon Labs strongly recommends starting development from one of the example applications.

Like everything in Simplicity Studio, the examples and the demos shown on the EXAMPLE PROJECTS & DEMOS tab are filtered based on the part you have connected or selected.

##### Demos

Demos are prebuilt firmware images that are ready to download to a compatible device. The quickest way to find if a demo is available for your part is by adding the part or board information in the My Products view and then navigating to the EXAMPLE PROJECTS & DEMOS tab in the Launcher Perspective. Turn off the Example Projects filter. The Solution Examples filter is provided for future use.

![Demos](/openthread-quick-start-guide/0.2/images/sld867-image5.png)

A set of prebuilt demo applications are provided with the OpenThread SDK.

##### Software Examples

Since typically you will finish by flashing a compiled application image to a device, connect a device to your computer and select it in the Debug Adapters view. In the EXAMPLE PROJECTS & DEMOS tab on the Launcher perspective, filter the example projects based on the ‘Thread’ technology type and turn off **Demos**.

![Software Examples](/openthread-quick-start-guide/0.2/images/sld867-image6.png)

Most of the example applications provided with the Silicon Labs OpenThread SDK come in two variants, Full Thread Device (FTD) and Minimal Thread Device (MTD), and are geared towards System-on-Chip (SoC), Radio Co-Processor (RCP), and Network Co-Processor (NCP) application models.

In general, the example applications are simple and illustrate the following functionality:

- Testing with a CLI
- Radio Co-Processor (RCP) and Network Co-Processor (NCP)
- Sleepy device
- Dynamic multiprotocol
- Multi-PAN RCP
- Multi-PAN RCP and CPC over SPI/UART

**OpenThread – SoC CLI (FTD)**: A CLI application equivalent to the ot-cli-ftd application in the OpenThread GitHub repo to test the OpenThread stack on a Full Thread Device (FTD) for SoC designs. This application can be used to test the OpenThread stack against the Thread Test Harness for interoperability testing.

**OpenThread – SoC CLI (MTD)**: A CLI application equivalent to the ot-cli-mtd application in the OpenThread GitHub repo to test the OpenThread stack on a Minimal Thread Device (MTD) for SoC designs.

**OpenThread – RCP**: An OpenThread Radio Co-Processor application equivalent to the ot-rcp application in the OpenThread GitHub repo. While the application layer and the OpenThread core is on the host processor, this application only runs the minimal OpenThread controller on the 802.15.4 SoC. This RCP software example must be used with a compatible OpenThread Border Router. This can be built from the same Silicon Labs OpenThread SDK or deployed from a docker image. For more details, refer to [Using the Silicon Labs Co-processors with the OpenThread Border Router](/openthread/{build-docspace-version}/using-sl-coprocessors-with-openthread-border-router).

**OpenThread – NCP**: An OpenThread Network Co-Processor application equivalent to the ot-ncp (ot-ncp-ftd/otncp-mtd) applications in the OpenThread GitHub repo. Unlike RCP, NCP runs the full OpenThread stack on the co-processor, with the application layer on the host processor. The host communicates with the NCP using the Spinel protocol over UART or SPI. NCP can also be used with OpenThread Border Router. For more details, refer to [Using the Silicon Labs Co-processors with the OpenThread Border Router](/openthread/{build-docspace-version}/using-sl-coprocessors-with-openthread-border-router).

**OpenThread – SoC Sleepy Demo (FTD)**: An application to start and form a Thread network on a Full Thread Device (FTD) for the sleepy-demo. This application is used in conjunction with the **OpenThread – SoC Sleepy Demo (MTD)** app.

**OpenThread – SoC Sleepy Demo (MTD)**: An application to demonstrate Sleepy End Device (SED) behavior on a Minimal Thread Device (MTD) that attaches to a Thread network started by a node running the **OpenThread – SoC Sleepy Demo (FTD)** app. This application demonstrates power manager feature support and deep sleep (EM2) mode for EFR32.

**OpenThread – SoC Synchronized Sleepy Demo (SSED)**: An application to demonstrate Synchronized Sleepy End Device (SSED) behavior using Coordinated Sampled Listening (CSL) that attaches to a Thread network started by a node running the **OpenThread – SoC Sleepy Demo (FTD)** app. This application demonstrates power manager feature support and deep sleep (EM2) mode for EFR32.

**OpenThread BLE DMP – SoC Free RTOS**: An application to test Dynamic Multiprotocol (DMP) with OpenThread and Bluetooth running on FreeRTOS. For more details about this application, refer to [Dynamic Multiprotocol Development with Bluetooth and OpenThread in GSDK v3.x](/openthread/{build-docspace-version}/multiprotocol-dynamic-ble-ot-on-soc).

**OpenThread (MultiPan) – RCP (UART)**: A multipan 802.15.4 RCP (Radio Co-Processor) application. In this application, access to 802.15.4 is provided using SPINEL carried over the CPC (Co-Processor Communication) protocol using a UART connection.

**OpenThread (MultiPan) – RCP (SPI)**: A multipan 802.15.4 RCP (Radio Co-Processor) application. In this application, access to 802.15.4 is provided using SPINEL carried over the CPC (Co-Processor Communication) protocol using a SPI connection.

**OpenThread (MultiPan/BLE) – RCP (UART)**: A multiprotocol and multipan RCP (Radio Co-Processor) application. This application runs multipan 802.15.4 and the Bluetooth Link Layer using DMP (Dynamic MultiProtocol). Access to 802.15.4 is provided using the SPINEL protocol, and access to Bluetooth is provided using the standard Bluetooth HCI (Host Controller Interface) protocol. Both are carried over the CPC (Co-Processor Communication) protocol using a UART connection.

**OpenThread (MultiPan/BLE) – RCP (SPI)**: A multiprotocol and multipan RCP (Radio Co-Processor) application. This application runs multipan 802.15.4 and the Bluetooth Link Layer using DMP (Dynamic MultiProtocol). Access to 802.15.4 is provided using the SPINEL protocol, and access to Bluetooth is provided using the standard Bluetooth HCI (Host Controller Interface) protocol. Both are carried over the CPC (Co-Processor Communication) protocol using a SPI connection.

For more details about multi-PAN RCP support, refer to [Running Zigbee, OpenThread, and Bluetooth Concurrently on a Linux Host with a Multiprotocol RCP](/openthread/{build-docspace-version}/multiprotocol-solution-linux).

#### Getting Started with Development

This section assumes that you have downloaded SSv6, Simplicity Studio Extension for VS Code and the Silicon Labs OpenThread SDK, and are familiar with the features of the SSv6 Launcher perspective.

The mainboard should be connected.

> **Note**: For best performance in Simplicity Studio, be sure that the power switch on your mainboard is in the Advanced Energy Monitoring or "AEM" position, as shown in the following figure.

![mainboard](/openthread-quick-start-guide/0.2/images/sld867-image7.png)

In these instructions you will compile and load a simple **ot-cli-ftd** application on two nodes. Section [Creating a Network](#creating-a-network) describes how to use the examples to create a network. Section [Network Analyzer](05-development-tools#network-analyzer) describes how to use Network Analyzer to observe traffic across the network.

When working with an example application in Simplicity Studio, you will be executing the steps in the following order:

1. Create a project based on an example.
2. Configure the project.
3. Build the application image and flash it to your device.

These steps are described in detail in the following sections. These procedures are illustrated for a mainboard with an EFR32MG24.

> **Note**: Your SDK version may be later than the version shown in the figures.

##### Creating a Project Based on an Example

Simplicity Studio (SSv6) offers a variety of ways to begin a project using an example application. The online _Simplicity Studio 6 User's Guide_, available both through [Simplicity Studio 6 User's Guide](https://docs.silabs.com/ssv6ug/latest/ssv6ug-overview/) and in the Learn & Support section on the Home page of SSv6. This guide uses the **Start A New Project** on the Home page, because it takes you through all three of the Project Creation Dialogs.

1. The Example Project Selection dialog opens. Use the ‘Thread’ Technology Type and Keyword filters to search for a specific example, in this case **OpenThread – SoC CLI (FTD)**. Select it and click **CREATE**.  
   ![creating a Project Based on an Example](/openthread-quick-start-guide/0.2/images/sld867-image9.png)
2. Select Compatible Device and click **NEXT**.  
   ![creating a Project Based on an Example](/openthread-quick-start-guide/0.2/images/sld867-image8.png)
3. The Project Configuration dialog opens. Here you can rename your project, change the default project file location, and determine if you will link to or copy project files. Note that if you change any linked resource, it is changed for any other project that references it. Do not change the default VS Code GCC toolchain supported by OpenThread. Click **FINISH**.  
   ![rename your project](/openthread-quick-start-guide/0.2/images/sld867-image10.png)
4. The SSv6 Perspective opens with a description of the project on a readme tab. Click the <project>.slcp tab to open the Project Configurator’s OVERVIEW tab. See the online _Simplicity Studio 6 User’s Guide_ for details about the functionality available through the SSv6 perspective and the Project Configurator.  
   ![project](/openthread-quick-start-guide/0.2/images/sld867-image11.png)

##### Configuring the Project

Silicon Labs OpenThread applications are built on a Gecko Platform component structure. Click the SOFTWARE COMPONENTS tab to see a complete list of Component categories.

> **Note**:  All EFR32 parts have a unique RSSI offset. In addition, board, antenna and enclosure design can also impact RSSI. When creating a new project, install the **RAIL Utility, RSSI** component. This feature includes the default RSSI Offset Silicon Labs has measured for each part. This offset can be modified if necessary after RF testing of your complete product.

![Configuring the Project](/openthread-quick-start-guide/0.2/images/sld867-image12.png)

The project is configured by installing and uninstalling components, and configuring installed components. Installed components are checked. Click **Installed Components** to see a filtered list of components installed by the example application.

Configurable components have a gear symbol. Select a component to see information about it.

![Configuring the Project](/openthread-quick-start-guide/0.2/images/sld867-image13.png)

If the component is configurable, click **CONFIGURE** to open the Component Editor.

For example, in the Stack (FTD) component you can enable or disable various stack functions. These are equivalent to build options that can be specified to the makefile when building the sample applications in GitHub.

![CONFIGURE](/openthread-quick-start-guide/0.2/images/sld867-image14.png)

> **Note**: Starting with SiSDK 2026.6, logging configuration has been moved to a dedicated **OpenThread Logging** component, separate from the main stack features component. If you are migrating a project from an earlier SDK and had logging options configured in the features component, search for the **OpenThread Logging** component in the Project Configurator and configure it there instead.

Any changes made are autosaved, and project files are autogenerated.

![Automatically](/openthread-quick-start-guide/0.2/images/sld867-image15.png)

##### Building the Project

You can either compile and flash the application automatically, or manually compile it and then flash it.

###### Automatically Compile and Flash

1. You can automatically compile and flash the application to your connected development hardware in the Simplicity Studio Extension for VS Code. Click the **Debug** control.  
   ![Automatically](/openthread-quick-start-guide/0.2/images/sld867-image16.png)
2. Progress is displayed in the console.  
   ![Compile](/openthread-quick-start-guide/0.2/images/sld867-image17.png)
3. When building and flashing are complete a Debug perspective is displayed. Click the **Resume** control to start the application running on the device.  
   ![Compile](/openthread-quick-start-guide/0.2/images/sld867-image18.png)

Next to the Resume control are **Suspend**, **Disconnect**, **Reconnect**, and **stepping** controls. Click **Disconnect** when you are ready to exit Debug mode.

![Resume control](/openthread-quick-start-guide/0.2/images/sld867-image19.png)

###### Manually Compile and Flash

1. After you generate your project files, instead of clicking Debug in the Simplicity Studio Extension for VS Code, click the **Build** control (hammer icon).  
   ![Build](/openthread-quick-start-guide/0.2/images/sld867-image20.png)
2. You can load the binary image through Project Explorer view.

Locate the <project>.bin, .hex, or .s37 file in the compiler subdirectory.

![binary image](/openthread-quick-start-guide/0.2/images/sld867-image21.png)

Click **Flash**. The process will be displayed in the Output Console.

![Flash to Device](/openthread-quick-start-guide/0.2/images/sld867-image22.png)

##### Flashing a Bootloader

All Silicon Labs examples require that a bootloader be installed. By default, a new device is factory-programmed with a dummy bootloader. If you have a new device, haven’t cleared the bootloader region for your part or have a supported bootloader image already flashed on your device, skip this step and continue with the next section.

With Silicon Labs OpenThread, the bootloader serves to start the application code within the image you created and flashed in the previous procedure. Once you have installed a bootloader image, it remains installed until you erase the bootloader region for your device.

If you need to flash a bootloader, select a bootloader example and build and flash it as you would any application. See _UG266: Silicon Labs Gecko Bootloader User’s Guide for GSDK 3.2 and Lower_, [Silicon Labs Gecko Bootloader User’s Guide for GSDK 4.0 and Higher](/openthread/{build-docspace-version}/bootloader-user-guide-gsdk-4), or [Silicon Labs Gecko Bootloader User’s Guide for Series 3 and Higher](/openthread/{build-docspace-version}/bootloader-user-guide-series3-and-higher) for more information.

##### Creating a Network

Depending on the example application, you may be able to interact with it through your development environment’s Console interface using a CLI (command line interpreter). The console interface allows you to form a network and send data.

To launch the Console interface, double-click **vcom** in the Simplicity Studio Extension for VS Code perspective.

![Launch Console](/openthread-quick-start-guide/0.2/images/sld867-image23.png)

To create a two-node network using the ot-cli-ftd example, flash the **ot-cli-ftd.s37** application image on two separate nodes. Launch the console for the two nodes and execute the commands given below:

###### Form a network

On Device A, enter:

```C
> dataset init new
Done
> dataset commit active
Done
> ifconfig up
Done
> thread start
Done
```

The previous commands should initialize the node with new network parameters and form a network. Check the status of the node after a few seconds by executing the following command. The node should now be a leader.

```C
> state
leader
Done
```

Check the network parameters by running the following command:

```C
> dataset
dataset
Active Timestamp: 1
Channel: 18
Channel Mask: 07fff800
Ext PAN ID: 4e08d5129185b233
Mesh Local Prefix: fda6:83fa:7cc1:e809/64
Master Key: d5bf78c4bbd96605c6e55a32fa2c03ab
Network Name: OpenThread-21ec
PAN ID: 0x21ec
PSKc: 3be0de54e5d0f2cd5622de0716680588
Security Policy: 0, onrcb
Done
```

###### Attach a second node (device B) to the network

> **Note**: Depending on the network parameters generated by node A (as seen above), update the channel number and the master key in the following commands.

On Device B enter:

```C
 > dataset channel 18
Done
> dataset networkkey d5bf78c4bbd96605c6e55a32fa2c03ab
Done
> dataset commit active
Done
> ifconfig up
Done
> thread start
Done
```

The above commands should cause the node to attach to the network started by node A. Check the status of the node after a few seconds by executing the following command. The node should now be a child (which will eventually update to a router).

```C
> state
child
Done
```

###### Test network connectivity

For this step, compute Node B’s IP addresses and ping it from Node A as follows.

On device B enter:

```C
> ipaddr
ipaddr
fda6:83fa:7cc1:e809:0:ff:fe00:e000
fda6:83fa:7cc1:e809:8a39:6c2d:e3d:ea61
fe80:0:0:0:e443:b790:285b:2f5f
Done
```

On node A enter:

```C
> ping fe80:0:0:0:e443:b790:285b:2f5f
ping fe80:0:0:0:e443:b790:285b:2f5f
Done
>
> 16 bytes from fe80:0:0:0:e443:b790:285b:2f5f: icmp_seq=2 hlim=64 time=21ms
```

The following figure shows the console at the end of the procedure.

![console at the end](/openthread-quick-start-guide/0.2/images/sld867-image24.png)

#### Next Steps

Some next steps:

- Run additional CLI commands supported by the ot-cli-ftd application (as covered in Chapter 3) to explore OpenThread functionality. A complete list of OpenThread CLI commands can be found here: [https://github.com/openthread/openthread/blob/master/src/cli/README.md](https://github.com/openthread/openthread/blob/master/src/cli/README.md)
- Compile and flash a different example application and explore the functionality it provides. For example, sleepy end device (SED) behavior is supported by the sleepy-demo-mtd application. Some interesting details about this application have been covered in [Multi-Node Energy Profiler](05-development-tools#multi-node-energy-profiler).
- Explore configuring one of the example applications to meet your needs. Much of the software configuration can be done through components. Select a component to see more information about it and, check if it is configurable.

#### Development Tools

##### Simplicity Commander

Simplicity Commander is a simple flashing tool which can be used to flash firmware images, erase flash, lock and unlock debug access, and write-protect flash pages via the J-Link interface. Both GUI and CLI (Command Line Interface) are available. See [Simplicity Commander Reference Guide](https://docs.silabs.com/simplicity-commander/latest/simplicity-commander-start/) for more information.

![Simplicity Commander](/openthread-quick-start-guide/0.2/images/sld867-image25.png)

##### Pin Tool

Simplicity Studio offers a Pin tool that allows you to easily configure new peripherals or change the properties of existing ones. In the CONFIGURATION TOOLS tab, click Open on the Pin Tool card or double-click the file <project>.pintool in the Project Explorer view.

![project](/openthread-quick-start-guide/0.2/images/sld867-image26.png)

Double-click a software component to open the Component Editor and configure that function. Pin Tool does not autosave.

##### Multi-Node Energy Profiler

Multi-Node Energy profiler is an add-on tool used to easily measure the energy consumption of your device in runtime. You can find peak and average consumption, and check for sleep mode current.

To profile the current project, drop down the Profile as menu in the Simplicity IDE perspective and select Profile as / Simplicity Energy Profiler target. This automatically builds your project, uploads it to the device, and starts Energy Profiler.

![profile the current project](/openthread-quick-start-guide/0.2/images/sld867-image27.png)

For details on how to launch the energy profiler or switch between Simplicity IDE and Energy Profiler perspectives, refer to _UG343: Multi-Node Energy Profiler User’s Guide._

![details](/openthread-quick-start-guide/0.2/images/sld867-image28.png)

For a node running the ot-cli-ftd application, the Energy Profiler capture should be as shown in the following figure:

![Current consumption capture on a node operating in EM0 (active) mode](/openthread-quick-start-guide/0.2/images/sld867-image29.png)

Please note that the energy consumption results you observe may differ as the peripherals and their energy mode are highly dependent on the specific device.

###### Energy Profiler with Sleepy-demo Applications

While the energy profiler can be used on any running project, a good example is the **OpenThread – SoC Sleepy Demo (MTD)** application available with the Silicon Labs OpenThread SDK. It demonstrates Sleepy End Device (SED) behavior using the EFR32's low power EM2 mode.

For this exercise, you will create a two-node network using the **OpenThread – SoC Sleepy Demo (FTD)** and **OpenThread – SoC Sleepy Demo (MTD)** application and monitor the current consumption on the sleepy node.

1. To build the **OpenThread – SoC Sleepy Demo (FTD)** and **OpenThread – SoC Sleepy Demo (MTD)** example applications, either flash the precompiled demo applications as described in section [Demos](02-about-example-applications-and-demos#demos) or build your own application as described in section [Getting Started with Development](03-getting-started-with-development#getting-started-with-development).
2. Since the **OpenThread – SoC Sleepy Demo (FTD)** and **OpenThread – SoC Sleepy Demo (MTD)** applications are already configured with default network parameters, flashing the applications should automatically form a 2-node network (without specifying any additional commands). Accordingly,  
   - On Node A, flash the **OpenThread – SoC Sleepy Demo (FTD)** application. This should cause Node A to form a network.  
   - On Node B, flash the **OpenThread – SoC Sleepy Demo (MTD)** application. This should cause node B to attach as a Minimal Thread Device (MTD) to the network formed by Node A.  
   - Try testing network connectivity by sending a ping from Node A(FTD) to Node B(MTD) as described in section [Creating a Network](03-getting-started-with-development#creating-a-network).
3. Launch the Energy Profiler, start a capture on Node B(MTD), and press button 0 Node B’s mainboard.  
   ![Energy Profiler](/openthread-quick-start-guide/0.2/images/sld867-image30.png)

Pressing button 0 on the Node B(MTD) mainboard will cause the node to toggle between operating as a Minimal End Device (MED) and a Sleepy End Device (SED) with the RX off when idle. As the node uses EFR32's low power EM2 (deep sleep) mode, current consumption will significantly drop (from mA to µA range) as seen in the capture below.

![Current consumption capture on a node operating in EM2 (deep sleep) mode](/openthread-quick-start-guide/0.2/images/sld867-image31.png)

The regular spikes seen every 2 seconds (i.e. the sleep period) represents the tiny interval during which the node briefly wakes up to send a data poll message to its parent, after which it goes back to sleep.

Profiling can be paused by clicking Play. For further analysis, click one of the peaks, and zoom in with time axis (x-axis) and the current axis (y-axis). Note that the maximum consumption may now be greater than it appeared on the diagram before you zoomed in. This is because in zoomed-out mode, the displayed values are averaged. If you need exact values, always zoom in. To measure average consumption, simply click and drag your mouse over a time interval. A new window appears in the upper right corner showing consumption information for the given interval.

![Energy profiler capture on a node operating in EM2 (deep sleep) mode for a given interval](/openthread-quick-start-guide/0.2/images/sld867-image32.png)

Multi-node Energy Profiler is also able to simultaneously measure the consumption of multiple devices. To start measuring a new device click the Quick Access menu (upper left corner), select **Start Energy Capture** and choose the **Multi-Node View**. To stop measuring, click the Quick Access menu, and select **End/Save session**.

![Multi-node Energy Profiler](/openthread-quick-start-guide/0.2/images/sld867-image33.png)

To learn more about how to use this tool, see _UG343: Multi-Node Energy Profiler User’s Guide_.

##### Network Analyzer

Silicon Labs Network Analyzer is a packet capture and debugging tool that can be used to debug connectivity between wireless nodes running OpenThread stack on EFR32 platform. It significantly accelerates the network and application development process with graphical views of network traffic, activity, and duration. See the online _Simplicity Network Analyzer_, available on [https://docs.silabs.com/dev-tools](https://docs.silabs.com/dev-tools/) for a guide to Network Analyzer.

The Packet Trace application captures the packets directly from the Packet Trace Interface (PTI) available on the Wireless Gecko SoCs and modules. It therefore provides a more accurate capture of the packets compared to air-based capture.

To capture OpenThread packets using the Silicon Labs Network Analyzer:

1. In the Preferences window (**Simplicity Studio**  **>** **Preferences)** , under **Network Analyzer > Decoding > Stack Versions**, select the OpenThread stack option.  
   ![OpenThread stack option](/openthread-quick-start-guide/0.2/images/sld867-image34.png)
2. In the same Preferences window (**Simplicity Studio**  **>** **Preferences)**, under **Network Analyzer > Decoding > Security Keys**, verify the correct Network Master Key used by the Thread network has been added to the list of known keys.
3. In the Debug Adapters view, right-click the device that is running on a Thread network and start capture.

![Thread network and start capture](/openthread-quick-start-guide/0.2/images/sld867-image35.png)

You should now be able to see the Thread traffic as shown below. Clicking the packets to see more details about its contents in the Event Detail view (on the right).

![Thread Traffic Capture with Packet Trace](/openthread-quick-start-guide/0.2/images/sld867-image36.png)

##### Crash Handler

When debugging crashes it may be useful to have detailed information about core registers, information about the C stack, and reset information. This data can be automatically collected and printed on the next boot-up by including the OT Crash Handler in your project.

![Crash Handler Component](/openthread-quick-start-guide/0.2/images/sld867-image37.png)

The crash data is printed using the OpenThread logging system, meaning the output location of the crash data can be controlled by modifying the value of the OPENTHREAD_CONFIG_LOG_OUTPUT configuration.

**Example: Crash dump**

[C] Platform------: Reset info: 0xa (FLT)
[C] Platform------: Extended Reset info: 0xA04 (USG)

[C] Platform------: Thread mode using main stack (20000000 to 20001200), SP = 20001040

[C] Platform------: 1072 bytes used (23%) in main stack (out of 4608 bytes total)

[C] Platform------: No interrupts active

[C] Platform------: Reset cause: Usage Fault

[C] Platform------: Instruction address: 8009272

[C] Platform------: CFSR.UNALIGNED: attempted an unaligned memory access

[C] Platform------: R0 = 0x0804f518, R1 = 0x00000000, R2 = 0x00000000, R3 = 0xdeadb000

[C] Platform------: R4 = 0x00000000, R5 = 0x200012f8, R6 = 0x080092a9, R7 = 0x00000008

[C] Platform------: R8 = 0x20007923, R9 = 0xffffffff, R10 = 0x1fff1200, R11 = 0x00000000

[C] Platform------: R12 = 0x20007e2f, R13(LR) = 0xfffffff9, MSP = 0x20001040, PSP = 0x00000000

[C] Platform------: PC = 0x08009272, xPSR = 0x69000000, MSP used = 0x00000430, PSP used = 0x00000000

[C] Platform------: CSTACK bottom  = 0x20000000, ICSR = 0x00000806, SHCSR = 0x000f0008, INT_ACTIVE0 = 0x00000000

[C] Platform------: INT_ACTIVE1 = 0x00000000, CFSR = 0x778fc80f, HFSR = 0x01000000, DFSR = 0x00000000

[C] Platform------: MMAR/BFAR = 0x00000000, AFSR = 0xe000ed34, Ret0 = 0x00000000, Ret1 = 0x080092b5

[C] Platform------: Ret2 = 0x08038d4f, Ret3 = 0x08016f17, Ret4 = 0x08028cff, Ret5 = 0x08010973

[C] Platform------: Dat0 = 0x08038c71, Dat1 = 0x7f33fbff,

##### OpenThread Diagnostics Support

OpenThread diagnostic capabilities can be enabled for source and certification library project builds by installing the Diagnostics component.

![Diagnostics Component](/openthread-quick-start-guide/0.2/images/sld867-image38.png)

This component will enable the standard OpenThread Factory Diagnostics Module in the project and implement the `diag` command in the OpenThread CLI. See [https://github.com/openthread/openthread/blob/main/src/core/diags/README.md](https://github.com/openthread/openthread/blob/main/src/core/diags/README.md) for a detailed reference on the diagnostics CLI commands.

## Fundamentals

### OpenThread Fundamentals

Silicon Labs has produced a series of documents on topics that provide useful background for Silicon Labs OpenThread developers. This page also includes resources for OpenThread.

#### Silicon Labs OpenThread Resources

- [**Thread Fundamentals**](/openthread/3.1.0/thread-fundamentals): For those new to OpenThread, includes a brief background on the emergence of Thread, this provides a technology overview and describes some key features of Thread to consider when implementing a Thread solution.
- [**Wireless Networking Fundamentals**](/openthread/3.1.0/wireless-networking-application-development-fundamentals): For those new to wireless networking, this introduces some fundamental concepts of wireless networking.

#### Additional OpenThread Resources

##### Documentation

Generic OpenThread end-user documentation and guides are located at [openthread.io](https://openthread.io/). Here you can:

- Learn more about OpenThread features and enhancements
- Use OpenThread in your products
- Learn how to build and configure a Thread network
- Port OpenThread to a new platform
- Build an application on top of OpenThread
- Certify a product using OpenThread

**Note**: For users in China, end-user documentation is available at [openthread.google.cn](https://openthread.google.cn).

##### Contributing

We would love for you to contribute to OpenThread and help make it even better than it is today! See the [Contributing Guidelines](https://github.com/openthread/openthread/blob/master/CONTRIBUTING.md) for more information.

Contributors are required to abide by OpenThread [Code of Conduct](https://github.com/openthread/openthread/blob/master/CODE_OF_CONDUCT.md) and [Coding Conventions and Style Guide](https://github.com/openthread/openthread/blob/master/STYLE_GUIDE.md).

##### Versioning

OpenThread follows the Semantic Versioning guidelines 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.

##### License

OpenThread is released under the BSD 3-Clause license. See the LICENSE file for more information.

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.

##### Need help?

There are numerous avenues for OpenThread support:

- Bugs and feature requests — submit to the [Issue Tracker](https://github.com/openthread/openthread/issues)
- Stack Overflow — [post questions using the openthread tag](https://stackoverflow.com/questions/tagged/openthread)
- Google Groups — [discussion and announcements at openthread-users](https://groups.google.com/forum/#!forum/openthread-users) This is the recommended place for users to discuss OpenThread and interact directly with the OpenThread team.

### Thread Fundamentals

#### Thread Fundamentals

**NOTE: This section replaces _UG103.11: Thread Fundamentals_. Further updates to this user guide will be provided here.**

This guide includes a brief background on the emergence of Thread, provides a technology overview, and describes some key features of Thread to consider when implementing a Thread solution.

Silicon Labs’ _Fundamentals_ series covers topics that project managers, application designers, and developers should understand before beginning to work on an embedded networking solution using Silicon Labs chips, networking stacks such as EmberZNet PRO or Silicon Labs Bluetooth, and associated development tools. These guides can be used as a starting place for anyone needing an introduction to developing wireless networking applications, or who is new to the Silicon Labs development environment.

#### Introduction

##### Silicon Labs and the Internet of Things

Internet Protocol version 4 (IPv4) was defined in 1981 in RFC 791, [DARPA Internet Program Protocol Specification](https://datatracker.ietf.org/doc/html/rfc791) ("RFC" stands for "Request for Comments."). Using 32-bit (4-byte) addressing, IPv4 provided 232 unique addresses for devices on the internet, a total of approximately 4.3 billion addresses. However, as the number of users and devices grew exponentially, it was clear that the number of IPv4 addresses would be exhausted and there was a need for a new version of the IP. Hence the development of IPv6 in the 1990s and its intention to replace IPv4. With 128-bit (16-byte) addressing, IPv6 allows for 2128 addresses, more than 7.9x1028 addresses than IPv4 ([http://en.wikipedia.org/wiki/IPv6](http://en.wikipedia.org/wiki/IPv6)).

The challenge for companies in the embedded industry like Silicon Labs is to address this technology migration and more importantly the demands of customers as we move to an ever-connected world of devices in the home and commercial space, what is often refer-red to as the Internet of Things (IoT). At a high level the goals of IoT for Silicon Labs are to:

- Connect all the devices in the home and commercial space with best-in-class networking, whether with Zigbee PRO, Thread, Blue-tooth, or other emerging standards.
- Leverage the company's expertise in energy-friendly microcontrollers.
- Enhance established low-power, mixed-signal chips.
- Provide low-cost bridging to existing Ethernet and Wi-Fi devices.
- Enable cloud services and connectivity to smartphones and tablets that will promote ease of use and a common user experience for customers.

Achieving all of these goals will increase adoption rates and user acceptance for IoT devices.

##### Thread Group

[Thread Group](https://www.threadgroup.org/) was launched on July 15, 2014. Silicon Labs was a founding company along with six other companies. Thread Group is a market education group that offers product certification and promotes the use of Thread-enabled device-to-device (D2D) and machine-to-machine (M2M) products. Membership in Thread Group is open.

Thread Specification 1.4.0 may be downloaded after submitting a request here: [Thread Specification Request Form](https://www.threadgroup.org/ThreadSpec). The latest 1.4.1-draft Thread specification is only available to Thread members (as of the release of this document).

##### What is Thread?

Thread is a secure, wireless mesh networking protocol. The Thread stack is an open standard that is built upon a collection of existing Institute for Electrical and Electronics Engineers (IEEE) and Internet Engineering Task Force (IETF) standards, rather than a whole new standard (see the following figure).

![Thread Stack Overview](/thread-fundamentals/0.2/images/thread-stack-overview.jpg)

##### Thread General Characteristics

The Thread stack supports IPv6 addresses and provides low-cost bridging to other IP networks and is optimized for low-power / battery-backed operation, and wireless device-to-device communication. The Thread stack is designed specifically for Connected Home and commercial applications where IP-based networking is desired and a variety of application layers can be used on the stack.

These are the general characteristics of the Thread stack:

- **Simple network installation, start-up, and operation**: The Thread stack supports several network topologies. Installation is simple using a smartphone, tablet, or computer. Product installation codes are used to ensure only authorized devices can join the network. The simple protocols for forming and joining networks allow systems to self-configure and fix routing problems as they occur.
- **Secure**: Devices do not join the network unless authorized and all communications are encrypted and secure. Security is provided at the network layer and can be at the application layer. All Thread networks are encrypted using a smartphone-era authentication scheme and Advanced Encryption Standard (AES) encryption. The security used in Thread networks is stronger than other wireless standards the Thread Group has evaluated.
- **Small and large home networks**: Home networks vary from several to hundreds of devices. The networking layer is designed to optimize the network operation based on the expected use.
- **Large commercial networks**: For larger commercial installations, a single Thread network is not sufficient to cover all the application, system and network requirements. The Thread Domain model allows scalability for up to 10,000s of Thread devices in a single deployment, using a combination of different connectivity technologies (Thread, Ethernet, Wi-fi, and so on).
- **Bi-directional service discovery and connectivity**: Multicast and broadcast are inefficient on wireless mesh networks. For off-mesh communication, Thread provides a service registry where devices can register their presence and services, and clients can use unicast queries to discover the registered services.
- **Range**: Typical devices provide sufficient range to cover a normal home. Readily available designs with power amplifiers extend the range substantially. A distributed spread spectrum is used at the Physical Layer (PHY) to be more immune to interference. For commercial installations, the Thread Domain model allows multiple Thread networks to communicate with each other over a backbone, thus extending the range to cover many mesh subnets.
- **No single point of failure**: The Thread stack is designed to provide secure and reliable operations even with the failure or loss of individual devices.  Thread devices can also incorporate IPv6-based links such as Wi-Fi and Ethernet into the topology to reduce the probability of multiple Thread partitions. This way, they can utilize the higher throughput, channel capacity, and coverage of those infrastructure links, while still supporting low-power devices.
- **Low power**: Devices efficiently communicate to deliver an enhanced user experience with years of expected life under normal battery conditions. Devices can typically operate for several years on AA type batteries using suitable duty cycles.
- **Cost-effective**: Compatible chipsets and software stacks from multiple vendors are priced for mass deployment and designed from the ground up to have extremely low-power consumption.

##### OpenThread

OpenThread released by Google is an open-source implementation of Thread®. Google has released OpenThread to make the networking technology used in Google Nest products more broadly available to developers, in order to accelerate the development of products for the connected home and commercial buildings.

With a narrow platform abstraction layer and a small memory footprint, OpenThread is highly portable. It supports system-on-chip (SoC), network co-processor (NCP), and radio co-processor (RCP) designs.

OpenThread defines an IPv6-based reliable, secure, and low-power wireless device-to-device communication protocol for home and commercial building applications. It implements all features defined up to draft Thread Specification 1.4.1 (as of the release of this document).

Silicon Labs has implemented an OpenThread-based protocol tailored to work with Silicon Labs hardware. This protocol is available on GitHub and also as a software development kit (SDK) installed with Simplicity Studio. The SDK is a fully tested snapshot of the GitHub source. It supports a broader range of hardware than does the GitHub version, and includes documentation and example applications not available on GitHub.

#### Thread Technology Overview

##### IEEE 802.15.4

The IEEE 802.15.4-2006 specification is a standard for wireless communication that defines the wireless Medium Access Control (MAC) and Physical (PHY) layers operating at 250 kbps in the 2.4 GHz band, with a roadmap to subGHz bands ([IEEE 802.15.4-2006 Specification](http://standards.ieee.org/findstds/standard/802.15.4-2006.html)). Designed with low power in mind, 802.15.4 is suitable for applications usually involving a large number of nodes.

The 802.15.4 MAC layer is used for basic message handling and congestion control. This MAC layer includes a Carrier Sense Multiple Access (CSMA) mechanism for devices to listen for a clear channel, as well as a link layer to handle retries and acknowledgement of messages for reliable communications between adjacent devices. MAC layer encryption is used on messages based on keys established and configured by the higher layers of the software stack. The network layer builds on these underlying mechanisms to provide reliable end-to-end communications in the network.

Starting with Thread Specification 1.2, several optimizations from the IEEE 802.15.4-2015 specification have been implemented to make Thread networks more robust, responsive and scalable:

- **Enhanced Frame Pending**: Improves the battery life and responsiveness of a sleepy end device (SED), by reducing the number of messages a SED can send over the air. Any data packet that arrives from an SED (not just data requests) can be acknowledged with the presence of upcoming pending data.
- **Enhanced Keepalive**: Reduces the amount of traffic required to maintain a link between a SED and a parent by treating any data message as a keepalive network transmission.
- **Coordinated Sampled Listening (CSL)**: This [IEEE 802.15.4-2015 Specification](https://standards.ieee.org/standard/802_15_4-2015.html) feature allows for better synchronization between a SED and a parent by scheduling synchronized transmit/receive periods without periodic data requests. This enables low-power devices that have low link latency and a network with a lower chance of message collisions.
- **Enhanced ACK Probing**: This [IEEE 802.15.4-2015 Specification](https://standards.ieee.org/standard/802_15_4-2015.html) feature allows an initiator granular control over link metric queries while saving energy by reusing regular data traffic patterns rather than separate probe messages.

##### Thread Network Architecture

###### Residential Architecture

Users communicate with a residential Thread network from their own device (smartphone, tablet, or computer) via Wi-Fi on their Home Area Network (HAN) or using a cloud-based application. The following figure illustrates the key device types in the Thread network architecture.

![Thread Network Architecture](/thread-fundamentals/0.2/images/thread-network-architecture.png)

The following device types are included in a Thread network, starting from the Wi-Fi network:

- **Border Routers** provide connectivity from the 802.15.4 network to adjacent networks on other physical layers (Wi-Fi, Ethernet, etc.). Border Routers provide services for devices within the 802.15.4 network, including routing services and service discovery for off network operations. There may be one or more Border Routers in a Thread network.
- A **Leader**, in a Thread network partition, manages a registry of assigned router IDs and accepts requests from router-eligible end devices (REEDs) to become routers. The Leader decides which should be routers, and the Leader, like all routers in a Thread network, can also have device-end children. The Leader also assigns and manages router addresses using CoAP (Constrained Application Protocol). However, all information contained in the Leader is present in the other Thread Routers. So, if the Leader fails or loses connectivity with the Thread network, another Thread Router is elected, and takes over as Leader without user intervention.
- **Thread Routers** provide routing services to network devices. Thread Routers also provide joining and security services for devices trying to join the network. Thread Routers are not designed to sleep and can downgrade their functionality and become REEDs.
- **REEDs** can become a Thread Router or a Leader, but not necessarily a Border Router that has special properties, such as multiple interfaces. Because of the network topology or other conditions, REEDs do not act as routers. REEDs do not relay messages or provide joining or security services for other devices in the network. The network manages and promotes router-eligible devices to routers if necessary, without user interaction.
- **End devices** that are not router-eligible can be either FEDs (full end devices) or MEDs (minimal end devices). MEDs do not need to explicitly synchronize with their parent to communicate.
- **Sleepy end devices** (SEDs) communicate only through their Thread Router parent and cannot relay messages for other devices.
- **Synchronized Sleepy End Devices** (SSEDs) are a class of Sleepy End Devices that use CSL from IEEE 802.15.4-2015 to maintain a synchronized schedule with a parent, avoiding the use of regular data requests.

###### Commercial Architecture

The Thread Commercial model takes the key device types for a residential network and adds new concepts. Users communicate with a commercial network through devices (smartphone, tablet, or computer) via Wi-Fi or through their enterprise network. The following figure illustrates a commercial network topology.

![Commercial Network Topology](/thread-fundamentals/0.2/images/commercial-network-topology.png)

The concepts are:

- The **Thread Domain model** supports seamless integration of multiple Thread Networks as well as seamless interface to non-Thread IPv6 networks. The main benefit of the Thread Domain is that devices are to some extent flexible to join any available Thread Network configured with a common Thread Domain, which reduces the need for manual network planning or costly manual reconfigurations when network size or data volume are scaled up.
- **Backbone Border Routers** (BBRs) are a class of Border Router in the commercial space which facilitate Thread Domain synchronization of multiple network segments and allow large scope multicast propagation into and out of each single mesh in a Thread Domain. A Thread network that is part of a larger domain must have at least one "Primary" BBR and can have multiple "Secondary" BBRs for fail-safe redundancy. The BBRs communicate with each other over a backbone which connects all the Thread networks.
- A **Backbone Link** is a non-Thread IPv6 link to which a BBR connects using an external interface used to implement the Thread Backbone Link Protocol (TBLP) to synchronize with other BBRs.
- Thread Devices in a commercial implementation are configured using Thread Domains and **Domain Unique Addresses** (DUAs). A device’s DUA never changes over its lifetime of being part of a Thread domain. This facilitates migration across different Thread networks in a single domain and makes sure that the respective BBRs facilitate routing across multiple Thread networks.

These concepts are illustrated in the following figure:

![Thread Domain Model](/thread-fundamentals/0.2/images/thread-domain-model.png)

##### No Single Point of Failure

The Thread stack is designed to not have a single point of failure. While there are a number of devices in the system that perform special functions, Thread is designed so they can be replaced without impacting the ongoing operation of the network or devices. For example, a sleepy end device requires a parent for communications, so this parent represents a single point of failure for its communications. However, the sleepy end device can and will select another parent if its parent is unavailable. This transition should not be visible to the user.

While the system is designed for no single point of failure, under certain topologies there will be individual devices that do not have backup capabilities. For example, in a system with a single Border Router, if the Border Router loses power, there is no means to switch to an alternative Border Router.  In this scenario, a reconfiguration of the Border Router must take place.

Starting with Thread Specification 1.3.0, Border Routers sharing an infrastructure link can facilitate no-single point of failure across a different medium (such as Wi-Fi or Ethernet) by utilizing a Thread Radio Encapsulation Link (TREL). With this feature, the probability of Thread partitions forming across links is reduced.

#### IP Stack Fundamentals

##### Addressing

Devices in the Thread stack support IPv6 addressing architecture as defined in RFC 4291 ([https://tools.ietf.org/html/rfc4291](https://tools.ietf.org/html/rfc4291): _IP Version 6 Addressing Architecture_). Devices support a Unique Local Address (ULA), a Domain Unique Address (DUA) in a Thread domain model, and one or more Global Unicast Address (GUA) addresses based on their available resources.

The high-order bits of an IPv6 address specify the network and the rest specify particular addresses in that network. Thus, all the addresses in one network have the same first N bits. Those first N bits are called the "prefix". The "/64" indicates that this is an address with a 64-bit prefix. The device starting the network picks a /64 prefix that is then used throughout the network. The prefix is a ULA ([https://tools.ietf.org/html/rfc4193](https://tools.ietf.org/html/rfc4193): _Unique Local IPv6 Unicast Addresses)_. The network may also have one or more Border Router(s) that each may or may not have a /64 that can then be used to generate a ULA or GUA. The device in the network uses its EUI-64 (64-bit Extended Unique Identifier) address to derive its interface identifier as defined in Section 6 of RFC 4944 ([https://tools.ietf.org/html/rfc4944](https://tools.ietf.org/html/rfc4944): _Transmission of IPv6 Packets over IEEE 802.15.4 Networks_ ). The device will support a link local IPv6 address configured from the EUI-64 of the node as an interface identifier with the well-known link local prefix FE80::0/64 as defined in RFC 4862 ([https://tools.ietf.org/html/rfc4862](https://tools.ietf.org/html/rfc4862): _IPv6 Stateless Address Autoconfiguration)_ and RFC 4944.

The devices also support appropriate multicast addresses. This includes link-local all node multicast, link local all router multicast, solicited node multicast, and a mesh local multicast.  With the presence of a backbone border router in a domain model, devices can also support higher scope multicast addresses if they register for them.

Each device joining the network is assigned a 2-byte short address as per the IEEE 802.15.4-2006 specification. For routers, this address is assigned using the high bits in the address field. Children are then assigned a short address using their parent’s high bits and the appropriate lower bits for their address. This allows any other device in the network to understand the child’s routing location by using the high bits of its address field.

##### 6LoWPAN

6LoWPAN stands for "IPv6 Over Low Power Wireless Personal Networks." The main goal of 6LoWPAN is to transmit and receive IPv6 packets over 802.15.4 links. In doing so it has to accommodate for the 802.15.4 maximum frame size sent over the air. In Ethernet links, a packet with the size of the IPv6 Maximum Transmission Unit (MTU) (1280 bytes) can be easily sent as one frame over the link. In the case of 802.15.4, 6LoWPAN acts as an adaptation layer between the IPv6 networking layer and the 802.15.4 link layer. It solves the issue of transmitting an IPv6 MTU by fragmenting the IPv6 packet at the sender and reassembling it at the receiver.

6LoWPAN also provides a compression mechanism that reduces the IPv6 header sizes sent over the air and thus reduces transmission overhead. The fewer bits that are sent over the air, the less energy is consumed by the device. Thread makes full use of these mechanisms to efficiently transmit packets over the 802.15.4 network. RFC 4944 ([](https://tools.ietf.org/html/rfc4944)) and RFC 6282 ([](https://tools.ietf.org/html/rfc6282)) describe in detail the methods by which fragmentation and header compression are accomplished.

##### Link Layer Forwarding

Another important feature of the 6LoWPAN layer is link layer packet forwarding. This provides a very efficient and low overhead mechanism for forwarding multi hop packets in a mesh network. Thread uses IP layer routing with link layer packet forwarding.

Thread uses the link layer forwarding to forward packets based on the IP routing table. In order to accomplish this, the 6LoWPAN mesh header is used in each multi-hop packet (see the following figure).

![Mesh Header Format](/thread-fundamentals/0.2/images/mesh-header-format.jpg)

In Thread, the 6LoWPAN layer fills the Mesh Header information with the originator 16-bit short address and final destination 16-bit source address. The transmitter looks up the next hop 16-bit short address in the Routing Table, and then sends the 6LoWPAN frame to the next hop 16-bit short address as destination. The next hop device receives the packet, looks up the next hop in the Routing Table / Neighbor Table, decrements the hop count in the 6LoWPAN Mesh Header, and then sends the packet to the next hop or final destination 16-bit short address as destination.

##### 6LoWPAN Encapsulation

6LoWPAN packets are constructed on the same principle as IPv6 packets and contain stacked headers for each added functionality. Each 6LoWPAN header is preceded by a dispatch value that identifies the type of header (see the following figure).

![General Format of a 6LoWPAN Packet](/thread-fundamentals/0.2/images/general-format-of-a-6lowpan-packet.jpg)

Thread uses the following types of 6LoWPAN headers:

- Mesh Header (used for link layer forwarding)
- Fragmentation Header (used for fragmenting the IPv6 packet into several 6LoWPAN packets)
- Header Compression Header (used for IPv6 headers compression)

The 6LoWPAN specification mandates that if more than one header is present, they must appear in the order mentioned above.

The following are examples of 6LoWPAN packets sent over the air.

In the following figure, the 6LoWPAN payload is composed of the compressed IPv6 header and the rest of the IPv6 payload.

![6LoWPAN Packet Containing IPv6 Payload with Compressed IPv6 Header](/thread-fundamentals/0.2/images/6lowpan-packet-containing-ipv6-payload-with-compressed-ipv6-header.jpg)

In the following figure, the 6LoWPAN payload contains the IPv6 header and part of the IPv6 payload.

![6LoWPAN Packet Containing Mesh Header, a Fragmentation Header, and a Compression Header](/thread-fundamentals/0.2/images/6lowpan-packet-containing-mesh-header-a-fragmentation-header-and-a-compression-header.jpg)

The rest of the payload will be transmitted in subsequent packets per the format in the following figure.

![6LoWPAN Subsequent Fragment](/thread-fundamentals/0.2/images/6lowpan-subsequent-fragment.jpg)

##### ICMP

Thread devices support the Internet Control Message Protocol version 6 (ICMPv6) protocol as defined in RFC 4443, [Internet Control Message Protocol (ICMPv6) for the Internet Protocol Version 6 (IPv6) Specification](https://datatracker.ietf.org/doc/html/rfc4443). They also support the echo request and echo reply messages.

##### UDP

The Thread stack supports User Datagram Protocol (UDP) as defined in RFC 768, [User Datagram Protocol](https://datatracker.ietf.org/doc/html/rfc768).

##### TCP

The Thread stack supports a Transport Control Protocol (TCP) variant called "TCPlp" (TCP Low Power) (See [usenix-NSDI20](https://www.usenix.org/system/files/nsdi20-paper-kumar.pdf)). A Thread-compliant device implements the TCP initiator and listener roles as described in:

- RFC 793, [Transmission Control Protocol](https://datatracker.ietf.org/doc/html/rfc793)
- RFC 1122, [Requirements for Internet Hosts](https://datatracker.ietf.org/doc/html/rfc1122)
- Thread Specification 1.3.0 and higher: Existing TCP implementations are typically not tuned to work optimally over wireless mesh networks and with the limited 802.15.4 frame sizes. Therefore, the specification defines those elements and parameter values required for an efficient TCP implementation over Thread Networks (see Thread Specification 1.3.0, section 6.2 TCP).

##### SRP

Service Registration Protocol (SRP) as defined in [Service Registration Protocol for DNS-Based Service Discovery](https://datatracker.ietf.org/doc/html/draft-ietf-dnssd-srp)is utilized on Thread devices starting with Thread Specification 1.3.0. There must exist a Service Registry, maintained by a border router. SRP clients on the mesh network can register to offer various services. An SRP server accepts DNS-based discovery queries and additionally offers public key cryptography for security, along with other minor enhancements to better support constrained clients.

#### Network Topology

##### Network Address and Devices

The Thread stack supports full mesh connectivity between all routers in the network. The actual topology is based on the number of routers in the network. If there is only one router, then the network forms a star. If there is more than one router then a mesh is automatically formed (see [Thread Technology Overview](./02-thread-technology-overview)).

##### Mesh Networks

Embedded mesh networks make radio systems more reliable by allowing radios to relay messages for other radios. For example, if a node cannot send a message directly to another node, the embedded mesh network relays the message through one or more intermediary nodes. As discussed in section [Routing and Network Connectivity](./05-routing-and-network-connectivity), all router nodes in the Thread stack maintain routes and connectivity with each other so the mesh is constantly maintained and connected. There is a limit of 64 router addresses in the Thread network, but they cannot all be used at once. This allows time for the addresses of deleted devices to be reused.

In a mesh network, the sleepy end devices or router-eligible devices do not route for other devices. These devices send messages to a parent that is a router. This parent router handles the routing operations for its child devices.

#### Routing and Network Connectivity

The Thread network has up to 32 active routers that use next-hop routing for messages based on the routing table. The routing table is maintained by the Thread stack to ensure all routers have connectivity and up-to-date paths for any other router in the network. All routers exchange with other routers their cost of routing to other routers in the network in a compressed format using Mesh Link Establishment (MLE).

##### MLE Messages

Mesh Link Establishment (MLE) messages are used to establish and configure secure radio links, detect neighboring devices, and maintain routing costs between devices in the network. MLE operates below the routing layer and uses one hop link local unicasts and multicasts between routers.

MLE messages are used to identify, configure, and secure links to neighboring devices as the topology and physical environment change. MLE is also used to distribute configuration values that are shared across the network such as the channel and Personal Area Network (PAN) ID. These messages can be forwarded with simple flooding as specified by MPL ([Multicast Protocol for Low power and Lossy Networks (MPL)](https://tools.ietf.org/html/draft-ietf-roll-trickle-mcast-11)).

MLE messages also ensure asymmetric link costs are considered when establishing routing costs between two devices. Asymmetric link costs are common in 802.15.4 networks. To ensure two-way messaging is reliable, it is important to consider bidirectional link costs.

##### Route Discovery and Repair

On-demand route discovery is commonly used in low-power 802.15.4 networks. However, on-demand route discovery is costly in terms of network overhead and bandwidth because devices broadcast route discovery requests through the network. In the Thread stack, all routers exchange one-hop MLE packets containing cost information to all other routers in the network. All routers have up-to-date path cost information to any other router in the network so on-demand route discovery is not required. If a route is no longer usable, the routers can select the next most suitable route to the destination.

Routing to child devices is done by looking at the high bits of the child’s address to determine the parent router address. Once the device knows the parent router, it knows the path cost information and next hop routing information for that device.

As route cost or the network topology change, the changes propagate through the network using the MLE single-hop messages. Routing cost is based on bidirectional link quality between two devices. The link quality in each direction is based on the link margin on incoming messages from that neighboring device. This incoming Received Signal Strength Indicator (RSSI) is mapped to a link quality from 0 to 3. A value of 0 means unknown cost.

When a router receives a new MLE message from a neighbor, either it already has a neighbor table entry for the device or one is added. The MLE message contains the incoming cost from the neighbor, so this is updated in the router’s neighbor table. The MLE message also contains updated routing information for other routers which is updated in the routing table.

The number of active routers is limited to the amount of routing and cost information that can be contained in a single 802.15.4 packet. This limit is currently 32 routers.

##### Routing

Devices use normal IP routing to forward packets. A routing table is populated with network addresses and the appropriate next hop.

Distance vector routing is used to get routes to addresses that are on the local network. When routing on the local network, the upper six bits of this 16-bit address define the router destination. This routing parent is then responsible for forwarding to the final destination based on the remainder of the 16-bit address.

For off network routing, a Border Router notifies the Router Leader of the particular prefixes it serves and distributes this information as network data within the MLE packets. The network data includes prefix data, which is the prefix itself, the 6LoWPAN context, the Border Routers, and the Stateless Address Autoconfiguration (SLAAC) or DHCPv6 server for that prefix. If a device is to configure an address using that prefix, it contacts the appropriate SLAAC or DHCP server for this address. The network data also includes a list of routing servers that are the 16-bit addresses of the default Border Routers.

Additionally, in a commercial space with a Thread Domain model, a Backbone Border Router notifies the router leader of the Domain Unique Prefix it serves, to indicate that this mesh is part of the larger Thread domain. The network data for this includes prefix data, 6LoWPAN context, and the border router ALOC. There are no SLAAC or DHCPv6 flags set for this prefix set, however the address assignment follows the stateless model. Additionally, there are also service and server TLVs indicating the "backbone" service capability of this border router. Duplicate address detection capability over the backbone exists for any device that registers its Domain Unique Address (DUA) with the BBR. A device’s DUA never changes over its lifetime of being part of a Thread domain. This facilitates migration across different Thread networks in a single domain and makes sure that the respective BBRs facilitate routing across multiple Thread networks. Over the backbone, standard IPv6 routing technologies such as IPv6 Neighbor Discovery (NS/NA as per RFC 4861) and Multicast Listener Discovery (MLDv2 as per RFC 3810) are used.

A Leader is designated to keep track of router-eligible devices becoming routers or allowing routers to downgrade to router-eligible devices. This Leader also assigns and manages the router addresses using CoAP. However, all information contained in this Leader is also periodically advertised to the other routers. If the Leader goes off the network, another router is elected, and takes over as Leader without user intervention.

Border Routers are responsible for handling 6LoWPAN compression or expansion and addressing to off network devices. Backbone Border Routers are responsible for handling MPL with IP-in-IP encapsulation and decapsulation for larger scope multicasts going into and out of the mesh.

For more information on Border Routers, see [Using the Silicon Labs Co-processors with the OpenThread Border Router](/openthread/3.1.0/using-sl-coprocessors-with-openthread-border-router).

##### Retries and Acknowledgements

While UDP messaging is used in the Thread stack, reliable message delivery is required and completed by these lightweight mechanisms:

- MAC-level retries–each device uses MAC acknowledgements from the next hop and will retry a message at the MAC layer if the MAC ACK message is not received.
- Application-layer retries– the application layer can determine if message reliability is a critical parameter. If so, an end-to-end acknowledgement and retry protocol can be used, such as CoAP retries.

#### Joining and Network Operation

Thread allows two joining methods:

- Share commissioning information directly to a device using an out-of-band method. This allows steering the device to the proper network using this information.
- Establish a commissioning session between a joining device and a commissioning application on a smartphone, tablet, or the web.
- For a commercial network with a Thread domain model, an Autonomous Enrollment process without user intervention that provisions operational certificates on joiners after authentication is specified by Thread Specification 1.2. The operational certificate encodes the domain information for the device and allows secure Network Master Key provision. This model requires a registrar or Thread Registrar Interface (TRI) on a backbone border router and facilitates communication with an external authority (MASA) using the ANIMA/BRSKI/EST protocols. A network that supports this commissioning model is called a CCM network.

For more information on commissioning Thread networks, see [Device Commissioning](./11-device-commissioning).

The frequently used 802.15.4 method of joining with the permit joining flag in the beacon payload is not used in Thread networks. This method is most commonly used for push button type joining where there is no user interface or out-of-band channel to devices. This method has issues with device steering in situations where there are multiple networks available and it can also pose security risks.

In Thread networks, all joining is user-initiated. After joining, a security authentication is completed at the application level with a commissioning device. This security authentication is discussed in [Security](./09-security).

Devices join a network as either a sleepy end device, end device (MED or FED), or a REED. Only after a REED has joined and learned the network configuration can it potentially request to become a Thread Router. Upon joining, a device is provided a 16-bit short address based on its parent. If a router-eligible device becomes a Thread Router, it is assigned a router address by the Leader. Duplicate address detection for Thread Routers is ensured by the centralized router address distribution mechanism which resides on the Leader. The parent is responsible for avoiding duplicate addresses for host devices because it assigns addresses to them upon joining.

##### Network Discovery

Network discovery is used by a joining device to determine what 802.15.4 networks are within radio range. The device scans all channels, issues an MLE discovery request on each channel, and waits for MLE discovery responses. The 802.15.4 MLE discovery response contains a payload with network parameters, including the network Service Set Identifier (SSID), extended PAN ID, and other values that indicate if the network is accepting new members and whether it supports native commissioning.

Network discovery is not required if the device has been commissioned onto the network because it knows the channel and extended PAN ID for the network. These devices then attach to the network using the commissioning material provided.

##### MLE Data

Once a device has attached to a network, there is a variety of information required for it to participate in the network. MLE provides services for a device to send a unicast to a neighboring device to request network parameters and update link costs to neighbors. When a new device joins, it also conducts a challenge response to set security frame counters as discussed in [Security](./09-security).

All devices support transmission and reception of MLE link configuration messages. This includes "link request", "link accept", and "link accept and request" messages.

The MLE exchange is used to configure or exchange the following information:

- The 16-bit short and 64-bit EUI 64 long address of neighboring devices
- Device capabilities information, including if it is a sleepy end device and the sleep cycle of the device
- Neighbor link costs if a Thread Router
- Security material and frame counters between devices
- Routing costs to all other Thread Routers in the network
- Collecting and distributing Link Metrics about various link configuration values

> **Note**:  MLE messages are encrypted except during the initial node bootstrapping operations when the new device has not obtained the security material.

##### CoAP

Constrained Application Protocol (CoAP) as defined in RFC 7252 ([https://tools.ietf.org/html/rfc7252](https://tools.ietf.org/html/rfc7252): _The Constrained Application Protocol (CoAP)_) is a specialized transport protocol for use with constrained nodes and low-power networks. CoAP provides a request/response interaction model between application endpoints, supports built-in discovery of services and resources, and includes key concepts of the web such as URLs. CoAP is used in Thread to configure mesh-local addresses and multicast addresses required by devices. Additionally, CoAP is also used for management messages such as to get and set diagnostic information and other network data on active Thread routers.

##### DHCPv6

DHCPv6 as defined in RFC 3315 is used as a client-server protocol to manage configuration of devices within the network. DHCPv6 uses UDP to request data from a DHCP server ([https://www.ietf.org/rfc/rfc3315.txt](https://www.ietf.org/rfc/rfc3315.txt): _Dynamic Host Configuration Protocol for IPv6 (DHCPv6)_).

The DHCPv6 service is used for configuration of:

- Network addresses
- Multicast addresses required by devices

Because short addresses are assigned from the server using DHCPv6, duplicate address detection is not required. DHCPv6 is also used by Border Routers that are assigning addresses based on the prefix they provide.

##### SLAAC

SLAAC (Stateless Address Autoconfiguration) as defined in RFC 4862 ([https://tools.ietf.org/html/rfc4862](https://tools.ietf.org/html/rfc4862): _IPv6 Stateless Address Autoconfiguration_) is a method in which a Border Router assigns a prefix, and then the last 64 bits of its address are derived by the router. The IPv6 stateless autoconfiguration mechanism requires no manual configuration of hosts, minimal (if any) configuration of routers, and no additional servers. The stateless mechanism allows a host to generate its own addresses using a combination of locally available information and information advertised by routers.

##### SRP

Service Registration Protocol (SRP) as defined in [Service Registration Protocol for DNS-Based Service Discovery](https://datatracker.ietf.org/doc/html/draft-ietf-dnssd-srp) is utilized on Thread devices starting with Thread Specification 1.3.0. There must exist a Service Registry, maintained by a border router. SRP clients on the mesh network can register to offer various services. An SRP server accepts DNS-based discovery queries and additionally offers public key cryptography for security, along with other minor enhancements to better support constrained clients.

#### Management

##### ICMP

All devices support Internet Control Message Protocol for IPv6 (ICMPv6) error messages, as well as the echo request and echo reply messages.

##### Device Management

The application layer on a device has access to a set of device management and diagnostics information that can be used locally or collected and sent to other management devices.

At the 802.15.4 PHY and MAC layers, the device provides the following information to the management layer:

- EUI 64 address
- 16-bit short address
- Capability information
- PAN ID
- Packets sent and received
- Octets sent and received
- Packets dropped on transmit or receive
- Security errors
- Number of MAC retries

##### Network Management

The network layer on the device also provides information on management and diagnostics that can be used locally or sent to other management devices. The network layer provides the IPv6 address list, the neighbor and child table, and the routing table.

#### Persistent Data

Devices operating in the field may be reset accidentally or on purpose for a variety of reasons. Devices that have been reset need to restart network operations without user intervention. For this to be done successfully, non-volatile storage must store the following information:

- Network information (such as PAN ID)
- Security material
- Addressing information from the network to form the IPv6 addresses for the devices

#### Security

Thread networks are wireless networks that need to be secured against over-the-air (OTA) attacks. They are also connected to the internet and therefore must be secured against internet attacks. Many of the applications being developed for Thread will serve a broad range of uses that require long periods of unattended operation and low power consumption. As a result, the security of Thread networks is critical.

Thread utilizes a network-wide key that is used at the Media Access Layer (MAC) for encryption. This key is used for standard IEEE 802.15.4-2006 authentication and encryption. IEEE 802.15.4-2006 security protects the Thread network from over-the-air attacks originating from outside the network. Compromise of any individual node could potentially reveal the network-wide key. As a result, it is usually not the only form of security used within the Thread network. Each node in the Thread network exchanges frame counters with its neighbors via an MLE handshake. These frame counters help protect against replay attacks. (For more information on MLE, see the Thread Specification.) Thread allows the application to use any internet security protocol for end-to-end communication.

Nodes obfuscate both their mesh-wide IP address interfaces and their MAC extended IDs by randomizing them. The stock EUI64 assigned to the node is used as a source address only during the initial join phase. Once a node is joined to a network, the node uses as its source either an address based on its two-byte node ID, or one of its randomized addresses mentioned above. The EUI64 is not used as a source address once the node is joined to a network.

Network management also needs to be secure. A Thread network management application can be run on any internet-connected device. If that device is not itself a member of a Thread network, it must first establish a secure Datagram Transport Layer Security (DTLS) connection with a Thread Border Router. Every Thread network has a management passphrase that is used in establishing this connection. Once a management application has been connected to the Thread network, new devices can be added to the network.

##### 802.15.4 Security

The IEEE 802.15.4-2006 specification describes wireless and media access protocols for PANs and HANs. These protocols are intended for implementation on dedicated radio devices such as those available from Silicon Labs. IEEE 802.15.4-2006 supports a variety of applications, many of which are security-sensitive. For example, consider the case of an alarm system application that monitors building occupancy. If the network is not secure and an intruder gains access to the network, messages could be broadcast to create a false alarm, modify an existing alarm, or silence a legitimate alarm. Each of these situations poses significant risks to the building occupants.

Many applications require confidentiality and most also need integrity protection. 802-15.4-2006 addresses these requirements by using a link layer security protocol with four basic security services:

- Access control
- Message integrity
- Message confidentiality
- Replay protection

The replay protection provided by IEEE 802.15.4-2006 is only partial. Thread delivers additional security using MLE handshakes between nodes discussed above to complete the replay protection.

##### Secure Network Management

Network management also needs to be secure. A Thread network management application can be run on any internet-connected device. There are two parts to the security:

- Over-the-air security which 802.15.4 takes care of. Thread implements 802.15.4-2006 level 5 security.
- CCM networks: If a device is not itself a member of a CCM network, it must establish a connection with a backbone border router in order to obtain its operational certificate to establish itself as part of the Thread domain.
- Non-CCM networks: Internet security: If a device is not itself a member of a Thread network, it must first establish a secure Datagram Transit Layer Security (DTLS) connection with a Thread Border Router. Every Thread network has a management passphrase that is used for establishing secure connections between external management devices and Border Routers. Once a management application has been connected to the Thread network, new devices can be added to the network.

#### Border Router

A Thread Border Router is a device that connects a Thread wireless network to other IP-based networks (such as Wi-Fi or Ethernet) in the outside world via a local home or enterprise network. Unlike gateways in other wireless solutions, it is fully transparent to the transport and application protocols that reside above the network layer. As a result, applications can communicate securely from end-to-end without any application layer translation.

A Thread Border Router minimally supports the following functions:

- End-to-end IP connectivity via routing between Thread devices and other external IP networks.
- External Thread Commissioning (for example, a mobile phone) to authenticate and join a Thread device to a Thread network.

There can be multiple Border Routers in a network, eliminating a "single point of failure" in the event one of them malfunctions. The Border Router enables every Thread device to directly connect to global cloud services, when enterprise networks run IPv6 and IPv4, or IPv4 only.

##### Border Router Features for Off-Mesh Communication

Thread can be immediately implemented in current working situations, before partial or full transition to IPv6 and Thread enables IPv4 backwards compatibility using Network Address Translation (NAT). NAT64 translates IPv6 packets to IPv4, and NAT64 translates IPv4 packets to IPv6. A Thread Border Router can function as an IPv4 host on the wide area network (WAN), capable of obtaining an IPv4 interface and router address. It can acquire an address using DHCP from an IPv4 address pool. The Thread Border Router may also implement Port Control Protocol (PCP) to control how incoming IPv4 packets are translated and forwarded and support static mappings. Most of the IPv4 to IPv6 (and vice versa) translations can be handled by the Thread Border Router, with minimal changes needed to an existing network.

Additionally, Thread Border Routers support bidirectional IPv6 connectivity with IPv6 neighbor discovery, router advertisements, multicast discovery, and packet forwarding.

##### Thread over Infrastructure

Thread Networks automatically organize into separate Thread Network Partitions when there is no connectivity between two or more sets of devices. Thread Partitions allow devices to maintain communication with other devices in the same Thread Partition but not with Thread Devices in other partitions.

Thread over Infrastructure allows Thread devices to incorporate IP-based link technologies (for example, Wi-Fi and Ethernet) into the Thread topology. These additional Thread links over other link technologies reduce the probability of occurrence of multiple Thread Network Partitions, while backward-compatibility with existing Thread 1.1 and 1.2 devices is guaranteed. These benefits are obtained for any network topology that includes at least two Border Routers connected via a shared adjacent infrastructure link.

For more information, refer to Thread Specification 1.4.0, Chapter 15 (Thread over Infrastructure).

##### OpenThread Border Router

OpenThread's implementation of a Border Router is called an OpenThread Border Router (OTBR). It supports a mesh interface using Radio Co-Processor (RCP) or a Network Co-Processor (NCP) models. Silicon Labs provides an implementation (supported on the Raspberry Pi) and source code as part of the Simplicity SDK. For more information, see [Using the Silicon Labs Co-processors with the OpenThread Border Router](/openthread/3.1.0/using-sl-coprocessors-with-openthread-border-router).

Documentation on the setup and architecture of the OTBR is available at [https://openthread.io/guides/border-router](https://openthread.io/guides/border-router).

#### Device Commissioning

Thread devices are commissioned on Thread networks in different ways as described in the following subsections.

##### Traditional Thread Commissioning

For the network commissioning of smaller networks (Thread Specification 1.1.1 or higher), installers can use the Thread commissioning app provided as a free resource for Android and iOS devices. This app can be used to easily add new devices to the network or reconfigure existing devices.

Thread uses the Mesh Commissioning Protocol (MeshCoP) to securely authenticate, commission, and join new, untrusted radio devices to a mesh network. Thread networks comprise an autonomous self-configuring mesh of devices with IEEE 802.15.4 interfaces and a link-level security layer that requires each device in the mesh to possess the current, shared secret master key.

The commissioning process begins when a Commissioner Candidate, typically a mobile phone connected via WiFi, discovers the Thread network through one of its Border Routers. Border Routers advertise their availability to Commissioners using whatever service location is appropriate. The discovery mechanism must provide a Commissioner Candidate with both a communication path and the network name, because the network name is used later as a cryptographic salt for establishing the Commissioning Session.

The Commissioner Candidate, after having discovered the Thread network of interest, securely connects to it using the Commissioning Credential (a human-selected passphrase for use in authenticating). The Commissioner Authentication step establishes a secure client/server socket connection between the Commissioner Candidate and a Border Router via DTLS. This secure session is known as a Commissioning Session. The Commissioning Session uses the assigned UDP port number advertised during the discovery phase. This port is known as the Commissioner Port. The credential used to establish the Commissioning Session is known as the Pre-Shared Key for the Commissioner (PSKc).

The Commissioner Candidate then registers its identity with its Border Router. The Leader responds by either accepting or rejecting the Border Router as a viable forwarder to the Commissioner. Upon acceptance, the Leader updates its internal state to track the active Commissioner, and the Border Router then sends a confirmation message to the Commissioner Candidate informing the device that it is now the Commissioner.

When there is an authorized Commissioner associated with the Thread Network, it becomes possible to join eligible Thread Devices. These are known as Joiners before they become part of the Thread network. The Joiner first creates a DTLS connection with the Commissioner to exchange commissioning material. It then uses the commissioning material to attach to the Thread network. The node is considered part of the network only after these two steps are complete. It may then participate in the join process for future nodes. All of these steps confirm that the correct device has joined the correct Thread network, and that the Thread network itself is secure against wireless and internet attacks. For more information on the Mesh Commissioning Protocol, see the Thread specification.

##### Enhanced Commissioning with Commercial Extensions in Thread 1.2

Thread Specification 1.2 and its Commercial Extensions now allow for much larger scale networks, such as the ones required in office buildings, public buildings, hotels, or other types of industrial or commercial buildings. Due to better support of subnetting, Thread Specification 1.2 more easily allows thousands of devices in one deployment, which can be configured manually, autonomously, and via advanced remote commissioning features.

The Commercial Extensions in Thread 1.2 allow for large-scale authentication, network joining, subnet roaming, and operation based on trusted identities in an Enterprise Domain. To enable reliable authentication of devices and verification of authorization information, a system installer can set up an Enterprise Certificate Authority to simplify deploying a large-scale network. This allows the installer to set up and maintain the network without direct access to the individual devices and without any direct interaction with these devices, by means of an automated enrollment process called Autonomous Enrollment. Unlike Thread 1.1, where device passcode pairing is used for authentication, the Commercial Extensions in Thread 1.2 will support a more scalable certificate-based form of authentication. An enterprise network can have one or more Thread Domains and each Thread Domain can be set up to integrate multiple Thread networks.

#### Application Layer

Thread is a wireless mesh networking stack that is responsible for routing messages between different devices in the Thread network described in [Thread Technology Overview](./02-thread-technology-overview). The following figure illustrates the layers in the Thread protocol.

![Thread Protocol Layers](/thread-fundamentals/0.2/images/thread-protocol-layers.jpg)

A standard definition of an application layer is an ["abstraction layer that specifies the shared protocols and interface methods used by hosts in a communications network"](https://en.wikipedia.org/wiki/Application_layer). Put more simply, an application layer is the "language of devices," for example, how a switch talks to a light bulb. Using these definitions, an application layer does not exist in Thread. Customers build the application layer based on the capabilities in the Thread stack and their own requirements. Although Thread does not supply an application layer, it does provide basic application services:

- UDP messaging  
  UDP offers a way to send messages using a 16-bit port number and an IPv6 address. UDP is a simpler protocol than TCP and has less connection overhead (for example, UDP does not implement keep-alive messages). As a result, UDP enables a faster, higher throughput of messages and reduces the overall power budget of an application. UDP also has a smaller code space than TCP, which leaves more available flash on the chip for custom applications.
- Multicast messaging  
  Thread provides the ability to broadcast messages, that is, sending the same message to multiple nodes on a Thread network. Multicast allows a built-in way to talk to neighbor nodes, routers, and an entire Thread network with standard IPv6 addresses.
- Application layers using IP services  
  Thread allows the use of application layers such as UDP and CoAP to allow devices to communicate interactively over the Internet. Non-IP application layers will require some adaptation to work on Thread. (See [RFC 7252](https://tools.ietf.org/html/rfc7252) for more information on CoAP.)

The Silicon Labs OpenThread SDK includes the following sample applications that are also available from the OpenThread GitHub repository:

- ot-cli-ftd
- ot-cli-mtd
- ot-rcp (used in conjunction with an OpenThread Border Router)

These applications can be used to demonstrate the features of a Thread network. In addition, the Silicon Labs OpenThread SDK also provides a sleepy end device sample app (sleepy-demo-ftd and sleepy-demo-mtd), which demonstrates how to use the Silicon Labs power manager features to create a low power device. Finally, the ot-ble-dmp sample application demonstrates how to build a dynamic multiprotocol application using OpenThread and the Silicon Labs Bluetooth stack. See the [OpenThread Quick-Start Guide](/openthread/3.1.0/openthread-quick-start-guide) for more information on working with example applications in Simplicity Studio.

#### Next Steps

The Silicon Labs OpenThread SDK includes a certified OpenThread networking stack and sample applications that demonstrate basic network and application behavior. Customers are encouraged to use the included sample applications to gain familiarity with Thread in general and the Silicon Labs offering in particular. Each of the applications demonstrates how devices form and join networks, as well as how messages are sent and received. The applications are available for use after loading Simplicity Studio  and the Silicon Labs OpenThread SDK. Simplicity Studio includes support for creating applications (Project Configurator) and decoding the network and application-layer messages (Network Analyzer) in Thread that provide additional insight into the operation of Thread networks. For more information, see the [OpenThread Quick-Start Guide](/openthread/3.1.0/openthread-quick-start-guide).

For more information about OpenThread Border Routers see [Using the Silicon Labs Co-processors with the OpenThread Border Router](/openthread/3.1.0/using-sl-coprocessors-with-openthread-border-router). For more information on developing Thread 1.4 sample applications see [Configuring OpenThread Applications for Thread 1.4](/openthread/3.1.0/configuring-openthread-apps-for-thread-1-4). Similarly, for developing Thread 1.3 sample applications see [Configuring OpenThread Applications for Thread 1.3](/openthread/3.1.0/configuring-openthread-apps-for-thread-1-3).

### Wireless Network App Development Fundamentals

#### Wireless Networking Application Development Fundamentals

> **Note: This section replaces _UG103.01: Wireless Networking Application Development Fundamentals_. Further updates to this user guide will be provided here**.

This volume of Silicon Labs’ _Fundamentals_ series introduces some fundamental concepts of wireless networking. Other documents in the series frequently refer to these concepts. If you are new to wireless networking, you should read this guide first. Silicon Labs’ _Fundamentals_ series covers topics that project managers, application designers, and developers should understand before beginning to work on an embedded networking solution using Silicon Labs chips, networking stacks, such as the EmberZNet stack or Silicon Labs Bluetooth, and associated development tools. These guides can be used as a starting place for anyone needing an introduction to developing wireless networking applications, or who is new to the Silicon Labs development environment.

#### Overview

As embedded system design has evolved and the Internet of Things (IoT) has emerged, the need for networking support has become a basic design requirement. Like more general-purpose computers, embedded systems have moved toward wireless networking. Most wireless networks have pushed toward ever-higher data rates and greater point-to-point ranges. But not all design applications require high-end wireless networking capabilities. Low-data-rate applications have the potential to outnumber the classic high-data-rate wireless networks worldwide. Simple applications such as lighting control, smart utility meters, Heating, Ventilation, and Air Conditioning (HVAC) control, fire/smoke/CO alarms, remote doorbells, humidity monitors, energy usage monitors, and countless other devices function very well with low-data-rate monitoring and control systems. The ability to install such devices without extensive wiring decreases installation and maintenance costs. Increased efficiencies and cost savings are the primary motives behind this applied technology.

A wireless sensor network (WSN) is a wireless network consisting of distributed devices using sensors at different locations to cooperatively monitor physical or environmental conditions, such as temperature, sound, vibration, pressure, motion, or pollutants.

In addition to one or more sensors, each node in a sensor network is typically equipped with a radio transceiver or other wireless communications device, a small microcontroller, and an energy source, usually a battery. The size of a single sensor node can vary from shoebox-sized nodes down to devices the size of coins. The cost of sensor nodes is similarly variable, depending on the size of the sensor network and the complexity required of individual sensor nodes. Size and cost constraints on sensor nodes result in corresponding constraints on resources such as energy, memory, computational speed, and bandwidth.

Wireless personal area networks have emerged partially as a result of the IEEE 802.15.4 standard for low data rate digital radio connections between embedded devices. Above the medium access control (MAC) and physical (PHY) layers, there have been a number of standards formed such as The Connectivity Standards Alliance and the Thread Group, which were formed to standardize industry efforts to supply technology for networking solutions that are based on 802.15.4, have low data rates, consume very little power, and are therefore characterized by long battery life. The Zigbee Standard makes possible complete and cost-effective networked homes and similar buildings where all devices are able to communicate for monitoring and control. The Thread Group brings IPv6 to these small embedded devices.

#### Embedded Networking

While the term wireless network may technically be used to refer to any type of network that functions without the need for interconnecting wires, the term most commonly refers to a telecommunications network, such as a computer network. Wireless telecommunications networks are generally implemented with radios for the carrier or physical layer of the network.

One type of wireless network is a wireless local area network or LAN. It uses radios instead of wires to transmit data back and forth between computers on the same network. The wireless LAN has become commonplace at hotels, coffee shops, and other public places. The wireless personal area network (WPAN) takes this technology into a new area where the distances required between network devices is relatively small and data throughput is low.

In the control world, embedded systems have become commonplace for operating equipment using local special-purpose computer hardware. Wired networks of such devices are now common in manufacturing environments and other application areas. Like all computer networks, the interconnecting cable systems and supporting hardware are messy, costly, and sometimes difficult to install. To overcome these problems, wireless networking of embedded systems (that is, embedded networking) has become commonplace. However, the costly embedded networking solutions have only been justifiable in high-end applications where the costs are a secondary consideration. Low-cost applications with low data rate communications requirements did not have a good standardized solution until the IEEE 802.15.4 standard for wireless personal area PHY and MAC layers was released in 2003.

> **Note**: The current version as of this writing is the [IEEE 802.15.4-2006 standard](https://standards.ieee.org/standard/802_15_4-2015.html).

The Connectivity Standards Alliance was formed to establish networking and application-level standards on top of the IEEE 802.15.4 standards, to allow flexibility, reliability, and interoperability. The Zigbee 802.15.4-2003 Specification 1.0 was ratified in 2005 and the Zigbee 2006 Specification was announced in 2006, obsoleting the 2004 stack. Working Groups (WGs) have been formed within the Internet Engineering Task Force (IETF) to establish open standard approaches for routing (roll WG) and interfacing low-powered wireless devices to IPV6 networks (6LoWPAN WG). More recently, the Thread Group was formed in 2014 to utilize open IP standards, mesh networking, and 802.15.4 to support a wide array of home networking products.

Although wireless networks eliminate messy cables and enhance installation mobility, the downside is the potential for interference that might block the radio signals from passing between devices. This interference may be from other wireless networks or from physical obstructions that interfere with the radio communications. Interference from other wireless networks can often be avoided by using different channels. Zigbee, for example, has a channel-scanning mechanism on start-up of a network to avoid crowded channels. Standards-based systems, such as Thread, Zigbee and Wi-Fi, use mechanisms at the MAC layer to allow channel sharing. In addition, Zigbee and Thread provide an interoperable standard for multi-hop wireless networking, allowing signals to reach their destination by traveling through multiple relay points. These networks can be comprised of many such relay points or "routers," each one within range of one or more other routers, creating an interconnected "mesh" of devices that can provide redundant paths for data within the network that are automatically rediscovered and used to avoid interference in a local area. This concept is collectively referred to as "mesh networking."

Another potential problem is that wireless networks may be slower than those that are directly connected through a cable. Yet not all applications require high data rates or large data bandwidth. Most embedded networks function very well at reduced throughputs. Application designers need to ensure their system data rates are within the range achievable with the system being used.

Wireless network security is also a problem, because the data can easily be overheard by eavesdropping devices. Zigbee and Thread have a set of security services designed around Advanced Encryption Standard (AES) 128 encryption, so that application designers have a choice of security levels based on the needs of their applications. Careful design around these standards helps maintain high levels of network security.

Other networking standards exist such as Bluetooth Classic. Each standard has its own unique strengths and essential areas of application. The bandwidth of Bluetooth is 1 Mbps, while 802.15.4-based protocols are one-fourth of this value. The strength of Bluetooth Classic lies in its ability to allow interoperability and replacement of cables. Zigbee and Thread's strengths are low cost, long battery life, and mesh networks for large network operation. Bluetooth is meant for point-to-point applications such as handsets and headsets, whereas Zigbee and Thread are focused on the sensors and remote controls market, large distributed networks, and highly reliable mesh networking. Bluetooth Low Energy is emerging rapidly as another low power wireless technology very well suited for point-to-point applications such as controlling a single device with your smartphone.

While in the past each chip supported a single protocol, new developments have allowed two protocols to operate on the same chip. This multiprotocol operation may be as simple as chip technology that supports different protocols, with the application protocol of choice loaded during manufacturing. More recently, chips such as Silicon Labs EFR32 products and shared operational infrastructure allow two protocols to share the same radio. For example, a dynamic multiprotocol implementation might allow an end user to use a smartphone app working with a Bluetooth Low Energy application on the device to control the device or perform diagnostics, while the device remains connected to its Zigbee home automation network. [Multiprotocol Fundamentals](https://docs.silabs.com/multiprotocol/latest/multiprotocol-fundamentals/) describes the four different multiprotocol modes, their operational requirements, and discusses some considerations to take into account when implementing a multiprotocol device.

#### Radio Fundamentals

Radio is the wireless transmission of signals by modulation of electromagnetic waves with frequencies below those of visible light. Electromagnetic waves are, in the case of radio, a form of non-ionizing radiation, which travels by means of oscillating electromagnetic fields that pass through electrical conductors, the air, and the vacuum of space. Electromagnetic radiation does not require a medium of transport like a sound wave. Information can be imposed on electromagnetic waves by systematically changing (modulating) some property of the radiated waves, such as their amplitude or their frequency. When radio waves pass an electrical conductor, the oscillating fields induce an alternating current in the conductor. This can be detected and transformed into sound or other signals that reproduce the imposed information.

The word 'radio' is used to describe this phenomenon, and radio transmission signals are classed as _radio frequency emissions_. The range or spectrum of radio waves used for communication has been divided into arbitrary units for identification. The Federal Communications Commission (FCC) and National Telecommunications and Information Association (NTIA) arbitrarily define the radio spectrum in the United States as that part of the natural spectrum of electromagnetic radiation lying between the frequency limits of 9 kilohertz and 300 gigahertz, divided into various sub-spectrums for convenience.

The following names are commonly used to identify the various sub-spectrums:

|Name|Sub-Spectrum|
|---|---|
|Very Low Frequencies (VLF)|3 kHz to 30 kHz|
|Low Frequencies (LF)|30 kHz to 300 kHz|
|Medium Frequencies (MF)|300 kHz to 3,000 kHz|
|High Frequencies (HF)|3,000 kHz to 30,000 kHz|
|Very High Frequencies (VHF)|30,000 kHz to 300,000 kHz|
|Ultra High Frequencies (UHF)|300,000 kHz to 3,000,000 kHz|
|Super High Frequencies (SHF)|3,000,000 kHz to 30,000,000 kHz|
|Extremely High Frequencies (EHF)|30,000,000 kHz to 300,000,000 kHz|

Each of the sub-spectrums listed above are further subdivided into many other sub-portions or ‘bands.’ For example, the American AM Broadcast Band extends from 535 kHz to 1705 kHz, which is within the portion of the spectrum classified as _Medium Frequencies_.

##### Frequency Bands

The radio spectrum is regulated by government agencies and by international treaties. Most transmitting stations, including commercial broadcasters, military, scientific, industrial, and amateur radio stations, require a license to operate. Each license typically defines the limits of the type of operation, power levels, modulation types, and whether the assigned frequency bands are reserved for exclusive or shared use. Three frequency bands can be used for transmitting radio signals without requiring licensing from the United States Government:

|Band|Description|
|---|---|
|900 MHz|The 900 MHz band was used extensively in different countries for different products including pagers and cellular devices. This band was considered to have good range characteristics. However, it can be less popular for products because it is not a worldwide unlicensed band, and products therefore need to be modified depending on where they are being used.|
|2400 MHz|The 2400 MHz band is a very commonly-used frequency band. This band was one of the first worldwide unlicensed bands and therefore became popular for wireless consumer products. Typical wireless technologies that use this band are 802.11b (1-11 Mbps), 802.11g (1-50 Mbps) and 802.15.4, as well as numerous proprietary radio types.|
|5200-5800 MHz|The 5200 MHz band has three sub-bands, the lowest being for indoor home use only, while the 5800 MHz frequencies can be used for long distance wireless links at very fast speeds (30 – 100 Mbps).|

A common strategy is to use 2400 MHz in residential and home environments. The Connectivity Standards Alliance and Thread Group endorse the use of this band, and both are looking at other SubGHz bands to expand their capabilities

##### Signal Modulation

Modulation is the process of changing the behavior of a signal so that it transfers information. Modulation can also be thought of as a way to encode information to be transmitted to a receiver that decodes, or demodulates, the information into a useful form.

The basic radio frequency (RF) signal has a fundamental frequency that can be visualized as an alternating current whose frequency is referred to as the _carrier wave frequency_. The earliest method used for encoding information onto the carrier wave involved switching the carrier wave on and off in a specific time duration pattern. This was known as continuous wave (CW) mode. The carrier frequency can also be varied in its amplitude (that is, signal strength) or its frequency. These two modulation methods are called amplitude modulation (AM) and frequency modulation (FM), respectively. It is possible to impose a signal onto the carrier wave using these three basic modulation techniques and creative variations of these techniques.

The Silicon Labs EFR32 integrated circuit family uses a form of _offset quadrature phase-shift keying_ (OQPSK) to modulate the carrier wave. _Phase-shift keying_ (PSK) is a digital modulation scheme that conveys data by changing, or modulating, the phase of a reference signal such as the carrier wave. PSK is a derivative of FM techniques.

All digital modulation schemes use a finite number of distinct signals to represent digital data. In the case of PSK, a finite number of phases are used. Each of these phases is assigned a unique pattern of binary bits. Usually, each phase encodes an equal number of bits. Each pattern of bits forms the symbol that is represented by the particular phase. The demodulator, which is designed specifically for the symbol set used by the modulator, determines the phase of the received signal and maps it back to the symbol it represents, thus recovering the original data. To do so, the receiver must compare the phase of the received signal to a reference signal. Such a system is termed _coherent_.

##### Antennas

An antenna (or aerial) is an arrangement of electrical conductors designed to emit or capture electromagnetic waves. The ability of an antenna to emit a signal that can be detected by another antenna is referred to as _radio propagation_. Antennas are made to a certain size based on the operating frequencies. An antenna from a 2400 MHz radio cannot be used effectively on a 5800 MHz radio, or vice versa. However, an antenna from one type of 2400 MHz technology, such as Wi-Fi or Bluetooth, can be used in another 2400 MHz technology, such as Zigbee or Thread.

Two fundamental types of antennas are described with reference to a specific three-dimensional space:

- Omni-directional: radiates equally in all directions
- Uni-directional (also known as directional): radiates more in one direction than in the other. All antennas radiate some energy in all directions in free space, but careful construction results in substantial transmission of energy in certain directions and negligible energy radiated in other directions.

In general, because of the nature of mesh networking, an omni-directional antenna is desired to provide as many communication paths as possible.

##### How Far Signals Travel

The distance a radio signal will travel and the amount of information that can be transmitted is based on:

- The amount of power the antenna is transmitting into the air.
- The distance between the transmitting and receiving stations.
- How much radio signal strength the receiving radio needs.
- What types of physical/electrical obstructions are in the way.

###### Radio Transmit Power

Radio transmit power is measured in watts, and typically discussed in terms of dBm, decibels referenced to 1 milliwatt. Converting wattage to dBm allows radio link calculations using simple addition and subtraction (dBm= 10*log10(P/ 0.001)).

For example, a typical power amplified wireless radio card transmits at 100 milliwatts (or mW), which translates to a power output of 20dBm.

If 1 mW, or 0 dBm, is the baseline for power in decibels, then +3 dBm is some power level above 1 mW (2 mW to be specific). The EFR32 platforms include some variants that go to +13 dBm and others that go up to +20 dBm, both without the aid of an external power amplifier.

###### Signal Degradation

The radio also needs to be able to hear a radio signal at a certain level. The minimum signal strength required for a receiver to understand the data is called the _receive sensitivity_.

As the radio signal travels through the air, it weakens. When a radio signal leaves the transmitting antenna, the dBm is a high number (for example, 20dBm). As it travels through the air, it loses strength and drops to a negative number. At some point, a minimum value for dBm is reached, below which the radio will no longer successfully receive the transmission. This value represents the "receive sensitivity" or "Rx sensitivity." This value will vary with the type of radio used but is typically between -90dBm and -100dBm. (Refer to the datasheet for your radio chip for specific receive sensitivity figures.)

If you can achieve a signal level of -75dBm and your radio has an Rx sensitivity of -95dBm, you have 20dBm of extra signal to accommodate interference and other issues. This is called _margin_.

###### How Far Can the Radio Signal Go

If you know the power out and the receiver sensitivity, you can determine whether you can broadcast over a given distance. In the following example, you want to know if you can receive a signal over five miles. To do so, you need to know the free space loss between the radio transmitter and the receiver.

For example, free space loss of a 2.4 GHz signal at 5 miles is 118.36 dB. So, you can estimate signal strength over the range of the network as:

|What|Add or subtract it|The value|
|---|---|---|
|Transmitter power|+|15 dBm|
|Transmitter antenna gain|+|14 dBi|
|Receiver antenna gain|+|14 dBi|
|Transmitter's coaxial cable loss|-|2 dB|
|Receiver's coaxial cable loss|-|2 dB|
|Free Space Loss @ 5 miles|-|118.36 dB|
| |Total|-79.36 dBm|

In other words, a 15 dBm radio hooked into a 14 dBi antenna, transmitting 5 miles through free space to another radio hooked up to a 14 dBi antenna, yields approximately -79 dBm of signal. Note that antennas with gain are necessarily directional, and would need to be aimed at each other. However, physical obstructions such as buildings or trees would have a substantial impact on these calculations. Typical Zigbee and Thread networks use smaller, lower-cost antennas without the gain increase and only use power amplifiers if extended range is required. Occasionally, external low-noise amplifiers (LNAs) may also be employed to boost RX sensitivity of incoming signals just before they reach the radio.

This calculation is provided as an example. See _Embedded Software Tools_ in [Manufacturing Test Overview](https://docs.silabs.com/zigbee/latest/mfg-test-overview/04-embedded-software-tools/) for a summary of the tools that can be used for performing range tests with different protocols.

The Flex SDK comes with the RangeTest application. It has a version that can be used to measure Bluetooth and IEEE 802.15.4 ranges, called "RangeTest BLE and IEEE 802.15.4". For more information, see [Range Test Demo User's Guide in Flex SDK v3.x](https://docs.silabs.com/rail/latest/flex-v3x-range-test-demo/) or _UG147: Range Test Demo User's Guide in Flex SDK v2.x_. Silicon Labs recommends that basic range testing be conducted in the expected environment to evaluate whether extended range is required.

#### Networking: Basic Concepts

A _network_ is a system of computers and other devices (such as printers and modems) that are connected in such a way that they can exchange data. This data may be informational or command-oriented, or a combination of the two.

A _networking system_ consists of hardware and software. Hardware on a network includes physical devices such as computer workstations, peripherals, and computers acting as file servers, print servers, and routers. These devices are all referred to as _nodes_ on the network.

If the nodes are not all connected to a single physical cable, special hardware and software devices must connect the different cables in order to forward messages to their destination addresses. A _bridge or repeater_ is a device that connects networking cables without examining the addresses of messages or making decisions as to the best route for a message to take. In contrast, a _router_ contains addressing and routing information that lets it determine, from a message's address, the most efficient route for the message. A message can be passed from router to router several times before being delivered to its target destination.

In order for nodes to exchange data, they must use a common set of rules defining the format of the data and the manner in which it is to be transmitted. A _protocol_ is a formalized set of procedural rules for the exchange of data. The protocol also provides rules for the interactions among the network's interconnected nodes. A network software developer implements these rules in software applications that carry out the functions required by the protocol.

Whereas a router can connect networks only if they use the same protocol and address format, a _gateway_ converts addresses and protocols to connect dissimilar networks. Such a set of interconnected networks can be referred to as an internet, intranet, wide area network (WAN), or other specialized network topology. The term Internet is often used to refer to the largest worldwide system of networks, also called the World Wide Web. The basic protocol used to implement the World Wide Web is called the Internet protocol, or IP.

A networking protocol commonly uses the services of another, more fundamental protocol to achieve its ends. For example, the Transmission Control Protocol (TCP) uses the IP to encapsulate the data and deliver it over an IP network. The protocol that uses the services of an underlying protocol is said to be a client of the lower protocol; for example, TCP is a client of IP. A set of protocols related in this fashion is called a _protocol stack_.

#### Wireless Networking

Wireless networking mimics the wired network, but replaces the wire with a radio signal as the data interconnection medium. Protocols are essentially the same as used in wired networks, although some additional functionality has been added so the two types of networks remain interoperable. However, wireless networks have emerged that do not have a wired counterpart requiring interoperability. These specialized networks have their own hardware and software foundations to enable reliable networking within the scope of their unique environments.

> **Note**: Most networking protocols are based, to some degree, on the Open Systems Interconnection (OSI) Model.

#### Networking Devices

Silicon Labs has developed networking hardware (the EFR32xG family) and SDKs containing protocol libraries, application examples, and development tools  to facilitate implementation of a wireless personal area network of devices for sensing and control applications. The following figure represents a typical wireless device using Zigbee, one of the Silicon Labs networking technologies. The RF data modem is the hardware responsible for sending and receiving data on the network. The microcontroller represents the computer control element that originates messages and responds to any information received. The sensor block can be any kind of sensor or control device. Such a system can exist as a node on a network without any additional equipment. Any two such nodes, with compatible software, can form a network. Large networks can contain thousands of such nodes.

![Typical Zigbee Device Block Diagram](/wireless-networking-application-development-fundamentals/0.2/images/typical-zigbee-device-block-diagram.png)

Chips such as the EFR32xG family provide both the RF and microcontroller portions of the figure above. When used as "network coprocessors," the chips provide only the RF and networking part of the system, acting as a co-processor to any microcontroller, Digital Signal Processing (DSP), or similar device required for the application.

For details of the various networking technologies, see the specific Fundamentals document(s) for that technology.

## OpenThread Developer's Guide

### OpenThread Developers Guide

The Developer's Guide content is organized in the following groups:

- [**Developing and Debugging**](/openthread/3.1.0/openthread-developing-debugging-overview): A description of development resources and detailed information on a variety of topics.
- [**OpenThread Border Router**](/openthread/3.1.0/openthread-border-router-overview): About developing and using an OpenThread Border Router.
- [**Coexistence**](/openthread/3.1.0/openthread-coexistence-overview): Background on coexistence issues and strategies for improving performance in the presence of other protocol traffic.
- [**Multiprotocol**](/openthread/3.1.0/openthread-multiprotocol-overview): Background on implementing multiprotocol applications and information on different multiprotocol models.
- [**Bootloading**](/openthread/3.1.0/openthread-bootloading-overview): Information on using the Gecko Bootloader with OpenThread applications.
- [**Non-Volatile Memory**](/openthread/3.1.0/openthread-memory-use-overview): Background on managing device memory.
- [**Security**](/openthread/3.1.0/openthread-security-overview): Describes Silicon Labs security resources and how to manage OpenThread security.
- [**Performance**](/openthread/3.1.0/openthread-performance-overview): Provides performance testing and measurement tools and techniques as well as results.

### Developing and Debugging

#### Developing and Debugging Silicon Labs OpenThread Applications

These pages provide details on developing and debugging OpenThread applications. Content
includes:

- [**Configuring and Building OpenThread Applications for Sleepy Devices**](/openthread/3.1.0/openthread-sleepy-devices): Describes how to configure OpenThread applications to operate on a proprietary sub-GHz band using the Silicon Labs OpenThread SDK and Simplicity Studio with a compatible mainboard. It also provides details on the proprietary radio PHY supported with this feature.
- [**Single-Band Proprietary Sub-GHz Support with OpenThread**](/openthread/3.1.0/openthread-single-band-proprietary-sub-ghz): Describes how to configure OpenThread applications to operate on a proprietary sub-GHz band using the Silicon Labs OpenThread SDK and Simplicity Studio with a compatible mainboard. It also provides details on the proprietary radio PHY supported with this feature.
- [**Using OpenThread with Free RTOS**](/openthread/3.1.0/openthread-with-free-rtos): Describes how to build OpenThread applications with FreeRTOS.
- [**Configuring OpenThread Applications for Thread 1.3**](/openthread/3.1.0/configuring-openthread-apps-for-thread-1-3): Provides instructions for configuring OpenThread SoC and Border Router applications to use Thread 1.3 features.
- [**Configuring OpenThread Applications for Thread 1.4**](/openthread/3.1.0/configuring-openthread-apps-for-thread-1-4): Provides instructions for configuring OpenThread SoC and Border Router applications to use Thread 1.4 features.

##### Development Tools

**Simplicity Studio and the Simplicity IDE**: Simplicity Studio® is the unified development environment for all Silicon Labs technologies, SoCs, and modules. It provides you with access to the target device-specific web and SDK resources, software and hardware configuration tools, and an integrated development environment (IDE) featuring industry-standard code editors, compilers, and debuggers. See the [Simplicity Studio page](https://www.silabs.com/developers/simplicity-studio) to download the tools and for more information.

**Network Analyzer**: Simplicity Studio's Network Analyzer enables debugging of complex wireless systems. This tool captures a trace of wireless network activity that can be examined in detail live or at a later time. See the Network Analyzer section of the Simplicity Studio User's Guide for more information.

**Wireshark**: Download instructions are provided for [Windows/Mac users](https://www.wireshark.org/download.html) or [Linux users](https://www.wireshark.org/docs/wsug_html_chunked/ChBuildInstallUnixInstallBins.html). Simplicity Studio supports live interaction between the application running on a Silicon Labs device and Wireshark.

**Energy Profiler**: Simplicity Studio's Energy Profiler enables you to visualize the energy consumption of individual devices, multiple devices on one target system, or a network of interacting wireless devices to analyze and improve the power performance of these systems. Real-time information on current consumption is correlated with the program counter providing advanced energy software monitoring capabilities. It also provides a basic level of integration with the Network Analyzer network analysis tool. See the Energy Profiler section of the Simplicity Studio User's Guide for more information.

**Simplicity Commander**: Simplicity Commander is a single, all-purpose tool to be used in a production environment. It is invoked using a simple Command Line Interface (CLI) that is also scriptable. Simplicity Commander enables customers to complete essential tasks such as configuring and building applications and bootloaders and flashing images to their devices. Simplicity Commander is available through Simplicity Studio or can be downloaded through [system-specific installers](https://www.silabs.com/developers/mcu-programming-options#programming). The Simplicity Commander User's Guide provides more information.

**Silicon Labs Configurator (SLC)**: SLC offers command-line access to application configuration and generation functions. Software Project Generation and Configuration with SLC-CLI provides instructions on downloading and using the SLC-CLI tool.

#### Configuring Sleepy Devices

##### Configuring and Building OpenThread Applications for Sleepy Devices

**Minimal Thread Devices (MTDs)** communicate only through their Thread Router parent and cannot relay messages for other devices. **Sleepy Devices** are MTDs that achieve low-power duty cycling by turning off their transceiver to reduce power. For more information on these device roles in a Thread network, refer to the Thread specification or [Thread Fundamentals](/openthread/3.1.0/thread-fundamentals).

There are two categories of Sleepy Devices:

- Regular Sleepy End Devices (SEDs) that poll for new messages when they wake up.
- Synchronized Sleepy End Devices (SSEDs) that use IEEE 802.15.4 Coordinated Sampled Listening (CSL) so that the parent can forward its messages during designated transmission and reception windows.

Silicon Labs provides sample applications to demonstrate both kinds of Sleepy End Device behavior.

Sleepy Devices can greatly help extend the battery power of power-limited devices. Note that simply building a Sleepy Device application does not guarantee the lowest power consumption, as this is dependent on many configuration parameters as well as the application’s general use case. Additionally, any application code such as shell or CLI, LCD code, or other peripheral components can also adversely affect the power consumption. For an example of such current consumption optimization, refer to the Silicon Labs example at [https://github.com/SiliconLabs/zigbee_applications/tree/master/zigbee_sed_z3switch](https://github.com/SiliconLabs/zigbee_applications/tree/master/zigbee_sed_z3switch).

> **Note**: This document describes how to configure Sleepy Devices. Further detail about power consumption, optimizing current consumption values, or providing benchmark values for various platforms is outside the document's scope.

##### Sleepy End Devices (SEDs)

SEDs achieve lower power consumption by sleeping for a set period and periodically waking up to send data polls (MAC data requests) to their parent. If the parent has any pending data to send to its child, it is indicated by a frame pending bit in the 802.15.4 Acknowledgement to the data poll. This lets the Sleepy End Device keep its receiver on for the anticipated data which the parent will send immediately.

Starting with Thread 1.2, the OpenThread stack automatically also makes use of the 802.15.4 Enhanced Frame Pending feature, which lets the SED use regular data messages to get an indication of pending data. Without this feature, the SED would have to send a data poll on its scheduled period to extract the data from the parent.

![SED Polling](/openthread-sleepy-devices/0.2/images/sld415-image1.png)

Note that a smaller poll period (that is, polling more frequently) means better latency at the cost of higher power consumption.

###### SED Configuration in OpenThread

**APIs:**

- `otLinkGetPollPeriod`/`otLinkSetPollPeriod`: Get/Set/Clear user-specified/external data poll period for sleepy end device.

> **Warning**: The following poll-related configuration items have standardized values in the Thread specification. Changing them might affect the certifiability of your component or end product.

- OPENTHREAD_CONFIG_MAC_MAX_TX_ATTEMPTS_INDIRECT_POLLS: Maximum number of received IEEE 802.15.4 Data Requests for a queued indirect transaction.
- OPENTHREAD_CONFIG_MAC_ATTACH_DATA_POLL_PERIOD: The Data Poll period during attach in milliseconds.
- OPENTHREAD_CONFIG_MAC_MINIMUM_POLL_PERIOD: Minimum poll period in milliseconds.
- OPENTHREAD_CONFIG_MAC_RETX_POLL_PERIOD: Retransmission poll period in milliseconds.

##### Synchronized Sleepy End Devices (SSEDs)

An SSED is an rx-off-when-idle end device that uses the IEEE 802.15.4-2015 CSL feature, available beginning with Thread 1.2, to further optimize power consumption. An SSED CSL receiver is synchronized with a parent that is a CSL transmitter, so both parent and child require implementation of this feature.

Coordinated Sampled Listening (CSL) involves the receiver sampling for any data from the transmitter during set intervals. The transmitter always targets the synchronized window, eliminating the need for data polling and optimizing power consumption. In the following figure, note the comparison of activity with a regular SED.

![Comparison of SED and SSED Power Consumption](/openthread-sleepy-devices/0.2/images/sld415-image2.png)

SSEDs automatically reduce the average TX-ON time by avoiding wasted data polling. Therefore, optimized power consumption depends on configuring minimal average RX-ON time for a platform and the given application use case.

Note that an SSED periodically must send some packets within a specified timeout to maintain synchronization with its parent. In OpenThread, the auto-synchronization happens automatically using data polls (usually at a much less frequent rate than regular polling). Note that other data packets from the SSED, including application data, can also be used by the stack to re-synchronize the connection as needed.

###### IEEE 802.15.4-2015 Coordinated Sampled Listening (CSL)

###### CSL Parameters and CSL Information Elements (heading level 7)

Following are the parameters that indicate CSL configuration on a receiver:

- **CSL Period**: A CSL receiver performs periodic channel sampling by configuring a non-zero period value _(see macCslPeriod in [IEEE802154-2015]_).
- **CSL Phase**: Thread uses the CSL phase definition from _[IEEE802154-2015]_: “_the time from the first symbol of the frame containing the CSL IE … until the next channel sample_”.  
  - “First symbol” is interpreted as the first symbol of the MAC header.  
  - The CSL receiver should be ready to receive slightly earlier than the preamble time (CSL-Phase – CCA time) and should stay in receive mode until after the CSL-Phase time to detect the Sync-Frame-Detect (SFD) of the incoming packet from the CSL transmitter. These timing values account for the platform’s implementation and accuracy.
- **CSL Channel**: If the CSL receiver expects to receive and process unsynchronized CSL transmissions, then it should use a different channel from the Thread network channel for receiving CSL messages. However, most Thread applications' use cases for SSEDs only involve synchronized communications, so the CSL channel can remain the same as the network channel.
- **CSL Timeout**: Timeout before which an SSED and its parent must re-synchronize to keep the connection valid and active.

CSL synchronization happens by the SSED child communicating CSL parameters to its parent. The CSL channel and timeout are initially configured during mesh link establishment (MLE) attach. The period and phase are communicated by the receiver in IEEE 802.15.4 Information Elements in the MAC header. The **CSL IE** in the IEEE 802.15.4 MAC header is a tuple containing **[CSL period, CSL phase]**.

An SSED device should include CSL IEs in all the IEEE 802.15.4 Commands, ACKs, and Data frames unicast to its parent router. IEEE 802.15.4 ACKs that include IEs are called **Enhanced Acknowledgements** (EnhAcks), as defined in _[IEEE802154-2015]_.

Note that the CSL period, channel, and timeout are configured by the application, whereas the phase value is dynamically determined on the SSED relative to the exact moment the frame containing the CSL IE is sent out to the parent. CSL retransmissions involve recalculating the CSL IE with a new phase value.

###### OpenThread CSL Timing Calculations (heading level 7)

The CSL transmitter’s delay for its scheduled transmission points to the moment when the end of the SFD will be present at the receiver’s local antenna, relative to the local radio clock. The CSL receiver should be ready to receive the first symbol of a scheduled frame’s Sync Header (SHR) at its own receive window start time. If no SHR is detected at the end of its minimum receive window, the radio should be turned off or switched to TX mode as needed.

The CSL sample window of the CSL receiver extends before and after its calculated sample time. This marks a timestamp in the CSL sample window where a frame would be received in "ideal conditions" if there was no inaccuracy or clock drift. However, the realistic sampling representation is as follows:

![sampling representation](/openthread-sleepy-devices/0.2/images/sld415-image3.png)

- **timeAhead** accounts for the SSED to wake and be ready for RX.
- **timeAfter** is the when the RX-ON window closes. If needed, the Silicon Labs radio driver automatically extends the duration of the receive window.
- **Uncert** is the fixed uncertainty (that is, random jitter) of the arrival time of CSL transmissions. In addition to uncertainty accumulated over elapsed time, the CSL channel sample ("RX window") must be extended by twice this value such that an actual transmission is guaranteed to be detected by the local receiver in the presence of random arrival time jitter.
- The calculations also account for clock drift and the estimated worst-case accuracy (maximum ± deviation from the nominal frequency) of the local radio clock used to schedule CSL operations.

###### SSED Configuration in OpenThread

**APIs:**

- `otPlatRadioEnableCsl` : Enable or disable CSL receiver.
- `otPlatRadioUpdateCslSampleTime`: Update CSL sample time in radio driver.
- `otPlatRadioGetCslAccuracy`: Get the current estimated worst-case accuracy of the local radio clock in PPM.
- `otPlatRadioGetCslUncertainty`: The fixed uncertainty (that is, random jitter) of the arrival time of CSL transmissions received by this device in 10-microsecond units.
- `otLinkGetCslChannel` / `otLinkSetCslChannel`: Get/Set CSL Channel.
- `otLinkGetCslPeriod` / `otLinkSetCslPeriod`: Get/Set CSL Period.
- `otLinkGetCslTimeout` / `otLinkSetCslTimeout`: Get/Set CSL Timeout.
- `otLinkIsCslEnabled`: Indicates whether or not CSL is enabled.
- `otLinkIsCslSupported`: Indicates whether the device is connected to a parent that supports CSL.

**Configurable parameters:**

The following parameters are configurable in the OpenThread stack component in Simplicity Studio or at run-time using the Silicon Labs Configurator (SLC):

- OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE: Configures CSL receiver support at build time.
- SL_OPENTHREAD_CSL_TX_UNCERTAINTY: CSL Scheduling Uncertainty (±10 microseconds). SSED’s receive window will increase by twice this value.
- OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE: Enables CSL debug printing (will affect timing, so use only for debug).

**WARNING:** The following configuration parameters have standardized values for Silicon Labs platforms. Changing them might affect certifiability of your component or end product:

- OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE: Enables CSL transmitter functions. Automatically enabled for Thread 1.2 or greater devices.
- OPENTHREAD_CONFIG_MAC_CSL_AUTO_SYNC_ENABLE: Configures CSL auto synchronization based on data poll mechanism in Thread 1.2. This is turned off for some reference devices for certification testing purposes. For OpenThread device end products, this should never be turned off.
- OPENTHREAD_CONFIG_MAC_CSL_MIN_PERIOD: Minimum CSL period in milliseconds.
- OPENTHREAD_CONFIG_MAC_CSL_MAX_TIMEOUT: Maximum CSL timeout in seconds.
- OPENTHREAD_CONFIG_CSL_TIMEOUT: Default CSL timeout in seconds.
- OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US: For a CSL transmitter, this indicates the time, measured in microseconds, by which the MAC should advance the delivery of the CSL frame to the radio layer before the actual transmit time.
- OPENTHREAD_CONFIG_CSL_TRANSMIT_TIME_AHEAD: Transmission scheduling and ramp-up time needed for the CSL transmitter to be ready, in microseconds.
- OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD: Reception scheduling and ramp up time needed for the CSL receiver to be ready, in microseconds.
- OPENTHREAD_CONFIG_MIN_RECEIVE_ON_AHEAD: The minimum time (in microseconds) before the MAC Header (MHR) start that the radio should be in the receive state in order to properly receive any IEEE 802.15.4 frame. Defaults to the duration of Sync Header (SHR) + PHY Header (PHR).
- OPENTHREAD_CONFIG_MIN_RECEIVE_ON_AFTER: The minimum time (in microseconds) after the MHR start that the radio should be in receive state in order to properly receive any IEEE 802.15.4 frame. For Silicon Labs products, this value is zero, as the Silicon Labs radio driver will automatically extend the receive window when the SHR is detected.
- SL_OPENTHREAD_HFXO_ACCURACY: Worst case HFXO XTAL accuracy in units of ± ppm. Set to platform’s SL_DEVICE_INIT_HFXO_PRECISION value.
- SL_OPENTHREAD_LFXO_ACCURACY: Worst case LFXO XTAL accuracy in units of ± ppm. Set to platform’s SL_DEVICE_INIT_LFXO_PRECISION value.

##### SSED Use Cases

- In dense network settings, SSEDs can reduce over-the-air radio traffic by avoiding frequent data polling.
- SSEDs are also useful in settings with sparse data traffic patterns that require high responsiveness from the sleepy devices.

When comparing viability of SEDs and SSEDs, note that SSEDs may have lower power consumption in a direct comparison only in settings that ensure tight CSL accuracy and uncertainty. With worse uncertainty deviations, or in settings with high interference, any power savings sought by reducing time spent in data polling traffic will be compromised by power spent on larger receive windows. Referring to the following figure, the goal should be to minimize receive windows on the SSEDs when there is no data, while still supporting the application use case.

![Comparison of SED and SSED Power Consumption](/openthread-sleepy-devices/0.2/images/sld415-image2.png)

##### Building and Using Silicon Labs Sleepy End Device Demo Applications

The EFR32 Sleepy applications demonstrate Sleepy End Device behavior using the EFR32's low power deep sleep EM2 mode.

- **OpenThread – SoC Sleepy Demo (FTD) (sleepy-demo-ftd):**  An application to start and form a Thread network on a Full Thread Device (FTD) for the sleepy-demo. This application is used in conjunction with the other sleepy demo applications.
- **OpenThread – SoC Sleepy Demo (sleepy-demo-mtd):**  An application to demonstrate Sleepy End Device (SED) behavior on a Minimal Thread Device (MTD) that attaches to a Thread network started by a node running **sleepy-demo-ftd**. This application demonstrates power manager feature support and EM2 mode for the EFR32.
- **OpenThread – SoC Synchronized Sleepy Demo (sleepy-demo-ssed):**  An application to demonstrate SSED behavior using CSL that attaches to a Thread network started by a node running **sleepy-demo-ftd**. This application demonstrates power manager feature support and EM2 mode for the EFR32.

###### Building Sleepy Demo Applications

1. With the target part connected to your computer, open Simplicity Studio’s **File** menu and select **New > Silicon Labs Project Wizard**.
2. The Target, SDK, and Toolchain Selection dialog opens. Click **NEXT**.
3. The Example Project Selection dialog opens. Use the Technology Type and Keyword filters to search for “**sleepy demo**” as the keyword.  
   ![Select sleepy demo example](/openthread-sleepy-devices/0.2/images/sld415-image4.png)  
   - To build an FTD that can act as a parent / router for the example, select **sleepy-demo-ftd**.  
   - To demonstrate a regular Sleepy End Device that can work with the **sleepy-demo-ftd** as its parent, select **sleepy-demo-mtd**.  
   - To demonstrate a Synchronized Sleepy End Device that can use CSL with the **sleepy-demo-ftd** as its parent, select **sleepy-demo-ssed**.  
   The Project Configuration dialog opens.
4. Rename the project, change the default project file location, and determine if you will link to or copy project files. Note that, if you change any linked resource, it is changed for any other project that references it. Click **FINISH**.
5. The Simplicity IDE Perspective opens with the Project Configurator open to the **OVERVIEW** tab. See the online Simplicity Studio User’s Guide for details about the functionality available through the Simplicity IDE perspective and the Project Configurator. The following is an example of how the **sleepy-demo-ftd** project will look in this perspective.  
   ![Sleepy-demo-ftd in Project Configurator](/openthread-sleepy-devices/0.2/images/sld415-image5.png)
6. Make any configuration changes to the software components. The autogeneration progress is available in the bottom right of the Simplicity IDE perspective. Make sure that progress is complete before you build.
7. Compile and flash the application image as described in the [OpenThread Quick Start Guide](/openthread/3.1.0/openthread-quick-start-guide).

###### Demonstration

For demonstration purposes, the network settings are hardcoded within the source files. Within a few seconds of powering on, the devices start Thread and form a network. In a real-life application, the devices should implement and go through a commissioning process to create a network and add devices.

When the **sleepy-demo-ftd** device is started, the CLI displays:

```C
sleepy-demo-ftd started
sleepy-demo-ftd changed to leader
```

When the **sleepy-demo-mtd** device is started, the CLI displays:

```c
sleepy-demo-mtd started
[poll period: 2000 ms.]
```

The application is configured to join the pre-configured Thread network, disabling Rx-on-when-idle mode to become a Sleepy End Device. The default poll period is set in sleepy-mtd.c.

Issue the command to retrieve child table in the FTD console and observe that the R (Rx-on-when-idle) flag of the child is 0.

```c
> child table
| ID  | RLOC16 | Timeout    | Age        | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt|Suprvsn| Extended MAC     |
+-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+-------+------------------+
|   1 | 0x8401 |        240 |          3 |     3 |    3 |0|0|0|  4| 0 |     0 |   129 | 667bf54fcc2aed8a |
Done
```

When the **sleepy-demo-ssed** device is started, the CLI displays:

```c
sleepy-demo-ssed started
[csl period: 500000 us.] [csl timeout: 20 sec.]
```

The application is configured to join the pre-configured Thread network, disabling Rx-on-when-idle mode to become a Synchronous Sleepy End Device. The default CSL parameters are set in sleepy-ssed.c

Issue the command to retrieve the child table in the FTD console and observe that the R (Rx-on-when-idle) flag of the child is 0 and that the CSL flag is 1.

```c
> child table
| ID  | RLOC16 | Timeout    | Age        | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt|Suprvsn| Extended MAC     |
+-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+-------+------------------+
|   1 | 0x8402 |        240 |          3 |     3 |    3 |0|0|0|  4| 1 |     0 |   129 | 8e8582dbd78c243c |
Done
```

###### Buttons on the MTD/SSED (heading level 7)

Pressing button 0 on the MTD/SSED toggles between EM2 (sleep) and EM1 (idle) modes.

Pressing button 1 on the MTD/SSED sends a multicast UDP message containing a pre-defined string. The FTD listens on the multicast address and displays “Message Received: <string>” in the CLI.

###### Buttons on the FTD (heading level 7)

Pressing either button 0 or 1 on the FTD sends a UDP message to the FTD containing the string "ftd button". Before pressing either button on the FTD, press the MTD's or SSED's button 1 to send a multicast message so that the FTD knows the address of the sleepy device to send messages to.

###### Monitoring Power Consumption of the MTD/SSED (heading level 7)

Open the Energy Profiler in Simplicity Studio 5 (SSv5). In the Quick Access menu select _**Start Energy Capture...**_ and select the MTD / SSED device. For further information on monitoring power consumption and energy profiler, see _Multi-Node Energy Profiler_ in [Silicon Labs OpenThread QuickStart Guide](/openthread/3.1.0/openthread-quick-start-guide/05-development-tools/multi-node-energy-profiler).

###### Notes on Sleeping, Sleepy Callback, and Interrupts (heading level 7)

To allow the EFR32 to enter sleepy mode, the application must register a callback with efr32SetSleepCallback. The return value of the callback is used to indicate that the application has no further work to do and that it is safe to go into a low power mode. Because the callback is called with interrupts disabled, it should do the minimum required to check if it can sleep.

#### Single-Band Proprietary Sub-GHz Support with OpenThread

##### Single-Band Proprietary Sub-GHz Support with OpenThread

**NOTE: This section replaces _AN1350: Single-Band Proprietary Sub-GHz Support with OpenThread_. Further updates to this application note will be provided here.**

This application note describes how to configure OpenThread applications to operate on a proprietary sub-GHz band using the Silicon Labs OpenThread software development kit (SDK) and Simplicity Studio with a compatible wireless starter kit (WSTK). It also provides details on the proprietary Radio PHY supported with this feature.

Sub-GHz radios, like 2.4 GHz ones, can offer relatively simple wireless solutions. When compared to 2.4 GHz, sub-GHz offers several advantages, depending on the target application.

Some notable advantages of using sub-GHz radios are:

- Longer range
- Less signal fading in congested environments
- Low interference
- Low power

This application note provides a step-by-step guide to creating, building, and running an OpenThread application on a sub-GHz band.

As Thread is a 2.4GHz protocol and the specification currently does not support a sub-GHz feature, sub-GHz support has been added using:

- Proprietary radio PHY made available with the SDK and,
- Proprietary radio configurations supported by the OpenThread stack.

> **Note**: This feature currently supports single-band operation only and so requires all the nodes in a mesh to be operating on the same sub-GHz band.

Our newest embedded software platform, the Simplicity Software Development Kit (SDK), is designed specifically for our Series 2 and Series 3 devices. The Simplicity SDK is a significant advancement in IoT development, providing our customers with a cohesive development environment for wireless innovation. It enables wireless developers to utilize advanced features and capabilities with the most recent Silicon Labs IoT devices.

Meanwhile, our Gecko Software Development Kit (GSDK) will continue to be available for users of our Series 0 and Series 1 devices. For additional information on Series 0 and Series 1 devices, refer to Series 0 and Series 1 EFM32/EZR32/EFR32 devices on silabs.com.

###### Proprietary Sub-GHz Radio PHY

For wireless applications to operate on a particular frequency band, the applications typically need the radio implementation to provide the necessary PHY support. For OpenThread applications supported on Silicon Labs platforms, the platform abstraction layer supports the 2.4GHz band by default. For the sub-GHz feature, proprietary radio PHY support has been added.

This section describes the proprietary radio PHY specifications that have been used to support this feature. The PHY specifications are compliant with the NA FCC Part 15.247 regulations.

###### Modulation Details (heading level 7)

The proprietary radio PHY currently supported with the OpenThread SDK uses the following modulation specifications.

|Parameter|Configuration|
|---|---|
|Modulation|2-Level GFSK|
|Data Rate|500 kbps|
|Tx Filter BT|0.5 (Gaussian)|
|Modulation Index|0.76|

###### Channel and Frequency Specifications (heading level 7)

Similarly, the channel and frequency specifications for the proprietary radio PHY, and the center frequency for the supported channels have been captured in the following tables.

###### Proprietary Sub-GHz PHY Band Parameters (heading level 8)

|Band Parameters|Value|
|---|---|
|Channel Page|23|
|Frequency Band (MHz)|902- 928|
|Channel Spacing (MHz)|1.0|
|Total Channels|25|
|Channel Numbers|0 – 24|
|1st Channel Center Freq. (MHz)|903.0|

###### Channels and Center Frequencies for 902-928 MHz Band (heading level 8)

|Chan. #|Fc (MHz)|
|---|---|
|0|903.0|
|1|904.0|
|…|…|
|23|926.0|
|24|927.0|

###### PHR Length (heading level 7)

The proprietary radio PHY supports 2-byte PHR. With a supported PSDU of 127 bytes, the last 7 bits of the 2nd byte represent the Frame Length.

###### Hardware Limitations

The proprietary sub-GHz feature is currently supported on radio boards supporting the 915 MHz band and using EFR32MG12 or EFR32MG13 parts only. Radio boards that could be used to test this feature are BRD4164a, BRD4170a and BRD4158a.

##### Building an OpenThread Sample App for Proprietary Sub-GHz

To build an OpenThread sample application for sub-GHz, you need Simplicity Studio and the Gecko SDK 3.2 (or higher) development environment with the OpenThread SDK package installed.

This section assumes that you have installed Simplicity Studio and the OpenThread SDK, and that you are familiar with Simplicity Studio and configuring, building, and flashing applications. If not, see the [OpenThread Quick-Start Guide](/openthread/3.1.0/openthread-quick-start-guide).

1. Connect your target development hardware (supporting proprietary sub-GHz as discussed in [Hardware Limitations](./index#hardware-limitations)), open Simplicity Studio's File menu, and select **New > Silicon Labs Project Wizard**. The Target, SDK, and Toolchain Selection dialog opens. Your target hardware should be populated. Click **NEXT**.
2. The Example Project Selection dialog opens. Use the **Technology Type** and **Keyword** filters to search for a specific example, for example **ot-cli-ftd**. Select it and click **NEXT**.  
   > **Note**: If you do not see the application, your connected hardware may not be compatible. To verify, in the Launcher Perspectives My Products view enter EFR32MGxx and select one of the boards. Go to the Examples tab, filter by Thread technology and verify you can see the app.
3. The Project Configuration dialog opens. Here you can rename your project, change the default project file location, and determine if you will link to or copy project files. Note that if you change any linked resource, it is changed for any other project that references it. Unless you know you want to modify SDK resources, use the default selection. Click **FINISH**. The Simplicity IDE opens with the **ot-cli-ftd** project open in the Project Configurator.
4. To configure proprietary sub-GHz support, on the **SOFTWARE COMPONENTS** tab, select **Installed Components** and select the **Platform Abstraction** component under OpenThread. Note that you can also search for a component by name in the search field.  
   ![Platform Abstraction](/openthread-single-band-proprietary-sub-ghz/0.2/images/sld699-image1.png)
5. Click **Configure** and enable the **Proprietary Sub-GHz Support** configuration option.  
   ![Proprietary Sub-GHz Support](/openthread-single-band-proprietary-sub-ghz/0.2/images/sld699-image2.png)  
   > **Note**: If you do not see the **Configure** control or the **Proprietary Sub-GHz Support** configuration option associated with the Platform Abstraction component, your hardware may not be compatible with the proprietary radio specifications discussed above.
6. Build the project. The generated ot-cli-ftd.s37 image may now be uploaded to your board using a Simplicity Studio tool such as the flash programmer or Simplicity Commander.

##### Working with a Proprietary Sub-GHz Network

Silicon Labs radio boards supporting the sub-GHz ISM band are designed to the operate in the US FCC 902-928 MHz band with an external whip antenna. Accordingly, when working with this feature, connect the external whip antenna using the SMA antenna connector on your radio board. For more information about this requirement, please refer to your radio board’s reference manual.

###### Creating a Proprietary Sub-GHz Network

As mentioned in the [Introduction](./index), the proprietary sub-GHz feature currently supports single band use only and so requires every node in the mesh to be running an application with the sub-GHz feature enabled. Accordingly, to create a sub-GHz network:

1. Build the **ot-cli-ftd** example with the proprietary sub-GHz feature enabled as discussed in [Building an OpenThread Sample App for Proprietary Sub-GHz](./02-building-an-openthread-sample-app-for-proprietary-sub-ghz) and flash the same application on all your nodes.
2. Use the standard OpenThread CLI commands to form and attach to a network. An example of this step is provided in the [OpenThread Quick Start Guide](/openthread/3.1.0/openthread-quick-start-guide/03-getting-started-with-development/creating-a-network).
3. The resulting network formed has nodes operating on the sub-GHz band (with channels supported between 0 – 24, covering 902 – 928 MHz).

###### Enabling Proprietary Sub-GHz Support on an OpenThread Border Router

This section assumes that you are familiar with the basic build and install instructions for an OpenThread Border Router. If not, refer to [Using the Silicon Labs Co-processors with the OpenThread Border Router](/openthread/3.1.0/using-sl-coprocessors-with-openthread-border-router).

To enable proprietary sub-GHz support on an OpenThread Border Router:

1. Build an RCP image using Simplicity Studio with the sub-GHz feature enabled. Start with the **ot-rcp** example and follow the steps described in [Building an OpenThread Sample App for Proprietary Sub-GHz](./02-building-an-openthread-sample-app-for-proprietary-sub-ghz#building-an-openthread-sample-app-for-proprietary-sub-ghz).
2. For the Border Router Host you can either:  
   - Use a pre-built docker image (Recommended)    
     [https://hub.docker.com/r/siliconlabsinc/openthread-border-router-proprietary-na-915/tags](https://hub.docker.com/r/siliconlabsinc/openthread-border-router-proprietary-na-915/tags)  
   - Or, manually build the border router image for your host with the following OpenThread proprietary radio configurations set. This option requires you to modify the OT BR build scripts (details of which are beyond the scope of this document).

|Configuration|Value|
|---|---|
|OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT|1|
|OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT|0|
|OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT|0|
|OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE|23|
|OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MIN|0|
|OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MAX|24|
|OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MASK|0x1ffffff|
|OPENTHREAD_CONFIG_DEFAULT_CHANNEL|0|

###### Verifying Sub-GHz Operation

To verify if your application has been configured correctly to operate on the sub-GHz band:

1. Execute the following CLI command on your node, to retrieve the supported channel mask:  
   ```C  
   > channel supported  
   0x1ffffff  
   Done  
   ```  
   For proprietary sub-GHz applications, the result of this command is `0x1ffffff`, indicating channels 0-24 supported for this configuration. For 2.4 GHz applications, the output returned is `0x7fff800`, indicating channels 11-26 supported for that band.
2. Alternately, for a node running the sub-GHz application and that is part of an OpenThread network, you can also verify the radio information using Silicon Labs Network Analyzer.  
   ![screenshot](/openthread-single-band-proprietary-sub-ghz/0.2/images/sld699-image3.png)

For more details on how to capture OpenThread packers using Silicon Labs Network Analyzer, refer to _Network Analyzer_ in [OpenThread Quick Start Guide](/openthread/3.1.0/openthread-quick-start-guide/05-development-tools/network-analyzer).

#### Using OpenThread with FreeRTOS

##### Using Open Thread with FreeRTOS

**NOTE: This section replaces _AN1264: Using Open Thread with FreeRTOS_. Further updates to this application note will be provided here.**

The Silicon Labs OpenThread SDK provides support for running on top of FreeRTOS, a full-featured Real-Time Operating System (RTOS) for microcontrollers and small microprocessors. Support for FreeRTOS is integrated into Simplicity Studio.

FreeRTOS is supported on the EFR32MGxx family. For documentation on Free RTOS, see [https://www.freertos.org/](https://www.freertos.org/).

##### Getting Started

Integrating FreeRTOS into your application is simply a matter of installing the FreeRTOS component for the project in Simplicity Studio. The ot-ble-dmp sample application runs on FreeRTOS by default, and you can add FreeRTOS to any OpenThread project. The ot-cli-ftd sample application is a good starting example.

1. In your project, double-click the **.slcp file** for the project in the Project Explorer to open the project window.
2. Click the SOFTWARE COMPONENTS tab to see a complete list of Component categories.
3. Find the FreeRTOS component located under RTOS > FreeRTOS in the list of components.
4. Select **FreeRTOS** and then click **Install**.  
   ![ot-cli-ftd screen](/openthread-with-free-rtos/0.1/images/sld698-image1.png)  
   This component brings all the FreeRTOS kernel files into your project, along with some integration files and some additional components it depends on.
5. Click **View Dependencies** to display the components.

One dependency is the CMSIS-RTOS2 component, which is an RTOS abstraction layer used by the integration files. Silicon Labs recommends that application developers use the FreeRTOS API directly rather than using the CMSIS-RTOS2 API.

Another dependency is the heap implementation used by FreeRTOS. FreeRTOS comes with five different heap implementations. Each appears as a component. By default, the FreeRTOS Heap 4 component is added to the project. You can change this by selecting a different heap component and clicking **Install**. For example, FreeRTOS HEAP 3 uses the system `malloc()` and `free()` implementation and is a common choice.

##### Main Function and Task Model

The FreeRTOS component is designed to be used along with the standard Silicon Labs main.c template. The standard main function works for both bare metal and kernel-based projects and takes care of all the required system initializations and task creation.

For OpenThread running on FreeRTOS, a single task is created that runs both the OpenThread stack and the application logic. It is not safe to call the OpenThread API from other tasks.

In a bare metal OpenThread application, application logic is placed in the `app_process_action()` callback. Instead, when running on FreeRTOS, application logic is placed in the `sl_ot_rtos_application_tick()` callback.

#### Configuring OpenThread Applications for Thread 1.3

##### Configuring OpenThread Applications for Thread 1.3

> **NOTE: This section replaces _AN1372: Configuring OpenThread Applications for Thread 1.3_. Further updates to this application note will be provided here**.

Thread 1.3 builds on Thread 1.1 and Thread 1.2’s robust foundation. It defines enhancements and additions to the Thread Border Router definition to enable bidirectional IPv6 connectivity, service discovery using DNS, and IPv4-backwards support using NAT. It includes support for Thread-over-infrastructure (non-802.15.4 IPv6) links. Finally, to remedy throughput concerns, the specification defines support for TCP as a standard component and protocol.

> **Note**: Starting with the **Simplicity SDK 2024.12.0** release, Silicon Labs includes the OpenThread stack with the current default protocol version 1.4 (=5). Prior releases defaulted to protocol version 1.3. For information on configuring OpenThread Applications for Thread 1.4, see [Configuring OpenThread Applications for Thread 1.4](/openthread/{build-docspace-version}/configuring-openthread-apps-for-thread-1-4).

Note that the Thread 1.3 border router certification program was sunset at the end of 2025, after which only Thread 1.4 border router certification applications may be submitted. Thread 1.3 components and end products may still be certified.

Silicon Labs provides components and configuration options that enable you to configure Thread 1.3 features with sample applications. These features are compatible with EFR32MG1x and EFR32MG2x SoCs, RCPs, and modules. This application note assumes you have a basic understanding of how Thread is implemented on EFR32 devices. For more information, see [Thread Fundamentals](/openthread/{build-docspace-version}/thread-fundamentals).

###### Key Points

- Including Thread 1.3 features in SoC Applications.
- Including Thread 1.3 features in an OpenThread Border Router.

##### Including Thread 1.3 features in SoC Applications

Silicon Labs provides a number of sample SoC OpenThread applications. You can modify these to include Thread 1.3 features (some of which are enabled by default). This chapter assumes you are familiar with creating and modifying OpenThread projects in Simplicity Studio. If you need more information, see the Simplicity Studio User’s Guide and the [OpenThread Quick Start Guide](/openthread/{build-docspace-version}/openthread-quick-start-guide).

As an example, the following procedure shows how to configure 1.3 features:

1. Create a project based on the example: **OpenThread – SoC CLI (FTD)**.
2. On the **SOFTWARE COMPONENTS** tab, search for and select the **Stack (FTD)** entry. Depending on your application, you may have to do this on a **Stack (MTD)** or **Stack (RCP)** component (this example is for an FTD application).  
   ![Software Components tab](/configuring-openthread-apps-for-thread-1-3/0.1/images/sld862-image1.png)
3. Configure the various compile-time settings. The options are explained in the OpenThread documentation.  
   ![compile-time settings](/configuring-openthread-apps-for-thread-1-3/0.1/images/sld862-image2.png)

**For Thread 1.3 features**, the following flags are required. The description for each flag indicates whether it is mandatory, optional, or recommended. **Do not enable** these flags for a Thread 1.1 application.

- **Thread Stack Protocol Version**: Set to Thread 1.3 (mandatory).
- **DNS Client** (mandatory): Required for Thread 1.3 compliance.
- **DNS-SD Server** (recommended): FTDs only. Required for Thread 1.3 compliance on Thread Border Routers. Optional otherwise.
- **SRP Client** (mandatory): Required for Thread 1.3 compliance.
- **SRP Server** (recommended): FTDs only. Required for Thread 1.3 compliance on Thread Border Routers. Optional otherwise.
- **TCP features**: For more information, see [TCP in Thread 1.3](#tcp-in-thread-13).  
  - **TCP** (recommended): Recommended for Thread 1.3 compliance. Declarative support required for Thread 1.3 component certification.  
  - **DNS Client over TCP**: Recommended for Thread 1.3 compliance. Declarative support required for Thread 1.3 component certification.
- **Thread over Infrastructure** (recommended): NCPs only. Required for Thread 1.3 compliance on Thread Border Routers (and enabled by default for the border router POSIX stack). For sample applications on EFR platforms, this applies only to NCPs, and as such is an untested feature, as Silicon Labs does not support full-featured NCP FTD/MTD applications.

Additional information about these features is included in the following table.

<table>
    <thead>
        <tr>
            <th>Flag</th>
            <th>Note</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>DNS Client<br>(OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE)</td>
            <td>
                Must always be turned on for Thread 1.3 conformance. Enables support for DNS client. Enables sending DNS queries for AAAA (IPv6) records.
            </td>
        </tr>
        <tr>
            <td>DNS-SD Server<br>(OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE)</td>
            <td>
                Enables support for DNS-SD server. Service information from a local SRP server is used to resolve DNS-SD queries.<br>
                A DNS server should implement the following features:
                <ul>
                    <li>DNS recursive resolver to answer queries for all valid DNS record types including, for example, host name records. DNS type "A" and "AAAA" address records.</li>
                    <li>DNS authoritative server that answers authoritatively for DNS-Based Service Discovery [RFC 6763] records and any other DNS records registered with the Thread Service Registry by clients using the Service Registration Protocol.</li>
                    <li>DNS Update Server: A server that accepts properly authenticated client requests to update authoritative DNS data.</li>
                </ul>
            </td>
        </tr>
        <tr>
            <td>SRP Client<br>(OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE)</td>
            <td>
                Must always be turned on for Thread 1.3 conformance. Enables support for SRP (Service Registration Protocol) client. An SRP client Thread Device registers services with the SRP server, communicates with the corresponding DNS-SD authoritative server for queries, and uses the DNS recursive resolver for DNS resolution as defined by the respective IETF specifications.<br>
                For more information, see:<br>
                <a href="https://github.com/openthread/openthread/blob/main/src/cli/README_SRP_CLIENT.md">https://github.com/openthread/openthread/blob/main/src/cli/README_SRP_CLIENT.md</a>
            </td>
        </tr>
        <tr>
            <td>SRP Server<br>(OPENTHREAD_CONFIG_SRP_SERVER_ENABLE)</td>
            <td>
                Enables support for SRP (Service Registration Protocol) server. An SRP server supports the DNS Update Server functions, plus additional public key cryptography for security and some other minor enhancements to better support constrained clients. For more information, see:<br>
                <a href="https://github.com/openthread/openthread/blob/main/src/cli/README_SRP.md">https://github.com/openthread/openthread/blob/main/src/cli/README_SRP.md</a>
            </td>
        </tr>
        <tr>
            <td>TCP API<br>(OPENTHREAD_CONFIG_TCP_ENABLE)</td>
            <td>
                Enables low-power TCP APIs.<br>
                For more information, see:<br>
                <a href="https://github.com/openthread/openthread/blob/main/src/cli/README_TCP.md">https://github.com/openthread/openthread/blob/main/src/cli/README_TCP.md</a>
            </td>
        </tr>
        <tr>
            <td>DNS Client over TCP<br>(OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE)</td>
            <td>
                Enables sending DNS queries over TCP.<br>
                For more information, see:<br>
                <a href="https://github.com/openthread/openthread/blob/main/src/cli/README.md#dns-config">https://github.com/openthread/openthread/blob/main/src/cli/README.md#dns-config</a>
            </td>
        </tr>
        <tr>
            <td>Thread over Infrastructure<br>(OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE)</td>
            <td>
                Must always be turned on for Thread 1.3 conformance. Enables TREL radio link for Thread over Infrastructure feature.<br> For sample applications, this is applicable to NCPs only, which are currently not supported by Silicon Labs. See the next section for information on how this applies to border router POSIX platforms.
            </td>
        </tr>
        <tr>
            <td>Delay Aware Queue Management<br>(OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE)</td>
            <td>
                Must always be turned on for Thread 1.3 conformance. Devices will monitor time-in-queue of messages in the direct tx queue and if the wait time is larger than specified thresholds it may update ECN flag (if message indicates it is ECN-capable) or drop the message.
            </td>
        </tr>
    </tbody>
</table>

###### TCP in Thread 1.3

Thread 1.3 component certification requires declarative support for TCP, which means the TCP APIs (OPENTHREAD_CONFIG_TCP_ENABLE and  OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE) must be enabled for conformance. However, if one chooses to not include TCP, then the third-party TCPlp implementation can be stubbed out using the “**TCPlp stubs for OpenThread TCP API**” component. If this component is installed, TCPlp implementation will be stubbed for an FTD / MTD application. Otherwise, the TCPlp implementation is included by default.

![TCPlp](/configuring-openthread-apps-for-thread-1-3/0.1/images/sld862-image3.png)

It is recommended to enable TCP for Thread 1.3 conformance so that OpenThread border routers using DNS can make use of well-known DNS-over-TCP queries and responses.

> **Note**: TCP is supported in SoC and POSIX host (OTBR) applications. It is not required on RCP applications, and can be supported on NCP applications, but Silicon Labs does not yet provide support for full-featured NCP FTD/MTD applications.

##### Including Thread 1.3 features in an OpenThread Border Router

Silicon Labs provides several sample OpenThread RCP applications. By default, the RCP applications on supported Silicon Labs hardware automatically support Thread 1.3 features if they are present on the host (the Border Router, which is Silicon Labs’ supported RCP model). None of the Thread 1.3 features are RCP-specific, so turning them on or off for the RCP sample application has no effect.

Refer to [Using the Silicon Labs RCP with the OpenThread Border Router](/openthread/{build-docspace-version}/using-sl-rcp-with-openthread-border-router) for detailed instructions on how to build an OpenThread Border Router for Raspberry Pi 3B+ or above. You must use a Thread protocol version 1.3 RCP with a Border Router that is also running a stack at protocol version 1.3.

Thread 1.3 features can be enabled on the border router separately by making sure the POSIX stack enables the following CMake flags. These flags are enabled by default with the current default OpenThread Border Router offering. (See [Thread 1.3 Configuration Flags](01-including-thread-1-3-features-in-soc-applications#including-thread-13-features-in-soc-applications) for more information on their purpose.)

<table>
    <thead>
        <tr>
            <th>CMake Flag</th>
            <th>Thread 1.3 Configuration Flag</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>OT_DNS_CLIENT</td>
            <td>OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE</td>
        </tr>
        <tr>
            <td>OT_DNSSD_SERVER</td>
            <td>OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE</td>
        </tr>
        <tr>
            <td>OT_SRP_CLIENT</td>
            <td>OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE</td>
        </tr>
        <tr>
            <td>OT_SRP_SERVER</td>
            <td>OPENTHREAD_CONFIG_SRP_SERVER_ENABLE</td>
        </tr>
        <tr>
            <td>OT_TREL</td>
            <td>
                OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE<br>
                Make sure that the <code>OPENTHREAD_CONFIG_POSIX_APP_TREL_INTERFACE_NAME</code> property is also set to the IPv6 link on which you wish to enable TREL (via the trel:// argument).<br>
                On a network with at least two border routers, if TREL is enabled on both border routers on the same shared infrastructure link, they can automatically use that link to provide a single Thread partition.
            </td>
        </tr>
    </tbody>
</table>

You can install a pre-built Docker container with OpenThread Border Router: [https://hub.docker.com/r/siliconlabsinc/openthread-border-router/tags](https://hub.docker.com/r/siliconlabsinc/openthread-border-router/tags).

Or you can manually install an OpenThread Border Router by following the steps in [Using the Silicon Labs Co-Processors with the OpenThread Border Router](/openthread/{build-docspace-version}/using-sl-coprocessors-with-openthread-border-router) or [https://openthread.io/guides/border-router/build](https://openthread.io/guides/border-router/build).

#### Configuring OpenThread Applications for Thread 1.4

##### Configuring OpenThread Applications for Thread 1.4

> **NOTE: This section replaces _AN1499: Configuring OpenThread Applications for Thread 1.4_. Further updates to this application note will be provided here**.

Thread 1.4 builds on Thread’s robust foundation. Thread 1.4 border routers seamlessly allow onboarding devices by making use of a standardized way to share credentials and join Thread networks. Device manufacturers can add compelling new features and offer dynamic support and cloud-powered features. Thread 1.4 also allows enhanced mesh diagnostics by providing more thorough visibility into a Thread mesh topology. Additionally, for professional installations and commercial building scenarios, Thread 1.4 defines secure commissioning at scale by using authenticated transport layers over standards such as Bluetooth.

> **Note**: Silicon Labs includes the OpenThread stack with the current default protocol version 1.4 (=5). All mandatory features in 1.3, 1.2, and 1.1 are automatically enabled, and the stack is backwards compatible.

Please note that the Thread 1.3 border router certification program is being sunset at the end of 2025, after which only Thread 1.4 border router certification applications may be submitted. Thread 1.3 components and end products may still be certified.

Silicon Labs provides components and configuration options that enable you to configure Thread 1.4 features with sample applications. These features are compatible with EFR32MG1x and EFR32MG2x SoCs, RCPs, and modules. This application note assumes you have a basic understanding of how Thread is implemented on EFR32 devices. For more information, see [Thread Fundamentals](/openthread/{build-docspace-version}/thread-fundamentals).

###### Key Points

- Including Thread 1.4 features in SoC Applications.
- Including Thread 1.4 features in an OpenThread Border Router.

##### Including Thread 1.4 features in SoC Applications

Silicon Labs provides a number of sample SoC OpenThread applications. You can modify these to include Thread 1.4 features (most of which are enabled by default). This chapter assumes you are familiar with creating and modifying OpenThread projects in Simplicity Studio. If you need more information, see the Simplicity Studio and the [OpenThread Quick Start Guide](/openthread/{build-docspace-version}/openthread-quick-start-guide).

As an example, the following procedure shows how to configure 1.4 features:

1. Create a project based on the example: **OpenThread – SoC CLI (FTD)**.
2. On the **SOFTWARE COMPONENTS** tab, search for and select the **Stack (FTD)** entry. Depending on your application, you may have to do this on a **Stack (MTD)** or **Stack (RCP)** component (this example is for an FTD application).  
   ![software components](/configuring-openthread-apps-for-thread-1-4/0.2/images/sld863-image1.png)
3. In the stack component, make sure to configure the **Thread Stack Protocol Version** to 1.4.  
   ![Thread Stack Protocol Version](/configuring-openthread-apps-for-thread-1-4/0.2/images/sld863-image2.png)

The following are border router features defined in the 1.4 specification of the Thread standard. The OpenThread stack allows configuration of these features for any Thread stack protocol version and on any router. However, a Thread 1.4 compliant border router device must enable these features.

- Ephemeral Key (ePSKc) support for Credential Sharing  
  - Mandatory and applicable to border routers only
- Extended Mesh Diagnostics  
  - Mandatory for border routers  
  - Can also be enabled on any router capable device
- DHCPv6 Prefix Delegation support  
  - Mandatory and applicable to border routers only

**There is one exception that is omitted from configuration**:

- Thread Commissioning over Authenticated TLS (TCAT over Bluetooth)  
  - Optional as a commercial feature, and not required for Thread 1.4 certification

Additional information about these features is included in the following table.

<table>
    <thead>
        <tr>
            <th>Flag</th>
            <th>Note</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td> Ephemeral Key for Credential Sharing (OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE) </td>
            <td> Enables setting an ephemeral PSKc to allow a commissioner (from a different network or ecosystem for example) to establish a secure connection with a border router. Once a connection is established with an ePSKc, the commissioner can set or program a different network active dataset, thus allowing multiple Thread networks to form a single, larger ranging mesh.<br><br> This feature is only meant for devices with a border agent, such as a Thread border router. <br><br> For more information, see: <br>
                <a href="https://github.com/openthread/openthread/blob/main/src/cli/README.md#ba-ephemeralkey">https://github.com/openthread/openthread/blob/main/src/cli/README.md#ba-ephemeralkey</a>
            </td>
        </tr>
        <tr>
            <td> Thread Admin Protocol (TAP) Ephemeral Key </td>
            <td> Extends the Border Agent ephemeral key mechanism with Thread Admin Protocol (TAP) key generation and validation using a Verhoeff checksum. Enables a standardized admin credential flow for credential sharing across Thread networks.</td>
        </tr>
        <tr>
            <td> Mesh Diagnostics (OPENTHREAD_CONFIG_MESH_DIAG_ENABLE) </td>
            <td> Allow sending diagnostic requests and queries to other nodes and process the responses. Includes support for enhanced network diagnostics in Thread 1.4 to enable better insight into network topology and neighbor links, helping one visualize network state. <br><br> For more information, see commands starting at: <br>
                <a href="https://github.com/openthread/openthread/blob/main/src/cli/README.md#meshdiag-topology-ip6-addrs-children">https://github.com/openthread/openthread/blob/main/src/cli/README.md#meshdiag-topology-ip6-addrs-children</a>
            </td>
        </tr>
        <tr>
            <td> DHCP6 PD feature (OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE) </td>
            <td> Supports handling platform generated ND messages. The prefix can be allocated by other software on the interface, which will advertise the assigned prefix to the thread interface via router advertisement messages. </td>
        </tr>
        <tr>
            <td> Border Agent Admitter (OPENTHREAD_CONFIG_BORDER_AGENT_ADMITTER_ENABLE) </td>
            <td> Enables the Border Agent Admitter, which manages the admission of enrollers (external commissioners) into the network. The Admitter tracks enroller state and handles joiner relay, forming part of the Thread 1.4 administration sharing model.</td>
        </tr>
        <tr>
            <td> Thread Commissioning over Authenticated TLS (implemented over Bluetooth) (OPENTHREAD_CONFIG_BLE_TCAT_ENABLE) </td>
            <td> This is an optional feature meant only for commercial networks. <br><br> To read more about Thread Commissioning at Scale, see example client at: <br>
                <a href="https://github.com/openthread/openthread/tree/main/tools/tcat_ble_client">https://github.com/openthread/openthread/tree/main/tools/tcat_ble_client</a>
            </td>
        </tr>
    </tbody>
</table>

##### Including Thread 1.4 features in an OpenThread Border Router

Silicon Labs provides several sample OpenThread RCP applications. On supported Silicon Labs hardware, RCP firmware will already include all functionality required for Thread 1.4 operations. Since Thread 1.4 features are border router-level features (not RCP-specific), enabling or disabling them in the RCP sample application has no effect.

Refer to [Using the Silicon Labs RCP with the OpenThread Border Router](/openthread/{build-docspace-version}/using-sl-rcp-with-openthread-border-router) for detailed instructions on how to build an OpenThread Border Router for Raspberry Pi 3B+ or above. You must use a Thread protocol version 1.4 RCP with a Border Router that is also running a stack at protocol version 1.4.

Most Thread 1.4 features can be enabled on the border router simply by setting the protocol version to Thread 1.4, as they will be enabled by default. However, you can review the following CMake flags for more information. (See [Thread 1.4 Configuration Flags](01-including-thread-1-4-features-in-soc-applications#including-thread-14-features-in-soc-applications) for more information on their purpose.)

|CMake Flag|Thread 1.4 Configuration Flag|Notes|
|---|---|---|
|OT_BORDER_AGENT_EPSKC|OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE|Ephemeral PSKc for credential sharing. Mandatory for Thread 1.4 border routers.|
|OT_MESH_DIAG|OPENTHREAD_CONFIG_MESH_DIAG_ENABLE|Extended mesh diagnostics. Mandatory for Thread 1.4 border routers.|
|OT_BORDER_ROUTING_DHCP6_PD|OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE|DHCPv6 Prefix Delegation. Mandatory for Thread 1.4 border routers.|
|OT_BLE_TCAT|OPENTHREAD_CONFIG_BLE_TCAT_ENABLE|Thread Commissioning over Authenticated TLS (Bluetooth). Optional; not required for Thread 1.4 certification.|
|OT_BORDER_AGENT_ADMITTER|OPENTHREAD_CONFIG_BORDER_AGENT_ADMITTER_ENABLE|Border Agent Admitter. Manages admission of external enrollers and handles joiner relay. Enabled automatically.|

Except for the TCAT feature, all of the other border router features are automatically enabled when building for Thread 1.4 version.

You can install a pre-built Docker container with OpenThread Border Router:

[https://hub.docker.com/r/siliconlabsinc/openthread-border-router/tags](https://hub.docker.com/r/siliconlabsinc/openthread-border-router/tags)

Or you can manually install an OpenThread Border Router by following the steps in [Using the Silicon Labs RCP with the OpenThread Border Router](/openthread/{build-docspace-version}/using-sl-rcp-with-openthread-border-router) or [https://openthread.io/guides/border-router/build](https://openthread.io/guides/border-router/build).

### OpenThread Border Router

#### OpenThread Border Router

A Thread Border Router connects a Thread Network to other IPbased networks, such as Wi-Fi® or Ethernet®. A Thread Network requires a Border Router to connect to other networks. The Border Router provides services for devices within the Thread Network, including routing services for off-network operations, bidirectional connectivity over IPv6 infrastructure links, and service registry to enable DNS-based service discovery.

- [**Using the Silicon Labs Co-processors with the OpenThread Border Router**](/openthread/3.1.0/using-sl-coprocessors-with-openthread-border-router): Describes using the OpenThread Border Router GitHub repository and the Silicon Labs OpenThread co-processor applications (Radio Co-Processor (RCP) and Network Co-Processor (NCP)) to create a Thread Border Router.

#### Using the Silicon Labs Co-processors with the OpenThread Border Router

##### Using the Silicon Labs Co-Processors with the OpenThread Border Router

**NOTE: This section replaces _AN1256: Using the Silicon Labs RCP with the OpenThread Border Router_. Further updates to this application note will be provided here.**

A Thread Border Router connects a Thread Network to other IP-based networks, such as Wi-Fi® or Ethernet®. A Thread Network requires a Border Router to connect to other networks. The Border Router provides services for devices within the Thread Network, including routing services for off-network operations, bidirectional connectivity over IPv6 infrastructure links, and service registry to enable DNS-based service discovery. Silicon Labs provides a Border Router Add-On Kit containing a Raspberry Pi device and an example Radio Co-Processor (RCP) application required to build Border Router software.

Refer to the OpenThread release notes for the stable version commits of OpenThread (openthread) and OpenThread Border Router (ot-br-posix) repos supported by a Silicon Labs release. OpenThread release notes are installed with the SDK and are also available within the [OpenThread documentation site](https://docs.silabs.com/openthread/latest/sisdk-ot-release-notes/). This applies to all default containers provided by Silicon Labs for the release, and the copies of these repos included in the release. While we support building using any commit on GitHub (using the [ot-efr32](https://github.com/openthread/ot-efr32) repo), note that the latest public code on GitHub can be unstable.

##### Introduction

This application note is intended for software engineers who wish to develop an OpenThread Border Router (OTBR). It assumes some familiarity with OpenThread and basic Thread concepts. For an introduction to OpenThread and information on Thread concepts, visit [https://openthread.io/](https://openthread.io/). For information on OTBR setup and installation, refer to [https://openthread.io/guides/border-router](https://openthread.io/guides/border-router).

This application note assumes that you have downloaded Simplicity Studio and the Silicon Labs OpenThread SDK and are generally familiar with the SSv6 Launcher perspective. SSv6 installation and getting started instructions, along with a set of detailed references, can be found in the online [Simplicity Studio 6 Overview](https://docs.silabs.com/ssv6ug/latest/ssv6ug-overview/) and through the SSv6 Learn & Support Section. Refer to the [OpenThread Quick Start Guide](/openthread/3.1.0/openthread-quick-start-guide) for more information about configuring, building, and flashing OpenThread sample applications.

This application note addresses the following topics:

- **Build and Installation Instructions for the RCP Images**  
  Explains the build and installation procedure for the Radio Co-Processor (RCP) image using UART as well as SPI interfaces.
- **Build and Installation Instructions for the NCP Images**  
  Explains the build and installation procedure for the Network Co-Processor (NCP) image, including configuration options for host wakeup GPIO functionality.
- **Build and Installation Instructions for the OpenThread Border Router**  
  Defines the build and installation procedure for the OpenThread Border Router on POSIX-based platforms, including an option to deploy a pre-built Docker container for the Raspberry Pi.
- **OTBR Configuration Information**  
  Provides OTBR information such as how to configure various Border Router features and the Network Address Translation (NAT64) interface.
- **Additional OpenThread Resources**  
  Includes links to OpenThread Resources.

###### Hardware Requirements

A Thread Border Router has two components:

- A Raspberry Pi host with Thread Border Router support (Recommended: Raspberry Pi 3 Model B+ or above)
- A Thread-capable Silicon Labs Radio Co-processor (RCP)

The RCP in this application is only compatible with the Thread protocol and utilizes a serial connection (UART or SPI) to interface with the host. Spinel is used on top of this serial connection to facilitate communication. Provided that the OTBR is correctly configured on the host, the RCP should function seamlessly, making it effectively host-agnostic. For more information on supported host platforms, see [OpenThread Border Router](https://openthread.io/guides/border-router).

To create the RCP, you need the following:

- [EFR32MG Wireless Starter Kit](https://www.silabs.com/products/development-tools/wireless/mesh-networking/mighty-gecko-starter-kit) or [Thunderboard Sense 2 Sensor-to-Cloud Advanced IoT Kit](https://www.silabs.com/development-tools/thunderboard/thunderboard-sense-two-kit)
- Silicon Labs board capable of Thread communication

> **Note**: For information about Silicon Labs Precompiled Images:
> 
> - **RCP**: see [Use Precompiled RCP Images](02-build-and-installation-instructions-for-the-rcp-images)
> - **NCP**: see [Use Precompiled NCP Images](03-build-and-installation-instructions-for-the-ncp-images)

##### Build and Installation Instructions for the RCP Images

> **Note**: The following instructions only apply to RCP images built using Simplicity Studio for a given Simplicity SDK release.

To build an RCP image using the latest OpenThread, follow instructions on the [ot-efr32](https://github.com/openthread/ot-efr32) repo.

###### Use Precompiled RCP Images

Silicon Labs has precompiled images available for these boards with their associated image locations. The default precompiled images are configured for UART interface.

> **Note**: By default, the Silicon Labs Simplicity SDK uses Thread protocol version 1.4. A set of prebuilt RCP demo applications are provided with the OpenThread SDK.

###### Build RCP Images Using Simplicity Studio

Silicon Labs has sample applications for several standard OpenThread images.

1. Select **ot-rcp** as an example for the default RCP image for the OpenThread Border Router over UART interface.
2. With your target part connected to your computer, open Simplicity Studio's **Project** tab and select **Create New Project**.
3. The Example Project & demos selection dialog opens. Use the **Keyword** filter to search for **ot-rcp** as an example for the default RCP image for the OpenThread Border Router. Select it and click **Create**.
4. The Project Configuration dialog opens. Rename your project, change the default project file location, and determine if you will link to or copy project files. Note that, if you change any linked resource, it is changed for any other project that references it. Click **FINISH.**
5. The Simplicity Project Configurator opens to the **OVERVIEW** tab. See the online _Simplicity Studio 6 Overview_ for details about the functionality available through the Project Configurator.  
   ![Overview tab](/using-sl-coprocessors-with-openthread-border-router/0.2/images/sld1037-image1.png)
6. Compile and flash the application image as described in the [OpenThread Quick Start Guide](/openthread/3.1.0/openthread-quick-start-guide).

###### Configure RCP Image for SPI From Default OT-RCP Application Using Simplicity Studio

1. Generate ot-rcp application as described in the previous section steps 1 – 4.
2. Under the **SOFTWARE COMPONENTS** tab in your RCP project (.slcp), expand the **Services** menu. Select **vcom** under the **IO STREAM USART or IO STREAM EUSART** component.  
   ![software components screen](/using-sl-coprocessors-with-openthread-border-router/0.2/images/sld1037-image2.png)
3. Click **Uninstall** to remove the component, which uninstalls the **IO STREAM** component as well.
4. Under the **SOFTWARE COMPONENTS** tab in your RCP project (.slcp), expand the **OpenThread** menu. Select the **NCP SPIDRV** component and click **Install**.  
   ![NCP SPIDRV](/using-sl-coprocessors-with-openthread-border-router/0.2/images/sld1037-image3.png)
5. Compile and flash the application image as described in the [OpenThread Quick Start Guide](/openthread/3.1.0/openthread-quick-start-guide).

###### Configure OpenThread Options in the RCP Images Using Simplicity Studio

1. Under the **SOFTWARE COMPONENTS** tab in your RCP project (.slcp), expand the **OpenThread** menu. Select **Stack (RCP)** for an RCP build.
2. Click **Configure** to change the settings associated with the OpenThread build.  
   > **Note**: You can select the **Configurable Components** and **Installed Components** checkboxes to filter only those components you can configure successfully.  
   ![Configure](/using-sl-coprocessors-with-openthread-border-router/0.2/images/sld1037-image4.png)
3. Configure the various compile-time settings for your RCP project. The various build options are explained in the OpenThread documentation at [https://openthread.io/guides/build](https://openthread.io/guides/build).
4. For Coexistence with WiFi configurations, see [Zigbee and OpenThread Coexistence with WiFi](https://docs.silabs.com/multiprotocol/latest/zigbee-openthread-coexistence-wifi/).

##### Build and Installation Instructions for the NCP Images

###### NCP Architecture Overview

In the standard NCP (Network Co-Processor) design, Thread network features run on the SoC while the application layer runs on a separate host processor. The host processor is typically more powerful than the OpenThread device, though it has higher power consumption.

This architecture offers power management advantages: the higher-power host can enter sleep mode while the lower-power OpenThread device stays active to maintain its Thread network connection. Additionally, since the SoC handles only the Thread stack and not the application layer, application development and testing can proceed independently of the OpenThread build.

###### Building NCP Images

> **Note**: The following instructions only apply to NCP images built using Simplicity Studio for a given Simplicity SDK release.

To build an NCP image using the latest OpenThread, follow instructions on the [ot-efr32](https://github.com/openthread/ot-efr32) repo.

###### Use Precompiled NCP Images

Silicon Labs has precompiled NCP images available for these boards with their associated image locations. The default precompiled images are configured for UART interface.

> **Note**: By default, the Silicon Labs Simplicity SDK uses Thread protocol version 1.4. A set of prebuilt NCP demo applications are provided with the OpenThread SDK.

###### Build NCP Images Using Simplicity Studio

Silicon Labs has sample applications for several standard OpenThread NCP images.

1. Select **ot-ncp-ftd** or **ot-ncp-mtd** as an example for the default NCP image for the OpenThread Border Router over UART interface.
2. With your target part connected to your computer, open Simplicity Studio's **Project** tab and select **Create New Project**.
3. The Example Project & demos selection dialog opens. Use the **Keyword** filter to search for **ot-ncp** as an example for the default NCP image for the OpenThread Border Router. Select **ot-ncp-ftd** for Full Thread Device or **ot-ncp-mtd** for Minimal Thread Device and click **Create**.
4. The Project Configuration dialog opens. Rename your project, change the default project file location, and determine if you will link to or copy project files. Note that, if you change any linked resource, it is changed for any other project that references it. Click **FINISH.**
5. The Simplicity Project Configurator opens to the **OVERVIEW** tab. See the online _Simplicity Studio 6 Overview_ for details about the functionality available through the Project Configurator.  
   ![screenshot](/using-sl-coprocessors-with-openthread-border-router/0.2/images/sld1037-image5.png)
6. Compile and flash the application image as described in the [OpenThread Quick Start Guide](/openthread/3.1.0/openthread-quick-start-guide).

###### Configure OpenThread Options in the NCP Images Using Simplicity Studio

1. Under the **SOFTWARE COMPONENTS** tab in your NCP project (.slcp), expand the **OpenThread** menu. Select **Stack (FTD)** for an NCP FTD build or **Stack (MTD)** for an NCP MTD build.
2. Click **Configure** to change the settings associated with the OpenThread build.  
   > **Note**: You can select the **Configurable Components** and **Installed Components** checkboxes to filter only those components you can configure successfully.  
   ![screenshot](/using-sl-coprocessors-with-openthread-border-router/0.2/images/sld1037-image6.png)
3. Configure the various compile-time settings for your NCP project. The various build options are explained in the OpenThread documentation at [https://openthread.io/guides/build](https://openthread.io/guides/build).  
   ![screenshot](/using-sl-coprocessors-with-openthread-border-router/0.2/images/sld1037-image7.png)

###### Enable Host Wakeup GPIO Functionality

###### Overview (heading level 7)

The Host Wakeup GPIO feature enables the NCP to wake a sleeping host processor using a dedicated GPIO pin. When the NCP needs host attention (incoming data, network events, etc.), it sets the GPIO HIGH for a configurable duration, allowing the host to remain in low-power sleep states until needed.

###### Configuration (heading level 7)

1. Open your NCP project in Simplicity Studio and navigate to the **SOFTWARE COMPONENTS** tab.
2. Under the **OpenThread** menu, locate the **Stack (FTD)** or **Stack (MTD)** component and click **Configure**.
3. In the configuration editor, find the **Host wakeup GPIO functionality** section.  
   ![screenshot](/using-sl-coprocessors-with-openthread-border-router/0.2/images/sld1037-image8.png)
4. Enable **Enable host wakeup using a GPIO pin** using the toggle button.
5. Configure the timeout duration (in milliseconds) for clearing the host wakeup GPIO pin using `SL_OPENTHREAD_HOST_CLEAR_PIN_TIMEOUT_MS` (default: 10ms).  
   ![screenshot](/using-sl-coprocessors-with-openthread-border-router/0.2/images/sld1037-image9.png)
6. Configure the GPIO pin assignment:  
   - Navigate to the **Pin Tool** section or GPIO configuration  
   - Set `SL_OPENTHREAD_HOST_WAKEUP_GPIO_PORT` to the desired port (e.g., `SL_GPIO_PORT_C`)  
   - Set `SL_OPENTHREAD_HOST_WAKEUP_GPIO_PIN` to the desired pin number (e.g., `0`)  
   ![screenshot](/using-sl-coprocessors-with-openthread-border-router/0.2/images/sld1037-image10.png)
7. Save your configuration and rebuild the project.

##### Build and Installation Instructions for the Border Router Host

> **Note**: In theory, the same Border Router host should work with both RCP and NCP architectures. The host automatically detects whether it is communicating with an RCP or NCP device and switches its operating mode accordingly. However, due to some advanced 1.4 OTBR features, we recommend different build steps for the host for RCP and NCP architectures. See [Build Steps](#build-steps).

###### Install the Hardware

Connect each Wireless Starter Kit main board and the host computer to an Ethernet switch with an Ethernet cable as shown in the following figure. These connections will permit programming and network analysis of the Co-Processor and end devices. Optionally, end devices may be connected to the host computer by USB rather than Ethernet. To connect Raspberry Pi Border Router with a Co-Processor over SPI, you can either hardwire the SPI pins with WSTK's expansion connector or you can use wireless expansion board (brd8016), which mounts on the top of Raspberry Pi.

![diagram](/using-sl-coprocessors-with-openthread-border-router/0.2/images/sld1037-image11.png)

###### Hardwire SPI Connections Between Raspberry Pi and WSTK (heading level 7)

Connect the SPI pins as shown below:

|Raspberry Pi Connector (SPI Pins)|WPK’s Expansion Connector (brd4002)|
|---|---|
|GPIO10 / Pin19 (MOSI)|Pin 4|
|GPIO9 / Pin21 (MISO)|Pin 6|
|GPIO11 / Pin23 (SCLK)|Pin 8|
|GPIO7/8 / Pin24/26 (CS0/CS1)|Pin 10|
|GPIO21 / Pin40 (Interrupt line)|Pin 7|
|GND / Pin 6|Pin 1|
|GPIO20 / Pin38 (Reset line)|Pin F4 on breakout connector|

![photo](/using-sl-coprocessors-with-openthread-border-router/0.2/images/sld1037-image12.jpg)

###### Wireless Expansion Board for SPI Connections Between Raspberry Pi and WSTK (heading level 7)

You can also use a [wireless expansion board](https://www.silabs.com/documents/public/user-guides/ug291-exp4320a-user-guide.pdf), which mounts on the top of Raspberry Pi to avoid hardwire connection, as shown below.

![photo](/using-sl-coprocessors-with-openthread-border-router/0.2/images/sld1037-image13.jpg)

> **Note**: Use correct OTBR_AGENT_OPTS as described in section [OTBR Configurations using SPI Interface](#otbr-configurations-using-spi-interface) depending on the SPI connections.

###### Install and Configure a Raspberry Pi for Use with a Co-Processor

Three different methods for installing the OTBR software in a Raspberry Pi for use with a Co-Processor are:

- Manual installation
- Pre-built packages for Debian bookworm
- Docker container (older releases only)

###### Manual Installation (heading level 7)

This guide covers how to build the OTBR using the tools provided by the Silicon Labs Simplicity SDK. You may also install the OTBR using the instructions detailed at [https://openthread.io/guides/border-router/build](https://openthread.io/guides/border-router/build). Note that this is a different build option, and either guide may be followed to build the OTBR.

Begin by cloning the Simplicity SDK repository:

```bash
git clone https://github.com/SiliconLabsSoftware/sisdk-release.git
```

Check out the desired SDK branch. For this guide, we use the `sisdk-2026.6` branch.

For the rest of this document, `$SDK_DIR` refers to the location the above repo is checked out. Set this variable now:

```bash
export SDK_DIR=<absolute path to the SDK checkout>
```

Silicon Labs maintains both `openthread` and `ot-br-posix` as forks of their respective upstream projects. To make the changes transparent, the SDK package ships the following under `$SDK_DIR/openthread_stack/`:

- `README.md` — overview of the patch files and how to inspect or apply them
- `openthread.patch` — diff between the upstream `openthread` base commit and the Silicon Labs fork
- `ot-br-posix.patch` — diff between the upstream `ot-br-posix` base commit and the Silicon Labs fork
- `upstream-commits` — the upstream base commit for each repository
- `CHANGELOG.md` — per-release summary of fork changes relative to upstream

The upstream commit SHAs are also listed in the Silicon Labs OpenThread Release Notes. You can use the patch files to inspect exactly what Silicon Labs changed, reproduce the diff independently, or apply it to your own fork with `git apply`.

Make sure that this symbolic link is present. If it does not exist, create it:

```bash
ln -s $SDK_DIR/openthread_stack/util/third_party/openthread \
  $SDK_DIR/openthread_stack/util/third_party/ot-br-posix/third_party/openthread/repo
```

###### Configuration Options Reference (heading level 8)

Starting with SiSDK 2026.6, we recommend that the bootstrap and setup scripts accept explicit environment variables that control which service implementations are compiled in. These must be passed consistently to **both** the bootstrap and setup steps.

The tables below document every environment variable recognized by `./script/bootstrap` and `./script/setup`. Variables shared by both scripts must be set to the same value for each script.

###### `./script/bootstrap` (heading level 9)

`bootstrap` resolves system package dependencies. These variables control which optional packages are installed.

|Variable|Repo Default|RCP Default|NCP Default|Description|Values|
|---|---|---|---|---|---|
|`OTBR_MDNS`|`openthread`|`openthread`|`mDNSResponder`|Select mDNS implementation|`openthread`, `mDNSResponder`|
|`OTBR_DHCP6_PD_CLIENT`|`none`|`dhcpcd`|`dhcpcd`|Select DHCPv6 Prefix Delegation client|`none`, `dhcpcd`, `openthread`|
|`NAT64`|`1`|`1`|`1`|Install NAT64-related packages (iptables, bind9)|`0`, `1`|
|`NAT64_SERVICE`|`openthread`|`openthread`|`tayga`|Select NAT64 translator implementation|`openthread`, `tayga`|
|`BACKBONE_ROUTER`|`1`|`1`|`1`|Install backbone router library packages (`libnetfilter-queue` etc.)|`0`, `1`|
|`FIREWALL`|`1`|`1`|`1`|Install firewall packages (`ipset` etc.)|`0`, `1`|
|`WEB_GUI`|`0`|`0`|`0`|Install web GUI dependencies (nodejs, npm)|`0`, `1`|
|`REFERENCE_DEVICE`|`0`|`0`|`0`|Install reference device packages (`radvd, dnsutils`, `iperf3`, `ndisc6` etc.)|`0`, `1`|
|`OT_BACKBONE_CI`|`0`|`0`|`0`|Install additional packages required for backbone CI testing (`dnsutils` etc.)|`0`, `1`|

###### `./script/setup` (heading level 9)

`setup` builds and installs the border router. These variables govern which Thread features are compiled in and how the infrastructure link is configured.

|Variable|Repo Default|RCP Default|NCP Default|Description|Values|
|---|---|---|---|---|---|
|`INFRA_IF_NAME`|`wlan0`|Valid interface|Valid interface|Infrastructure network interface name|Any valid interface, e.g. `eth0`, `wlan0`|
|`OTBR_MDNS`|`openthread`|`openthread`|`mDNSResponder`|mDNS implementation compiled into `otbr-agent`|`openthread`, `mDNSResponder`|
|`OTBR_DHCP6_PD_CLIENT`|`none`|`dhcpcd`|`dhcpcd`|DHCPv6 PD client compiled in; `dhcpcd` or `openthread` also enables `-DOTBR_DHCP6_PD=ON`|`none`, `dhcpcd`, `openthread`|
|`NAT64`|`1`|`1`|`1`|Enable NAT64 support (`-DOTBR_NAT64=ON`)|`0`, `1`|
|`NAT64_SERVICE`|`openthread`|`openthread`|`tayga`|NAT64 dataplane translator|`openthread`, `tayga`|
|`NAT64_DYNAMIC_POOL`|`192.168.255.0/24`|`192.168.255.0/24`|`192.168.255.0/24`|IPv4 address pool for the OpenThread NAT64 translator; passed as `-DOT_POSIX_NAT64_CIDR`|Any valid CIDR|
|`BORDER_ROUTING`|`1`|`1`|`1`|Enable Thread border routing (IPv6 RA install/forwarding)|`0`, `1`|
|`BACKBONE_ROUTER`|`1`|`1`|`1`|Enable Thread Backbone Router feature (`-DOTBR_BACKBONE_ROUTER=ON`)|`0`, `1`|
|`DISCOVERY_PROXY`|`1`|`1`|`1`|Enable DNS-SD discovery proxy; automatically disabled when `BORDER_ROUTING=0`|`0`, `1`|
|`FIREWALL`|`1`|`1`|`1`|Install and enable the `otbr-firewall` init service (`-DOT_FIREWALL=ON/OFF`)|`0`, `1`|
|`WEB_GUI`|`0`|`0`|`0`|Build the OTBR web management UI (`-DOTBR_WEB=ON`)|`0`, `1`|
|`REST_API`|`0`|`0`|`0`|Build the OTBR REST API (`-DOTBR_REST=ON`)|`0`, `1`|
|`REFERENCE_DEVICE`|`0`|`0`|`0`|Build as a Thread certification reference device; sets `-DOT_REFERENCE_DEVICE=ON`, `-DOTBR_NO_AUTO_ATTACH=1`, and related flags|`0`, `1`|
|`OTBR_VENDOR_NAME`|`OpenThread`|`OpenThread`|`OpenThread`|Vendor name advertised by the OTBR service (`-DOTBR_VENDOR_NAME`)|Any string|
|`OTBR_PRODUCT_NAME`|`BorderRouter`|`BorderRouter`|`BorderRouter`|Product name advertised by the OTBR service (`-DOTBR_PRODUCT_NAME`)|Any string|
|`OTBR_OPTIONS`|(unset)|(see build command)|(see build command)|Additional CMake `-D` flags appended verbatim to the build command|Any valid CMake flags|

> **Note**: `OTBR_MDNS=avahi` is deprecated as of SiSDK 2026.6 and is not recommended.

###### Build Steps (heading level 8)

> **Note**: The values shown below are what Silicon Labs builds the host with when qualifying our co-processor for certification. Alternate values are supported — if your integration requires a different combination, try the available options. For example, if your ecosystem relies on mDNSResponder, set `OTBR_MDNS=mDNSResponder` independently.

Set up the required environment variable before running any build commands (if not already set):

```bash
export SDK_DIR=<absolute path to the SDK checkout>
```

For the following steps, run from the `ot-br-posix` directory in the SDK:

```bash
cd $SDK_DIR/openthread_stack/util/third_party/ot-br-posix/
```

**Step 1: Run the bootstrap script**

For RCP:

```bash
sudo OTBR_MDNS=openthread OTBR_DHCP6_PD_CLIENT=dhcpcd NAT64=1 NAT64_SERVICE=openthread \
  ./script/bootstrap
```

For NCP:

```bash
sudo OTBR_MDNS=mDNSResponder OTBR_DHCP6_PD_CLIENT=dhcpcd NAT64=1 NAT64_SERVICE=tayga \
  ./script/bootstrap
```

**Step 2: Copy the Silicon Labs configuration header**

To use Silicon Labs-specific configuration settings for border router, copy the special configuration header hosted in the SDK:

```bash
sudo cp $SDK_DIR/openthread/platform-abstraction/posix/openthread-core-silabs-posix-config.h \
  $SDK_DIR/openthread_stack/util/third_party/openthread/src/posix/platform/
```

> **Note**: This configuration header overrides many settings to recommended values from the certifiable OTBR configuration. Review the file for any settings specific to your scenario.

**Step 3: Run the setup script**

Adjust `INFRA_IF_NAME` to match your network interface (`eth0` for Ethernet, `wlan0` for Wi-Fi).

These examples are for UART. To build OTBR with SPI interface, include the `-DOT_POSIX_RCP_SPI_BUS=ON` flag in `OTBR_OPTIONS`.

For RCP:

```bash
sudo INFRA_IF_NAME=eth0 OTBR_MDNS=openthread OTBR_DHCP6_PD_CLIENT=dhcpcd NAT64=1 \
  NAT64_SERVICE=openthread \
  OTBR_OPTIONS="-DOT_THREAD_VERSION=1.4 -DOT_PLATFORM_CONFIG=openthread-core-silabs-posix-config.h \
  -DOTBR_DUA_ROUTING=ON -DOTBR_DHCP6_PD=ON" \
  ./script/setup
```

For NCP:

```bash
sudo INFRA_IF_NAME=eth0 OTBR_MDNS=mDNSResponder OTBR_DHCP6_PD_CLIENT=dhcpcd NAT64=1 \
  NAT64_SERVICE=tayga \
  OTBR_OPTIONS="-DOT_THREAD_VERSION=1.4 -DOT_PLATFORM_CONFIG=openthread-core-silabs-posix-config.h \
  -DOTBR_DUA_ROUTING=ON -DOTBR_DHCP6_PD=ON" \
  ./script/setup
```

Refer to `openthread/src/core/config` and `openthread/examples/README.md` for compile-time constants and cmake build options, respectively.

###### Build Troubleshooting (heading level 8)

###### Missing `.default-version` file** (heading level 9)

If CMake reports an error such as:

> ```c
> file failed to open for reading: .../third_party/openthread/repo/.default-version
> ```

Create the missing file manually from the root of `ot-br-posix` before re-running setup:

> ```bash
> echo "0.01.00" > ./third_party/openthread/repo/.default-version
> ```

###### OTBR Feature Configuration for Certification (heading level 8)

For information on how to properly configure OpenThread Border Router features and services, visit [https://openthread.io/guides/border-router](https://openthread.io/guides/border-router#features_and_services).

> **Important**: For Thread Certification, the OpenThread and ot-br-posix repositories used for your build **must** be based on one of the following:
> 
> - The versions included in the Silicon Labs Simplicity SDK for the target release (as described in [Manual Installation](#manual-installation)), or
> - The specific upstream commits associated with the release, listed in the Silicon Labs OpenThread Release Notes.
> 
> Builds based on unverified forks or commits not tied to a supported release are not suitable for certification.

For information on Thread 1.4, see [Configuring OpenThread Applications for Thread 1.4](/openthread/3.1.0/configuring-openthread-apps-for-thread-1-4).

The build commands for a Thread Certification **Device Under Test (DUT)** are as follows. Run from `$SDK_DIR/openthread_stack/util/third_party/ot-br-posix/` (set `$SDK_DIR` as described in [Build Steps](#build-steps)). Use the RCP or NCP variant as appropriate for your co-processor type.

**RCP — bootstrap:**

```bash
sudo OTBR_MDNS=openthread OTBR_DHCP6_PD_CLIENT=dhcpcd NAT64=1 NAT64_SERVICE=openthread \
  ./script/bootstrap
```

**RCP — setup:**

```bash
sudo INFRA_IF_NAME=eth0 OTBR_MDNS=openthread OTBR_DHCP6_PD_CLIENT=dhcpcd NAT64=1 \
  NAT64_SERVICE=openthread \
  OTBR_OPTIONS="-DOT_THREAD_VERSION=1.4 -DOT_PLATFORM_CONFIG=openthread-core-silabs-posix-config.h \
  -DOTBR_DUA_ROUTING=ON -DOTBR_DHCP6_PD=ON" \
  ./script/setup
```

**NCP — bootstrap:**

```bash
sudo OTBR_MDNS=mDNSResponder OTBR_DHCP6_PD_CLIENT=dhcpcd NAT64=1 NAT64_SERVICE=tayga \
  ./script/bootstrap
```

**NCP — setup:**

```bash
sudo INFRA_IF_NAME=eth0 OTBR_MDNS=mDNSResponder OTBR_DHCP6_PD_CLIENT=dhcpcd NAT64=1 \
  NAT64_SERVICE=tayga \
  OTBR_OPTIONS="-DOT_THREAD_VERSION=1.4 -DOT_PLATFORM_CONFIG=openthread-core-silabs-posix-config.h \
  -DOTBR_DUA_ROUTING=ON -DOTBR_DHCP6_PD=ON" \
  ./script/setup
```

###### OTBR Serial Configuration (heading level 7)

###### OTBR Configurations Using UART Interface (heading level 8)

- Configure the desired tty* port to use for the OTBR to connect your Co-Processor at startup. Look for the tty* port of the Co-Processor device. The easiest way to do this is to look for a `/tty/dev…` entry once the device is connected. It should generally either be `/dev/ttyUSB0` or `/dev/ttyACM0`.
- Edit the `/etc/default/otbr-agent` file and look for the `OTBR_AGENT_OPTS` configuration. Include the tty* port name in that parameter as follows:  
  ```C  
  OTBR_AGENT_OPTS="-I wpan0 spinel+hdlc+uart:///dev/ttyACM0"  
  ```
- If running a Backbone Border Router (Thread protocol version 1.2 or above), add the backbone interface as follows:  
  ```C  
  OTBR_AGENT_OPTS="-I wpan0 -B eth0 spinel+hdlc+uart:///dev/ttyACM0"  
  ```
- If running a Thread 1.3 or greater Border Router, specify the Thread Radio Encapsulation Link (TREL) interface to enable Thread over Infrastructure links as follows:

```C
OTBR_AGENT_OPTS="-I wpan0 -B eth0 spinel+hdlc+uart:///dev/ttyACM0 trel://eth0"
```

###### UART Baud Rate Settings (heading level 9)

- By default, OTBR agent (the host daemon) is built to work with a baud rate of 460800. We recommend this configuration for a lot of realistic OTBR deployment scenarios.  Please make sure your Co-Processor and WSTK/WPK/STK adapter are configured accordingly.
- If you wish to change the default baud rate, we recommend that you update the radio URL options:

For example, to lower the baud rate to 115200:

```C
OTBR_AGENT_OPTS="-I wpan0 spinel+hdlc+uart:///dev/ttyACM0?uart-baudrate=115200 trel://eth0"
```

If intending to change this value, you _must_ also match the correct baud rate on the adapter board by issuing the following command on the admin console of the Co-Processor WSTK/WPK/STK adapter:

> serial vcom config speed 115200 (optional if you have updated adapter firmware that will autosense this rate)

Make sure to also set the correct baud rate in your Co-Processor project in the IO STREAM USART or IO STREAM EUSART component as shown in the following figure:

![screenshot](/using-sl-coprocessors-with-openthread-border-router/0.2/images/sld1037-image14.png)

- To verify the baud rate in use, issue the command:  
  ```C  
  stty -F /dev/ttyACM0  
  ```

###### OTBR Configurations using SPI Interface (heading level 8)

- Configure the desired SPI ports to use for the OTBR to connect your Co-Processor at startup. Ensure the SPI interface is enabled on the Raspberry Pi. If not:
- Enable it by adding following in /boot/config.txt  
  ```C  
  dtparam=spi=on  
  dtoverlay=disable-bt    #Maybe not required  
  ```
- Remove/comment `dtoverlay=spi0-1cs`,`cs0_pin=26` if it is defined in /boot/config.txt.
- Reboot the Raspberry Pi.
- Use this command to validate the SPI port `ls /dev | grep spi`.
- Two devices should be visible, `spidev0.0` and `spidev0.1`, depending on your Raspberry Pi version.
- Edit the `/etc/default/otbr-agent` file and look for the OTBR_AGENT_OPTS configuration. Include the correct GPIOs in that parameter as per the hardware setup in sections [Hardwire SPI Connections Between Raspberry Pi and WSTK](#hardwire-spi-connections-between-raspberry-pi-and-wstk) and [Wireless Expansion Board for SPI Connections Between Raspberry Pi and WSTK](#wireless-expansion-board-for-spi-connections-between-raspberry-pi-and-wstk).
- Use the following parameters if Raspberry Pi is hardwired to SPI pins on WSTK’s expansion connector. Use spidev0.0 if using Raspberry Pi’s CS0 or replace to spidev0.1 for CS1 pin.

```C
   OTBR_AGENT_OPTS="-I wpan0 -B eth0 spinel+spi:///dev/spidev0.0?gpio-int-device=/dev/gpiochip0&gpio-int-line=21&gpio-reset-device=/dev/gpiochip0&gpio-reset-line=20&no-reset=1&spi-speed=1000000"
```

- Use the following parameters if the radio board is mounted on Raspberry Pi using wireless expansion board (brd8016A).

```C
   OTBR_AGENT_OPTS="-I wpan0 -B eth0 spinel+spi:///dev/spidev0.0?gpio-int-device=/dev/gpiochip0&gpio-int-line=22&gpio-reset-device=/dev/gpiochip0&gpio-reset-line=23&no-reset=1&spi-speed=1000000"
```

- If using custom hardware connections, make sure to provide respective GPIOs pins.
- Start `otbr-agent` service by either rebooting the Raspberry Pi or issue `> sudo systemctl restart otbr-agent`.
- Issue the `> sudo ot-ctl state` command on the Raspberry Pi to see the status of the connection between the host and Co-Processor.
- Check whether all required services are running on the OTBR.  
  `sudo systemctl status` should not report any services as running in a “degraded” state.
- Check `/var/log/syslog` for a running log of otbr-agent.

###### OTBR Configuration for CSL (heading level 8)

> **Note**: This setting applies only when OpenThread runs on an **RCP**. In NCP mode the Thread stack runs on the co-processor, so this host-side configuration does not apply.

When the OTBR acts as a CSL transmitter with an RCP, you may see `Handle transmit done failed: Abort` if `OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US` is too low. Use **5000** µs with Series-2 RCPs and **18000** µs with Series-3 RCPs. Series-3 may require more preparation lead time today; Silicon Labs continues to optimize this path.

This value controls how early the host hands the frame to the radio stack. The on-air transmit time stays anchored to the child's CSL receive window, so extra lead time does not change over-the-air timing.

The bundled `openthread-core-silabs-posix-config.h` uses **18000** µs so one configuration works with both RCP families. Use the latest header when following [Build Steps](#build-steps).

If building the OTBR on your own using the instructions on the OpenThread website, then either:

- Modify the value of `OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US` in `ot-br-posix/third_party/openthread/repo/src/core/config/mac.h`  
  or
- Include the configuration flag under `OTBR_OPTIONS`, using `5000` for Series-2 RCPs or `18000` for Series-3 RCPs:  
  `-DCMAKE_CXX_FLAGS='-DOPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US=5000'`  
  `-DCMAKE_CXX_FLAGS='-DOPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US=18000'`

###### Pre-built Option - Host Packages (heading level 7)

Refer to [Running Host Applications with Pre-Built Packages](https://docs.silabs.com/openthread/latest/multiprotocol-solution-linux/running-multiprotocol-with-packages#running-host-applications-with-pre-built-packages) for a sample OTBR package built for Debian bookworm for the CPC/multiprotocol architecture, which can still be run purely for the Thread interface.

###### Pre-built Option - Docker Installation (heading level 7)

> **Note**: The following Docker containers are only supposed to be used with older GSDKs (prior to 2025.12), for RCPs built using Simplicity Studio for those SDKs. Be sure to match the container tag version with the Simplicity SDK version that you are using for testing.

[https://hub.docker.com/r/siliconlabsinc/openthread-border-router/tags](https://hub.docker.com/r/siliconlabsinc/openthread-border-router/tags)

###### Prerequisites (heading level 8)

- On the SD card, make sure to flash the [Raspberry Pi OS Lite image](https://www.raspberrypi.org/downloads/raspberry-pi-os/) or [Raspberry Pi OS with Desktop](https://www.raspberrypi.org/downloads/raspberry-pi-os/).
- Make sure to update the local repositories and package manager (**apt-get update** and **apt-get upgrade** prior to installing Docker).
- Optional but recommended: Install Haveged for better entropy conditions.

###### Installation Guidance (heading level 8)

> **Note**: Replace the string <version> in the following commands with the actual version you are using. For example, gsdk-4.4.0, sisdk-2024.6.0, etc.

- Make sure to reboot after any updates:  
  ```C  
  curl -sSL https://get.docker.com | sh  
  ```
- Once finished, you can modify the Docker user settings to not require sudo before each command:  
  ```C  
  sudo usermod -aG docker $USER  
  ```
- Raspberry Pi and Linux users, make sure to run:  
  ```C  
  sudo modprobe ip6table_filter  
  ```  
  for OTBR firewall support. This allows OTBR scripts to create rules inside the Docker container before otbr-agent starts.  
  To make sure this setting persists between reboots, add the following line to `/etc/modules`:  
  ```C  
  ip6table_filter  
  ```  
  If this step is not completed, modprobe errors may be displayed when starting a Docker container.
- Issue the following commands to install the containers. Note that only one Border Router container can be running at one time with a Co-Processor. Also, be sure to verify the device version (Thread protocol version 1.4) that should be run against this container.  
  ```C  
  UART interface:  
  docker pull siliconlabsinc/openthread-border-router:<version>  
    
  SPI interface:  
  docker pull siliconlabsinc/openthread-border-router:<version>_spi  
  ```
- To run an OpenThread Border Router (default is Thread protocol version 1.4), issue the following command:  
  - Example for UART interface  
  ```C  
  docker run -d --name "otbr" \  
        --sysctl "net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1" \  
        -p 8080:80 --dns=127.0.0.1 -it \  
        --volume /dev/ttyACM0:/dev/ttyACM0 \  
        --privileged siliconlabsinc/openthread-border-router:<version> \  
        --radio-url “spinel+hdlc+uart:///dev/ttyACM0” \  
  --backbone-interface eth0  
  ```

See sections [OTBR Configurations Using UART Interface](#otbr-configurations-using-uart-interface) and [UART Baud Rate Settings](#uart-baud-rate-settings) for notes on configuring the UART Radio URL.

- Example for SPI interface (for more information, see [OTBR Configurations using SPI Interface](#otbr-configurations-using-spi-interface))

```C
docker run -d --name "otbr" \
      --sysctl "net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1" \
      -p 8080:80 --dns=127.0.0.1 -it \
      --volume /dev/spidev0.0:/dev/spidev0.0 \
      --privileged siliconlabsinc/openthread-border-router:<version> \
      --radio-url “spinel+spi:///dev/spidev0.0?gpio-int-device=/dev/gpiochip0&gpio-int-line=21&gpio-reset-device=/dev/gpiochip0&gpio-reset-line=20&no-reset=1&spi-speed=1000000” \
      --backbone-interface eth0
```

(See section [OTBR Configurations using SPI Interface](#otbr-configurations-using-spi-interface) for notes on configuring the SPI Radio URL.)

Use additional arguments to configure the containers. For more information, see the section below or the Dockerfile in the ot-br-posix installation directory.

###### Docker Configuration Notes (heading level 8)

> **Note**: Silicon Labs-hosted Docker containers are only supposed to be used with RCPs built using Simplicity Studio for a given release. Be sure to match the container tag version with the Simplicity SDK version that you are testing with.

> **Note**: Replace the string <version> in the following commands with the actual version you are using. For example, gsdk-4.4.0, sisdk-2024.6.0, etc.

- Configure the desired TTY port for the OTBR to connect the Co-Processor at startup. Look for the TTY port of the Co-Processor device. The easiest way to do this is to look for a /tty/dev… entry once the device is connected. It should generally either be `/dev/ttyUSB0` or `/dev/ttyACM0`.
- Run the Docker installation as follows (example for UART interface):

```C
docker run -d --name "otbr" \
      --sysctl "net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1" \
      -p 8080:80 --dns=127.0.0.1 -it \
      --volume /dev/ttyACM0:/dev/ttyACM0 \
      --privileged siliconlabsinc/openthread-border-router:<version> \
      --radio-url “spinel+hdlc+uart:///dev/ttyACM0” \
      --backbone-interface eth0
```

- `-d` ensures that the container runs in detached mode.
- Review the running logs for the container any time using the `docker logs` command.
- `--name` is sticky until the docker container is properly closed (or removed).
- Port `8080` indicates the port of the web server hosting the Border Router management webpage.
- Issue commands directly to the container without having to attach to it:  
  `docker exec -ti otbr sh -c "sudo ot-ctl state"`  
  For more information, see the [docker exec documentation](https://docs.docker.com/engine/reference/commandline/exec/).
- Directly obtain an interactive shell above by issuing this command:  
  `docker exec -ti otbr sh -c "sudo ot-ctl"`
- Check the window running the OTBR Docker container for running log output of the Border Router, or follow the container log as follows:  
  `docker logs [container-id] -f`
- Manage the containers as shown below if they are loaded improperly:  
  ```C  
  # list all container images  
  docker images otbr  
  # remove existing container  
  docker image rm -f \<container ID\>  
  # list running containers  
  docker ps -a  
  # to remove running container  
  docker rm -f \<container name\>  
  ```

###### Usage

Check whether all required services are running on the OTBR.

`sudo systemctl status` should not report any services as running in a “degraded” state.

Check `/var/log/syslog` for a running log of otbr-agent.

For a full command list, run:

```C
> sudo ot-ctl help
```

Refer to [https://openthread.io/guides/border-router/form-network](https://openthread.io/guides/border-router/form-network) or [https://openthread.io/guides/border-router/external-commissioning](https://openthread.io/guides/border-router/external-commissioning) for examples on how to manually set up a Thread Network, by hand (out-of-band) or using external commissioning.

Run these two commands to check for a running Thread Network:

```C
> sudo ot-ctl state
```

and

```C
> sudo ot-ctl ifconfig
```

> **Note**: The error message OpenThread Daemon is not running indicates a problem with the Co-Processor connection. Check both for a valid /dev/tty entry and that a valid Co-Processor application was flashed onto the device.

###### Known Issues

When OpenThread runs on the NCP (network co-processor), the border router does not forward DNS queries from Thread devices to upstream resolvers on the infrastructure link, so upstream DNS forwarding is not supported in NCP mode. Product flows where the border router must resolve arbitrary Internet names for Thread clients are out of scope for this release. If you need assistance implementing upstream DNS forwarding or integrating a host-side DNS forwarder into an NCP OTBR platform image, please contact Silicon Labs support.

###### Guidance

- Silicon Labs does not recommend using the default NAT configuration on a network using 192.168.x.x addresses because that NAT uses those addresses by default on the NAT64 interface.
- For properly resolving mDNS queries, make sure the "**hosts:**" line under **/etc/nsswitch.conf** looks like the following:  
  `hosts:          files mdns4 minimal mdns5 minimal dns`
- If using native OpenThread based DHCP prefix delegation client, dhcpcd management of ipv6 should be disabled.

Check that the following lines are present in **/etc/dhcpcd.conf:**

```C
noipv6
noipv6rs
```

##### OpenThread Resources

Silicon Labs provides components and configuration options that enable you to configure Thread 1.4 features with sample applications. For more information, see [Configuring OpenThread Applications for Thread 1.4](/openthread/3.1.0/configuring-openthread-apps-for-thread-1-4).

For information specific to Thread 1.3, see [Configuring OpenThread Applications for Thread 1.3](/openthread/3.1.0/configuring-openthread-apps-for-thread-1-3). Thread 1.4 is the recommended protocol version starting with the silabs-2024.12 SDK for the soc and starting with silabs-2025.6 SDK for the otbr.

To find more resources or take advantage of the OpenThread community pages, visit: [https://openthread.io/resources](https://openthread.io/resources).

For information about the OpenThread Border Router, visit: [https://openthread.io/guides/border-router](https://openthread.io/guides/border-router).

Consult these troubleshooting webpages for more information:

- [https://openthread.io/guides/border-router/build#verify-services](https://openthread.io/guides/border-router/build#verify-services)
- [https://openthread.io/guides/border-router/access-point#troubleshooting](https://openthread.io/guides/border-router/access-point#troubleshooting)

### Coexistence

#### Coexistence

This section describes implementing managed coexistence to improve coexistence of 2.4 GHz IEEE 802.11b/g/n Wi-Fi and other 2.4 GHz radios with IEEE 802.15.4-based radios such as Zigbee® and OpenThread.

- [**Wi-Fi Coexistence Fundamentals**](/openthread/3.1.0/multiprotocol-wifi-coexistence-fundamentals): Introduces methods to improve the coexistence of 2.4 GHz IEEE 802.11b/g/n Wi-Fi and other 2.4 GHz radios such as Bluetooth, Bluetooth Mesh, Bluetooth Low Energy, and IEEE 802.15.4-based radios such as Zigbee and OpenThread.
- [**Zigbee and OpenThread Coexistence with Wi-Fi**](/openthread/3.1.0/zigbee-openthread-coexistence-wifi): Details the impact of Wi-Fi on Zigbee and Thread, and methods to improve coexistence. First, methods to improve coexistence without direct interaction between Zigbee/Thread and Wi-Fi radios are described. Second, Silicon Labs's Packet Traffic Arbitration (PTA) support to coordinate 2.5 GHz RF traffic for co-located Zigbee/Thread and Wi-Fi radios is described (for the EFR32MG only).
- [**Configuring Antenna Diversity for OpenThread**](/openthread/3.1.0/configuring-antenna-diversity-for-openthread): Describes how to use Project Configurator and Component Editor in Simplicity Studio to configure antenna diversity in OpenThread applications.

#### Wi-Fi Coexistence Fundamentals

##### Wi-Fi Coexistence Fundamentals

> **Note: This section replaces _UG103.17: Wi-Fi Coexistence Fundamentals_. Further updates to this user guide will be provided here**.

These pages describe methods to improve coexistence of 2.4 GHz IEEE 802.11b/g/n Wi-Fi and other 2.4 GHz radios such as Bluetooth®, Bluetooth Mesh, Bluetooth Low Energy, and IEEE 802.15.4-based radios such as Zigbee® and OpenThread. These techniques are applicable to the EFR32MGxx and EFR32BGxx families.

For additional details about the implementation of managed coexistence for EFR32 devices refer to the following application notes:

- [Bluetooth Coexistence with Wi-Fi](/coexistence/1.0.1/bluetooth-coexistence-with-wifi)
- [Zigbee and OpenThread Coexistence with Wi-Fi](/coexistence/1.0.1/zigbee-openthread-coexistence-wifi)
- _AN1243: Timing and Test Data for EFR32 Coexistence with Wi-Fi_ (available under non-disclosure from Silicon Labs Sales).

For evaluating the Silicon Labs EFR32 software coexistence solution, the [Silicon Labs Coexistence Development Kit (SLWSTK-COEXBP)](/coexistence/1.0.1/coexistence-development-kit) can be used.

###### Silicon Labs' Fundamentals Series

Silicon Labs’ Fundamentals series covers topics that project managers, application designers, and developers should understand before beginning to work on an embedded networking solution using Silicon Labs chips, networking stacks such as EmberZNet PRO or Silicon Labs Bluetooth®, and associated development tools. The documents can be used as a starting place for anyone needing an introduction to developing wireless networking applications, or who is new to the Silicon Labs development environment.

###### Coexistence of Radio Standards

The 2.4 GHz Industrial, Scientific and Medical (ISM) band supports Wi-Fi, Bluetooth, and 802.15.4. The simultaneous and co-located operation of these different 2.4 GHz radio standards can degrade performance of one or more of the radios. To improve interference robustness, each of the 2.4 GHz ISM radio standards support some level of collision avoidance and/or message retry capability. At low data throughput rates, low power levels, and/or sufficient physical separation, these 2.4 GHz ISM standards can co-exist without significant performance impacts. However, recent customer trends are making coexistence more difficult:

- Increased Wi-Fi transmit power level for “extended range.”  
  +30 dBm Wi-Fi Access Points are now common.
- Increased Wi-Fi throughput.  
  Depending on achievable Signal-to-Noise Ratio (SNR), high throughput requirements for file transfers and/or video streaming may result in high Wi-Fi duty cycle within the 2.4 GHz ISM band.
- Integrating Wi-Fi, Bluetooth, and 802.15.4 into the same device for gateway functionality.  
  This is required by Home Automation and Security applications and provides easier end-node commissioning.

> **Note**: Zigbee and OpenThread devices (802.15.4) operate at less than +20 dBm transmit power level. With normal network activity, Zigbee/Thread solutions have a relatively low RF duty cycle and Bluetooth solutions implement Adaptive Frequency Hopping (AFH). Silicon Labs’ testing of Bluetooth blocked by Zigbee/Thread shows low impact on Bluetooth with AFH enabled and normal Zigbee/Thread network activity. However, if 100% RF duty cycle, Zigbee/Thread can degrade co-located Bluetooth performance.

##### Wi-Fi Impact on Bluetooth and 802.15.4 Radios

Worldwide, Wi-Fi supports up to 14 overlapping 20/22 MHz bandwidth channels across the 2.4 GHz ISM band with transmit power levels up to +30 dBm. Bluetooth supports 40 non-overlapping channels at 2 MHz spacing with transmit powers up to +20 dBm (Bluetooth Core Specification v5.0). IEEE 802.15.4 supports 16 non-overlapping 2 MHz bandwidth channels at 5 MHz spacing with transmit powers up to +20 dBm. The Bluetooth and 802.15.4 channel mappings are shown in the following figure, where yellow highlighted channels are the three Bluetooth advertising (ADV) channels.

![Wi-Fi, Bluetooth, and 802.15.4 Channel Mapping (World-Wide)](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image8.png)

Actual channels available vary by country. For example, in the USA, only Wi-Fi channels 1 through 11 are available. Bluetooth channels 0 through 39 are available worldwide and Zigbee channels 11 through 26 are available, although channels 25 and 26 require reduced transmit power levels to meet FCC requirements (North America only).

Silicon Labs completed testing to understand the effects of Wi-Fi on Bluetooth and 802.15.4 radios. The following subsections summarize the key findings and results from this testing.

###### Bluetooth

To better understand the effects of Wi-Fi on Bluetooth, Silicon Labs measured the impact of a 100% duty-cycled 802.11n (MCS3, 20 MHz bandwidth) blocker transmitting at various power levels while receiving a Bluetooth 1Mbps 37-byte payload message transmitted at power level sufficient to achieve 0.1% BER (receive sensitivity). The results for co-channel, adjacent channel, and “far-away” channels are shown in the following figure. All 802.11n and Bluetooth power levels are referenced to the Silicon Labs EFR32MG21 RF input. The test application was developed using the Silicon Labs Bluetooth 2.11.0 or later stack with the soc-dtm sample application running on the EFR32 DUT (Device Under Test) and a test script to control the DUT and RF test equipment.

![Bluetooth Low Energy Receive Sensitivity with 100% Duty-Cycled 802.11n (MCS3/20 MHz) Wi-Fi Blocker](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image9.jpg)

From the figure above, the key observations about the impact of Wi-Fi (channel 1, MCS3/20 MHz) on Bluetooth are:

- Co-Channel (Bluetooth overlapping Wi-Fi):  
  - For Bluetooth RF channels 0 through 10, EFR32MG21 can receive a Bluetooth 1Mbps signal at 4 dB weaker than aggregate Wi-Fi transmit power (100% duty cycle).  
  - This receive sensitivity limitation impacts both co-located and remote, not co-located, Bluetooth radios.
- Adjacent Channel (Bluetooth within one Wi-Fi bandwidth):  
  - At Bluetooth RF channel 11, EFR32MG21 can receive a -90 dBm Bluetooth 1Mbps with -40 dBm or weaker Wi-Fi transmit power (100% duty cycle).
- “Far-Away” Channel (Bluetooth beyond one Wi-Fi bandwidth):  
  - At Bluetooth RF channels 19 through 39, EFR32MG21 can receive a -96 dBm Bluetooth 1Mbps signal with -40 dBm or weaker Wi-Fi transmit power (100% duty cycle).

In a real-world environment, Wi-Fi is typically not 100% duty cycle and only approaches 100% duty cycle during file transfers or video stream in low Wi-Fi SNR conditions. As seen in the previous figure, the EFR32xGxx receive sensitivity varies as the Wi-Fi blocker turns ON/OFF. The net result is the ability to see weaker signals when Wi-Fi is OFF, but not when strong Wi-Fi is ON (actively transmitting).

The following figure illustrates the receive range of a node (blue node) near a strong Wi-Fi transmitter. Relative to the blue Bluetooth node, the area inside the green circle represents the receive range when Wi-Fi is ON. The area between the green and yellow circles represents the receive range when Wi-Fi is OFF. From this figure:

- The green node is always receivable by the blue node.
- The yellow node is only receivable by the blue node when Wi-Fi is OFF.
- The red node is never receivable by the blue node.
- The yellow and red nodes are always receivable by the green node.

![EFR32 Receiver Desensitized when Wi-Fi Transmitting](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/efr32-receiver.png)

Depending on each Bluetooth device’s TX level, RX sensitivity vs. blocker, channel, and relative attenuation and Wi-Fi TX level and duty cycle, the impact of strong Wi-Fi turning ON/OFF will vary. Based on the figure above, the following example assumes:

- Wi-Fi co-located with Blue device
- Bluetooth channel is “far-away” (RF channel 39) from Wi-Fi channel (channel 1), indicating minimum Bluetooth RX sensitivity vs. Wi-Fi RX levels:  
  ![Bluetooth RX sensitivity vs. Wi-Fi RX levels](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image11.png)
- Typical radio TX levels:  
  ![Typical radio TX levels](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image12.png)
- Attenuation between radios  
  ![Attenuation between radios](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image13.png)
- Bluetooth device RX **success**/**fail** with Wi-Fi **OFF**:  
  ![Bluetooth device RX success/fail with Wi-Fi OFF](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image14.png)
- Bluetooth device RX **success**/**fail** with Wi-Fi **ON**:  
  ![Bluetooth device RX success/fail with Wi-Fi ON](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image15.png)

Using the example assumptions, all radio communication is maintained between Wi-Fi ON/OFF except:

- Blue device receives yellow device when Wi-Fi ON, but not Wi-Fi OFF.
- Green device receives red device when Wi-Fi ON, but not Wi-Fi OFF.

If devices are Bluetooth devices (point-to-point), blue device communication with:

- Green device is not impacted by Wi-Fi TX.
- Yellow device is erratic as Wi-Fi TX goes ON/OFF.  
  - For high-duty cycle Wi-Fi TX, connection can become unstable as multiple connection intervals fail.
- Red device is not possible.

If devices are Bluetooth mesh devices (mesh network), blue device communication with:

- Green device is not impacted by Wi-Fi TX.
- Yellow device shows erratic communication as Wi-Fi TX goes ON/OFF.  
  - For high-duty cycle Wi-Fi TX, communication would require a relay to forward/repeat missed RX messages from yellow device to blue device.
- Red device is not directly possible, and a relay is required to forward messages between blue device and red device.  
  - If green device is relay, communication with red device shows erratic communication as Wi-Fi TX goes ON/OFF.  
  - If yellow device is relay, communication with red device is not impacted by Wi-Fi TX.

###### 802.15.4

To better understand the effects of Wi-Fi on 802.15.4 radios, Silicon Labs measured the impact of a 100% duty-cycled 802.11n (MCS3, 20 MHz bandwidth) blocker transmitting at various power levels while receiving an 802.15.4 message transmitted at power level sufficient to achieve 1% PER (receive sensitivity). The following figure shows the results for co-channel, adjacent channel, and “far-away” channel. All 802.11n and 802.15.4 power levels are referenced to the Silicon Labs’ EFR32MG21 RF input. The test application was developed using Silicon Labs’ EmberZNet PRO (Zigbee) stack with NodeTest running on the EFR32 DUT (Device Under Test) and a test script to control the DUT and RF test equipment.

![802.15.4 Receive Sensitivity with 100% Duty Cycled 802.11n (MCS3/20 MHz) Wi-Fi Blocker](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image16.jpg)

These are the key observations about the impact of Wi-Fi on 802.15.4 from the figure above.

- Co-Channel (Zigbee overlapping Wi-Fi):  
  - At channel 12, Zigbee can receive an 802.15.4 signal down to 9 dBm weaker than aggregate Wi-Fi transmit power (100% duty cycle).    
    - This receive sensitivity limitation impacts both co-located and remote, not co-located, 802.15.4 radios.  
  - At channel 12, Zigbee with and without LNA (Low Noise Amplifier) can receive an 802.15.4 signal down to 9 dB weaker than aggregate Wi-Fi transmit power (100% duty cycle).  
  - 802.15.4 transmits can also be blocked by Wi-Fi transmit power tripping the 802.15.4 -75 dBm CCA (Clear Channel Assessment) threshold.
- Adjacent Channel (Zigbee within one Wi-Fi bandwidth):  
  - At channel 15, Zigbee can receive a -87 dBm 802.15.4 signal with -30 dBm or weaker Wi-Fi transmit power (100% duty cycle).    
    - Maximum receive sensitivity is attained at -38 dBm or weaker Wi-Fi transmit power (100% duty cycle).  
  - At channel 15, Zigbee without LNA can receive a -87 dBm 802.15.4 signal with -30 dBm or weaker Wi-Fi transmit power (100% duty cycle); -34 dBm or weaker with LNA enabled.
- “Far-Away” Channel (Zigbee beyond one Wi-Fi bandwidth):  
  - At channels 17 through 26, Zigbee can receive a -96 dBm 802.15.4 signal with -30 dBm or weaker Wi-Fi transmit power (100% duty cycle).    
    - Maximum receive sensitivity is attained at -40 dBm or weaker Wi-Fi transmit power (100% duty cycle).  
  - At channels 17 through 26, Zigbee without LNA can receive a -96 dBm 802.15.4 signal with -30 dBm or weaker 100% Wi-Fi transmit power (100% duty cycle); -34 dBm or weaker with LNA enabled.

In a real-world environment, Wi-Fi is typically not 100% duty cycle and only approaches 100% duty cycle during file transfers or video stream in low Wi-Fi SNR (Signal to Noise Ratio) conditions. As seen in the figure above, the EFR32xGxx receive sensitivity varies as the Wi-Fi blocker turns ON/OFF. The net result is the ability to see weaker signals when Wi-Fi is OFF, but not when strong Wi-Fi is ON (actively transmitting).

The following figure illustrates the receive range of a node (blue node) near a strong Wi-Fi transmitter. Relative to the blue 802.15.4 node, the area inside the green circle represents the receive range when Wi-Fi is ON. The area between the green and yellow circles represents the receive range when Wi-Fi is OFF. From this figure:

- The green node is always receivable by the blue node.
- The yellow node is only receivable by the blue node when Wi-Fi is OFF.
- The red node is never receivable by the blue node.
- The yellow and red nodes are always receivable by the green node.

![EFR32MGx Receiver Desensitized when Wi-Fi Transmitting](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/efr32-m-gx-receiver.png)

Depending on each node’s type (Coordinator, Router, or End Device) and the Wi-Fi duty cycle, the impact of strong Wi-Fi turning ON/OFF will vary.

In a Zigbee network:

- **Coordinator**: Tasked with network creation, the control of network parameters, and basic maintenance, in addition to performing an application function, such as aggregating data or serving as a central control point or gateway.
- **Router**: In addition to running an application function, a Router can receive and retransmit data from other nodes.
- **End Device**: Typically, a battery-powered device (Sleepy End Device or SED) running an application function and able to talk to a single parent node (either the Coordinator or a Router). End Devices cannot relay data from other nodes.

In an OpenThread network:

- **Border Router**: Provides network node connectivity to other devices in external networks (for example, Internet access).
- **Router**: In addition to running an application function, a Router can receive and retransmit data from other nodes and provide join and security capability. When routing function is not needed by network, a Router can downgrade to a Router-Eligible End Device (REED).
- **REED**: In addition to running an application function, a REED can receive and retransmit data from other nodes. When additional routers needed by network, a REED can upgrade to a Router.
- **SED**: Typically, a battery-powered device running an application function and able to talk to a single parent node (either a Border Router, Router, or REED). SEDs cannot relay data from other nodes.

Two Zigbee cases are considered below, but many other cases are possible.

###### Case 1: Zigbee Coordinator near strong Wi-Fi plus three end-nodes (heading level 7)

For this case, the figure above is composed of:

- **Coordinator**: Blue node
- **End Devices**: Green, Yellow, and Red nodes

In this simple network, each end device attempts to join the network formed by the coordinator. However, the red node is outside of receive range and cannot join. With Wi-Fi OFF, both the green and yellow nodes successfully join the network and have no issues sending messages to the Coordinator. Regardless of Wi-Fi ON/OFF duty cycle, the green node remains successful sending messages to the Coordinator.

With Wi-Fi ON/OFF at low-duty cycle, some messages from the yellow node are periodically blocked, but Zigbee retry mechanisms are effective in getting the messages to the coordinator. However, with Wi-Fi ON/OFF at high-duty cycle, many messages from the yellow node are blocked and Zigbee retry mechanisms may be exhausted. Even when retry mechanisms are successful, the message latency increases. If the yellow node is a battery-powered Sleepy End Device, it must remain active longer to execute retries, reducing battery life.

###### Case 2: Zigbee Coordinator near strong Wi-Fi, Router within always receive range, plus two end-nodes (heading level 7)

For this case, the figure above is composed of:

- **Coordinator**: Blue node
- **Router**: Green node
- **End Devices**: Yellow and Red nodes

In this simple network, the green Router forms a route directly to the Coordinator, maintained regardless of Wi-Fi ON/OFF duty cycle. With Wi-Fi OFF, the yellow node forms a route directly to the blue Coordinator at a lower route cost than a route via the green Router. The red node cannot be received by the Coordinator and its messages are also routed through the Router to the Coordinator.

With Wi-Fi OFF, the green Router, the yellow node, and the red node (via the green Router) have no issues sending messages to the Coordinator. Regardless of Wi-Fi ON/OFF duty cycle, the green Router and the red node (via the green Router) remain successful sending messages to the Coordinator. With Wi-Fi ON/OFF at low-duty cycle, some messages from the yellow node are periodically blocked, but Zigbee retry mechanisms are effective in getting the messages to the Coordinator.

With Wi-Fi ON/OFF at high-duty cycle, many messages from the yellow node are blocked and Zigbee retry mechanisms may be exhausted. If Wi-Fi ON/OFF stays at high-duty cycle for enough time, the network responds by restructuring the yellow node to route messages to the Coordinator via the Router. However, this route rediscover takes time and messages may be lost. If Wi-Fi ON/OFF remains high-duty cycle, the yellow node messages will continue to go through the Router, which forwards messages to the Coordinator.

However, when Wi-Fi ON/OFF returns to low-duty cycle, the network will, due to lower route cost, return to the original structure with the yellow node sending messages directly to the Coordinator.

Under conditions with Wi-Fi ON/OFF switching between low and high duty cycles, the network may switch back and forth between these two route states. During these switching events, messages from the yellow end-node to the Coordinator are lost.

##### Unmanaged Coexistence

The unmanaged coexistence recommendations that follow provide guidance on how to maximize the EFR32MGxx/EFR32BGxx message success with strong nearby Wi-Fi.

###### Implement Frequency Separation

You can minimize the blocking effect of high-power Wi-Fi on other 2.4 GHz radios by moving the Wi-Fi to one end of the pass band. This impacts fewer Bluetooth channels and allows 802.15.4 channels to move to the other end of the pass band where Wi-Fi will have minimal blocking effect.

###### Bluetooth (heading level 7)

Bluetooth co-channel operation with Wi-Fi has the most impact on Bluetooth communication. For Bluetooth devices (point-to-point) and Bluetooth mesh devices using Generic Attribute Profile (GATT) bearer communication, at least one ADV channel is minimally blocked and supports establishing a connection via Advertising, Scanning, and Initiating link-layer states. While establishing a connection, the Bluetooth connection master specifies the channel map, but the connection master can also update the channel map during connection. However, the Bluetooth connection slave must follow the channel map provided by master.

If EFR32 becomes the connection master, the Bluetooth channel map can be specified via:

`sl_status_t sl_bt_gap_set_data_channel_classification (uint8 channel_map_len, const uint8\* channel_map_data)`

This command can be used to specify a channel classification for data channels. This classification persists until overwritten with a subsequent command or until the system is reset.

`channel_map` is 5 bytes and contains 37 1-bit fields. The _n_th such field (in the range 0 to 36) contains the value for the link layer channel index _n_:

0: Channel _n_ is bad.

1: Channel _n_ is unknown.

The most significant bits are reserved and shall be set to 0 for future use. At least two channels shall be marked as unknown.

For Bluetooth mesh devices using Advertising bearer communication, at least one ADV channel is minimally blocked and supports establishing communication via Advertising and Scanning.

###### 802.15.4 (heading level 7)

From the observations in [802.15.4](./02-wi-fi-impact-on-bluetooth-and-802-15-4-radios#802154) co-channel operation of 802.15.4 with 100% duty cycle Wi-Fi blocks most of the 802.15.4 messages and must be avoided. Also, EFR32MGxx tolerates up to 20 dB stronger Wi-Fi signal in “far-away” channel case than in adjacent channel case. The 802.15.4 network performance is improved by maximizing the frequency separation between the Wi-Fi network and the 802.15.4 network.

If the Wi-Fi and 802.15.4 radios are implemented with a common host (MCU controlling both radios), then the host should attempt to maximize the frequency separation. For Wi-Fi networks, the Access Point (AP) establishes the initial channel and, in auto channel configuration, is free to move the network to another channel using the Channel Switch Announcement (CSA), introduced in 802.11h, to schedule the channel change.

For OpenThread networks, frequency separation implementation depends on the application layer. For Zigbee networks, the Coordinator establishes the initial channel.

###### Operate Wi-Fi with 20 MHz Bandwidth

Because Wi-Fi 802.11n uses OFDM (Orthogonal Frequency-Division Multiplexing) sub-carriers, third-order distortion products from these sub-carriers extend one bandwidth on each side of the Wi-Fi channel. 802.11n can operate in 20 MHz or 40 MHz modes. If operated in 40 MHz mode, 40 MHz of the 80 MHz ISM band is consumed by the Wi-Fi channel. However, an additional 40 MHz on each side can be affected by third-order distortion products. These third-order products can block the Bluetooth and 802.15.4 receiver and is the primary reason adjacent channel performance is up to 20 dB worse than “far-away” channel performance.

In proposing 40 MHz mode for 802.11n, the Wi-Fi standard anticipated potential issues with other 2.4 GHz ISM devices when Wi-Fi operated in 40 MHz mode. During association, any Wi-Fi station can set the **Forty MHz Intolerant** bit in the HT Capabilities Information. This bit informs the Wi-Fi access point that other 2.4 GHz ISM devices are present, forcing the entire Wi-Fi network to 20 MHz mode.

###### Bluetooth (heading level 7)

If the Wi-Fi and Bluetooth radios are implemented with a common host, then the host should have the Wi-Fi radio set the 40 MHz Intolerant bit during association to force the Wi-Fi to 20 MHz mode, increasing the number of channels available to Bluetooth and improving the Bluetooth performance.

If the application requires Wi-Fi to operate in 40 MHz mode, frequency separation can be maximized by placing Wi-Fi channel at upper or lower end of 2.4 MHz ISM band, minimizing the adjacent channels.

###### 802.15.4 (heading level 7)

If the Wi-Fi and 802.15.4 radios are implemented with a common host, then the host should have the Wi-Fi radio set the Forty MHz Intolerant bit during association to force the Wi-Fi to 20 MHz mode, improving the 802.15.4 performance.

If the application requires Wi-Fi to operate in 40 MHz mode, frequency separation must be maximized by placing Wi-Fi channels and 802.15.4 channel at opposite ends of the 2.4 GHz ISM band.

From Silicon Labs’ managed coexistence testing, 802.15.4 performance with 40 MHz Wi-Fi, for the same Wi-Fi RF duty cycle, is comparable to 802.15.4 performance with 20 MHz Wi-Fi. While 802.15.4 performance with 100% Wi-Fi RF duty cycle is inherently impaired, 40 MHz Wi-Fi, for the same target Wi-Fi data rate, has a lower RF duty cycle than 20 MHz Wi-Fi, providing the 802.15.4 radio more frequent and longer time gaps for successful transmits and receives.

###### Increase Antenna Isolation

From the observations in [Wi-Fi Impact on Bluetooth and 802.15.4 Radios](./02-wi-fi-impact-on-bluetooth-and-802-15-4-radios), minimizing the Wi-Fi energy seen by the EFR32 RF input improves the EFR32 receive range. For example, in the “far-away” channel case for Zigbee (Wi-Fi channel 1 and Zigbee channel 25) with 100% Wi-Fi duty cycle, a -96 dBm 802.15.4 message can be received when the average Wi-Fi energy at EFR32MGxx input is -30 dBm or less. If the Wi-Fi average transmit power level is +10 dBm, 40 dB or more antenna isolation between the Wi-Fi transmitter and 802.15.4 RF input is required to always receive a -96 dBm 802.15.4 signal, Wi-Fi ON or OFF.

Increased antenna isolation can be achieved by:

- Increasing the distance between antennas. In open-space, far-field, power received is proportional to 1/R2, where R is the distance between antennas.
- Taking advantage of antenna directionality. A monopole antenna provides a null along the axis of the antenna, which can be directed toward the Wi-Fi antenna(s).

###### Implement Protocol and Stack Retry Mechanisms

You can maximize the use of built-in protocol MAC and stack retry mechanisms to minimize missed messages in the presence of high-power Wi-Fi.

###### Bluetooth (heading level 7)

Bluetooth (point-to-point) messages requires responses. If a response is not received within programmable time, the application can re-send the message up to a programmable limit.

Bluetooth mesh (mesh network) messages are sent via ADV payloads and responses are received during SCAN. Bluetooth mesh specifies:

- Optional relay nodes, which after a programmable time-out with no responses, can retransmit the original message for a programmable number of hops.
- Originator, after a programmable time-out with no response, can retransmit the original message for a programmable number of time-outs.

Both mechanisms improve Bluetooth mesh message success but should be used with caution. More relays nodes, shorter time-outs, and more retries may improve an individual message’s success, but these mechanisms can stress the mesh network by flooding too many identical messages. See [Bluetooth Mesh Network Performance](https://docs.silabs.com/btmesh/latest/btmesh-11-network-performance/) for details on these considerations.

###### 802.15.4 (heading level 7)

The 802.15.4 specification requires retries at the MAC (Media Access Control) layer, which are implemented in Silicon Labs’ EmberZNet PRO stack. To further improve message delivery robustness, Silicon Labs EmberZNet PRO stacks also implements NWK (Zigbee net-work layer) retries, wrapping the MAC retries. The user application can also take advantage of APS (Zigbee Application Support (APS) Sub-Layer) retries, wrapping the NWK retries. More information on the retry mechanisms can be found at:

- [Zigbee Fundamentals](https://docs.silabs.com/zigbee/latest/zigbee-fundamentals/)
- [How does the EmberZNet stack retry work?](https://www.silabs.com/community/wireless/zigbee-and-thread/knowledge-base.entry.html/2012/06/29/how_does_the_emberzn-po1M)

These retry mechanisms are effective at improving message delivery. However, under high interference conditions, message latency increases.

For OpenThread networks, 802.15.4 retries at the MAC layer still apply. However, other message and retry mechanisms depend on the application layer.

###### Remove FEM (or Operate FEM LNA in Bypass)

EFR32xGxx can deliver nearly +20 dBm transmit power and has excellent receiver sensitivity without an external FEM (Front End Module). However, an external FEM can increase transmit power to +20 dBm for increased range (in regions where this is permitted, for example, the Americas). The additional FEM LNA receive gain also improves sensitivity. However, this additional gain also degrades the EFR32xGx linearity performance in the presence of strong Wi-Fi.

For best receive sensitivity in the presence of strong Wi-Fi blockers, either eliminate the FEM or operate the FEM LNA in bypass mode. This recommendation is a trade-off as receive sensitivity without Wi-Fi blockers is improved with FEM LNA gain enabled.

##### Managed Coexistence

The market trends of higher Wi-Fi transmit power, higher Wi-Fi throughput, and integration of Wi-Fi and Bluetooth radios into the same device has the following impacts:

- Advantages:  
  - Host can implement frequency separation between Wi-Fi, Bluetooth, 802.15.4.  
  - Co-located Wi-Fi radio can force Wi-Fi network to operate with 20 MHz bandwidth.  
  - Co-located Wi-Fi, Bluetooth, and 802.15.4 radios can communicate pending and/or in-progress activity on 2.4 GHz ISM transmits and receives.
- Disadvantages:  
  - Higher Wi-Fi transmit power requires greater antenna isolation.  
  - Higher Wi-Fi throughput results in higher Wi-Fi duty cycle.  
  - Antenna isolation is usually limited by the size of the product (only 15-20 dB isolation is not unusual).

Assuming frequency separation achieves the “far-away” channel case and Wi-Fi only uses 20 MHz bandwidth, a +20 dBm Wi-Fi transmit power level at 100% duty cycle requires 50 dB antenna isolation to receive -92 dBm Bluetooth or 45 dB antenna isolation to receive -80 dBm 802.15.4 messages. This is generally not achievable in small devices with co-located Wi-Fi and Bluetooth or 802.15.4.

Managed Coexistence takes advantage of communication between the co-located Wi-Fi, Bluetooth, and 802.15.4 radios to coordinate each radio’s access to the 2.4 GHz ISM band for transmit and receive. For the EFR32, Silicon Labs has implemented a coordination scheme compatible with Wi-Fi devices supporting PTA. This PTA-based coordination allows the EFR32 to signal the Wi-Fi device when receiving a message or wanting to transmit a message. When the Wi-Fi device is made aware of the EFR32 requiring the 2.4 GHz ISM band, any Wi-Fi transmit can be delayed, improving Bluetooth or 802.15.4 message reliability.

> **Note**: EFR32 Bluetooth and Bluetooth Mesh coexistence is supported in Bluetooth 2.13.x and Bluetooth Mesh 2.10.x. Not all coexistence support features in Bluetooth 2.13.x and Bluetooth Mesh 2.10.x are present in earlier versions.

###### PTA Support Options

PTA is described in IEEE 802.15.2 (2003) Clause 6 and is a recommendation, not a standard. 802.15.2 originally addressed coexistence between 802.11b (Wi-Fi) and 802.15.1 (Bluetooth Classic) and does not describe an exact hardware configuration. However, 802.15.2 recommends that the PTA implementation consider the following:

- TX REQUEST from 802.11b to PTA and TX REQUEST from 802.15.1 to PTA
- TX CONFIRM from PTA to 802.11b and TX CONFIRM from PTA to 802.15.1
- STATUS information from both radios:  
  - Radio state [TX, RX, or idle]  
  - Current and future TX/RX frequencies  
  - Future expectation of a TX/RX start and duration  
  - Packet type  
  - Priority (Fixed, Randomized, or QoS based)

The following figure describes how 802.15.2 considers radio state, transmit/receive, and frequencies.

![IEEE 802.15.2 2.4 GHz ISM Co-Located Radio Interference Possibilities](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image17.png)

From the figure above, the frequency separation recommendations from section [Unmanaged Coexistence](./03-unmanaged-coexistence) remain required for managed coexistence:

- 802.15.2 “In-Band” is equivalent to Co-Channel operation, which showed significant Wi-Fi impact on co-channel Bluetooth or 802.15.4.
- 802.15.2 “Out-of-Band” covers both Adjacent and “Far-Away” Channel operation, which showed ~20 dB improvement in “Far-Away” Channel vs. Adjacent Channel (802.15.4).

As such, for Managed Coexistence, Silicon Labs recommends continuing to apply the unmanaged coexistence recommendations described in the following sections:

- [Implement Frequency Separation](03-unmanaged-coexistence#implement-frequency-separation)
- [Operate Wi-Fi with 20 MHz Bandwidth](./03-unmanaged-coexistence#operate-wi-fi-with-20-mhz-bandwidth)
- [Increase Antenna Isolation](./03-unmanaged-coexistence#increase-antenna-isolation)
- [Implement Protocol and Stack Retry Mechanisms](./03-unmanaged-coexistence#implement-protocol-and-stack-retry-mechanisms)
- [Remove FEM (or Operate FEM LNA in Bypass)](./03-unmanaged-coexistence#remove-fem-or-operate-fem-lna-in-bypass)

In reviewing existing PTA implementations, Silicon Labs finds the PTA master implementation has been integrated into many Wi-Fi devices, but not all Wi-Fi devices support a PTA interface. Although describing typical PTA implementations is sometimes referred to in the industry as Wi-fi/Bluetooth, 802.15.4 can be used interchangeably with Bluetooth for [1-Wire PTA](#1-wire-pta), [2-Wire PTA](#2-wire-pta), [3-Wire PTA](#3-wire-pta), or [4-Wire PTA](#4-wire-pta) when applied to EFR32. The following figure shows the most common Wi-Fi/PTA implementations supporting Bluetooth.

![Typical Wi-Fi/Bluetooth PTA Implementations](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image18.jpg)

###### 1-Wire PTA (heading level 7)

In 1-Wire PTA, the Wi-Fi/PTA device asserts a GRANT signal when Wi-Fi is not busy transmitting or receiving. When GRANT is asserted, the Bluetooth radio is allowed to transmit or receive. This mode does not allow the external radio to request the 2.4 GHz ISM and is not recommended.

An alternate 1-Wire implementation is a REQUEST signal from Bluetooth to Wi-Fi/PTA, where Bluetooth asserts REQUEST whenever it needs the 2.4 GHz ISM band and expects Wi-Fi to always yield. This mode works very well for Bluetooth, but high priority Wi-Fi traffic can be compromised which impacts Wi-Fi performance.

###### 2-Wire PTA (heading level 7)

In 2-Wire, the REQUEST is added with the GRANT signal, allowing the Bluetooth radio to request the 2.4 GHz ISM band. The Wi-Fi/PTA device internally controls the prioritization between Bluetooth and Wi-Fi, and on a conflict, the PTA can choose to either GRANT Bluetooth or Wi-Fi.

###### 3-Wire PTA (heading level 7)

In 3-Wire, the PRIORITY signal is added, allowing the Bluetooth radio to signify a high- or low-priority message is either being received or transmitted. The Wi-Fi/PTA device compares this external priority request against the internal Wi-Fi priority, which may be high/low or high/mid/low and can choose to either GRANT Bluetooth or Wi-Fi.

PRIORITY can be implemented as static or directional (enhanced) priority.

- **Static**: PRIORITY is either high or low during REQUEST asserted for the transmit or receive operation.
- **Directional**: PRIORITY is either high or low for a typically 20µs duration after REQUEST asserted, but switches to low during receive operation and high during transmit operation.

For platforms, such as Wi-Fi data routers that can achieve high Wi-Fi duty cycles, as well as IoT hubs that stream Bluetooth classic audio, implementing PRIORITY is highly recommended as it provides the Wi-Fi/PTA device with insight on the EFR32 REQUEST. PRIORITY is also configurable, both at compile time and at runtime, to address various product optimization requirements. However, PRIORITY may not be necessary for platforms that do not experience high Wi-Fi duty cycles nor support Bluetooth audio streaming, freeing a GPIO pin on the EFR32 and SoC. Low RF duty cycle protocols such as 802.15.4 are even more likely to not require PRIORITY in these conditions.

###### 4-Wire PTA (heading level 7)

In 4-Wire PTA, the FREQ signal is added, allowing the Bluetooth radio to signify an “in-band” or “out-of-band” message is either being received or transmitted. Silicon Labs recommends maximizing frequency separation, making the FREQ signal mute. For this reason, Silicon Labs’ EFR32 does not support the FREQ signal and, for any 4-wire Wi-Fi/PTA with a FREQ input, Silicon Labs therefore recommends asserting the FREQ input to the Wi-Fi/PTA.

Additional details about the implementation of managed coexistence and test results are available in an expanded version of this application note, _AN1243: Timing and Test Data for EFR32 Coexistence with Wi-Fi_, available under non-disclosure from Silicon Labs Sales.

###### Multiple EFR32s Connected to Wi-Fi/PTA (heading level 7)

The 802.15.2 recommendation only addresses a single 802.11b radio connected to a single 802.15.1 radio. However, market trends are requiring multiple co-located 2.4 GHz ISM radios to operate with a Wi-Fi/PTA device only designed for one external radio. Silicon Labs has addressed this requirement by enhancing the REQUEST signal with the “shared” REQUEST feature. For 802.15.4 radios only, the Silicon Labs EFR32 PRIORITY can also be configured as “shared” to support additional multi-EFR32 radio configurations.

The “shared” REQUEST and “shared” PRIORITY features have the following characteristics:

- Operate REQUEST and PRIORITY in open-drain or open-source (an external 1 kΩ ±5% pull-up for open-drain or pull-down for open source is required).
- Before asserting REQUEST, test REQUEST to determine if another EFR32 has already asserted REQUEST.
- If not already asserted, assert REQUEST.
- If already asserted:  
  - Wait for REQUEST to de-assert.  
  - Delay random time (programmable).  
  - Re-test REQUEST.  
  - If not already asserted, assert REQUEST.  
  - If already asserted:    
    - Then another radio has secured REQUEST.    
    - Return to wait for REQUEST to de-assert.

With these enhanced REQUEST and PRIORITY features, multiple EFR32s implement a PTA interface, which to the Wi-Fi/PTA device appears to be a single external radio.

An example of three EFR32 radios using 2-Wire PTA interfaced to one Wi-Fi/PTA interface using 2-Wire PTA is shown in the following figure (REQUEST pull-up or pull-down not shown).

![Three EFR32s Supporting Single Wi-Fi 2-Wire PTA Interface](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image19.jpg)

With these enhanced REQUEST features and “shared” PRIORITY, an example of three EFR32 radios using 3-Wire PTA interfaced to one Wi-Fi/PTA interface using 3-Wire PTA is shown in the following figure (REQUEST and PRIORITY pull-ups or pull-downs not shown).

![Three EFR32s Supporting Single Wi-Fi/PTA 3-Wire PTA Interface](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image20.jpg)

###### Wi-Fi/PTA Considerations

From Silicon Labs’ testing, Wi-Fi/PTA implementations vary. Best results are attained when the Wi-Fi/PTA implementation has the following characteristics.

###### Wi-Fi/PTA Supports Wi-Fi TX Preemption (heading level 7)

Wi-Fi RF duty cycle varies based on the Modulation and Coding Scheme (MCS) index, guard interval, bandwidth, and target data rate. Wi-Fi signal strength and SNR vary with distance and objects/walls between AP and STA, particularly with mobile Wi-Fi devices. When Wi-Fi signal strength and SNR drop, the MCS index is lowered to modulations able to operate at the lower SNR. However, lowering the MSC index also lowers the maximum data rate, and the Wi-Fi RF duty cycle increases as the AP and STA attempt to maintain the target data rate.

After REQUEST asserted, Wi-Fi/PTA devices not supporting Wi-Fi TX preemption delay GRANT to EFR32 until the end of the in-progress packet. At MCS0 with 20MHz bandwidth and large aggregated Wi-Fi packets, this delay can be as long as 16ms. This delay is much longer than the typical REQUEST_WINDOW (Bluetooth) or ~200µs REQUEST to end of CCA, and much longer than the maximum received 802.15.4 packet. (802.15.4). As such, the lack of Wi-Fi TX preemption during high Wi-Fi RF duty cycle increases retries, increases end-node battery consumption, and increases message loss.

After REQUEST asserted, Wi-Fi/PTA devices supporting TX preemption stop the Wi-Fi transmit, ramp down the Wi-Fi PA (within-gate delays up to tens of µs), and quickly GRANT the 2.4GHz band to the radio. The short the time from REQUEST to quiet Wi-Fi and GRANT improves message success on first attempt, which decreases retries, decreases end-node battery consumption, and decreases message loss.

Wi-Fi TX preemption impacts Wi-Fi performance as in-progress Wi-Fi TX packets are aborted and corrupted. In response to a corrupted packet, most Wi-Fi devices assume SNR or interferer and temporarily lower the MCS index to maintain connection but recover to optimum MSC index as subsequent messages are successful. However, Bluetooth and 802.15.4 networks are low duty cycle and the infrequent Wi-Fi TX preemption events have a low impact on Wi-Fi performance.

###### Wi-Fi/PTA Prevents Wi-Fi Transmit when GRANT Asserted (heading level 7)

Some Wi-Fi/PTA devices have been optimized to work with classic Bluetooth devices and have also been optimized to continue Wi-Fi TX events for a short time after asserting GRANT. This works only for that particular Wi-Fi/PTA and classic Bluetooth combination, as the minimum time from REQUEST asserted to Bluetooth TX start was used to determine how long Wi-Fi TX can continue after GRANT asserted. For EFR32 Bluetooth receives, the Wi-Fi/PTA device must stop all Wi-Fi TX when GRANT asserted and/or the EFR32 REQUEST_WINDOW must be increased to equal or exceed the Wi-Fi/PTA continued TX event.

Some Wi-Fi/PTA devices also attempt Wi-Fi TX based on the FREQ feature described in the 4-Wire PTA. If the FREQ feature is set by GPIO pin (or Wi-Fi/PTA internal register) to different frequencies, Wi-Fi/PTA will assert GRANT, but continue to Wi-Fi transmit. Given co-located Wi-Fi and Bluetooth radios with insufficient antenna isolation, the FREQ feature must be set to always blocking, FREQ GPIO pin (or Wi-Fi/PTA internal register) held asserted.

###### Wi-Fi/PTA and Application Implements Reasonable Prioritization (heading level 7)

Wi-Fi TX preemption impacts Wi-Fi performance as in-progress Wi-Fi TX packets are aborted and corrupted. In response to a corrupted packet, most Wi-Fi devices assume SNR or interferer and temporarily lower the MCS index to maintain connection but recover to optimum MSC index as subsequent messages are successful. However, Bluetooth and 802.15.4 networks are low duty cycle and the infrequent Wi-Fi TX preemption events have a low impact on Wi-Fi performance.

###### Bluetooth (heading level 8)

The Bluetooth connection interval and window are fixed. If co-located Bluetooth misses too many connection intervals, the connection is dropped and must be re-established. Frequent drops are unacceptable to end-users. Silicon Labs has implemented priority levels and priority escalation within EFR32. When EFR32 Bluetooth asserts high PRIORITY, the Wi-Fi/PTA devices should GRANT to EFR32, except for only highest priority Wi-Fi traffic.

###### 802.15.4 (heading level 8)

802.15.4 RX packets are asynchronous and missing a packet ensures retries, increased message latency, and decreased end-node battery life. As such, 802.15.4 RX priority should be set as high as possible for the application.

802.15.4 TX packets, originating from the co-located Wi-Fi/802.15.4 may have more discretion, as these devices are typically wall powered. However, 802.15.4 transmits showing high latency can result in an unsatisfactory user experience. As such, 802.15.4 TX should also be set as high as possible for the application.

###### Wi-Fi/PTA Implements Aggregation (heading level 7)

Ensure aggregation is enabled on the Wi-Fi/PTA device. During suitable conditions, aggregation combines multiple smaller packets into fewer larger packets, reducing total packet overhead. As such, aggregation aids Wi-Fi, Bluetooth, and 802.15.4 performance as Wi-Fi TX idle periods combine into larger idle periods for freer RF airtime and improved 802.15.4 PREAMBLE/SYNCH and Bluetooth mesh reception.

###### Wi-Fi/PTA Supports Directional PRIORITY (heading level 7)

Directional PRIORITY is a common feature that can be enabled on Wi-Fi/PTA devices and is recommended due to the additional radio state information provided by the Bluetooth radio which allows the possibility for concurrent Wi-Fi and Bluetooth reception when utilizing channel separation as well as concurrent Wi-Fi and Bluetooth transmission, also when utilizing channel separation. In this way, implementing Pulsed Directional PRIORITY maximizes Wi-Fi and Bluetooth throughput.

###### TX PRIORITY Escalation (802.15.4) (heading level 7)

To improve Wi-Fi performance with 802.15.4 coexistence, it is possible to start all 802.15.4 TX messages at low priority. However, to avoid blocking, all 802.15.4 TX messages during busy Wi-Fi, TX PRIORITY Escalation will escalate TX to high priority after a programmable number of CCA/GRANT or MAC failures. Then, after a successful TX message, de-escalate TX back to low priority.

###### Signal Identifier for High Duty Cycle Wi-Fi

###### Background (heading level 7)

The following figure shows a 15.485 ms capture of the Wi-Fi TX Active signal during maximum-throughput iperf TCP transmission. The waveform contains multiple Wi-Fi TX idle periods. The first group of idle periods is shown in an expanded view.

![High-Duty Cycle Wi-Fi TX Active](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image21.png)

From the first group, only the 0.306ms idle period is sufficient for packet receive (Bluetooth) or PREAMBLE/SYNCH detection (802.15.4) with a 0.146ms detection window (0.306ms–0.160ms). The following table lists all Wi-Fi TX idle period durations in the above 15.485ms capture.

|**Wi-Fi TX** **Idle (ms)**|**Detection Window** **(Idle–0.160ms)**|
|---|---|
|0.306|0.146|
|0.113|0.000|
|0.061|0.000|
|0.246|0.086|
|0.077|0.000|
|0.260|0.100|
|0.061|0.000|
|0.065|0.000|
|0.016|0.000|
|0.065|0.000|
|0.016|0.000|
|0.065|0.000|
|0.016|0.000|
|0.245|0.085|
|0.105|0.000|
|0.098|0.000|
|0.016|0.000|
|0.171|0.011|

The sum of all Wi-Fi TX Idle periods is 2.002ms. This Wi-Fi TX Activity indicates 87.1% duty cycle [(15.485–2.002) / 15.485 x 100%], but this calculation does not include the additional Wi-Fi RX activity.

The sum of all Detection Windows is 0.428ms, providing only 2.8% probability of receiving the packet [0.428 / 15.485 x 100%]. But again, this calculation does not include Wi-Fi RX Activity. With only 2.8% probability of packet reception and a target 1% or less BT Mesh message loss, 165 retries are required [CEILING(LOG(1%)/LOG(100%–2.8%)]. This large number of required retries is far beyond the Bluetooth mesh network retries and, even if retries are extended, the message latency becomes impractical. Large retries also quickly degrade lifetime for battery power end-nodes.

To resolve this issue, Bluetooth or 802.15.4 radios require additional RF airtime to listen for incoming packets without being blocked by co-located Wi-Fi transmissions. The following options are available:

1. **Limit the Wi-Fi TX RF duty cycle**  
   Configure the co-located Wi-Fi device to enforce a maximum allowed Wi-Fi TX RF duty cycle.
2. **Use the Signal Identifier feature**  
   Configure the co-located Bluetooth or 802.15.4 radio to use the unique Signal Identifier feature, which improves detection probability even in high-duty-cycle Wi-Fi environments.
3. **Assert REQUEST at high PRIORITY**  
   Configure the co-located Bluetooth or 802.15.4 radio to assert `REQUEST` at high `PRIORITY`, forcing the Wi-Fi transmitter to enter regular quiet periods.

Silicon Labs has not found option #1 to be reliable over various Wi-Fi/PTA devices. Option #2 is covered in this section of this document. Silicon Labs has developed a PWM extension feature to support option #3 (For more details, see [PWM for High Duty Cycle Wi-Fi](#pwm-for-high-duty-cycle-wi-fi).)

###### Signal Identifier Feature Description (heading level 7)

The Signal Identifier feature in supported devices, such as the EFR32xG24, uses a signal detector to detect IoT signals (802.15.4 or Bluetooth) during Wi-Fi's required inter-frame spacing (IFS) and interrupts the Wi-Fi radio only when a signal is detected. Upon signal detection, PTA REQUEST is asserted to halt Wi-Fi TX and capture the expected retry signal. This feature can be used to improve RX performance in high-duty-cycle Wi-Fi scenarios with minimal impact on maximum Wi-Fi throughput.

Two options are available for using the Signal Identifier feature. The difference between these variants is the method used to reset the internal signal detector that identifies a valid 802.15.4 or Bluetooth signal.

###### GPIO-based SI: (heading level 8)

For this option, configure an additional GPIO input and drive it with the Wi-Fi TX Active signal (typically the Wi-Fi TX FEM PAEN signal) to restart signal detection when Wi-Fi TX is deasserted. This approach generally provides the fastest detection of the two options. If the device schematic and layout allow for this additional connection, Silicon Labs recommends implementing this option.

###### AGC-based SI: (heading level 8)

With this option, signals from the radio's internal Automatic Gain Control (AGC) block are used to detect inactive periods during Wi-Fi TX. Because no additional GPIO connection is required between the EFR32 device and the Wi-Fi device, this option is well suited for designs with hardware constraints.

###### 802.15.4 (heading level 7)

The standard 802.15.4 packet detection requires the PREAMBLE/SYNC sequence (160 µs) to align with Wi-Fi TX idle periods, resulting in a low detection probability at high Wi-Fi duty cycles. The Signal Identifier feature enables the device to detect a valid 802.15.4 signal much more quickly and does not require PREAMBLE/SYNC alignment for signal detection.
Note that PREAMBLE/SYNC alignment is still required for successful 802.15.4 packet reception. As a result, at least one retry may still occur during high-duty-cycle Wi-Fi operation. However, this method significantly increases the probability of signal detection, reduces end-node retries and latency, and improves end-node battery life.

The following figure illustrates the improved signal detection process. When the 802.15.4 signal detector detects a signal during the Wi-Fi IFS, PTA REQUEST is asserted to halt Wi-Fi TX and allow the retransmitted packet to be received.
REQUEST is deasserted after the device successfully receives the packet and confirms reception with an ACK. If an 802.15.4 packet is not received before the programmable Receive Retry timeout expires, REQUEST is deasserted, allowing Wi-Fi operation to resume.

![802.15.4 Signal Identifier](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image24.png)

For more information on configuring the 802.15.4 signal identifier, see [Zigbee and OpenThread Coexistence with Wi-Fi](/coexistence/1.0.1/zigbee-openthread-coexistence-wifi).

###### Bluetooth (heading level 7)

If the signal identifier module triggers when the Bluetooth radio is scanning and Bluetooth PREAMBLE/SYNCH is not detected, then the radio quickly switches to next primary advertising channel in an effort to capture the ADV event repeated over all three primary advertising channels.

This feature is most useful on BT Mesh using ADV bearer method as all messages are sent over ADV channels at an unknown arrival timing.

If a Bluetooth packet is received after switching ADV channels, the packet is processed, which may include multiple RX/TX events. When the ADV event is finished, the REQUEST signal is de-asserted, allowing Wi-Fi operation to resume. The time-out after switching is programmable and, if no packet is received within time-out, the REQUEST signal is de-asserted, allowing Wi-Fi operation to resume.

![Bluetooth Signal Identifier](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image25.png)

For more information on configuring the Bluetooth signal identifier, see [Bluetooth Coexistence with Wi-Fi](/coexistence/1.0.1/bluetooth-coexistence-with-wifi).

###### PWM for High Duty Cycle Wi-Fi

PWM is a feature that allows the EFR32 to reserve a timed slot for REQUEST assertion. This can be beneficial in high-duty-cycle Wi-Fi environments because scheduled EFR32 TX and RX activity enables the Wi-Fi device to more readily grant airtime when it is anticipated. Note that GRANT is not guaranteed; the Wi-Fi device ultimately determines whether to provide airtime.

This PWM feature can be applied to any use case in which EFR32 Bluetooth Passive Scan, Bluetooth Mesh, or 802.15.4 packet reception is impaired by a co-located, controllable external device. This includes, but is not limited to, the following use cases:

- High duty-cycle co-located Wi-Fi TX
- Bluetooth or 802.15.4 co-channel with Wi-Fi
- External T/R switch requiring Bluetooth or 802.15.4 to share a single antenna with Wi-Fi

###### Bluetooth (heading level 7)

100% Passive SCAN and Bluetooth mesh have no knowledge of when an incoming packet will arrive. As the co-located Wi-Fi TX RF duty cycle increases, the Wi-Fi TX idle periods become smaller and there are fewer idle periods allowing Passive SCAN or Bluetooth mesh receives. As such, the probability of a packet arriving during an acceptably large Wi-Fi TX idle period decreases significantly.

The PWM feature implements regular PTA REQUESTs to interrupt the high Wi-Fi RF duty cycle and ensure adequate windows for receives. However, this feature does degrade Wi-Fi performance. Additionally, the exact PWM period and duty cycle must be carefully selected to not alias PWM period with Wi-Fi beacons to avoid collapsing the Wi-Fi network.

> **Note**: Ensure aggregation is enabled on Wi-Fi/PTA device. During suitable conditions, aggregation combines multiple smaller packets into fewer larger packets which reduces the total packet overhead. As such, aggregation aids Passive SCAN and Bluetooth mesh ADV-Bearer packet detection as Wi-Fi TX idle periods combine into larger idle periods.

###### 802.15.4 (heading level 7)

802.15.4 radios have no knowledge of when an incoming packet will arrive and must capture the 802.15.4 PREAMBLE/SYNCH (160µs duration) to detect an incoming packet. As the co-located Wi-Fi TX RF duty cycle increases, the Wi-Fi TX idle periods become smaller and there are fewer idle periods exceeding 160µs. As such, the probability of a 160µs PREAMBLE/SYNCH arriving during an acceptably large Wi-Fi TX idle period decreases significantly.

The PWM feature implements regular PTA REQUESTs to interrupt the high Wi-Fi RF duty cycle and ensure adequate windows for PREAMBLE/SYNCH detection. However, this feature does degrade Wi-Fi performance. Additionally, the exact PWM period and duty cycle must be carefully selected to not alias PWM period with Wi-Fi beacons to avoid collapsing the Wi-Fi network.

> **Note**:  Ensure aggregation is enabled on Wi-Fi/PTA device. During suitable conditions, aggregation combines multiple smaller packets into fewer larger packets which reduces the total packet overhead. As such, aggregation aids 802.15.4 packet detection as Wi-Fi TX idle periods combine into larger idle periods for improved PREAMBLE/SYNCH detection.

###### PWM Feature Description (heading level 7)

The PWM feature does not require any additional GPIOs for single EFR32 designs. However, multi-EFR32 designs require an additional GPIO to be available for each EFR32.

###### Bluetooth (heading level 8)

The PWM feature is used to periodically stop co-located Wi-Fi TX activity to ensure sufficient idle TX time windows to receive Bluetooth packets during Passive SCAN and Bluetooth mesh ADV-Bearer receive.

The optimum PWM period and duty cycle can vary based on Bluetooth operation and Wi-Fi activity. If Wi-Fi activity is low, PWM could be disabled and fall back to normal PTA activity as described in [PWM for High Duty Cycle Wi-Fi](#pwm-for-high-duty-cycle-wi-fi). During very high Wi-Fi duty cycle for Bluetooth mesh ADC-Bearer, a PWM programmed to 39ms period and >44% duty is effective in reducing message loss to less than 1%. As noted, Wi-Fi throughput is impacted and, under 39ms/>44% condition, the high duty cycle Wi-Fi TX throughput noted above dropped ~50%.

###### 802.15.4 (heading level 8)

Under the same Wi-Fi TX Active throughput conditions as in section [Background](#background), the following figure shows the REQUEST||PWM (shown as REQUEST||TIMER1) signal interrupting the Wi-Fi TX with a 19.5 ms period and 20% duty cycle, sufficient to allow 802.15.4 RX with < 1% message loss.

![EFR32 RX Success with PWM during High-Duty Cycle Wi-Fi TX](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image22.jpg)

The green arrows show cases where the PREAMBLE/SYNCH was detected during a high PWM cycle, driving REQUEST||PWM high. This high PWM cycle stopped Wi-Fi TX Activity to allow detection of incoming 802.15.4 PRERAMBLE/SYNCH. As also seen, the REQUEST||PWM cycle is longer than all other cycles due to assertion of normal PTA REQUEST signal OR’ed with PWM cycle.

The red boxes are cases where the remote 802.15.4 radio executed packet transmits that were not detectable due to Wi-Fi TX Activity and packets’ PREAMBLE/SYNCH not aligning with the PWM cycle.

Optimum PWM period and duty cycle can vary based on 802.15.4 operation and Wi-Fi activity. If Wi-Fi activity is low, PWM could be disabled and fall back to normal PTA activity as described earlier. During very high Wi-Fi duty cycle for unicast 802.15.4 traffic, a PWM programmed to 19.5 ms period and 20% duty is effective in reducing message loss to less than 1% with APS retries disabled. As noted, Wi-Fi throughput is impacted and, under 19.5 ms/20% condition, the high duty cycle Wi-Fi TX throughput noted above dropped ~20% (~270 Mbps without PWM to ~220 Mbps with PWM).

During very high Wi-Fi duty cycle for broadcast 802.15.4 traffic without ACKs (for example, as occurs during network join sequence), a higher PWM duty cycle is required (e.g., 19.5 ms period and 80% duty cycle). This higher duty cycle can be disruptive to Wi-Fi but is only needed during expected broadcast message activity. This higher duty cycle can be reduced as soon as broadcast activity (for example, device join) completes.

###### PWM Implementation (heading level 7)

The optimum PWM implementation varies with single-EFR32 vs. multi-EFR32 applications. This implementation difference is primarily due to the REQUEST sharing feature used in multi-EFR32 applications. However, if addressed during circuit board design, this difference is easily resolved.

###### Bluetooth (heading level 8)

Use of an BGAPI or CLI command are needed to allow the host application to control the PWM period, duty cycle, and priority. For more information, see [Bluetooth Low Energy Coexistence with Wi-Fi](https://docs.silabs.com/bluetooth/latest/bluetooth-coexistence-with-wifi/).

###### 802.15.4 (heading level 8)

Use of an API or coexistence CLI command are needed to allow the host application to control the PWM period, duty cycle, and priority. For more information, see [Zigbee and OpenThread Coexistence with Wi-Fi](https://docs.silabs.com/multiprotocol/latest/zigbee-openthread-coexistence-wifi/).

###### Multi-EFR32 Radio with PWM (heading level 8)

For multi-EFR32 radio applications not implementing PWM, the Shared REQUEST signal is used to arbitrate which EFR32 radio has access to the 2.4 GHz ISM band when GRANT is asserted from the Wi-Fi/PTA. If the PWM signal were to be applied to the shared REQUEST pin, other EFR32 radios would interpret the REQUEST as busy when it might just be forcing Wi-Fi TX idle to allow any of the IoT radios to detect an incoming packet. Instead, the Shared REQUEST feature is accommodated by separating the Shared REQUEST signal from the REQUEST||PWM signal resulting in a separate non-Wi-Fi/PTA signal connection between the EFR32 devices for Shared REQUEST.

The PWM enabled Wi-Fi/PTA connections between the Wi-Fi and Zigbee, OpenThread, and BLE EFR32 radios are the same as the multi-EFR32 radio with typical 3-Wire PTA schema. Only one radio, selected at run time, controls the PWM and ORs the PWM signal in software with its own REQUEST (arbitrated by the Shared REQUEST signal). The other EFR32 radios must also implement PWM in Hardware Configurator to provide their REQUEST to the PTA interface, (also arbitrated by Shared REQUEST). Each EFR32 radio’s REQUEST||PWM GPIO is set to open-drain/open-source, allowing a hardware OR function of any EFR32 radio’s REQUEST with REQUEST||PWM.

If the radio driving REQUEST||PWM needs to go off-line (for example, for a firmware update), its PWM outputs can be halted and another EFR32 radio’s PWM outputs can then be activated for continued PWM operation. In the following figure (REQUEST and PRIORITY pull-ups and pull-downs not shown), the Zigbee radio implements the PWM feature and the Thread radio or the Bluetooth radio provide PWM backup.

![Three EFR32s Supporting PWM and Wi-Fi/PTA with Single 3-Wire PTA Interface](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image23.jpg)

###### Directional PRIORITY

Directional PRIORITY provides radio state information on the same signal line as PRIORITY by a timed pulse indicating a priority transaction, followed by a radio state indicating to the Wi-Fi part that the EFR32 is transmitting or receiving. PRIORITY signal options must be configured for implementing Directional PRIORITY.

Directional PRIORITY requires the use of a Timer and either four PRS channels (EFR32xG2x) or five PRS channels (EFR32xG1x). When Directional PRIORITY is enabled, the designer will need to verify the Timer and PRS channels selected for Directional PRIORITY are not used by the protocol SDK stack, plugins or custom application code.

RACPAEN and REQUEST is either one PRS channel which inverts ORs in one operation (EFR32xG2x) or two separate PRS channels (EFR32xG1x).

###### Single-EFR32 PTA with Directional PRIORITY (heading level 7)

The following figures show the GPIO, Radio, and Timer signals for Single-EFR32 PTA with and without SDK PWM and with Directional PRIORITY.

![Single-EFR32 PTA with SDK PWM, with Directional PRIORITY](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image26.jpg)

![Single-EFR32 PTA without SDK PWM, with Directional PRIORITY](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image27.jpg)

###### Directional PRIORITY (heading level 8)

- EFR32 Directional Priority output.
- GPIO connects to Wi-Fi PTA.
- Requires active high Static PRIORITY and active high REQUEST

###### Static PRIORITY (heading level 8)

- EFR32 PTA output.
- Directional PRIORITY input.
- The active high PRIORITY is not assigned to a GPIO.
- Not connected to any external circuit.

###### REQUEST (heading level 8)

- EFR32 PTA output.
- Directional PRIORITY input.
- Compared in Pulse Width Timer.
- GPIO connects to Wi-Fi PTA.

###### GRANT (heading level 8)

- EFR32 PTA input.
- GPIO connects to Wi-Fi PTA.

###### RACPAEN (heading level 8)

- EFR32 radio transmit PA enable output.
- Directional PRIORITY input.

###### Pulse Width (heading level 8)

- EFR32 Timer compared with REQUEST GPIO.
- Directional PRIORITY input.

###### Multi-EFR32 PTA with Directional PRIORITY (heading level 7)

The following figure shows the GPIO, Radio, and Timer signals for Multi-EFR32 PTA with SDK PWM and Directional PRIORITY (1K 5% pull-ups and pull-downs not shown).

![Multi-EFR32 PTA with SDK PWM and Directional PRIORITY](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image28.jpg)

###### Shared REQUEST (heading level 8)

- EFR32 PTA input / output.
- GPIO connects to all EFR32s for PTA bus arbitration between EFR32s  
  - Configured as open source / drain.  
  - The active high Shared REQUEST is open-source and an external 1 kΩ ±5% pull-down is required.

###### Directional PRIORITY (heading level 8)

- EFR32 Directional Priority output
- GPIO Connects to Wi-Fi PTA and all EFR32s  
  - Configured as open source / drain.    
    - The active high Directional PRIORITY is open-source and an external 1 kΩ ±5% pull-down is required.

###### Static PRIORITY (heading level 8)

- EFR32 PTA output.
- Directional PRIORITY input.
- The active high PRIORITY is not assigned to a GPIO.
- Not connected to any external circuit.

###### PWM REQUEST (heading level 8)

- EFR32 PTA output.
- Directional PRIORITY input.
- GPIO Connects to Wi-Fi PTA and all EFR32s.  
  - Configured as open source / drain.    
    If active high, PWM REQUEST is open-source and an external 1 kΩ ±5% pull-down is required.  
  - Compared with Pulse Width in EFR32 Timer.

###### GRANT (heading level 8)

- EFR32 PTA input.
- GPIO connects to Wi-Fi PTA and all EFR32s.

###### RACPAEN (heading level 8)

- EFR32 Radio transmit PA enable output.
- Directional PRIORITY input.

###### Pulse Width (heading level 8)

- EFR32 Timer compared with PWM REQUEST GPIO.
- Directional PRIORITY input.

The following figure shows the PRS and Timer logic diagram for Directional PRIORITY for EFR32xG1x.

![EFR32xG1x Directional PRIORITY Logic Diagram](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image29.jpg)

The following figure shows the PRS and Timer logic diagram for Directional PRIORITY for EFR32xG2x.

![EFR32xG2x Directional PRIORITY Logic Diagram](/multiprotocol-wifi-coexistence-fundamentals/0.1/images/sld484-image30.jpg)

##### Conclusions

Co-located, strong Wi-Fi can have a substantial impact on Bluetooth and 802.15.4 performance. It can be improved by using the following [unmanaged coexistence](/coexistence/1.0.1/multiprotocol-wifi-coexistence-fundamentals/03-unmanaged-coexistence) techniques:

1. Implement Frequency Separation
2. Operate Wi-Fi with 20 MHz Bandwidth
3. Increase Antenna Isolation
4. Implement Protocol and Stack Retry Mechanisms
5. Remove FEM (or Operate FEM LNA in Bypass)

With market trends toward higher Wi-Fi TX power, higher Wi-Fi throughput, and integration of Wi-Fi, Bluetooth, and other radios, into the same device, unmanaged techniques alone may prove insufficient, so a managed coexistence solution is required. Even with a managed coexistence solution, all unmanaged coexistence recommendations are still necessary.

Silicon Labs recommends the following managed coexistence strategies:

- Wi-Fi/PTA devices providing 802.15.4-derived Packet Traffic Arbitration.
- Silicon Labs’ EFR32 PTA solution:  
  - Implement one to four GPIOs as a combination of REQUEST, GRANT, PRIORITY, and RHO (two additional GPIOs are required to implement the PWM with High-Duty Cycle Wi-Fi feature for multi-EFR32 configurations).  
  - Supports both single-EFR32 and multi-EFR32 configurations with single Wi-Fi/PTA interface.  
  - Silicon Labs’ coexistence library and coexistence-hal-config.h #define settings to configure EFR32 PTA support for available GPIO pins and for compatibility with the chosen Wi-Fi/PTA device.  
  - Silicon Labs’ API, supporting run-time PTA reconfiguration. For more information, see [Zigbee and OpenThread Coexistence with Wi-Fi](/coexistence/1.0.1/zigbee-openthread-coexistence-wifi) or [Bluetooth Coexistence with Wi-Fi](/coexistence/1.0.1/bluetooth-coexistence-with-wifi).

###### Bluetooth

Wi-Fi/Bluetooth coexistence test results show substantial Bluetooth performance improvements when PTA is utilized:

- Connection stability  
  - Prevent user frustration with unstable product function as Wi-Fi throughput varies.
- Substantially reduced message failure with associated throughput improvement:  
  - Improves end-node battery life.  
  - Reduces message latency.  
  - Bluetooth remains operational, even during high Wi-Fi duty cycles.

###### 802.15.4

Wi-Fi/802.15.4 coexistence test results show substantial 802.15.4 performance improvements when PTA is utilized:

- Improved device join success:  
  - However, device join utilizes broadcast messages, which are not retried.  
  - If possible, device join success can be further improved by temporarily reducing Wi-Fi traffic during devices joining 802.15.4 network.
- Substantially reduced MAC retries:  
  - Reduces message latency.  
  - Improves end-node battery life.  
  - Frequency separation remains important, as best managed coexistence performance is for “far-away” channels.
- Substantially reduced message failure:  
  - 802.15.4 network remains operational, even during high Wi-Fi duty cycles.

#### Zigbee and OpenThread Coexistence with Wi-Fi

##### Zigbee and OpenThread Coexistence with Wi-Fi

**NOTE: This section replaces _AN1017: Zigbee and OpenThread Coexistence with Wi-Fi_. Further updates to this application note will be provided here.**

This application note describes methods to improve coexistence of 2.4 GHz IEEE 802.11b/g/n Wi-Fi and IEEE 802.15.4-based radios such as Zigbee® and OpenThread. This section assumes you have a basic understanding of the concepts and principles of Wi-Fi coexistence with 802.15.4 radios and how Wi-Fi coexistence is implemented on EFR32 devices. For more information, see [Wi-Fi Coexistence Fundamentals](/coexistence/1.0.1/multiprotocol-wifi-coexistence-fundamentals).

This application note describes EFR32 Zigbee and OpenThread coexistence support for EmberZNet PRO 6.9.0.0 and OpenThread 1.1.0.0 versions and above. For information on enabling and configuring coexistence support with AppBuilder, refer to document version 1.1.6 or earlier.

Additional details about the implementation of managed coexistence are included in _AN1243: Timing and Test Data for EFR32 Coexistence with Wi-Fi_, available under non-disclosure from Silicon Labs Sales.

##### Configuring Wi-Fi Coexistence

This section describes how to configure the Silicon Labs Packet Traffic Arbitration (PTA) using the Simplicity Studio Component Editor for Zigbee and OpenThread.

###### PTA Software Setup with the Component Editor (Zigbee and OpenThread)

The steps to set up PTA Software for Zigbee and OpenThread using the Component Editor are described below. These steps assume you have installed Simplicity Studio 5, EmberZNet SDK 7.x, and the OpenThread SDK, and that you have a project open in the Simplicity IDE.

1. On the **SOFTWARE COMPONENTS** tab, search for _coex_ in the **component’s name** search field (at the top right).  
   - For Zigbee, under Platform->Radio components, select and install the **RAIL Utility, Coexistence** component directly under Platform components.  
   - For OpenThread, under **OpenThread** components, select the **Coexistence** component and click **Install** as shown in the following figure. Installing the Coexistence component will install the RAIL Utility, Coexistence component for your project.  
   > **Note**: For Silicon Labs OpenThread SDK releases prior to 1.2.0.0, select and install the **RAIL Utility, Coexistence** component directly under Platform components.  
   ![image](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image4.png)
2. Once the relevant coexistence component(s) are successfully installed, click **Configure** or the configure symbol next to the **RAIL Utility, Coexistence** component name to open the coexistence properties as shown in the following figure.  
   ![image](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image5.png)

The following figures show the different coexistence properties in the Component Editor. For more information on coexistence properties, see Coexistence Configurations below.

![image](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image6.png)

![image](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image7.png)

![image](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image8.png)

###### Coexistence Configurations

The following subsections describe the coexistence configurations in detail.

> **Note**: To configure GPIO pins for a coexistence signal in the Component Editor, use the equivalent **SL_RAIL_UTIL_COEX <signal>** section of the configuration header.

###### REQUEST (heading level 7)

###### REQUEST signal enabled (heading level 8)

- If selected, REQUEST is mapped to GPIO pin and is used by PTA implementation.
- If not selected, REQUEST is not mapped to GPIO pin.

###### REQUEST signal is shared (heading level 8)

- If selected, REQUEST is shared and implements open-drain or open-source I/O for multi-EFR32 radio applications.
- If active low, REQUEST is open-drain and an external 1 k ±5% pull-up is required.
- If active high, REQUEST is open-source and an external 1 k ±5% pull-down is required.
- If not selected, REQUEST is not shared and implements a push-pull output for single EFR32 radio applications.

###### REQUEST signal active high (heading level 8)

- If selected, REQUEST GPIO pin is driven high (> Voh) when REQUEST is asserted.
- If not selected, REQUEST GPIO pin is driven low (< Vol) when REQUEST is asserted.

###### REQUEST signal GPIO port and REQUEST signal GPIO pin (heading level 8)

- Select REQUEST port and pin matching circuit board configuration.
- To configure GPIO port and pin for the REQUEST signal in the Component Editor, use the **SL_RAIL_UTIL_COEX_REQ** section of the configuration header.

![REQUEST signal in the Component Editor](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image9.png)

###### REQUEST signal max backoff mask [0-255] (heading level 8)

- REQUEST signal max backoff determines the random REQUEST delay mask (only valid if REQUEST signal is shared).
- Random delay (in µs) is computed by masking the internal random variable against the entered mask.
- The mask should be set to a value of 2n-1 to insure a continuous random delay range.

###### Receive Retry (heading level 8)

###### Receive retry REQUEST enabled (heading level 8)

- If selected, REQUEST is held after a corrupted receive packet or after a successful receive packet with GRANT denied until timeout expires, or another packet is received.

> **Note**: This feature is useful to hold 2.4 GHz band clear while the remote device re-transmits a packet, maximizing the opportunity to receive an uncorrupted retry packet from the remote device, reducing 2.4 GHz RF traffic and improving battery life.

- If not selected, REQUEST is not held after a corrupted receive packet or after a successful receive packet with GRANT denied.

###### Receive retry timeout (milliseconds) [0-255] (heading level 8)

- Selects the timeout for REQUEST hold after a corrupted receive packet.

**Notes**:

1. 16ms is recommended to allow for maximum 802.15.4 packet duration and MAC retry random delay.
2. Many Wi-Fi/PTA implementations have a maximum GRANT timeout, which should be set to received retry timeout plus 6ms to allow for maximum size corrupted packet, maximum random delay, and maximum size retry packet.
3. The Receive Retry timeout is used by the 802.15.4 Signal Identifier for supported devices (like the EFR32xG24) as the timeout after asserting REQUEST from signal identifier.

###### REQUEST high PRIORITY on receive retry (heading level 8)

- If selected, PRIORITY is asserted during REQUEST hold after a corrupted receive packet or after a successful receive packet with GRANT denied.
- If not selected, PRIORITY is de-asserted during REQUEST hold after a corrupted receive packet or after a successful receive packet with GRANT denied.

###### GRANT (heading level 7)

###### GRANT signal enabled (heading level 8)

- If selected, GRANT is mapped to GPIO pin and is used by PTA implementation.
- If not selected, GRANT is not mapped to GPIO pin and GRANT is always asserted.

###### GRANT signal active high (heading level 8)

- If selected, GRANT is asserted when GRANT GPIO pin is high (> Vih).
- If not selected, GRANT is asserted when GRANT GPIO pin is low (< Vil).

###### GRANT signal GPIO port and GRANT signal GPIO pin (heading level 8)

- Select GRANT port and pin matching circuit board configuration.
- To configure GPIO port and pin for the GRANT signal in the Component Editor, use the **SL_RAIL_UTIL_COEX_GNT** section of the configuration header.

![image](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image10.png)

###### Abort Transmission Mid Packet If GRANT Is Lost (heading level 8)

- If selected, losing GRANT during an 802.15.4 TX will abort the 802.15.4 TX.
- If not selected, losing GRANT after the initial evaluation at end of CCA will not abort the 802.15.4 TX.

> **Note**: In the Component Editor, this option has been moved from the GRANT section to the common section of the configuration header.

###### ACK Disable (heading level 7)

###### Disable ACKing when GRANT de-asserted, RHO asserted, or REQUEST not secured (shared REQUEST only) (heading level 8)

- If selected, the ACK to a valid RX packet, requiring an ACK, is **not** transmitted if GRANT is de-asserted, RHO is asserted, or REQUEST is not secured (shared REQUEST only).

> **Note**: This feature allows completing an 802.15.4 message, regardless of PTA signals, to minimize additional retries from remote device, reducing 2.4 GHz RF traffic and improving battery life.

- If not selected, the ACK to a valid RX packet requiring an ACK is transmitted regardless of GRANT, RHO, or REQUEST state.

> **Note**: In the Component Editor, this option has been moved from the GRANT section to the common (IEEE802.15.4 Only Configuration) section of the configuration header.

![image](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image11.png)

###### PRIORITY (heading level 7)

###### PRIORITY signal enabled (heading level 8)

- If selected, PRIORITY is mapped to GPIO pin and is used by PTA implementation.
- If not selected, PRIORITY is not mapped to GPIO pin.

###### PRIORITY signal active high (heading level 8)

- If selected, PRIORITY GPIO pin is driven high (> Voh) when PRIORITY is asserted.
- If not selected, PRIORITY GPIO pin is driven low (< Vol) when PRIORITY is asserted.
- If Enable Directional PRIORITY equals True, PRIORITY assert signal level must be set to High.

###### PRIORITY signal GPIO port and PRIORITY signal GPIO pin (heading level 8)

- Select PRIORITY port and pin matching circuit board configuration.
- To configure GPIO port and pin for the PRIORITY signal in the Component Editor, use the **SL_RAIL_UTIL_COEX_PRI** section of the configuration header.

![image](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image12.png)

###### Enable PRIORITY shared mode (heading level 8)

- If enabled. PRIORITY is shared and implements open-drain or open-source I/O for multi-EFR32 radio applications.
- If active low, PRIORITY is open-drain and an external 1 k ±5% pull-up is required.
- If active high, PRIORITY is open-source and an external 1 k ±5% pull-down is required.
- If not enabled, PRIORITY is not shared and implements a push-pull output for single EFR32 radio applications.

###### TX high PRIORITY (heading level 8)

- If selected, PRIORITY is asserted during 802.15.4 TX.
- If not selected, PRIORITY is de-asserted during 802.15.4 TX.

###### RX high PRIORITY (heading level 8)

- If selected, PRIORITY is asserted during 802.15.4 RX.
- If not selected, PRIORITY is de-asserted during 802.15.4 RX.

###### Include TX PRIORITY Escalation (heading level 8)

- If enabled. TX PRIORITY Escalation feature is compiled into firmware.
- If not enabled, TX PRIORITY Escalation feature is not compiled into firmware and CCA/GRANT TX PRIORITY Escalation Threshold and MAC Fail TX PRIORITY Escalation Threshold must be set to 0 when writing to ptaOptions via run-time API.

###### CCA/GRANT TX PRIORITY Escalation Threshold (heading level 8)

- If set to 0 (000b, default):
- CCA/GRANT TX PRIORITY Escalation is disabled.
- PRIORITY during TX is asserted as per “TX high PRIORITY” setting.
- If set between n=1 (001b) to 7 (111b) [requires “TX high PRIORITY” set to low priority (0)]:
- CCA/GRANT TX PRIORITY Escalation is enabled.
- PRIORITY during TX becomes asserted high after n MAC failures due to four CCA and/or GRANT denial failures.
- PRIORITY during TX remains asserted high until a successful MAC TX and RX ACK.

###### MAC Fail TX PRIORITY Escalation Threshold (heading level 8)

- If set to 0 (00b, default):
- CCA/GRANT TX PRIORITY Escalation is disabled.
- PRIORITY during TX is asserted as per “TX high PRIORITY” setting.
- If set to n=1 (01b) to 3 (11b) [requires “TX high PRIORITY” set to low priority (0)]:
- CCA/GRANT TX PRIORITY Escalation is enabled.
- PRIORITY during TX is asserted high after n MAC failures due to CCA (four CCA failures) or MAC ACK fails (four MAC TX and RX ACK failures).
- PRIORITY during TX remains asserted high until a successful MAC TX and RX ACK.

###### Signal Identifier (on supported devices) (heading level 7)

The Signal Identifier is a unique feature available on supported devices (like the EFR32xG24), which allows the radio to detect incoming 802.15.4 signals in a much shorter time window compared to normal detection.
For a more detailed description on how the feature works, see the relevant section in [Wi-Fi Coexistence Fundamentals](/coexistence/1.0.1/multiprotocol-wifi-coexistence-fundamentals).

###### Enable coexistence IEEE802.15.4 signal identifier (heading level 8)

- If selected, the signal identifier feature is enabled to trigger REQUEST when an 802.15.4 signal is detected.
- If not selected, the signal identifier feature is not enabled.

###### IEEE802.15.4 signal identifier mode (heading level 8)

- If configured to "15.4 with AGC reset", the internal signals of the radio will be used for resetting the Signal Identifier detection.
- If configured to "15.4 with PRS reset", an external signal through a GPIO is used for resetting the Signal Identifier detection. If this option is used, the correct polarity and pin must also be configured for the COEX_WIFI_TX signal.  
  ![image](/zigbee-openthread-coexistence-wifi/0.1/images/154-si-configuration.png)

###### Polarity of Wi-Fi TX signal (heading level 8)

- If set to high, Wi-Fi TX PAEN is detected as asserted when the GPIO pin is high (> Vih).
- If set to low, Wi-Fi TX PAEN is detected as asserted when the GPIO pin is low (< Vil).

###### Wi-Fi TX signal GPIO port and GPIO pin (heading level 8)

- Select Wi-Fi TX port and pin matching circuit board configuration.
- To configure GPIO port and pin for the Wi-Fi TX signal in the Component Editor, use the **SL_RAIL_UTIL_COEX_WIFI_TX** section of the configuration header.
- If configured, signal detector will be restarted when Wi-Fi TX is de-asserted

![image](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image23.png)

###### PWM (heading level 7)

###### PWM REQUEST signal (shared REQUEST only) (heading level 8)

- If REQUEST signal is NOT shared, PWM REQUEST signal must be set to “Disabled”.
- If REQUEST signal is shared, the REQUEST GPIO pin is used to arbitrate REQUEST between multiple EFR32 radios and a second GPIO is required to drive REQUEST||PWM. PWM REQUEST signal specifies the REQUEST||PWM GPIO.

###### PWM REQUEST signal level (shared REQUEST only) (heading level 8)

- If PWM REQUEST signal is “Disabled”, then PWM REQUEST signal level selection is ignored. Else, PWM REQUEST signal is shared and implements open-drain or open-source I/O for multi-EFR32 radio applications.
- If active low, PWM REQUEST is open-drain, an external 1 k ±5% pull-up is required, and PWM REQUEST GPIO pin is driven low (< Vol) when REQUEST||PWM is asserted.
- If active high, PWM REQUEST is open-source, an external 1 k ±5% pull-down is required, and PWM REQUEST GPIO pin is driven high (> Voh) when REQUEST||PWM is asserted.

###### PWM REQUEST signal GPIO port and PWM REQUEST signal GPIO PIN (heading level 8)

- To configure GPIO port and pin for the PWM REQUEST signal in the Component Editor, use the **SL_RAIL_UTIL_COEX_PWM_REQ** section of the configuration header.

![image](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image13.png)

###### Enable PWM REQUEST at startup (heading level 8)

- If enabled, PWM REQUEST is enabled at firmware startup as per specified period, duty-cycle, and priority.
- If not enabled, PWM REQUEST is disabled at firmware startup, but can be enabled via run-time API.

###### PWM REQUEST Period (0.5 ms) (heading level 8)

> **Note**: PWM REQUEST Period selection cannot be an integer sub-multiple of the Wi-Fi beacon or a significant number of consecutive Wi-Fi beacons may be missed, causing AP to collapse Wi-Fi network or STA to disassociate. Silicon Labs achieves <1% 802.15.4 message receive loss with PWM REQUEST set to 39 ms (or 78 half-ms) period, 20% duty-cycle, and high priority, which results in ~30% reduction in Wi-Fi TCP throughput over MCS0 to MCS7 and 20 or 40 MHz bandwidth.

- Sets PWM REQUEST Period from 5 ms (10) to 109 ms (218) in 0.5 ms steps.

###### PWM REQUEST Duty-Cycle (%) (heading level 8)

> **Note**: Large PWM REQUEST Duty-Cycle selection will substantially impact the Wi-Fi throughput as it reserved more time for 802.15.4 listening. Silicon Labs achieves <1% 802.15.4 message receive loss with PWM REQUEST set to 39 ms (or 78 half-ms) period, 20% duty-cycle, and high priority, which results in ~30% reduction in Wi-Fi TCP throughput over MCS0 to MCS7 and 20 or 40 MHz bandwidth.

- Sets PWM REQUEST Duty-Cycle from 1% to 95% in 1% steps.

###### Assert PRIORITY when PWM REQUEST asserted (heading level 8)

- Sets PWM REQUEST PRIORITY to assert or not when PWM REQUEST asserts.

###### Radio Hold Off (heading level 7)

###### RHO (Radio Hold Off) signal enabled (heading level 8)

- If selected, RHO is mapped to GPIO pin and is used by PTA implementation.
- If not selected, RHO is not mapped to GPIO pin and RHO is always de-asserted.

###### RHO (Radio Hold Off) active high (heading level 8)

- If selected, RHO is asserted when RHO GPIO pin is high (> Vih).
- If not selected, RHO is asserted when RHO GPIO pin is low (< Vil).

###### RHO (Radio Hold Off) signal GPIO port and RHO (Radio Hold Off) signal GPIO pin (heading level 8)

- Select RHO port and pin matching circuit board configuration.
- To configure GPIO port and pin for the RHO signal in the Component Editor, use the **SL_RAIL_UTIL_COEX_RHO** section of the configuration header.

![image](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image14.png)

###### Directional PRIORITY (heading level 7)

###### Enable Directional PRIORITY (heading level 8)

- If True:  
  - Directional PRIORITY signal is connected to the Wi-Fi PTA and multiplexes priority state and radio state information.  
  - Allows the Wi-Fi PTA master to obtain radio state information from the EFR32 using the Directional PRIORITY signal.  
  - When requesting network airtime, the EFR32 will assert a pulse on the Directional PRIORITY line depending on the requirement of that transaction and then switch to communicating the state of the radio on the same Directional PRIORITY line.  
  - The Directional PRIORITY line is held low when the radio is in receive mode and is held high when the radio is in transmit mode.  
  - The PRIORITY signal is not assigned a GPIO and is set to Disabled, has no physical connection to the Wi-Fi PTA and is used as Static PRIORITY input to the Directional PRIORITY logic block with the remaining PRIORITY signal configuration options described in [PRIORITY](#priority) applied.

> **Note**: Hardware Configurator PRIORITY configuration fields are disabled and therefore not editable when Enable Directional PRIORITY is set to True. A workaround is to assign any GPIO to PRIORITY signal, edit the PRIORITY configuration options, and then set PRIORITY signal to Disabled.

- If False:  
  - The Directional PRIORITY signal is not used or connected to the Wi-Fi PTA.  
  - The PRIORITY signal is connected to the Wi-Fi PATA and operates as Static PRIORITY and is either high or low during REQUEST asserted for the transmit or receive operation.

###### Directional PRIORITY Timer (Component Editor) (heading level 8)

To configure the Directional PRIORITY Timer in the Component Editor.

1. Use the **SL_RAIL_UTIL_COEX_DP_TIMER** to choose an unused timer.
2. Set the **TIMER Compare/Capture Channel 0 (CC0) pin** to match the same GPIO pin used for REQUEST.

![image](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image18.png)

> **Note**: For EFR32xG2x devices, the **Timer Compare / Capture Channel** is selected by the stack code. However, for EFR32xG1x devices, the stack code does not select the Timer Compare / Capture Channel pin.

###### Directional PRIORITY Pulse Width [0-255] (heading level 8)

- Set to 20 (0x14) by default.
- Selects the hold time of the Directional PRIORITY RX Priority pulse in microseconds for a range of 1 to 255 depending on the requirement of the Wi-Fi PTA. Silicon Labs recommends the default of 20 microseconds for typical Wi-Fi PTA implementations.
- Set to 0 to bypass Directional PRIORITY.

###### Directional Priority PRS Channel (heading level 8)

To configure the Directional PRIORITY PRS Channel:

1. Choose any group of four PRS channels (EFR32xG2x) or five PRS channels (EFR32xG1x) not currently used by the SDK stack software, other plugins, or custom code.
2. Assign the highest PRS channel number from this group to the Directional Priority PRS Channel value.

> **Note**: The External FEM plugin is recommended to be enabled for monitoring the EFR32 radio TX activity and radio RX activity for the custom coexistence application test and development purposes and can use up to two additional PRS channels.

The SDK stack software automatically selects the preceding three PRS channels from the group.

**Example**: For EFR32xG2x, the designer selects PRS channel 2 as the Directional Priority PRS Channel value. From the designer’s choice, the SDK stack software automatically selects PRS channel 1, PRS channel 0 and PRS channel 11 for use in the Directional Priority PRS Channel group. In this example, the SDK stack software automatically wraps around from the lowest PRS channel number to the highest PRS channel number until all three additional required PRS channels are assigned.

To configure the Directional PRIORITY PRS Channel in SSv5 Component Editor:

1. Use the **SL_RAIL_UTIL_COEX_DP_OUT** section of the configuration header.
2. Selecting the **Directional PRIORITY PRS Channel** displays the **PRS Channel Output Pin** configuration option.  
   ![image](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image19.png)

###### PRS Channel Output Pin (heading level 8)

- Directional PRIORITY output GPIO pin
- Connects to the Wi-Fi PTA

###### Inverted Request PRS Channel (heading level 8)

For EFR32xG2x Series 2 EFR32 devices, the **Inverted Request PRS Channel** is selected by the stack code. However, the stack code does not select the Inverted Request PRS Channel for EFR32xG1x Series 1 EFR32 devices.

- Choose any PRS channel not used by the SDK stack software, other plugins, custom code, or the Directional Priority PRS Channel option.
- For EFR32xG2x, leave this option Disabled.
- To configure the Inverted Request PRS Channel in the Component Editor, use the **SL_RAIL_UTIL_COEX_DP_REQUEST_INV** section of the configuration header.

![image](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image20.png)

###### RX Active (heading level 7)

###### RX active signal enabled (heading level 8)

- If selected, MODEM_FRAME_DETECT is mapped to GPIO pin using a PRS channel.
- If not selected, MODEM_FRAME_DETECT is not mapped to GPIO pin and does not use a PRS channel.

###### RX active assert signal level (heading level 8)

- Selecting High results in a high signal output when the receive packet is detected and a low output otherwise.
- Selecting Low results in a low signal output when the receive packet is detected and a high output otherwise.

###### RX active PRS channel (heading level 8)

- Selects PRS channel used to assign MODEM_FRAME_DETECT signal to an output GPIO.

###### PRS channel output pin (heading level 8)

- Choose the GPIO to output the MODEM_FRAME_DETECT signal.

**Notes**:

1. The RX Active feature passes the 802.15.4 MODEM_FRAME_DETECT radio signal to a GPIO pin using a PRS channel.
2. To configure the GPIO port and pin in the Component Editor use the **SL_RAIL_UTIL_COEX_RX_ACTIVE** section of the configuration header as shown in the following figure.  
   ![image](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image21.png)

This completes the Coexistence Configurator setup. When working in the Component Editor, the configurations are automatically saved and the generate step is not required. The coexistence configurations are saved in sl_rail_util_coex_config.h and sl_rail_util_coex_common_config.h.

###### Run-Time PTA Reconfiguration

The following PTA options, which can be configured at compile time, can also be re-configured at run-time:

- Receive retry timeout (milliseconds) [0-255]
- Disable ACKing when GRANT de-asserted, RHO asserted, or REQUEST not secured (shared REQUEST only)
- Abort transmission mid packet if GRANT is lost
- TX high PRIORITY
- RX high PRIORITY
- REQUEST high PRIORITY on receive retry
- Receive retry REQUEST enabled
- RHO (Radio Hold Off) signal enabled
- CCA/GRANT TX PRIORITY Escalation Threshold
- MAC Fail TX PRIORITY Escalation Threshold
- PWM REQUEST

The following PTA options cannot be configured during compile time and can only be configured at run-time:

- Enable or disable PTA
- Disable REQUEST (force holdoff)
- Synch MAC to GRANT (MAC holdoff)
- REQUEST/PRIORITY Assert (Preamble/Synch or Address Detection)

For descriptions of the above PTA options fields, see _PTA Option Descriptions_ below.

For OpenThread using the Component Editor, the Synch MAC to GRANT (MAC holdoff) option has been added as a configuration option under the **OpenThread Coexistence** component so you can configure it at compile time. This requires that you have already installed the **Coexistence** component as a part of your project.

![image](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image24.png)

> **Note**: For Silicon Labs OpenThread SDK releases prior to 1.2.0.0, the Synch MAC to GRANT (MAC holdoff) configuration option is available under the **OpenThread Platform Abstraction** component.

###### PTA Option Descriptions (heading level 7)

The descriptions of the above PTA options fields follow.

###### Disable REQUEST (force holdoff) (heading level 8)

- If not set (default), REQUEST operates per the description in [Wi-Fi Coexistence Fundamentals](https://docs.silabs.com/multiprotocol/latest/multiprotocol-wifi-coexistence-fundamentals/).
- If set, REQUEST stays disabled, effectively halting all radio TX/RX functions.

###### Synch MAC to GRANT (MAC holdoff) (heading level 8)

- If not set (default), Synch MAC to GRANT is disabled for 802.15.4-compliant random MAC delays.
- If set, MAC CCA/TX is delayed until GRANT is asserted, synching all TX operations with GRANT.
- Synch MAC to GRANT is not strictly 802.15.4 compliant as it prevents random MAC delay execution.
- Synch MAC to GRANT should only be enabled during known, higher priority, Wi-Fi or BT interfering activity and disabled as soon as such activity completes.

###### REQUEST/PRIORITY Assert (Preamble/Synch or Address Detection) (heading level 8)

- If set to 0 (00b, default and recommended):
- REQUEST during RX is asserted at Preamble/Synch.
- PRIORITY during RX is asserted at Preamble/Synch, as per “RX high PRIORITY” setting.
- If set to 1 (01b) or 3 (11b) [requires “RX high PRIORITY” set to high priority (1)]:
- REQUEST during RX is asserted at Address Detection (for this radio).
- PRIORITY during RX is asserted at Address Detection (for this radio).
- If set to 2 (10b) [requires “RX high PRIORITY” set to low priority (0)]:
- REQUEST during RX is asserted at Preamble/Synch.
- PRIORITY during RX is asserted at Address Detection (for this radio).

**Notes**:

1. For Zigbee, the API function calls for re-configuring coexistence PTA vary based on SoC or EZSP application.
2. For OpenThread, the API functions for re-configuring coexistence PTA is currently supported on SoC only.
3. For Run-Time API options not supported by selected EmberZNet PRO or OpenThread releases, the corresponding ptaOptions bit fields are RESERVED and must be written to 0.

###### SoC Application API (using Component Editor for Zigbee and OpenThread) (heading level 7)

To avoid warnings and errors at build time associated with the API function calls in this section, add the following # include into the application's <xxx>-callbacks.c file.

```C
#include "platform/radio/rail_lib/plugin/coexistence/protocol/ieee802154_uc/coexistence-802154.h";
```

The following two SoC API function calls enable and disable the PTA at run-time:

```C
bool sl_rail_util_coex_is_enabled(void;
sl_status_t sl_rail_util_coex_set_enable(bool enabled);
```

The following two SoC API function calls re-configure the PTA at run-time:

```C
sl_rail_util_coex_options_t sl_rail_util_coex_get_options(void);
sl_status_t sl_rail_util_coex_set_options(sl_rail_util_coex_options_t options);
```

Where `sl_rail_util_coex_options_t` is a `uint32_t` with the following bitmap definition.

|PTA Feature|Bit Position|Size (bits)|
|---|---|---|
|Receive retry timeout (milliseconds) [0-255]|0|8|
|Disable ACKing when GRANT de-asserted, RHO asserted, or REQUEST not secured (shared REQUEST only)|8|1|
|Abort transmission mid packet if GRANT is lost|9|1|
|TX high PRIORITY|10|1|
|RX high PRIORITY|11|1|
|REQUEST high PRIORITY on receive retry|12|1|
|RHO (Radio Hold Off) signal enabled|14|1|
|Reserved (Reserved bits MUST be written 0)|15|1|
|Disable REQUEST (force holdoff)|16|1|
|Synch MAC to GRANT (MAC holdoff)|17|1|
|REQUEST/PRIORITY Assert (Preamble/Synch or Address Detection)|18|2|
|CCA/GRANT TX PRIORITY Escalation Threshold|20|3|
|Reserved (Reserved bits MUST be written 0)|23|2|
|MAC Fail TX PRIORITY Escalation Threshold|25|2|
|Reserved (Reserved bits MUST be written 0)|27|5|

The following two SoC API function calls re-configure the PWM REQUEST at run-time:

```C
const sl_rail_util_coex_pwm_args_t *sl_rail_util_coex_get_request_pwm_args(void);
sl_status_t sl_rail_util_coex_set_request_pwm (sl_rail_util_coex_req_t ptaReq,
sl_rail_util_coex_cb_t ptaCb, uint8_t dutyCycle, uint8_t periodHalfMs);
```

Where sl_rail_util_coex_pwm_args_t is an alias for COEX_PwmArgs_t and:

```C
typedef struct COEX_PwmArgs {
      COEX_Req_t req;
      uint8_t dutyCycle;
      uint8_t periodHalfMs;
} COEX_PwmArgs_t;
```

and

```C
ptaReq/req:   0x00 => PWM REQUEST disabled
              0x80 => PWM REQUEST enabled at low priority
              0x82 => PWM REQUEST enabled at high priority
ptaCb:        NULL
dutyCycle:    PWM REQUEST duty-cycle from 5% to 95% in 1% steps
periodHalfMs: PWM REQUEST Period from 5 ms (10) to 109 ms (218) in 0.5 ms steps
```

The following two SoC API function calls re-configure the Directional PRIORITY at run-time:

```C
uint8_t sl_rail_util_coex_get_directional_priority_pulse_width(void);
sl_status_t sl_rail_util_coex_set_directional_priority_pulse_width(uint8_t pulseWidthUs);
```

Where:

```C
pulseWidthUs:    Pulse width (0 to disable, 1-255µs)
```

When multiple EFR32s are connected to Wi-Fi/PTA, TX PRIORITY Escalation can be controlled at run-time via "CCA/GRANT TX PRIORITY Escalation Threshold" and "MAC Fail TX PRIORITY Escalation Threshold" fields of PTA Options. When using this feature, "TX high PRIORITY" field must be set to 0 to avoid driving PRIORITY high on all TX messages.

###### Zigbee Network Coprocessor Application using EZSP API (heading level 7)

The following two EZSP (EmberZNet Serial Protocol) API function calls enable and disable the PTA, re-configure the PTA, and reconfigure the PWM REQUEST at run-time:

```C
sl_status_t sl_zigbee_ezsp_get_value(sl_zigbee_ezsp_value_id_t valueId, uint8_t *valueLength, uint8_t *value)
sl_status_t sl_zigbee_af_set_ezsp_value(sl_zigbee_ezsp_value_id_t valueId, uint8_t valueLength, uint8_t *value, const char * valueName)```

Where valueId and valueLength have the following PTA related options.

:::custom-table{width=45,10,15%,30%}
| EZSP Value ID | Value | Length (bytes) | Description |
|---------------|-------|----------------|-------------|
| SL_ZIGBEE_EZSP_VALUE_ENABLE_PTA | 0x31 | 1 | Enable (1) or disable (0) packet traffic arbitration. |
| SL_ZIGBEE_EZSP_VALUE_PTA_OPTIONS | 0x32 | 4 | Set packet traffic arbitration (PTA) configuration options. |
| SL_ZIGBEE_EZSP_VALUE_PTA_PWM_OPTIONS | 0x35 | 3 | Configure PWM REQUEST options. |
| SL_ZIGBEE_EZSP_VALUE_PTA_DIRECTIONAL_PRIORITY_PULSE_WIDTH | 0x36 | 1 | Pulse width (0 to disable, 1-255µs) |
:::

Where PTA configuration options are a `uint32_t` with the following bitmap definition.

| PTA Feature | Bit Position | Size (bits) |
|-------------|--------------|-------------|
| Receive retry timeout (milliseconds) [0-255] | 0 | 8 |
| Disable ACKing when GRANT de-asserted, RHO asserted, or REQUEST not secured (shared REQUEST only) | 8 | 1 |
| Abort transmission mid packet if GRANT is lost | 9 | 1 |
| TX high PRIORITY | 10 | 1 |
| RX high PRIORITY | 11 | 1 |
| REQUEST high PRIORITY on receive retry | 12 | 1 |
| Receive retry REQUEST enabled | 13 | 1 |
| RHO (Radio Hold Off) signal enabled | 14 | 1 |
| Reserved (Reserved bits MUST be written 0) | 15 | 1 |
| Disable REQUEST (force holdoff) | 16 | 1 |
| Synch MAC to GRANT (MAC holdoff) | 17 | 1 |
| REQUEST/PRIORITY Assert (Preamble/Synch or Address Detection) | 18 | 2 |
| CCA/GRANT TX PRIORITY Escalation Threshold (requires additional application code) | 20 | 3 |
| Reserved (Reserved bits MUST be written 0) | 23 | 2 |
| MAC Fail TX PRIORITY Escalation Threshold (requires additional application code) | 25 | 2 |
| Reserved (Reserved bits MUST be written 0) | 27 | 5 |

PWM REQUEST configuration options is a three-byte uint8_t array:

```C
{uint8_t ptaReq, uint8_t dutyCycle, uint8_t periodHalfMs}
```

Where:

```C
ptaReq:       0x00 => PWM REQUEST disabled
              0x80 => PWM REQUEST enabled at low priority
              0x82 => PWM REQUEST enabled at high priority
dutyCycle:    PWM REQUEST duty-cycle from 5% to 95% in 1% steps
periodHalfMs: PWM REQUEST Period from 5 ms (10) to 109 ms (218) in 0.5 ms steps
```

and the Directional PRIORITY parameter is:

```C
uint8_t dp_pulse: Pulse width (0 to disable, 1-255µs)
```

When multiple EFR32s are connected to Wi-Fi/PTA, TX PRIORITY Escalation can be controlled at run-time via "CCA/GRANT TX PRIORITY Escalation Threshold" and "MAC Fail TX PRIORITY Escalation Threshold" fields of PTA Options. When using this feature, "TX high PRIORITY" field must be set to 0 to avoid driving PRIORITY high on all TX messages.

###### Coexistence Configuration Setup Examples for Different Wi-Fi/PTA Applications (Zigbee)

###### Example 1: Configure EFR32 PTA support to operate as single EFR32 with typical 3-Wire Wi-Fi/PTA (heading level 7)

- Single EFR32 radio
- RHO unused
- REQUEST unshared, active high, PC10
- Compatible 3-Wire Wi-Fi PTA devices sometimes refer to this signal as RF_ACTIVE or BT_ACTIVE (active high).
- GRANT, active low, PF3
- Compatible 3-Wire Wi-Fi PTA devices sometimes refer to this signal as WLAN_DENY (deny is active high, making grant active low).
- PRIORITY, active high RX and TX (escalation disabled), PD12
- Compatible 3-Wire W-Fi PTA devices sometimes refer to this signal as RF_STATUS or BT_STATUS (active high).

> **Note**: PRIORITY is static, not directional. If operated with a 3-Wire PTA expecting directional:

- Static high PRIORITY is interpreted as high PRIORITY and always in TX mode, regardless of actual TX or RX.
- Static low PRIORITY is interpreted as low PRIORITY and always in RX mode, regardless of actual TX or RX.
- PWM REQUEST disabled.
- Other options enabled to maximize 802.15.4 performance:
- 802.15.4 RX and TX both at high priority
- Receive retry REQUEST enabled with 16ms timeout and high priority.
- Enabled ACKing when GRANT de-asserted.

The logic analyzer capture in the following figure shows the PTA interface, Wi-Fi radio state, and EFR32 radio state for an EFR32 radio configured for typical 3-Wire Wi-Fi/PTA.

![Example 802.15.4 TX for Single EFR32 typical 3-Wire Wi-Fi/PTA Logic Analyzer Capture](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image26.png)

Where:

- **REQUEST**: active high, push-pull REQUEST output
- **nGRANT**: active low GRANT input
- **PRIORITY**: active high PRIORITY output
- **TXA**: EFR32 FEM TX Active control signal (configured via FEM Control plugin)
- **RXA**: EFR32 FEM RX Active control signal (configured via FEM Control plugin)
- **FRC_DFRAME**: EFR32 Frame Control Data Frame signal (packet trace frame/synch)
- **FRC_DOUT**: EFR32 Frame Control Data Out signal (packet trace data)
- **WiFi_TXA**: Wi-Fi TX Active signal

This logic analyzer sequence shows:

1. Wi-Fi starts a transmit but is immediately pre-empted (WiFi_TXA pulse) by higher priority 802.15.4 transmit asserting REQUEST and PRIORITY.
2. GRANT is asserted by Wi-Fi/PTA.
3. EFR32 radio completes CCA and CCA passes, and GRANT is asserted.
4. EFR32 radio proceeds with transmit (RXA de-asserts, followed by TXA assert).
5. After transmitting, EFR32 waits for ACK (TXA de-asserts, followed by RXA assert).
6. EFR32 receives ACK (second FRC_DFRAME pulse). _<= 802.15.4 TX message successfully completed_
7. EFR32 de-asserts PRIORITY and REQUEST.
8. Wi-Fi/PTA de-asserts GRANT.

###### Example 2: Configure EFR32 PTA support to operate with multi-radio 2-Wire Wi-Fi/PTA with active-low REQUEST (heading level 7)

- Multiple EFR32 radios (external 1 k ±5% pull-up required on REQUEST)
- RHO unused
- REQUEST shared, active Low, PC10
- GRANT, active Low, PF3
- PRIORITY unused
- PWM REQUEST disabled
- Other option settings to maximize 802.15.4 performance:
- Enable REQUEST to receive retry with 16ms timeout.
- Do not Disable (Enable) ACKing when GRANT is de-asserted RHO asserted, REQUEST not secured.

The logic analyzer capture in the following figure shows the PTA interface, Wi-Fi radio state, and EFR32 radio state for an EFR32 radio configured for multi-radio 2-Wire PTA with active-low REQUEST:

![Example 802.15.4 RX for Multi-EFR32 2-Wire Wi-Fi/PTA with active-low REQUEST Logic Analyzer Capture](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image28.png)

Where:

- **nREQUEST**: active low, shared (open-drain) REQUEST input/output
- **nGRANT**: active low GRANT input
- **TXA**: EFR32 FEM TX Active control signal (configured via FEM Control plugin)
- **RXA**: EFR32 FEM RX Active control signal (configured via FEM Control plugin)
- **FRC_DFRAME**: EFR32 Frame Control Data Frame signal (packet trace frame/synch)
- **FRC_DOUT**: EFR32 Frame Control Data Out signal (packet trace data)
- **nWiFi_RXA**: Wi-Fi RX Active signal
- **WiFi_TXA**: Wi-Fi TX Active signal

This logic analyzer sequence shows:

1. 802.15.4 packet is detected (FRC_DFRAME asserted) while Wi-Fi is receiving a packet (nWiFi_RXA asserted).
2. Shared REQUEST signal is tested and found not asserted by another EFR32 radio, so receiving EFR32 radio asserts REQUEST.
3. Wi-Fi ACK is transmitted (WiFi-TXA asserted) during 802.15.4 receive (no Wi-Fi TX pre-emption or higher priority Wi-Fi activity).
4. After Wi-Fi ACK completes, GRANT is asserted by Wi-Fi/PTA.
5. 802.15.4 receive is completed but CRC failed as packet was corrupted by co-located Wi-Fi ACK transmit during receive.
6. Since PTA configured with _Receive retry REQUEST enabled_ using 16ms timeout, REQUEST is held up to 16ms for 802.15.4 retry with 2.4 GHz quiet (Wi-Fi held off).
7. Wi-Fi continues to receive packets (nWiFi_RXA asserts) but does not ACK while 802.15.4 radio has GRANT.
8. After 3.5 ms gap for end-node ACK timeout and MAC random delay, the 802.15.4 retry packet arrives and is received without error.
9. 802.15 ACK is transmitted (TXA asserted). _<= 802.15.4 RX message successfully completed_.
10. After 802.15.4 ACK completes, REQUEST is de-asserted, followed by GRANT de-assert.

###### Example 3: Configure EFR32 PTA support to operate with multi-radio typical 3-Wire Wi-Fi/PTA (heading level 8)

- Multiple EFR32 radios (external 1 k ±5% pull-down required on REQUEST and external 1 k ±5% pull-down required on PRIORITY)
- RHO unused
- REQUEST shared, active High, PC10
- GRANT, active Low, PF3
- PRIORITY shared, active high RX and TX (escalation disabled), PD12
- PWM REQUEST disabled
- Other option settings to maximize 802.15.4 performance:
- Enable REQUEST to receive retry with 16ms timeout.
- Do not Disable (Enable) ACKing when GRANT is de-asserted RHO asserted, REQUEST not secured.

##### Coexistence CLI Commands

The Coexistence CLI commands can support run-time console control of PTA enable/disable, PTA run-time configuration (ptaOptions), and PTA debug counters. These custom CLI commands are not only useful in manual testing of various coexistence configurations, but also support run-time reconfiguration.

###### Coexistence CLI Commands for SoC (using Component Editor for Zigbee and OpenThread)

For Zigbee, support for Coexistence CLI is enabled by installing the **Zigbee Utility, Coexistence CLI** component under **Platform > Radio components**.

![image](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image31.png)

For OpenThread, support for Coexistence CLI is:

- Enabled by default for all applications.
- Available as a configuration option on the **OpenThread Coexistence** component.
- Requires that you have already installed the **OpenThread CLI** and the **Coexistence** components in your project.

For Silicon Labs OpenThread SDK releases prior to 1.2.0.0, the Coexistence CLI commands are:

- Supported in SoC applications only and,
- Available as a configuration option on the OpenThread CLI component.

The complete list of support commands is summarized in the following table. For more details about the CLI commands and parameters, refer to `sl_zigbee_coexistence_cli.c` for Zigbee and `coexistence_cli.c` for OpenThread.

|Command|Command Description|API Function|
|---|---|---|
|coexistence get-dp-state|Returns the Directional PRIORITY state along with pulse width (µs).|sl_rail_util_coex_get_directional_priority_pulse_width|
|coexistence set-dp-state|Set Directional PRIORITY state.|sl_rail_util_coex_set_directional_priority_pulse_width|
|coexistence get-gpio-input|Returns GPIO Input override from console.|sl_rail_util_coex_get_gpio_input_override|
|coexistence set-gpio-input|Sets GPIO Input override from console.|sl_rail_util_coex_set_gpio_input_override|
|coexistence get-phy-state|Returns PHY select state.|sl_rail_util_coex_get_phy_select_timeout|
|coexistence set-phy-state|Sets PHY select timeout.|sl_rail_util_coex_set_phy_select_timeout|
|coexistence get-pta-options|Returns ptaOptions.|sl_rail_util_coex_get_options|
|coexistence set-pta-options|Sets ptaOptions.|sl_rail_util_coex_set_options|
|coexistence get-pta-state|Returns PTA state.|sl_rail_util_coex_is_enabled|
|coexistence set-pta-state|Sets PTA state.|sl_rail_util_coex_set_enable|
|coexistence get-pwm-state|Returns PWM state from console with period and duty cycle.|sl_rail_util_coex_get_request_pwm_args|
|coexistence set-pwm-state|Sets PWM State.|sl_rail_util_coex_get_request_pwm_args|
|coexistence reset-counters|Clears coex counters.|efr32RadioClearCoexCounters|
|coexistence get-counters|Returns the PTA-specific debug counter values.|efr32RadioGetCoexCounters|

> **Note**: For Zigbee, the coexistence CLI command to get the PTA specific debug counters is _coexistence result-counters._

To disable Coexistence CLI support:

1. Open the configuration options of the OpenThread Coexistence component.  
   ![image](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image32.png)
2. Disable the option.  
   ![image](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image33.png)

> **Note**: For Silicon Labs OpenThread SDK releases prior to 1.2.0.0, disable the Coexistence CLI configuration option on the OpenThread CLI component.

###### PTA-specific Debug Counters

For Zigbee, MAC and APS stack counters are documented in the stack API documentation and can also be accessed via CLI. The following table describes the six coexistence PTA-specific debug counters.

|Counter Index|Meaning|
|---|---|
|SL_ZIGBEE_COUNTER_PTA_LO_PRI_REQUESTED|Occurrences of REQUEST asserted with low priority.|
|SL_ZIGBEE_COUNTER_PTA_HI_PRI_REQUESTED|Occurrences of REQUEST asserted with high priority.|
|SL_ZIGBEE_COUNTER_PTA_LO_PRI_DENIED|Occurrences of GRANT denied with low priority REQUEST.|
|SL_ZIGBEE_COUNTER_PTA_HI_PRI_DENIED|Occurrences of GRANT denied with high priority REQUEST.|
|SL_ZIGBEE_COUNTER_PTA_LO_PRI_TX_ABORTED|Occurrences of TX aborted by GRANT de-asserted with low priority REQUEST.|
|SL_ZIGBEE_COUNTER_PTA_HI_PRI_TX_ABORTED|Occurrences of TX aborted by GRANT de-asserted with high priority REQUEST.|

For OpenThread, support for coexistence PTA-specific debug counters are:

- Enabled by default.
- Available as a configuration option on the **OpenThread Coexistence** component.
- Requires that you have already installed the **OpenThread Coexistence** component in your project.

The following table describes the six coexistence PTA-specific debug counters for OpenThread.

|Counter Index|Meaning|
|---|---|
|SL_RAIL_UTIL_COEX_EVENT_LO_PRI_REQUESTED|Occurrences of REQUEST asserted with low priority.|
|SL_RAIL_UTIL_COEX_EVENT_HI_PRI_REQUESTED|Occurrences of REQUEST asserted with high priority.|
|SL_RAIL_UTIL_COEX_EVENT_LO_PRI_DENIED|Occurrences of GRANT denied with low priority REQUEST.|
|SL_RAIL_UTIL_COEX_EVENT_HI_PRI_DENIED|Occurrences of GRANT denied with high priority REQUEST.|
|SL_RAIL_UTIL_COEX_EVENT_LO_PRI_TX_ABORTED|Occurrences of TX aborted by GRANT de-asserted with low priority REQUEST.|
|SL_RAIL_UTIL_COEX_EVENT_HI_PRI_TX_ABORTED|Occurrences of TX aborted by GRANT de-asserted with high priority REQUEST.|

###### OpenThread Specific Debug Counters (heading level 7)

OpenThread offers several coexistence debug counters to help with development. These are enabled by default when **OpenThread Coexistence** component is pulled into an application.

The following table describes OpenThread specific debug counters.

|Counter Index|Meaning|
|---|---|
|mNumGrantGlitch|Number of grant glitches.|
|mNumTxRequest|Number of Tx Requested = mNumTxGrantImmediate + mNumTxGrantWait.|
|mNumTxGrantImmediate|Number of tx requests while grant was active.|
|mNumTxGrantWait|Number of tx requests while grant was inactive.|
|mNumTxGrantWaitActivated|Number of tx requests while grant was inactive that were ultimately granted.|
|mNumTxGrantWaitTimeout|Number of tx requests while grant was inactive that timed out.|
|mNumTxGrantDeactivatedDuringRequest|Number of tx that were in progress when grant was deactivated.|
|mNumTxDelayedGrant|Number of tx requests that were not granted within 50us.|
|mAvgTxRequestToGrantTime|Average time in usec from tx request to grant.|
|mNumRxRequest|Number of rx requests.|
|mNumRxGrantImmediate|Number of rx requests while grant was active.|
|mNumRxGrantWait|Number of rx requests while grant was inactive.|
|mNumRxGrantWaitActivated|Number of rx requests while grant was inactive that were ultimately granted.|
|mNumRxGrantWaitTimeout|Number of rx requests while grant was inactive that timed out.|
|mNumRxGrantDeactivatedDuringRequest|Number of rx that were in progress when grant was deactivated.|
|mNumRxDelayedGrant|Number of rx requests that were not granted within 50us.|
|mAvgRxRequestToGrantTime|Average time in usec from rx request to grant.|
|mNumRxGrantNone|Stats collection stopped due to saturation.|

###### OpenThread-Specific Debug Counters for the OpenThread Border Router (OTBR)

OpenThread Coexistence metrics support is available in **Simplicity SDK 2024.6.0 and later**. To enable Coexistence Metrics, users must build both their OTBR and RCP with the `OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE` build argument. Once enabled, users can use the OTBR's [CLI](https://github.com/openthread/openthread/blob/main/src/cli/README.md#coex) to view the coexistence metrics.

###### EFR32 Platform-Specific Coexistence Counters

OpenThread also supports enabling platform-specific coexistence counters on EFR32 devices.

1. Add the **EFR32 Platform Extension** component to your RCP/SoC project in Simplicity Studio.
2. Ensure `SL_CATALOG_OPENTHREAD_EFR32_CLI_PRESENT = 1`.
3. If using the OTBR, build with<br />`-DOT_CLI_VENDOR_EXTENSION=$GSDK_DIR/protocol/openthread/platform-abstraction/posix/posix_vendor_cli.cmake`<br />to enable the host-side CLI commands (`SL_OPENTHREAD_EFR32_CLI_ENABLE`).

When using the OT-CLI console, entering `sudo ot-ctl coexistence` should list out the commands available. The output should look similar to the following:

![image](/zigbee-openthread-coexistence-wifi/0.1/images/sld515-image35.png)

#### Configuring Antenna Diversity for OpenThread

##### Configuring Antenna Diversity for OpenThread

> **Note: This section replaces _AN1294: Configuring Antenna Diversity for OpenThread_. Further updates to this application note will be provided here**.

These pages describe how to use Project Configurator and Component Editor in Simplicity Studio to configure Antenna Diversity in OpenThread applications. Both receive and transmit antenna diversity configuration are discussed.

The information in these pages apply to OpenThread SDK releases beginning with 1.1.0.0.

##### About Antenna Diversity

Antenna diversity is a technique used to improve radio performance by using two different antennas to transmit and/or receive messages. For a more detailed explanation of the problems with signal transmission and reception in indoor environments and how antenna diversity can mitigate those problems, see [Using Antenna Diversity to Create Highly Robust Radio Links](https://www.silabs.com/documents/public/white-papers/using-antenna-diversity-to-create-highly-robust-radio-links.pdf).

Antenna diversity may be applied to transmission (Tx) and/or reception (Rx). Diversity is achieved using an external RF switch, either standalone or as part of a FEM (Front End Module) / LNA (Low Noise Amplifier).

The Tx algorithm uses reception of the packet acknowledgement (ACK) to determine if it should change antennas. If the device does not receive an ACK after packet transmission it toggles the RF switch to the other antenna and tries again. It retries two more times, for a total of four attempts, before the MAC (Media Access Layer) fails the transmit up to the network layer. Specifically, the worst-case scenario is as follows:

- New MAC packet transmitted on antenna 1.
- No ACK received so antenna is switched to antenna 2.
- MAC retransmit #1 sent on antenna 2.
- No ACK received, so antenna is switched to antenna 1.
- MAC retransmit #2 sent on antenna 1.
- No ACK received, so antenna is switched to antenna 2.
- MAC retransmit #3 sent on antenna 2.
- No ACK received, so antenna is switched to antenna 1.
- (MAC retries have exhausted, so MAC fails transmit to network layer).
- The next transmit will start on antenna 1.

If transmission is successful, at the beginning of the next transmission the device starts on the last successfully-used antenna.

In Rx antenna diversity with RSSI (Received Signal Strength Indicator), the receiver alternates between antenna 1 and antenna 2 during the timing search looking for a valid timing pattern on the incoming signal. When a valid timing pattern is found, antenna diversity tries to select the best antenna for receiving the rest of the frame. To achieve this, the signal quality for the currently active antenna is saved/updated at every subsequent antenna switch. Therefore, at the first timing detect event the algorithm already has a fresh quality metric for one antenna.

To perform a valid comparison between antenna 1 and antenna 2, the radio switches simultaneously with the timing detect event to the other antenna to perform a signal quality evaluation/update there. Finally, antenna quality results get compared, and the algorithm selects the better antenna for packet reception. If the better antenna is the current antenna, then the Rx operation carries on with packet reception without further antenna switching. If the better antenna is the other antenna, then the radio switches to that one, reacquires timing and carries on with packet reception on that antenna.

In antenna diversity, longer preambles are often used to provide the antenna diversity algorithm time to detect and evaluate the signal on each antenna to ensure that a true preamble is found. However, shorter preambles are preferred as they reduce MCU on-time and in turn reduce MCU current consumption. The RSSI measurement technique for evaluating signal quality requires less preamble time than other methods such as timing correlation.

> **Note**: Antenna Rx diversity is available for testing and evaluation purposes on the Gecko SDK suite. Due to the short preambles on the 802.15.4 packets, customers will need to make their own assessment on the performance and production readiness of this feature.

Rx and Tx antenna diversity are independent operations. In practice this means that, for example, Tx antenna diversity will begin the next transmission on the last successfully used antenna for Tx (for example antenna 1), even though in the intervening receive Rx antenna diversity found better signal quality on antenna 2.

##### Configuring Antenna Diversity

The antenna diversity configuration options available for OpenThread consist of selecting Rx and/or Tx antenna diversity and configuring the underlying peripherals correctly. To configure antenna diversity, you must be familiar with your device’s overall antenna configuration, that is, if it uses a FEM/LNA for either Tx or Rx, and also be familiar with the device’s pin layout. Check the data sheet for your device for these settings or contact Silicon Labs Support if you have questions.

These instructions assume you have installed Simplicity Studio and the OpenThread SDK (Software Development Kit), and that you have a project open in the Simplicity IDE (integrated development environment).

The steps to set up antenna diversity are described in detail below. In summary:

1. Install the **Antenna Diversity** component.
2. Configure the **RAIL Utility - Antenna Diversity Configuration** component.
3. Configure Antenna Pin configurations.
4. Configure the FEM (Optional).

###### Install Antenna Diversity Component

1. On the SOFTWARE COMPONENTS tab, search for **antenna** in the ‘component’s name’ search field (at the top right).
2. Under OpenThread components, select the **Antenna Diversity** component and click **Install** as shown in the following figure.  
   ![Antenna Diversity](/configuring-antenna-diversity-for-openthread/0.2/images/sld701-image1.png)

Installing the **Antenna Diversity** component will install the **RAIL Utility, Antenna Diversity Configuration** component for your project.

> > **Note**: For Silicon Labs OpenThread SDK releases prior to 1.2.0.0, select and install the **RAIL Utility, Antenna Diversity Configuration** component directly under **Platform** components.

###### Configure RAIL Utility - Antenna Diversity Configuration Component

Once the **Antenna Diversity Configuration** component is successfully installed, click **Configure** or the configure symbol next to the **RAIL Utility - Antenna Diversity Configuration** component name as shown in the following figure.

![Antenna Diversity](/configuring-antenna-diversity-for-openthread/0.2/images/sld701-image2.png)

###### Configure Tx Diversity (heading level 7)

The default configuration in the Antenna Diversity Configuration component for Tx Diversity is disabled. To enable Tx Diversity, select the **Enable Antenna Diversity** option from the Tx Antenna Diversity Mode drop-down menu as shown in the following figure.

![RAIL Utility - Antenna Diversity Configuration](/configuring-antenna-diversity-for-openthread/0.2/images/sld701-image3.png)

The different options for TX Antenna Diversity Mode are as follows:

- **Disable Antenna Diversity**: Antenna Diversity component does not control ANTENNA_SELECT_GPIO.
- **Enable Antenna Diversity**: Tx antenna selection is dynamic and Tx diversity is enabled.
- **Use Antenna 0 only**: ANTENNA_SELECT_GPIO is set to high during Tx.
- **Use Antenna 1 only**: ANTENNA_SELECT_GPIO is set to low during Tx.

###### Configure Rx Diversity (heading level 7)

The default configuration in the **Antenna Diversity Configuration** component for Rx Diversity is disabled. To enable Rx Diversity, select the **Enable Antenna Diversity** option from the Rx Antenna Diversity Mode drop-down menu as shown in the following figure.

![Antenna Diversity Configuration](/configuring-antenna-diversity-for-openthread/0.2/images/sld701-image4.png)

Similar to TX Antenna Diversity Mode, the different options for RX Antenna Diversity Mode are as follows:

- **Disable Antenna Diversity**: Antenna Diversity component does not control ANTENNA_SELECT_GPIO.
- **Enable Antenna Diversity**: Rx antenna selection is dynamic and Rx diversity is enabled.
- **Use Antenna 0 only**: ANTENNA_SELECT_GPIO is set to high during Rx.
- **Use Antenna 1 only**: ANTENNA_SELECT_GPIO is set to low during Rx.

###### Configure Rx and Tx Diversity (heading level 7)

To configure both Rx and Tx antenna diversity, select the **Enable Antenna Diversity** option for both the Rx and Tx Antenna Diversity Mode as shown in the following figure.

![Antenna Diversity Configuration](/configuring-antenna-diversity-for-openthread/0.2/images/sld701-image5.png)

###### Configure Antenna Pins

To configure Antenna pins, use the **SL_RAIL_UTIL_ANT_DIV** section of the component configurations.

The available antenna pins are:

- **Antenna select (ANT0)**: Pin used to control the external antenna switch.
- **Complementary antenna select (ANT1)**: Pin for the inverted external antenna signal.

For Rx Antenna Diversity (either alone or with Tx diversity), you **must** select the antenna port pin through the Modem peripheral.

1. From the **Selected Module** drop-down menu, select the **MODEM** option.
2. Select the pins for **Antenna Select (ANT0)** and, if applicable, **Complementary antenna select (ANT1)**. The Antenna select signal goes high to select Antenna 0 and low to select Antenna 1.

For Tx Antenna Diversity (being configured alone), you can set the **Antenna select (ANT0)** and the **Complementary antenna select (ANT1)** pins either directly or Tx Antenna Diversity can inherit the setting from Modem Peripheral.

###### Configure the FEM (optional)

If antenna diversity is implemented using an FEM, you must:

1. Install the Radio Utility, FEM component.
2. Configure the component.

###### Install Radio Utility, FEM Component (heading level 7)

This procedure is similar to the one for Antenna Diversity.

1. On the SOFTWARE COMPONENTS tab, search for **fem** in the ‘component’s name’ search field (at the top right).
2. Under Platform components, select the **Radio Utility, FEM** component and click **Install** as shown in the following figure.  
   ![Radio Utility, FEM](/configuring-antenna-diversity-for-openthread/0.2/images/sld701-image7.png)

###### Configure the FEM Component (heading level 7)

Once the FEM component is successfully installed, click **Configure** or the configure symbol next to the component name and configure the options as per the FEM datasheet.

Not all FEM configuration options apply to all FEMs. Specifically **Bypass** and **Tx Power** only apply to FEMs with the pins to support the features. Check your FEM datasheet for the settings required.

> **Note**: Because there are a limited number of PRS channels, you must take care so they do not conflict with channels that might be selected in other plugins.

The main FEM configuration options are:

- **Enable RX mode**: Configures RX mode on the FEM. This option is disabled by default and must be enabled along with the Antenna Diversity Component for the underlying antenna diversity radio configuration settings to be used. If it is disabled, then the standard radio configuration settings are used.
- **Enable TX mode**: Configures TX mode on the FEM. This option is disabled by default.
- **Enable Bypass Mode (Optional)**: Enables communication that bypasses the LNA (Low Noise Amplifier).
- **Enable TX High Power Mode (Optional)**: Enables high power Tx, Enable low power Tx if disabled.
- **RX PRS channel (SL_FEM_UTIL_RX)**: PRS Channel for Rx control (FEM pin CRX). If no Tx is defined, it is a dual-use (Rx/Tx) pin. The options are Disabled or a channel number. Select the PRS channel first and then configure it by selecting the port/pin as shown in the following figure.  
  ![SL_FEM_UTIL_RX](/configuring-antenna-diversity-for-openthread/0.2/images/sld701-image9.png)
- **Sleep PRS channel (SL_FEM_UTIL_SLEEP)**: PRS channel for sleep control (FEM pin CSD). The options are Disabled or a channel number. If the setting is enabled, it must be configured to be the channel immediately following the RX PRS channel. If set incorrectly, it will result in a compile error. Once a channel is enabled, the **PRS channel output pin** can be configured.
- **TX PRS channel (SL_FEM_UTIL_TX)**: PRS channel for Tx control (FEM pin CTX). The options are Disabled or a channel number. If the setting is disabled, then the software assumes that the FEM pin CRX is a dual use pin. Once a channel is enabled, the **PRS channel output pin** can be configured.
- **Bypass Pin (SL_FEM_UTIL_BYPASS)**: If the FEM has a pin for the bypass signal (CPS), select it.
- **TX power pin. (SL_FEM_UTIL_TX_HIGH_POWER)**: If the FEM has a pin for Tx power mode (CHL), select it.

##### Configuring Antenna Diversity Command Line Interface

OpenThread SDK releases beginning with 1.1.0.0 provide a means to query and set Rx and Tx diversity modes using the Antenna Diversity CLI. Support for the Antenna Diversity CLI is:

- Enabled by default.
- Available as a configuration option on the **OpenThread Antenna Diversity** component.
- Requires the OpenThread CLI and Antenna Diversity components installed in your project.  
  > **Note**: For Silicon Labs OpenThread SDK releases prior to 1.2.0.0, the Antenna Diversity CLI is available as a configuration option on the OpenThread CLI component.

The complete list of supported Antenna Diversity CLI commands is summarized in the following table.

<table>
    <tbody>
        <tr>
            <th>Command</th>
            <th>Command Description</th>
            <th>API Function</th>
            <th colspan="3">Arguments</th>
        </tr>
        <tr>
            <td></td>
            <td></td>
            <td></td>
            <th>Name</th>
            <th>Type</th>
            <th>Description</th>
        </tr>
        <tr>
            <td>antenna get-tx-mode</td>
            <td>Returns the current setting for the Antenna Tx Diversity mode.</td>
            <td>sl_rail_util_ant_div_get_tx_antenna_mo de</td>
            <td>N/A</td>
            <td>N/A</td>
            <td rowspan="4">Interpretation of returned results for get operations and permissible values for set
                operations are as follows: SL_RAIL_UTIL_ANTENNA_MODE_DISABLED: 0 (Don't alter antenna selection)
                SL_RAIL_UTIL_ANTENNA_MODE_ENABLE1: 1 (Use antenna 1) SL_RAIL_UTIL_ANTENNA_MODE_ENABLE2: 2 (Use antenna
                2) SL_RAIL_UTIL_ANTENNA_MODE_DIVERSITY: 3 (Choose antenna 1 or 2 dynamically)</td>
        </tr>
        <tr>
            <td>antenna set-tx-mode</td>
            <td>Sets Tx Diversity mode to argument.</td>
            <td>sl_rail_util_ant_div_set_tx_antenna_mo de</td>
            <td>Tx Antenna Mode</td>
            <td>uint8_t</td>
        </tr>
        <tr>
            <td>antenna get-rx-mode</td>
            <td>Returns the current setting for Antenna Rx Diversity mode.</td>
            <td>sl_rail_util_ant_div_get_rx_antenna_mode</td>
            <td>N/A</td>
            <td>N/A</td>
        </tr>
        <tr>
            <td>antenna set-rx-mode</td>
            <td>Sets Rx Diversity mode to argument.</td>
            <td>sl_rail_util_ant_div_set_rx_antenna_mode</td>
            <td>Rx Antenna Mode</td>
            <td>uint8_t</td>
        </tr>
        <tr>
            <td>antenna get-active-phy</td>
            <td>Returns the current PHY being used.</td>
            <td>sl_rail_util_get_active_radio_config</td>
            <td>N/A</td>
            <td>N/A</td>
            <td>-</td>
        </tr>
    </tbody>
</table>

Tx diversity settings can be changed using the CLI without any restriction. However, Rx Diversity options that require switching from standard PHY to diversity PHY or vice-versa are only permitted when the **Runtime PHY Select** configuration option on the RAIL Utility Antenna Diversity Configuration component is enabled, as shown in the following figure:

![Chip-external Antenna Diversity Configuration](/configuring-antenna-diversity-for-openthread/0.2/images/sld701-image10.png)

###### Disable Antenna Diversity CLI Support

1. Open the configuration options of the OpenThread **Antenna Diversity** component.
2. Disable the option.  
   ![Disable Antenna Diversity CLI Support](/configuring-antenna-diversity-for-openthread/0.2/images/sld701-image12.png)

> **Note**: For Silicon Labs OpenThread SDK releases prior to 1.2.0.0, disable the Antenna Diversity CLI configuration option on the OpenThread CLI component.

### Multiprotocol

#### OpenThread in Multiprotocol Applications

This section provides background information on multiprotocol applications and details on using OpenThread in multiprotocol applications, including dynamic multiprotocol and concurrent multiprotocol models.

- [**Multiprotocol Fundamentals**](/openthread/3.1.0/multiprotocol-fundamentals): Describes the four multiprotocol modes, discusses considerations when selecting protocols for multiprotocol implementations, and reviews the Radio Scheduler, a required component of a dynamic multiprotocol solution.
- [**Dynamic Multiprotocol User's Guide**](/openthread/3.1.0/multiprotocol-dynamic-ug): Describes how to implement a dynamic multiprotocol solution.
- [**Dynamic Multiprotocol Development with Bluetooth and OpenThread on SoCs**](/openthread/3.1.0/multiprotocol-dynamic-ble-ot-on-soc): Provides details on developing Dynamic Multiprotocol applications for SoCs using Bluetooth and OpenThread.
- [**Running Zigbee, OpenThread, and Bluetooth Concurrently on a Linux Host with a Multiprotocol Co-Processor**](/openthread/3.1.0/multiprotocol-solution-linux): Describes how to run any combination of Zigbee EmberZNet, OpenThread, and Bluetooth networking stacks on a Linux host processor, interfacing with a single EFR32 Radio Coprocessor (RCP) with multiprotocol and multi-PAN support. It also describes how to run the Zigbee stack on the EFR32 as a network co-processor (NCP) alongside the OpenThread RCP.
- [**Running Zigbee, OpenThread, and Bluetooth Concurrently on a System-on-Chip**](/openthread/3.1.0/concurrent-mp-soc): Describes how to run a combination of Zigbee, Bluetooth, and OpenThread networking stacks and the Zigbee application layer on a System-on-Chip (SoC).
- [**Using the Co-Processor Communication Daemon (CPCd)**](/openthread/3.1.0/using-co-processor-communication-daemon): Documents the steps needed to properly configure and run the CPC daemon (CPCd) on Linux or Android.

#### Multi-Instance OpenThread Support

This section describes how to enable multi-PAN support for OpenThread SoC applications through the use of multiple OpenThread instances. By default, OpenThread is configured to support a single instance per device. Enabling multiple instances allows a single device to participate in multiple independent Thread networks simultaneously, which is useful for scenarios requiring network isolation or concurrent operation of separate Thread networks.

##### Key Points

- Understanding multiple OpenThread instances.
- Build configuration requirements.
- Application code modifications.

##### Understanding Multiple OpenThread Instances

OpenThread supports running multiple independent instances on a single SoC device. Each instance operates as a separate Thread network stack with its own:

- Network configuration and credentials.
- Thread network state.
- Routing tables.
- Security keys and certificates.
- Network buffers and memory resources.

This multi-instance capability enables scenarios such as:

- Participating in multiple Thread networks simultaneously.
- Network isolation for security or testing purposes.
- Gateway applications connecting different Thread networks.

###### Comparison With Multi-PAN RCP

![SoC Architecture](/openthread-multi-instance/0.1/images/multi-pan-soc.png)  ![RCP Architecture](/openthread-multi-instance/0.1/images/multi-pan-rcp.png)

In a multi-instance SoC application, multiple OpenThread network stacks run concurrently on a single SoC device, enabling it to participate in several independent Thread networks using local resources.

In contrast, a multi-PAN RCP (Radio Co-Processor) configuration allows a host processor to control multiple IEEE 802.15.4 networks by connecting to a single RCP, where the RCP itself does not manage network state but instead provides radio services for multiple networks managed by the host. Typically, this configuration is intended to manage a Zigbee and a Thread network, but multiple Thread networks are architecturally possible.

The key distinction is that multi-instance is handled fully on the SoC, whereas multi-PAN RCP splits network management between the host and the RCP.

##### Prerequisites

Before enabling multiple OpenThread instances, ensure that:

- Sufficient memory and processing resources are available (see [Memory Comparison](#memory-comparison) for details).
- You are using a compatible version of the Silicon Labs OpenThread SDK.
- Simplicity Studio is installed and configured.

##### Enabling Multiple Instances in Build Configuration

###### Using Simplicity Studio

1. Open your OpenThread project in Simplicity Studio.
2. Navigate to the **Software Components** tab.
3. Open the configuration for the selected **OpenThread Stack**, and enable the following options:  
   1. `Multiple OpenThread Instances`  
   2. `Multiple Static Instance Support`  
   3. Set `Number of OpenThread Instances` to the desired number of instances
4. If running instances on separate channels, enable the following, under **RAIL_IEEE802154_OPTIONS**: (Platform > Radio):  
   1. `RAIL Utility, IEEE802.15.4 Fast Channel Switching Configuration`  
   2. `RAIL Utility, DMA`
5. Generate and build your project.

###### Using Command Line Build

If building from the command line, add the following configuration options:

- `OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE:1`
- `OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE:1`
- `OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM:\<number of instances\>`

If running instances on separate channels, add the following to enable channel switching support:

- Add the following configuration option:  
  - `SL_RAIL_UTIL_IEEE802154_FAST_CHANNEL_SWITCHING_DEFAULT_ENABLED:1`
- Generate your project with the following components:  
  - `sl_rail_util_ieee802154_fast_channel_switching`  
  - `sl_rail_util_dma`

##### Application Code Modifications

After enabling multiple instances in the build configuration, update your application code to create and manage multiple OpenThread instances.

###### API Changes

- Use `otInstanceInitMultiple` to initialize multiple instances rather than `otInstanceInitSingle`.
- Use `otInstanceGetIndex` to retrieve the index of a provided instance pointer.
- Use `otInstanceGetInstance` to retrieve the instance pointer associated with a provided index.

##### Example: Multi-Instance CLI Sample Application

To help you get started, we provide a ready-to-use sample application and a utility component designed specifically for multi-instance OpenThread development:

- **Sample App:** _OpenThread - SoC CLI (FTD multi-instance)_  
  This sample demonstrates how to build and use a CLI-based app that manages and interacts with multiple Thread instances.
- **Multi-Instance CLI Utility Component:** _OpenThread | Multi-Instance CLI Support_  
  This component provides support for CLI control of multiple OpenThread instances by adding an `instance` CLI user command set to the OpenThread CLI interpreter, which can perform the following operations:  
  - List initialized instances, their static index, and their id value.  
  - Print the index of the instance that the CLI is controlling.  
  - Reset the CLI context to operate on a different instance of a specified index.

**Using the CLI with Multiple Instances:**

The sample application initializes 2 instances and sets the CLI to operate on instance 0. The following shows a representative interaction with forming a network on each instance using the CLI:

```c
Initialized CLI for instance 0
Use 'instance list' to list all instances
Use 'instance set <index>' to change between instances (0-1)
> instance list
* Index: 0, Id: 657576498
  Index: 1, Id: 468683675
Done
> instance get
Current instance index: 0
Done
> instanceid
657576498
Done
> state
disabled
Done
> dataset init new
Done
> dataset commit active
Done
> dataset active
Active Timestamp: 1
Channel: 12
Wake-up Channel: 19
Channel Mask: 0x07fff800
Ext PAN ID: 7d6b2f27a1b7e5b2
Mesh Local Prefix: fd9f:a842:932:a11f::/64
Network Key: 4e67428cf67b82bb2040eb8ed667096a
Network Name: OpenThread-394a
PAN ID: 0x394a
PSKc: f4716182bdeb97dfd628c38f483fa96b
Security Policy: 672 onrc 0
Done
> ifconfig up
Done
> thread start
Done
> state
leader
Done
> instance set 1
Done
Switched to instance 1
> instance get
Current instance index: 1
Done
> instanceid
468683675
Done
> state
disabled
> dataset init new
Done
> dataset commit active
Done
> dataset active
Active Timestamp: 1
Channel: 13
Wake-up Channel: 18
Channel Mask: 0x07fff800
Ext PAN ID: eb15c96173f3fff9
Mesh Local Prefix: fd47:1d63:616f:e841::/64
Network Key: 6bfe51d1392e4177af4efd0de6faf66c
Network Name: OpenThread-9d8a
PAN ID: 0x9d8a
PSKc: 7b3986c065c21435eccd7f37098cd94a
Security Policy: 672 onrc 0
Done
> ifconfig up
Done
> thread start
Done
> state
leader
```

###### Topology Examples

The diagrams below represent how multi-PAN SoC devices can interact with single instance SoC devices as well as other multi-PAN SoC devices.

A single multi-instance device communicating with single instance devices over separate PANs and channels:

![Three Device Network](/openthread-multi-instance/0.1/images/three-device-network.png)

Two multi-instance devices communicating with each other across two independent connections:

![Two Device Network](/openthread-multi-instance/0.1/images/two-device-network.png)

##### Configuration Considerations

###### Network Configuration

Each OpenThread instance must be configured with unique network parameters:

- **Extended PAN ID**: Each instance should use a different Extended PAN ID.
- **Network Key**: Each instance requires its own network key.
- **Channel**: Instances can operate on the same or different channels.
- **PAN ID**: Each instance should have a unique PAN ID if operating on the same channel.

###### Platform Limitations

Some platforms may have limitations when running multiple instances:

- Platform constraints may limit the number of concurrent instances to no more than 3.
- Multi-instance support limited to statically initialized instances with key references enabled.

###### Memory Comparison (heading level 7)

Enabling multi-instance support will require more RAM and use more code size than a single instance application. While the exact difference will vary based on the specific OpenThread configuration values used, here is a relative comparison between applications with a comparable FTD configuration:

|Application|RAM Usage (.bss+.data)|Code Size|
|---|---|---|
|ot-cli-ftd|~24 kB|~324 kB|
|ot-cli-ftd-multi-instance|~41 kB|~334 kB|
|ot-ble-dmp|~33 kB|~465 kB|

**Measurement Details**:

- SDK release: sisdk-2025.12
- Target part: EFR32MG24
- Link Time Optimization (LTO): Enabled

#### Multiprotocol Fundamentals

##### Multiprotocol Fundamentals

> **Note: This section replaces _UG103.16: Multiprotocol Fundamentals_. Further updates to this user guide will be provided here**.

Multiprotocol is a way to use more than one protocol on a single chip. Implementing more than one protocol on a single device can improve:

- Cost savings: A single device can perform more than one function.
- Space savings: End user product packaging can be smaller and simpler when protocols can share a single radio.
- Energy savings: The number of devices on a network is reduced.

The following pages describe:

- Different ways to implement multiprotocol devices and the infrastructure requirements for an effective implementation.
- Aspects of protocol operation to be considered when selecting a protocol for a multiprotocol implementation.
- The operation of the Silicon Labs Radio Scheduler, an essential component of a dynamic multiprotocol implementation.

These pages describe the use of multiprotocol on a single chip. For more information about coordinating multiple radio chips, like one from Silicon Labs and an external Wi-Fi radio, see [Wi-Fi Coexistence Fundamentals](https://docs.silabs.com/multiprotocol/latest/multiprotocol-wifi-coexistence-fundamentals/).

##### Types of Multiprotocol Implementations

Multiple protocols can be implemented on a single device in different ways:

- **Switched**: The application switches between protocols using a bootloader.
- **Dynamic**: The application time-slices between two protocols.
- **Concurrent**: The application is able to receive multiple protocols full time by:  
  - Running on the same RF channel of a single radio,  
  - Using Silicon Labs Concurrent Listening technology on a single radio, or  
  - Using a multi-chip or multi-radio solution.

The following discusses these implementations in more detail.

###### Switched Multiprotocol

In switched multiprotocol, a device is initially programmed with one protocol, and then uses a bootloader to switch to another protocol at some point in the future. Switched multiprotocol has two primary use cases.

**Future Proofing**: Device manufacturers may need to sell their devices into different protocol environments, or they may need to plan for an environment changing over time.

**Commissioning by Smartphone**: Device manufacturers who are committed to a single protocol environment, such as Zigbee Home Automation, may want to make the process of adding a new device more secure and user friendly. In this case, the device is initially programmed with a Bluetooth commissioning application. The Home Gateway/Trust Center is also programmed with a Bluetooth application, and the customer uses a commissioning app on their smartphone. Using the smartphone app they can join a new device onto the network, set up a pairing with other appropriate devices in the network, then switch over to the network protocol. Each wireless network protocol has its own mechanisms for joining and pairing devices, but all can be accommodated with this mechanism.

An effective switched multiprotocol implementation requires:

- A multiprotocol-enabled platform with sufficient memory.
- A consistent API to use the radio.
- A cross-compatible bootloader.

###### Dynamic Multiprotocol

In a dynamic multiprotocol implementation, two protocols run concurrently, but the application time-slices the radio and rapidly changes radio configurations, such as channel, to enable different wireless protocols to operate reliably at the same time. In time-slicing, the software schedules tasks based around their priority and minimum duration and will default to a background listen task between scheduled tasks. The scheduler can interrupt or delay a lower priority task if a higher priority task is scheduled.

A dynamic multiprotocol implementation allows a device to perform multiple simultaneous functions. For example, an end user can use a smartphone app to connect to the device via Bluetooth to control it or to perform diagnostics, while at the same time the device is connected to the Zigbee network routing packets and acting on Zigbee Cluster Library commands.

In addition to a multiprotocol-enabled platform with sufficient memory, common code infrastructure, common radio interface, and common API to use the radio such as that provided by Silicon Labs RAIL, an effective dynamic multiprotocol implementation requires:

- An RTOS to support task switching and resource sharing.
- A radio scheduler to manage time-slicing. See [Radio Scheduler](./04-radio-scheduler) for background on the Silicon Labs radio scheduler.

See [Dynamic Multiprotocol User’s Guide](https://docs.silabs.com/multiprotocol/latest/multiprotocol-dynamic-ug/) for additional information about Silicon Labs dynamic multiprotocol implementation and [RAIL Fundamentals](https://docs.silabs.com/rail/latest/rail-fundamentals/) for more information about Silicon Labs RAIL.

###### Single Channel Concurrent Multiprotocol

In a single radio, single channel concurrent multiprotocol implementation, two protocols are able to run simultaneously on a single radio by sharing the same radio channel.

For example, a Zigbee/Thread gateway or controller device could manage both a Zigbee-based and a Thread-based network at the same time. In the case of sharing a single channel, the networks must coordinate what channel they use. In addition, they must be able to accommodate reduced bandwidth either by scaling their traffic or by limiting the number of devices on the network.

###### Concurrent Listening

Concurrent Listening, a Silicon Labs technology, allows the Zigbee and Thread stacks to operate on independent 802.15.4 channels when used with the multiprotocol radio co-processor (RCP). This feature works on EFR32xG21 and EFR32xG24 parts only. For detailed information on the multiprotocol RCP, see [Running Zigbee, OpenThread, and Bluetooth Concurrently on a Linux Host with a Multiprotocol Co-Processor](https://docs.silabs.com/multiprotocol/latest/multiprotocol-solution-linux/).

Concurrent Listening allows a single radio to listen on two channels simultaneously. This is accomplished by extremely rapid switching (on the order of tens of microseconds) that allows the radio to detect preambles for incoming packets on either channel. Once a preamble is detected, the radio stays on that channel until the packet is received, then resumes rapid switching.

Note that this fast switching differs from the time slicing used in dynamic multiprotocol, because the dwell on each channel is just sufficient to detect a preamble and shorter than the full preamble time, so incoming packets are not missed on either channel. In contrast, for dynamic multiprotocol the time slot is large enough to receive scheduled packets, which means that the other channel is not being listened to during that time.

When enabling the concurrent listening feature the PHY performance is slightly degraded. The sensitivity of the IEEE 802.15.4 PHY in this mode drops to around -98 dBm for both the EFR32xG21 and EFR32xG24.

##### Protocol Considerations

It is important to recognize that a single radio cannot receive or transmit two packets for two different protocols simultaneously. To share a single radio, both protocols have to accept that they will not have 100% use of the radio. They must therefore be able to survive loss of radio without significantly decreasing performance or losing application messages. In this case, _significantly_ means that the application is working outside of its intended operational parameters. Examples are higher than acceptable message latency or message loss.

While some protocols and applications will have strict timing requirements for packet transmission, in most cases the critical area of concern with multiprotocol is the receipt of incoming packets from the networks.

For a protocol to be a good candidate for a dynamic or concurrent multiprotocol implementation, the following considerations must apply:

- Neither protocol can take over the radio for long periods (longer than a few milliseconds at a time) under normal operating conditions, excluding commissioning, firmware upgrades, and other related operations.
- One or both protocols must have a robust mechanism for managing loss of incoming packets, such as MAC retries.
- One or both protocols must have very short packets and/or a short time required on the radio.
- For dynamic multiprotocol, one or both protocols must implement strict time slots/connection intervals.

> **Note**: When using dynamic multiprotocol, a radio has to switch between two different PHYs and it 'disappears' from one network or another. For protocols where the device might be the 'parent' of a sleepy end device, if the parent device is not available when the sleepy end device wakes up to send a message, regular dependency on retries will impact the sleepy end device’s battery life.

> **Note**: If you have a protocol that uses multiple radio configurations, you do not need to implement multiprotocol. For example, Silicon Labs' RAIL and the radio configurator support multiple radio configurations in the same protocol. See _AN971: EFR32 Radio Configurator Guide_ for more details. Multiprotocol is used if protocols have mostly independent protocol stacks.

###### Dynamic Multiprotocol Example

Bluetooth LE and Zigbee are suitable protocols for a dynamic multiprotocol implementation. Because of the low duty cycle for Zigbee traffic and the retry mechanisms in the Zigbee networking stack, a Zigbee Router can switch its radio to some other frequency or protocol for short periods without dropping any messages at an application level.

Bluetooth LE radio usage can be predicted and planned in advance. Bluetooth beacons are quite short packets, usually less than 30 bytes. The radio only needs about 1 ms to transmit the beacon, and the interval between beacons is typically no shorter than 100 ms, thus providing a duty cycle of just 1%. This means that the radio can devote at least 99% of its time to the main Zigbee network.

If you are using Silicon Labs products, adding Bluetooth LE to most proprietary networks is also possible because of the short Bluetooth LE packets and the long delay between communications. The proprietary stack must be updated to work with the Bluetooth stack, and all proprietary communications should be examined to determine the priority and required timing accuracy of it. For more details, see [Dynamic Multiprotocol User’s Guide](https://docs.silabs.com/multiprotocol/latest/multiprotocol-dynamic-ug/) and [Dynamic Multiprotocol Development with Bluetooth and Proprietary Protocols on RAIL](https://docs.silabs.com/multiprotocol/latest/multiprotocol-dynamic-ble-proprietary-on-rail/).

###### Concurrent Multiprotocol Example

Zigbee and Thread are examples of suitable protocols for a concurrent multiprotocol implementation.

The multiprotocol device can control which IEEE 802.15.4 channel it is operating on to connect to other Zigbee and Thread devices. It only has a single IEEE 802.15.4 MAC/PHY to listen to or send on both networks concurrently, meaning that switching radio PHYs is not required.

The multiprotocol device manages incoming packets from either network by filtering both PAN IDs, directing to the appropriate networking stack.

Operational constraints include:

- The multiprotocol device must control selection of the same IEEE 802.15.4 channel for both Zigbee and Thread, which means that it is most likely a Zigbee Coordinator/Thread Leader, in practice the Gateway/Controller in both networks. If using Concurrent Listening on an EFR32xG21 or EFR32xG24, this constraint does not apply.
- The device cannot be required to receive Zigbee and Thread packets simultaneously. However, because of MAC retries, in most cases this should not be a limitation.
- The combined duty cycle of the Zigbee and Thread traffic to or from the device must not exceed what would normally be tolerated by a single Zigbee or Thread device.

##### Radio Scheduler

A radio scheduler is an essential component of a dynamic multiprotocol implementation. It is a system for on-demand scheduling of radio tasks as requested by wireless stacks and the manufacturer’s applications. This page introduces basic radio scheduler concepts. For details, including examples, of the Silicon Labs radio scheduler operation, see the [Dynamic Multiprotocol User’s Guide](https://docs.silabs.com/connect/latest/multiprotocol-dynamic-ug/).

The Silicon Labs Radio Scheduler is part of the RAIL library. It operates above the radio hardware and below the RAIL API, as shown in the figure:

![Silicon Labs Radio Scheduler](/multiprotocol-fundamentals/0.1/images/sld482-image8.png)

Different radio events in each protocol may be more or less important, or more or less time sensitive, depending on the situation. The Radio Scheduler can take those into account when making decisions about conflicts and how to adjudicate them.

Micrium OS is an RTOS that allows stacks and application logic to share CPU execution time.

The Radio Scheduler uses the following concepts.

**Radio Operation**: A radio operation is a specific action to be scheduled that has both a radio configuration and a priority. Each stack can request that the radio scheduler perform three radio operations:

- Background receive: Continuous receive, intended to be interrupted by other radio operations
- Scheduled receive: Receive at a future time with a minimum duration
- Scheduled transmit: Transmit at a future time with a minimum duration

**Radio Config**: Radio config determines the state of the hardware that must be used to perform a radio operation.

**Priority**: Each operation from each stack has a default priority. An application can change default priorities.

**Slip Time**: Slip time is the maximum time in the future when the operation can be started if it cannot begin at the requested start time.

**Yield**: A stack must voluntarily yield at the end of an operation or sequence of operations, unless it is performing a background receive. Until the stack yields, the scheduler will not schedule lower priority tasks.

The Radio Scheduler can interrupt a scheduled radio operation if a higher priority task conflicts with it. This could occur in the following two circumstances:

1. A scheduled radio operation takes longer than expected, and the corresponding stack does not yield before the higher priority radio operation must start.
2. A higher priority radio operation has just been scheduled to occur in the future and conflicts with a lower priority operation already scheduled.

Certain long-lived radio operations can have an outsized impact on the correct operation of the product. The application may need to coordinate these tasks between the protocols. If the application does not, then the radio scheduler priorities will take precedence. This can result in the task being interrupted prematurely.

#### Dynamic Multiprotocol

##### Dynamic Multiprotocol User's Guide

> **Note: This section replaces _UG305: Dynamic Multiprotocol User's Guide_. Further updates to user guide will be provided here**.

Silicon Labs software is designed to be used by multiple protocols on a single wireless chip. This user's guide provides details about implementing an application using Silicon Labs' Dynamic Multiprotocol solution. Dynamic multiprotocol time-slices the radio and rapidly changes configurations to enable different wireless protocols to operate reliably at the same time.

> **Note**: The Zigbee-specific information in this document applies to version 6.10.x and lower.

Details on specific dynamic multiprotocol implementations are provided in the following application notes:

- [Dynamic Multiprotocol Development with Bluetooth and Zigbee EmberZNet SDK 7.x and Higher](https://docs.silabs.com/multiprotocol/latest/dynamic-multiprotocol-bluetooth-zigbee-sdk-7x-higher/)
- [AN1134: Dynamic Multiprotocol Development with Bluetooth and Proprietary Protocols on RAIL in GSDK v2.x](https://www.silabs.com/documents/public/application-notes/an1134-bluetooth-rail-dynamic-multiprotocol.pdf)
- [Dynamic Multiprotocol Development with Bluetooth® and Proprietary Protocols on RAIL in GSDK v3.x and Higher](https://docs.silabs.com/multiprotocol/latest/multiprotocol-dynamic-ble-proprietary-on-rail/)
- [AN1209: Dynamic Multiprotocol Development with Bluetooth and Connect](https://www.silabs.com/documents/public/application-notes/an1209-dynamic-multiprotocol-connect-bluetooth.pdf)
- [Dynamic Multiprotocol Development with Bluetooth® and OpenThread in GSDK v3.x](https://docs.silabs.com/multiprotocol/latest/multiprotocol-dynamic-ble-ot-on-soc/)

###### Terminology

The following lists some of the terminology specific to the dynamic multiprotocol implementation.

**Radio Abstraction Interface Layer (RAIL)**: The common API through which higher level code gains access to the EFR32 radio.

**Radio Operation**: A specific action to be scheduled. A radio operation has both a radio configuration and a priority. Each stack can request that the radio scheduler perform up to two radio operations (background receive and either Scheduled Receive or Scheduled transmit) at a time:

- **Background Receive**: Persistent receive, intended to be interrupted by Scheduled operations, and returned to after their completion.
- **Scheduled Receive**: Receive packets or calculate RSSI at a specified time and duration. (Developers working on RAIL, note that in terms of the RAIL API, “Scheduled Receive” as used in this document refers to any receive operation, other than `RAIL_StartRx`, and is not just limited in scope to `RAIL_ScheduleRx`.)
- **Scheduled Transmit**: Any one of various transmit operations including immediate transmit, scheduled (future) transmit, or CCA-dependent transmit. (Developers working on RAIL, note that in terms of the RAIL API, “Scheduled Transmit” as used in this document refers to any transmit operation, and is not limited in scope to `RAIL_StartScheduledTx`.)

**Radio Config**: Determines the state of the hardware that must be used to perform a radio operation.

**Radio Scheduler**: RAIL component that arbitrates between different protocols to determine which will have access to the radio.

**Priority**: Each operation from each stack has a default priority. An application can change default priorities.

**Slip Time**: The maximum time in the future when the operation can be started if it cannot begin at the requested start time.

**Yield**: A stack must voluntarily yield at the end of an operation or sequence of operations, unless it is performing a background receive. Until the stack yields, the scheduler will not scheduler lower priority tasks.

**RTOS (Real Time Operating System) Kernel**: The part of the operating system that is responsible for task management, and inter-task communication and synchronization.

###### Architecture

Dynamic Multiprotocol makes use of the EFR32 hardware and the RAIL software as its building blocks. Zigbee, Bluetooth, and/or any other standards-based or proprietary protocols can then be built on top of these foundational layers, using Micrium to manage execution of code between different protocols. The following diagram illustrates the general structure of the software modules.

![General Dynamic Multiprotocol Software Architecture](/multiprotocol-dynamic-ug/0.2/images/sld485-image8.png)

Beginning with version 2.0, RAIL requires the passing of a radio configuration handle to the RAIL API calls. This configuration describes various PHY parameters that are used by the stack.

The OS is an RTOS that allows stacks and application logic to share CPU execution time.

The Radio scheduler is a software library that intelligently answers requests by the stacks to perform radio operations to maximize reliability and minimize latency. API’s provided by RAIL that do not engage the radio bypass the Radio scheduler.

The RAIL core configures the EFR32 hardware in response to instructions from the radio scheduler.

###### Single Firmware Image

Dynamic Multiprotocol allows a software developer to generate a single monolithic binary that is loaded onto an EFR32. Software updates are done by upgrading the entire binary. This is accomplished using the Gecko Bootloader, the details of which can be found in _UG266: Silicon Labs Gecko Bootloader User’s Guide for GSDK 3.2 and Lower_, [Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/) or [Silicon Labs Gecko Bootloader User’s Guide for Series 3 and Higher](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-series3-and-higher/).

###### Independent Stack Operation

The Silicon Labs stacks still operate independently of one another in a Dynamic Multiprotocol situation. Certain long-lived radio operations will have an impact on another protocol’s latency and compliant operation. It is up to the application to determine any special considerations for these events. See [The Radio Scheduler](02-the-radio-scheduler) for more information.

##### The Radio Scheduler

The Radio Scheduler is a component of RAIL (Radio Abstraction Interface Layer). RAIL provides an intuitive, easily-customizable radio interface layer and API, which supports proprietary or standards-based wireless protocols. The Radio Scheduler is designed to allow for radio operations that can be scheduled and prioritized. Different radio operations in each protocol may be more or less important, or more or less time sensitive, depending on the situation. The scheduler can take those into account when making decisions about conflicts and how to adjudicate them.

Unless you are developing applications with a custom protocol on RAIL, most radio scheduler functions are handled automatically by underlying stack and RAIL code. You only need to use the stack through its normal API.

At a high level, the stack sends a radio operation (for example a Scheduled Receive or Scheduled Transmit). The radio operations are queued and then serviced at a future time based upon their parameters. When it is time to start the radio operation the scheduler examines whether a competing event exists and whether the operation can be delayed. If the scheduler cannot run the event, it returns the result to the higher layer, which may retry with new parameters.

Once the radio operation has started, the corresponding stack can send the scheduler additional operations based on the results of the previous operation (for example waiting for an ACK). At the end of each operation or sequence of operations the stack must yield use of the radio.

###### Radio Operations

Each event in the scheduler is broken up into elements called Radio Operations, which are associated with a radio config and a priority.

Every operation has a priority and is interrupted if the scheduler receives a higher priority operation that overlaps in time. Lower priority radio operations that cannot be run based on their schedule parameters will fail, and it is up to the respective stack to retry them. Once the scheduler actively runs a radio operation from the stack, the stack can continue to send additional radio operations until it voluntarily yields, or until the scheduler receives a higher priority radio operation and preempts it.

- Background Receive
- Scheduled Receive
- Scheduled Transmit

Each stack can ask the Radio Scheduler to perform up to two radio operations (background receive and either Scheduled Receive or Scheduled transmit) at a time.

Each operation has the following parameters:

|**Parameter**|**Description**|
|---|---|
|Start Time|An indication at what point in the future this radio operation will run. This could be “run right now” or some value in microseconds in the future.|
|Priority|A number that indicates the relative priority of the operation. When using the default settings, Bluetooth LE radio operations are almost always higher priority than Zigbee operations.|
|Slip Time|An amount of time that the event can be delayed beyond its start time and still be acceptable to the stack. This may be 0, in which case the event cannot be slipped.|
|Transaction Time|The approximate amount of time that it takes to complete the transaction. Transmit events usually have a much more well-defined transaction time, while receive events are often unknown. This is used to help the radio scheduler determine whether an event can be run.|

The stack defines these various parameters appropriate to the operation being executed. For example, Bluetooth connection events are often scheduled in the future and have no allowed slip, whereas Zigbee transmit events can often be delayed a small amount and start later.

From the perspective of the RAIL Radio Scheduler, Scheduled transmit and Scheduled receive are identical. They are both simply operations that require use of the radio, and thus cannot be executed simultaneously. The difference is only apparent at RAIL API layer, where either a TX or RX API is called.

###### Background Receive (heading level 7)

This is a continuous receive mode that is intended to be interrupted by other operations and returned to after their completion. If Background Receive is higher priority than other operations, those radio operations will not be scheduled and will not run. It is up to the stacks or application to change the priority or voluntarily yield. See [Examples with Background Receive, Yield Radio and State Transition](./05-implementing-multiprotocol-with-rail#examples-with-background-receive-yield-radio-and-state-transition) for examples of how Background receive interacts with Scheduled operations.

###### Scheduled Receive (heading level 7)

This is a receive at a future time with a specified duration. The radio scheduler takes into consideration the radio switching time in deciding whether the operation will be scheduled. If it cannot be scheduled, then the scheduler sends a fail event to the calling stack. The radio operation is automatically extended until the stack voluntarily yields, or the scheduler receives a higher priority operation and interrupts it. Extending the receive allows the stack to continue a radio operation based on the requirements of the higher level protocol, for example transmission of a response based on the received data.

###### Scheduled Transmit (heading level 7)

This is a transmit at a future time with a minimum duration. This minimum duration can include expected follow-on events, for example an ACK to an IEEE 802.15.4 transmit. However, the minimum time for this operation does not have to include unexpected events that may extend the time beyond the minimum duration, for example backoffs due to CCA failures in IEEE 802.15.4. The radio scheduler takes into consideration the radio switching time in deciding whether the operation will be scheduled. If it cannot be scheduled, then the scheduler sends a fail event to the calling stack.

###### Radio Config

Each radio operation is associated with a predefined radio config that determines the state of the hardware that must be used to perform the operation. The Radio Configs keep track of the stack's current state so that future radio operations will use the same radio parameters. Radio Configs may be active or dormant. If the stack changes an active Radio Config then RAIL makes an immediate change to the hardware configuration as well, for example changing a channel. If the radio config is not currently active then the next scheduled radio operation will use the new radio config.

###### Priority

Each radio operation has a priority which indicates to the scheduler which operation should be executed if there is a timing overlap between multiple operations. The scheduler treats a priority of 0 as the highest priority and 255 as the lowest priority. The radio scheduler will allow the task with the highest priority to access the physical radio hardware. With most tasks control is returned to the radio scheduler only on completion, but tasks like background receive will be interrupted in case a task with higher priority becomes active.

The stacks each have a default set of priorities based on Silicon Labs’ analysis of how best to cooperate to maximize the duty cycle and avoid dropped connections for a generic use case. Specific use cases may have different needs. The priorities are as follows, from highest to lowest:

1. Bluetooth LE Scheduled Transmit
2. Bluetooth LE Scheduled Receive
3. Other protocol Scheduled Transmit
4. Other protocol Background Receive

These priorities may be overridden or changed by the application. It is up to the application to decide under what circumstances to change them. [802.15.4 Rail Priority](./04-implementing-multiprotocol-with-an-802-15-4-based-stack#802154-rail-priority) and [Bluetooth Priorities](./06-implementing-multiprotocol-with-bluetooth#bluetooth-priorities) contain more details on priorities for their specific instances.

###### Slip Time

Every radio operation must have a "slip time," or maximum start time, meaning the furthest time in the future when the operation can be started if it cannot begin at the requested start time. This allows for the scheduler to work around higher priority events that are occurring at the same time, or higher priority events that extend beyond their expected duration. The protocol generally dictates what the slip time can be, but the radio scheduler is capable of handling this on a per-operation basis, allowing a stack to slip some events but not others. In general, IEEE 802.15.4 has longer slip time and Bluetooth LE has a minimal slip time.

###### Yield

Once a sequence of radio operations is actively being run, the stack may continue to add operations extending the initial operation until the stack has nothing more to do for the particular message exchange. A stack must voluntarily yield unless it is performing a background receive. If a stack does not yield then it will continue to extend its radio operation, and lower priority radio operations will then trigger a failure back to the corresponding stack that requested that radio operation. A higher priority operation cannot interrupt a currently-running, lower priority radio operation that has not yielded. See [Examples with Background Receive, Yield Ration and State Transition](./05-implementing-multiprotocol-with-rail#examples-with-background-receive-yield-radio-and-state-transition) for examples of situations where explicitly yielding the radio is necessary.

###### Interrupting a Radio Operation

A scheduled radio operation may be interrupted if a higher priority operation conflicts with it. This could occur in the following two circumstances:

1. A scheduled radio operation takes longer than expected and the corresponding stack does not yield before the higher priority radio operation must start.
2. A higher priority radio operation has just been scheduled to occur in the future and conflicts with a lower priority operation already scheduled.

###### Long-Lived Radio Operations

Certain long-lived Radio Operations can have an outsized impact on the correct operation of the product. The application may need to coordinate these operations between the protocols. If the application does not, then the radio scheduler priorities take precedence. For example, an IEEE 802.15.4 energy scan can require that the radio stay on to gather sufficient energy readings. If the application does not properly coordinate the operations, the scan could be interrupted prematurely due to a higher priority Bluetooth operation.

##### Radio Scheduler Examples

All examples use Bluetooth LE and Zigbee, but the principles apply to other Bluetooth/802.15.4 combinations.

The scheduler starts out by having a low priority Zigbee background receive operation. This represents an always-on router that may need to receive IEEE 802.15.4 packets at unknown times. A Bluetooth LE connection is also active and requires the stack to be ready to receive every 30 ms. The Bluetooth LE stack may schedule this well in advance due to the connection’s predictable nature.

###### Priority Scheduling

This provides a basic example of adjudicating priorities of the different radio operations.

![Priority Scheduling](/multiprotocol-dynamic-ug/0.2/images/sld485-image9.jpg)

The Zigbee stack decides that it needs to send a packet. It may do this as an on-demand event, meaning the stack decides that it wants to send a packet _now_ without informing the scheduler well in advance. This is in contrast to how Bluetooth LE operates, where the scheduled operations are known reasonably far in advance. The scheduler evaluates that it is possible to perform the Zigbee TX 1 radio operation and still service the higher priority Bluetooth LE reception event in the future. Therefore, the scheduler allows the transmit event to occur. The Zigbee stack performs all the pieces of this transmit operation (waiting for a MAC ack) and then voluntarily yields. The estimated transaction time of the Zigbee transmit radio operation does NOT include retries.

In this example, Bluetooth LE is _already_ scheduled to receive in the future and the Zigbee stack wants to transmit. For the first Zigbee TX 1 radio operation there is enough time before the Bluetooth LE RX 1 radio operation, so the scheduler allows the stack to perform the operation. Later, when the Zigbee stack tries to schedule Zigbee TX 2 the scheduler determines there is not enough time before the high priority Bluetooth LE RX 2 event. However, the Zigbee stack has indicated that this action may slip its start time. The radio scheduler determines that given the expected duration of the Bluetooth LE radio operation the Zigbee operation can start after that event and still be within the slip time indicated by the Zigbee stack.

If all goes as expected, the Zigbee transmit operation will have its first attempt occur without any failures due to scheduling.

###### Priority Interruption Example

This example illustrates a higher priority operation interrupting a lower priority one.

![Priority Interruption Example](/multiprotocol-dynamic-ug/0.2/images/sld485-image10.jpg)

This example starts in the same way as the previous example. Zigbee and Bluetooth LE both have a radio operation that is scheduled without any collision.

Later, the Zigbee stack decides it wants to send another packet for the Zigbee TX 2 event. The scheduler determines that it should be possible to schedule this event and service the Bluetooth LE RX 2 event later, based on the minimum time that the Zigbee TX 2 event must take. However, the Zigbee TX 2 event takes longer than expected due to a long random backoff and does not yield in time. This causes the event to collide with a higher priority radio operation, and so the Radio Scheduler interrupts the Zigbee event and returns a failure to the higher-level stack. The Bluetooth LE event occurs normally and when it is complete it voluntarily yields to any lower-priority operations.

Upon receiving the failure from the radio scheduler, the Zigbee stack immediately attempts to retry the MAC message. It schedules the operation and includes a slip time. At this point the Bluetooth LE stack has priority over the radio and thus the operation cannot be started yet, but the scheduler accepts the new radio operation. The Bluetooth LE stack completes its scheduled receive and yields the radio. The scheduler then triggers the Zigbee transmit operation to occur because it is still within the slip time of the initial start operation. After the transmit completes the scheduler returns to the background receive operation.

###### Higher Priority Operation that is Extended

This example shows what happens when a higher priority operation takes longer than originally anticipated and causes a lower priority operation to miss its opportunity.

![Higher Priority Operation that is Extended](/multiprotocol-dynamic-ug/0.2/images/sld485-image11.jpg)

In this case, Bluetooth LE has a Scheduled receive that is currently taking place. Zigbee decides to send a packet but it cannot be run right now. The scheduler accepts the operation under the assumption that the Bluetooth LE event will complete before the end of the slip time of the Zigbee event. However, the Bluetooth LE event extends longer since additional packets are sent between the devices. The Bluetooth LE operation has priority, so the Zigbee operation eventually runs out of slip. An error is returned to the stack. Zigbee decides to re-transmit the packet. Again, the Zigbee stack indicates the operation should start now but may slip into the future. The scheduler is in the middle of changing the radio config so it cannot begin the operation immediately. Instead, it slips the radio operation start time a small amount and then executes the operation.

###### Higher Priority Operation Without Interruption

In this example the radio scheduler is running on a node acting as a Bluetooth LE peripheral and that node has a number of connections to different central devices. It also has a periodic advertising beacon that is transmitted. The following figure shows a case where these events are occurring virtually back-to-back and do not allow for enough time to switch back to the Zigbee radio config. Therefore it will create a period where the Zigbee stack is unable to transmit even with the slip time.

![Higher Priority Operation Without Interruption](/multiprotocol-dynamic-ug/0.2/images/sld485-image12.jpg)

Zigbee asks the scheduler to schedule a transmit radio operation. Even though the scheduler knows that the event will fail due to scheduled higher priority operations, it still accepts the scheduled event. This is done for two reasons. First, circumstances may change and the event can be executed. Second, the stack sitting on top of the radio scheduler may try to retry the action. If the result of the failed scheduling was returned immediately then the stack's attempt to retry would be unlikely to succeed since no time has passed. Instead, by queuing the event and returning the failure _after_ the slip time has expired, a retry (with its own slip time) has a better chance of success as the set of upcoming radio operations will be different.

###### Receive When a Higher Priority Operation is Running

This example illustrates what happens when Bluetooth LE is active and a lower priority operation will be receiving data.

![Receive When a Higher Priority Operation is Running](/multiprotocol-dynamic-ug/0.2/images/sld485-image13.jpg)

In the first case, when an IEEE 802.15.4 message is sent and the Bluetooth LE stack is utilizing the radio for an active receive the Zigbee stack will not be online to receive the message. However, the Zigbee sender of the message will retry in most cases and with backoffs and other timing alterations is not going to conflict with another higher priority scheduled Bluetooth receive events unlikely to collide. The Zigbee message is received successfully.

The second case shows that, in the case of an active receive, the Zigbee stack may still be interrupted and not receive (or ACK) the message. Successful communication relies on retries at the MAC or higher layer to send this message again and verify the Dynamic Multiprotocol device receives the message.

While there may be considerations for whether or not active receive should be interrupted, it is difficult for the scheduler to make that determination. In general, the robustness of the protocols should allow for messages to be successfully received even with interruptions.

##### Implementing Multiprotocol with an 802.15.4-Based Stack

This section offers general information about implementing an 802.15.4-based stack, such as Zigbee or Connect, as part of a multiprotocol applications. For specifics on how to configure plugins and other details specific to a particular protocol, see one of the following application notes:

- [Dynamic Multiprotocol Development with Bluetooth and Zigbee EmberZNet SDK 6.x and Lower](https://docs.silabs.com/multiprotocol/latest/dynamic-multiprotocol-bluetooth-zigbee-sdk-7x-higher/)
- _AN1209: Dynamic Multiprotocol Development with Bluetooth and Connect_

###### Wireless Protocol Support

Different wireless protocols have different characteristics that have been leveraged with the design of Dynamic Multiprotocol. For example, Bluetooth Low Energy is very strict and predictable in its schedule of radio operations; advertisement and connection intervals occur at set times. In contrast, an 802.15.4 protocol is more flexible in the timing of many message events; CSMA (carrier sense multiple access) in IEEE 802.15.4 adds random backoffs so that event delays are on the order of milliseconds. This allows IEEE 802.15.4 messages to be sent around the Bluetooth Low Energy events and still be reliably received.

###### 802.15.4 RAIL Priority

802.15.4 protocols currently have three RAIL priorities.

|**No.**|**Name**|**Default Setting**|**Exit Criterion**|
|---|---|---|---|
|1|Active TX|100|MAC ACK received (or not)|
|2|Active RX|255|Packet filtered or MAC ACK sent|
|3|Background RX|255|Task with higher Priority present|

If an Active TX gets executed the radio will be released at the time the corresponding MAC acknowledgement was received (or a time-out occurred).

Background RX will leave the radio in receive state ready to receive asynchronous messages. If the active RX priority is different than the background RX priority, the receive priority will be raised whenever a sync word is detected and only lowered once that packet is filtered or completed and its ACK is sent if one was requested.

###### Balancing Priorities (heading level 7)

As explained in [Implementing Multiprotocol with Bluetooth](./06-implementing-multiprotocol-with-bluetooth), by default the Bluetooth priority range is mapped into the RAIL priority range 16 - 32. In general, Bluetooth starts out using low priority (32) and dynamically increases the priority up to the maximum (16) as needed if messages are not succeeding.

As described in [Radio Scheduler Examples](./03-radio-scheduler-examples), an 802.15.4-based stack such as Zigbee or Connect uses default RAIL priority values of 255 for background RX, 255 for active RX, and 100 for active TX.

As a result of these default RAIL priorities, in an 802.15.4 protocol-Bluetooth multiprotocol application, by default Bluetooth traffic will always take priority over 802.15.4 protocol traffic. This is a good choice for many applications, because Bluetooth traffic has stringent timing requirements, unlike 802.15.4 protocols. However, if Bluetooth traffic load is very high (for example, sending lots of data using a very small connection interval), it is possible for 802.15.4 protocol traffic to be completely blocked from access to the radio because of its lower priority and the very small windows of available radio time left by the Bluetooth traffic.

> **Note**: The following information is currently only applicable to the EmberZNet Zigbee stack. Silicon Labs Connect does not yet have the API needed to change the priorities.

If you are developing an 802.15.4-based dynamic multiprotocol application, and it is important for that traffic to succeed in the presence of very high load Bluetooth traffic, you can adjust the default priorities as shown in the table below using the following API:

|**No.**|**Name**|**Default Setting**|
|---|---|---|
|1|Active TX|23|
|2|Active RX|24|
|3|Background RX|255|

Because the Bluetooth initially sets its RAIL priority to 32, these 802.15.4 priority settings give 802.15.4 traffic higher priority than Bluetooth initially, which gives the 802.15.4 protocol a chance to transmit or receive traffic successfully even in the presence of a very high load of Bluetooth traffic. On the other hand, Bluetooth will dynamically increase its priority if it is bumped from the scheduler by the 802.15.4 traffic, up to a high priority of 16. Thus, after allowing the 802.15.4 protocol access to the radio initially, Bluetooth will take priority on subsequent retries if necessary.

This approach allows both protocols to compromise on their use of the radio without one being able to completely dominate over the other.

##### Implementing Multiprotocol with RAIL

This section offers more information about RAIL for users who consume the RAIL API directly to develop proprietary protocols. In particular, it offers details on how to work with the RAIL APIs to handle specific radio scheduler cases.

###### Examples with Background Receive, Yield Radio and State Transition

The fundamentals of the RAIL Multiprotocol priority system are straightforward: a radio event with a higher priority (that is, smaller in number) will always usurp any other radio events with lower priority. However, this topic becomes more complicated when considering state transitions and APIs such as `RAIL_StartRx()`, which put the radio into a certain state for an indefinite amount of time. This section provides some illustrations and examples to demonstrate how these time-unbounded states are handled, and how the application layer can use APIs such as `RAIL_YieldRadio()` to control them. The examples are as follows:

- [State Transitions with a Single Protocol](#state-transitions-with-a-single-protocol)
- [State Transitions with Two Protocols](#state-transitions-with-two-protocols)
- [State Transitions with Two Protocols and Monotonically Increasing Priorities](#state-transitions-with-two-protocols-and-monotonically-increasing-priorities)

In these examples, `RAIL_StartTx()` is the source of the TX event that interrupts the background RX. Note, however, that these examples are applicable to any radio API except for `RAIL_StartRx()`. In other words, the examples are applicable to any API that starts a radio event that is not a background RX.

These examples illustrate expected multiprotocol behaviors regarding state transitions. To summarize:

- In a state transition, the new state is treated as an indefinite extension of the originating event at that same priority until `RAIL_YieldRadio()` is called.
- Background RX events are not affected by `RAIL_YieldRadio()`. Only `RAIL_Idle()` can permanently remove a protocol from the background RX state.
- An event with a higher priority will always usurp an event with lower priority, regardless of any other API calls.
- Only `RAIL_StartRx()` receives can be ‘returned to’ from a higher priority event through `RAIL_YieldRadio()` or `RAIL_Idle()`.
- All radio events other than `RAIL_StartRx()` require `RAIL_YieldRadio()` in order to end and progress to the next event.
- The call to `RAIL_YieldRadio()` cannot be replaced with `RAIL_Idle()`. `RAIL_Idle()` clears out _all_ events for the given protocol.

###### State Transitions with a Single Protocol (heading level 7)

This first example examines the behavior of the radio with a single protocol (that is, where the same `RAIL_Handle_t` is used for all radio function calls). The radio starts in RX with an initial call to `RAIL_StartRx()`, then moves into a TX with a higher priority call to `RAIL_StartTx()`. It is important to note that after the transmit is done, the radio transitions to the state specified by `RAIL_SetTxTransitions()`, and it stays in the state indefinitely at the same priority and channel as the TX until `RAIL_YieldRadio()` is called. After that, the radio returns to RX, with the initially specified priority and channel.

![State Transitions with Calls to RAIL_StartTx(), RAIL_StartRx(), RAIL_YieldRadio() with a Single Protocol](/multiprotocol-dynamic-ug/0.2/images/sld485-image14.png)

The need to actively yield the radio, and thus the `RAIL_YieldRadio()` API were necessary largely due to ACK’ing. The design philosophy is that, because both a TX and a received ACK are viewed as part of the same transaction, if a node transmits and expects an ACK it should be able to both transition to RX and continue listening for the ACK as part of the same operation (and therefore same priority) as the original TX. In general, however, RAIL on its own cannot know whether or not an ACK is required. This can depend on other factors, such as packet contents, or other application logic, and so cannot be simply determined by checking whether ACK’ing has been configured with `RAIL_ConfigAutoAck()`.Therefore, discretion as to when a radio transaction is complete is left to the application/stack.

In the case that an ACK is not required, Silicon Labs recommends calling `RAIL_YieldRadio()` as part of handling the `RAIL_EVENT_TX_PACKET_SENT` event. Doing this causes the green line in the above figure to be minimized down to the interrupt latency time. If the application does expect an ACK, `RAIL_YieldRadio()` should be called when the ACK is received or has been deemed to time out.

###### State Transitions with Two Protocols (heading level 7)

This scenario is similar to the first scenario regarding state transitions after TX, but introduces another protocol.

![State Transitions with Calls to RAIL_StartTx(), RAIL_StartRx(), RAIL_YieldRadio() With Two Protocols](/multiprotocol-dynamic-ug/0.2/images/sld485-image15.jpg)

In this situation, it is important to note that `RAIL_StartRx()` can be called at any time during the TX transaction. As long as its priority is less than or equal to the priority of the TX, the RX will not come into effect until the application calls `RAIL_YieldRadio()` on Protocol A. When `RAIL_StartRx()` is called during the TX, the RX is merely added to the queue of events to be handled.

Another key point is that, although `RAIL_YieldRadio()` on Protocol A will transition from TX on Protocol A to RX on Protocol B, a `RAIL_Idle()` on Protocol B is required to transition from the RX on Protocol B to the RX on Protocol A. The philosophy here is that Background RXs can’t really be yielded, since the event is never really over. The only way to exit is to stop the Background RX with a call to `RAIL_Idle()`.

###### State Transitions with Two Protocols and Monotonically Increasing Priorities (heading level 7)

The final scenario is nearly identical to the previous one, except the call to `RAIL_StartRx()` on Protocol B is at a higher priority than the call to `RAIL_StartTx()` on Protocol A.

![Example of State Transitions with Calls to RAIL_StartTx(), RAIL_StartRx(), RAIL_YieldRadio() with Two Protocols and Different Priorities](/multiprotocol-dynamic-ug/0.2/images/sld485-image16.png)

In this case, since the priority of the second `RAIL_StartRx()` is higher than the priority of the call to `RAIL_StartTx()`, a call to `RAIL_YieldRadio()` is no longer necessary. Because the second `RAIL_StartRx()` is at a higher priority, it usurps the `RAIL_StartTx()` event, taking control of the radio and removing the TX event from the state. At any time during that RX on Protocol B, `RAIL_Idle()` can be called to return to the RX on Protocol A, just as in the previous example.

Note here, that when the application calls `RAIL_Idle()` on Protocol B’s RX, the application does not return to the TX Transition of Protocol A. Instead, it goes right to the background RX, even though the application never called `RAIL_Idle()` on Protocol A’s TX. For Scheduled radio operations (that is, any radio operation started by an API other than `RAIL_StartRx()`), once a radio event is usurped by a higher priority event, it is removed entirely and will not be returned to later. Only Background receives, started by `RAIL_StartRx()`, can be maintained in the background and ‘returned to’ through a call to `RAIL_YieldRadio()` or `RAIL_Idle()`.

To emphasize the difference between `RAIL_YieldRadio()` and `RAIL_Idle()` it is important to note that, for all these examples, the call to `RAIL_YieldRadio()` cannot be replaced with `RAIL_Idle()`. `RAIL_Idle()` clears out _all_ events for the given protocol – both the Background (that is, started by `RAIL_StartRx()`) and Scheduled (that is, started by APIs other than `RAIL_StartRx()`) operations. `RAIL_Idle()` would indeed still cause the application to exit out of the TX transition state, but it would also clear out the Background RX, causing the application to return to idle, not RX.

##### Implementing Multiprotocol with Bluetooth

For details on how the RAIL/Bluetooth light/switch multiprotocol example was implemented, and for more information on developing a multiprotocol application with your own protocol on RAIL, see [Dynamic Multiprotocol Development with Bluetooth and Proprietary Protocols on RAIL in GSDK v3.x and Higher](https://docs.silabs.com/multiprotocol/latest/multiprotocol-dynamic-ble-proprietary-on-rail/).

###### Bluetooth Priorities

As opposed to Zigbee with statically defined priorities for different operation types, Bluetooth uses a range and offset approach to assign all tasks to a given area of the priority spectrum.

![Mapping of Bluetooth Priority Range to RAIL Priority Range](/multiprotocol-dynamic-ug/0.2/images/sld485-image17.png)

In this example the Bluetooth priority range, which itself spans from 0 to 255, is mapped to a limited portion of the shared RAIL priority space.

Unlike Zigbee, Bluetooth has much more stringent timing requirements where missing a given slot may result in a connection terminating. Also Bluetooth has a range of different tasks like (potentially multiple) connections, advertisement, scanning, and Periodic Advertising with Responses (PAwR) transmissions and receptions.

|**No.**|**Name**|**Default Setting**|**Exit Criterion**|
|---|---|---|---|
|1|Connection|135 to 0|Connection Event Ends|
|2|Connection Initiation|55 to 15|Initiation Window Ends|
|3|Advertisement|175 to 127|Advertisement Event Ends|
|4|Scanner|191 to 143|Scan Window Ends|
|5|PAwR TX|15 to 5|Advertiser: PAwR Transmit Event Ends; Synchronizer: PAwR Response Slot Ends|
|6|PAwR RX|20 to 10|Advertiser: PAwR Response Slot Ends; Synchronizer: PAwR Response Slot Delay Ends|

In order to handle this the Bluetooth scheduler, whose priorities are mapped to the RAIL radio scheduler, takes into account the following parameters for each task:

1. Start Time
2. Minimum time
3. Maximum time
4. Priority

![Bluetooth Task](/multiprotocol-dynamic-ug/0.2/images/sld485-image18.png)

If the start time is moved the total running time is reduced respectively, that is the slack is reduced. Also priorities can be dynamically adjusted.

###### Connections (heading level 7)

Connections have a relatively high priority. The start time of a connection cannot be moved.

The priority is dynamically increased by the Bluetooth scheduler the closer the connection gets to the supervision timeout, and reaches the maximum priority close to it. A TX packet in the TX queue also increases the priority of a connection.

###### Connection Initiation (heading level 7)

Connection initiation scans advertisements from target device to establish a connection. It has a higher priority compared to a scanner to allow more robust connection establishment.

###### Advertisements (heading level 7)

Advertisements by default have a lower priority and their start point can be moved. Start time and Maximum time are defined by the advertisement interval.

If an advertisement could not be sent out, the priority of advertisements increases slowly and is reset once an advertisement was successfully sent.

###### Scanner (heading level 7)

By default, these tasks have the lowest priority. Start, minimum and maximum time are defined by the scanning interval and window size. Scanning can continue even when interrupted by a higher priority task. If this happens the scan time is accumulated to make sure the desired scan window size is reached at each scanning interval.

As with advertisements, the priority is increased in case the desired scan interval or window size could not be previously met. It is reset back to its initial priority once the scan interval or window size has been met.

###### Periodic Advertising with Responses (PAwR) (heading level 7)

Sending Periodic Advertising with Responses has the highest priority by default over all other Bluetooth tasks, followed by receiving responses in PAwR to maintain synchronization in an electronic shelf label (ESL) network.

A PAwR task priority is increased if the task scheduling fails twice in a row. The priority is either increased by 1/6th of the priority range, or at least by one until the maximum priority has been reached. The task priority is reset back to the minimum after successful scheduling. The same procedure applies to both PAwR advertiser and synchronizer in both directions.

###### Example of Bluetooth Scheduler Operation

This example illustrates how the Bluetooth scheduler will schedule three connection tasks and one advertisement task, each holding different priorities. In the following figures the gray part indicates the minimum runtime a task requires and the blue part indicates the maximum runtime the task can use and, if flexible, the region where the task can be moved. The following figure shows the initial setup.

![Task Scheduling Example: Setup](/multiprotocol-dynamic-ug/0.2/images/sld485-image19.png)

As shown below, Conn1 is the first task to run as it does not overlap with any higher priority task.

![Task Scheduling Example: 1st Step](/multiprotocol-dynamic-ug/0.2/images/sld485-image20.png)

Adv1 overlaps with the higher priority Conn2. Adv1 is flexible and therefore gets moved in, as illustrated in the following figure.

![Task Scheduling Example: 2nd Step](/multiprotocol-dynamic-ug/0.2/images/sld485-image21.png)

Conn2 overlaps with higher priority task Conn4. As Conn2 is not flexible, the scheduling of Conn2 fails.

![Task Scheduling Example: 3rd Step](/multiprotocol-dynamic-ug/0.2/images/sld485-image22.png)

Conn4 does not overlap with other tasks, therefore Conn1 end is adjusted to stop before Conn4 starts.

![Task Scheduling Example: 4th Step](/multiprotocol-dynamic-ug/0.2/images/sld485-image23.png)

Finally, Adv1 is run. Conn4 is adjusted to end before Adv1 starts.

![Task Scheduling Example: 4th Step](/multiprotocol-dynamic-ug/0.2/images/sld485-image24.png)

###### Modifying Priorities (heading level 7)

The "sl_bt_configuration_t" (v3.x)/"gecko_configuration_t" (v2.x) struct defines the sl_bt_stack_config_t struct, which contains the field “bluetooth.linklayer_priorities” that is a pointer to the priority configuration. If the pointer is NULL then the stack uses its default priorities as listed in [Bluetooth Priorities](#bluetooth-priorities), as well as this section.

In case the pointer is not null it must point to a struct of priority settings as defined below:

```c
typedef struct {
  uint8_t scan_min;
  uint8_t scan_max;
  uint8_t adv_min;
  uint8_t adv_max;
  uint8_t conn_min;
  uint8_t conn_max;
  uint8_t init_min;
  uint8_t init_max;
  uint8_t rail_mapping_offset;
  uint8_t rail_mapping_range;
  uint8_t _reserved;
  uint8_t adv_step;
  uint8_t scan_step;
  uint8_t pawr_tx_min;
  uint8_t pawr_tx_max;
  uint8_t pawr_rx_min;
  uint8_t pawr_rx_max;
} sl_btctrl_ll_priorities;
```

The parameters `scan_min`, `can_max`, `adv_min`, `adv_max`, `conn_min`, `conn_max`, `init_min`, and `init_max` define the minimum and maximum priorities for scanning, advertisement, connections, and initiations respectively. The priorities will move between the min and max boundaries as described in:

- [Connections](#connections)
- [Connection Initiation](#connection-initiation)
- [Advertisements](#advertisements)
- [Scanner](#scanner)

The RAIL mapping parameters, `rail_mapping_offset` and `rail_mapping_range`, define how the Bluetooth link layer priorities are mapped to the global RAIL radio scheduler priorities. The mapping of these values can be seen in [Bluetooth Priorities](#bluetooth-priorities). The default for both `rail_mapping_offset` and `rail_mapping_range` is 16.

The `adv_step` and `scan_step` parameters define the step size when the priority of scanning and advertising is changed dynamically.

Finally, the parameters `pawr_tx_min`, `pawr_tx_max`, `pawr_rx_min`, and `pawr_rx_max` define the priority range for the PAwR advertiser and synchronizer TX and RX events in each sub-event.

#### Dynamic Multiprotocol Development with BLE and OT on SoCs

##### Dynamic Multiprotocol Development with Bluetooth and OpenThread on SoCs in GSDK v3.x and Higher

**This section replaces _AN1265: Dynamic Multiprotocol Development with Bluetooth and OpenThread on SoCs in GSDK v3.x and Higher_. Further updates to this application note will be provided here.**

These pages provide instructions on getting started with dynamic multiprotocol (DMP) applications using Silicon Labs OpenThread and Bluetooth running over the FreeRTOS real time operating system.

The sample application **ot-ble-dmp** is a test application that demonstrates the components that go into building a DMP application. It provides a command line interface (CLI) that allows the user to execute basic OpenThread and Bluetooth commands. It also demonstrates how the power manager component can be used to save power by allowing the device to enter low power (EM2) mode in between activities.

The term 'dynamic' in DMP refers to the fact that both protocols are operating simultaneously. The radio scheduler takes care of multiplexing the transmitted and received packets over the radio. For more information on how the radio scheduler works, see the [Dynamic Multiprotocol User’s Guide](https://docs.silabs.com/connect/latest/multiprotocol-dynamic-ug/).

The instructions assume that you have installed Simplicity Studio 5 (SSv5) and the OpenThread and Bluetooth SDKs, and that you are familiar with SSv5 and configuring, building, and flashing applications. If not, see _QSG170: Silicon Labs OpenThread Quick Start Guide_.

###### Hardware Requirements

- An EFR32 part with at least 512 kB of flash.

##### Building the ot-ble-dmp Sample App

Precompiled demo application images are provided with the Gecko SDK Suite 3.0, compatible with:

- brd4180a
- brd4186c
- brd2703a
- brd4116a

To get started quickly, in the Simplicity Studio 5 (SSv5) Launcher Perspective, go to the DEMOS tab. Find the **ot-ble-dmp** demo and click **RUN**. This uploads the application image to your board.

To build the **ot-ble-dmp** sample app from source, you will need SSv5 and the Gecko SDK 3.x development environment with the following packages installed:

- OpenThread SDK
- Bluetooth SDK

The GNU ARM toolchain is installed with SSv5. The IAR-EWARM toolchain is not compatible with OpenThread.

1. With your target development hardware connected, open SSv5’s File menu and select **New > Silicon Labs Project Wizard**. The Target, SDK, and Toolchain Selection dialog opens. Your target hardware should be populated. Click **NEXT**.
2. The Example Project Selection dialog opens. Use the Technology Type and Keyword filters to search for a specific example, in this case **ot-ble-dmp**. Select it and click **NEXT**.  
   Note that if you do not see the application, your connected hardware may not be compatible. To verify, in the Launcher Perspectives My Products view enter **EFR32MGxx** and select one of the boards. Go to the Examples tab, filter by Thread technology, and verify you can see the app.
3. The Project Configuration dialog opens. Here you can rename your project, change the default project file location, and determine if you will link to or copy project files. Note that if you change any linked resource, it is changed for any other project that references it. Unless you know you want to modify SDK resources, use the default selection. Click **FINISH**.

The Simplicity IDE opens with the **ot-ble-dmp** project open in the Project Configurator. You may now build the project. For those used to Simplicity Studio 4, no generation step is necessary because it is done automatically. The ot-ble-dmp.s37 image will be located in the GNU ARM directory and may be uploaded to your board using an SSv5 tool such as the flash programmer or Simplicity Commander.

##### CLI Commands

If you have used the **ot-cli-ftd** sample application, the OpenThread commands available in the **ot-ble-dmp** app are identical. Type `help` at the prompt to see a list. A complete OpenThread CLI reference is available here:

[OpenThread CLI Reference](https://github.com/openthread/openthread/blob/master/src/cli/README.md)

A quick tutorial on using the CLI to form a two-node OpenThread network and send a ping is available here:

[OpenThread CLI Reference](https://github.com/openthread/openthread/tree/master/examples/apps/cli)

The **ot-ble-dmp** app adds a set of Bluetooth commands that can be used to exercise the bluetooth stack. Type `ble` at the prompt to see a list of subcommands:

- `get_address`
- `create_adv_set`
- `set_adv_timing`
- `set_adv_random_address`
- `start_adv`
- `stop_adv`
- `start_discovery`
- `set_conn_timing`
- `conn_open`
- `conn_close`

These commands are implemented in the bluetooth_cli.c file, and each of them calls a corresponding Bluetooth C API function. For detailed documentation on the underlying functions, see [Developing with Silicon Labs Bluetooth Low Energy](https://docs.silabs.com/bluetooth/latest). Note that the C API prefixes for the Bluetooth SDK changed from `gecko_` to `sl_bt_` in version 3.0. See _AN1255: Transitioning from the v2.x to the v3.x Bluetooth® SDK_ for more information about this and other BGAPI changes.

`ble get_address`

- Prints out the public Bluetooth address.
- Example: `ble get_address`
- Calls `sl_bt_system_get_identity_address()`

`ble create_adv_set`

- Create an advertising set. Must be called to obtain a handle for use in the other advertising commands.
- Example: `ble create_adv_set`
- Calls `sl_bt_advertiser_create_set()`

`ble set_adv_timing <handle> <interval_min> <interval_max> <duration> <max_events>`

- Set the advertising timing parameters of the given advertising set.
- Example: `ble set_adv_timing 0 160 320 0 0`
- Calls `sl_bt_advertiser_set_timing()`

`ble set_adv_random_address <handle>`

- Set the advertiser on an advertising set to use a random address.
- Example: `ble set_adv_random_address 1`
- Calls `sl_bt_advertiser_set_random_address()`

`ble start_adv <handle> <discoverableMode> <connectableMode>`

- Starts advertising on a given advertising set with specified discoverable and connectable modes.
- Example: `ble start_adv 0 2 2`
- Calls `sl_bt_advertiser_start()`

`ble stop_adv`

- Stops advertising on the given handle.
- Example: `ble stop_adv`
- Calls `sl_bt_advertiser_stop()`

`ble start_discovery <mode>`

- Scans for advertising devices.
- Example: `ble start_discovery 1`
- Calls `sl_bt_scanner_start()`

`ble set_conn_timing <min_interval> <max_interval> <latency> <timeout>`

- Sets the default Bluetooth connection parameters.
- Example: ble set_conn_timing 6 400 0 800
- Calls sl_bt_connection_set_default_parameters()

`ble conn_open <address> <address_type>`

- Connects to an advertising device. Address type 0=public address, 1=random address. Initiating phy argument hard coded to 1.
- Example: `ble conn_open 80fd34a198bf 0`
- Calls `sl_bt_connection_open()`

`ble conn_close <handle>`

- Closes a Bluetooth connection.
- Example: `ble conn_close 0`
- Calls `sl_bt_connection_close()`

###### Establishing a Bluetooth Connection Between Two Nodes

To establish a Bluetooth connection, the client starts advertising on advertising set 0 with modes discoverable and connectable. The server connects using the client's public address.

CLIENT:

```c
> ble create_adv_set
ble create_adv_set
success handle=0
>
> ble start_adv 0 2 2
ble start_adv 0 2 2
success
>
> ble get_address
ble get_address
BLE address: 90fd9f7b5d39
```

SERVER:

```c
> ble conn_open 90fd9f7b5d39 0
ble conn_open 90fd9f7b5d39 0
success
>
> BLE connection opened handle=1 address=90fd9f7b5d39 address_type=1 master=1 advertising_set=255
BLE connection parameters handle=1 interval=40 latency=0 timeout=100 security_mode=0 txsize=27
BLE event: 0x40800a0
BLE event: 0x900a0
BLE connection parameters handle=1 interval=40 latency=0 timeout=100 security_mode=0 txsize=251
```

##### Important Software Components and Files

With the **ot-ble-dmp** project open in Project Configurator, click the Software Components tab to see all the software components in the Component Library. Filter on Installed Components to see the components used in the project. Four key component categories pertain to building a DMP application:

- Bluetooth components
- OpenThread components
- Rail Library Multiprotocol component (under Platform > Radio)
- FreeRTOS components (under Third Party)

You need to include these components in any project to add OpenThread and Bluetooth DMP functionality.

When the FreeRTOS component is added to a project, the Project Configurator automatically takes care of adding the CMSIS RTOS2-based adaptation layers necessary to run the OpenThread and Bluetooth stacks over FreeRTOS. The adaptation files for OpenThread and Bluetooth are located in the following Simplicity Studio 5 locations:

- developer/sdks/gecko_sdk_suite/<version>/protocol/openthread/platform-abstraction/rtos/sl_ot_rtos_adaptation.c
- developer/sdks/gecko_sdk_suite/<version>/protocol/bluetooth/src/sl_bt_rtos_adaptation.c

The three application source files for this project (the only source files that are not part of a Gecko Platform component) are stored at the top level of the project and are named:

- main.c
- app.c
- bluetooth_event_callback.c

###### The Main Function and Initialization

The **ot-ble-dmp** app uses the same main function definition as used by other OpenThread sample applications. The call to `sl_system_init()`, which is defined in `sl_system_init.c`, initializes the entire system, including calls to `sl_bt_rtos_init()` and `sl_ot_rtos_init()` that are responsible for creating the Bluetooth and OpenThread threads.

The application can use the `app_init()` function to perform any necessary initialization steps prior to starting the kernel. The call to `sl_system_kernel_start()` starts the FreeRTOS scheduler.

The OpenThread instantiation and CLI initialization is handled by the OpenThread initialization thread by calling `sl_ot_init()` defined in `sl_ot_init.c`. More details on the different threads and their priorities are provided in the next section.

###### FreeRTOS Tasks

The **ot-ble-dmp** app creates the following five RTOS threads by default:

- OpenThread initialization thread (priority 53)
- OpenThread main thread (priority 24)
- Bluetooth link layer thread (priority 52)
- Bluetooth stack event thread (priority 51)
- Bluetooth event handler thread (priority 50)

The OpenThread threads are created in sl_ot_rtos_adaptation.c, and the Bluetooth tasks are created in sl_bt_rtos_adaptation.c.

The OpenThread initialization thread handles the OpenThread instantiation and CLI initialization. As the Bluetooth event callback `sl_bt_on_event()` utilizes OpenThread CLI for prints (discussed in [Handling Bluetooth Events](#handling-bluetooth-events)), the OpenThread initialization thread starts with the highest priority.

Once the initialization is complete, the initialization thread also creates the main (operating) thread for OpenThread. By default, the main thread uses a low priority compared to Bluetooth, thus enabling Bluetooth threads to take over.

The priorities for the different Bluetooth threads and the OpenThread main thread are configurable and are defined in `sl_bt_rtos_config.h` and `sl_openthread_rtos_config.h`.

Silicon Labs Bluetooth has a serialized API which allows for commands and events to be passed between RTOS tasks in a thread-safe manner. OpenThread does not have a serialized API. For this reason, it is most convenient for the application logic to run in the OpenThread task. An application tick callback is provided for this purpose, and is called from within the OpenThread task's run loop: `sl_ot_rtos_application_tick()`. The **ot-ble-dmp** app includes a simple implementation of the tick callback in the app.c file.

OpenThread API calls made from within the application tick are thread-safe because they are executed within the OpenThread task. Because the Bluetooth API is serialized, Bluetooth API calls may be made from any task. The Bluetooth task is responsible for consuming and processing these serialized events. This happens transparently to the application.

###### Handling Bluetooth Events

Bluetooth events are dispatched to the application via the `sl_bt_on_event()` callback. For the **ot-ble-dmp** app, an implementation of this callback is located in bluetooth_event_callback.c. This example handler simply prints out some information about the event. In a real application, these events would be processed by application handlers.

Bluetooth events are processed within a dedicated Bluetooth Event Handler thread. This is a separate thread whose sole purpose is to check for waiting Bluetooth events, and call `sl_bt_on_event()` when they become available. This thread is automatically created during initialization.

###### Power Manager Integration

The **ot-ble-dmp** app also includes the Power Manager component (under Platform > Service > System), which is responsible for putting the system to sleep when possible.

The Power Manager component includes seamless FreeRTOS integration. It runs automatically from within the FreeRTOS Idle Task. When all threads have suspended (because they are pending on some event to continue processing), the Idle Task runs, and the power manager code can then put the system to sleep.

The application informs the power manager what sleep level it would like by adding and removing energy requirements via the API calls `sl_power_manager_add_em_requirement()` and `sl_power_manager_remove_em_requirement()`. Adding an EM1 requirement tells the Power Manager that the lowest energy level allowed is EM1, which only idles the processor and does not go to sleep. Removing the EM1 requirement allows the power manager to enter energy level EM2, which is deep sleep. See the reference for your MCU on [Silicon Labs Developer Documentation](https://docs.silabs.com/) under Modules>Platform Services>Power Manager.

##### OpenThread Sleepy End Device Demo

The **ot-ble-dmp** app starts out by adding an EM1 requirement during initialization, in the `sl_ot_rtos_application_init()` callback. This prevents the device from going into EM2 sleep mode, so that the CLI is responsive, and the user can enter commands.

To demonstrate an OpenThread Sleepy End Device, first form a two node OpenThread network by following the instructions at: [OpenThread CLI Example](https://github.com/openthread/openthread/tree/master/examples/apps/cli) .

Next, on the device that joined the network (not the leader), type the following commands:

```c
> mode s
> pollperiod 1000
```

The `mode` command puts the device into sleepy child mode. The `pollperiod` command tells the child to send data polls once every second. At this point the child is still not sleeping, and the CLI is still responsive.

Pressing either button PB0 or PB1 on the WSTK development board will toggle the energy mode requirement. Specifically, the first time the button is pressed, the EM1 requirement will be removed, allowing EM2 sleep. The child will start sleeping in EM2 mode in between data polls, and the CLI will no longer be responsive. You can verify that the child is still able to send and receive messages by sending a ping from the leader node. There will be up to one second of latency due to the child's sleep cycle. Pressing either button again will add back the EM1 requirement, which will bring the device out of EM2 so that the CLI can be used.

To monitor the power consumption of the device while performing the above steps, use the Energy Profiler tool in Simplicity Studio to connect to the device and start an energy capture. See _UG343: Multi-Node Energy Profiler User’s Guide_ for more information about the Energy Profiler.

#### Multiprotocol Solution on Linux

##### Running Zigbee, OpenThread, and Bluetooth Concurrently on a Linux Host with a Multiprotocol Co-Processor

> **Note: This section replaces AN1333: Running Zigbee, OpenThread, and Bluetooth Concurrently on a Linux Host with a Multiprotocol Co-Processor for SiSDK 2024.12.0 and up. Further updates to this application note will be provided here**.

This section describes how to run any combination of Zigbee EmberZNet, OpenThread, and Bluetooth networking stacks on a Linux host processor, interfacing with a single EFR32 Radio Co-processor (RCP) or Network Co-Processor (NCP) with multiprotocol and multi-PAN support. The intended use case is for gateway products that wish to run any combination of the three protocols on the Linux host processor with a single, shared EFR32 RCP/NCP. Each stack can use the co-processor to communicate simultaneously and independently. Key points covered include:

- **System Architecture**: Describes the components of the Multiprotocol and Multi-PAN RCP solution, including the RCP and NCP image, Coprocessor Communication Daemon (CPCd), Zigbeed, OpenThread and Bluetooth host applications. It explains how these components interact to enable concurrent operation of Zigbee, OpenThread, and Bluetooth on a single EFR32. Additionally, this section introduces various combinations of these three protocols, showcasing the flexibility and versatility of these multiprotocol solutions.
- **Co-Processor Configuration**: Instructions to set up the RCP and NCP using Simplicity Studio, such as building and flashing the RCP and NCP image for different boards.
- **Host Setup**: Guides you through setting up the required host code on a Raspberry Pi 4B with Raspbian OS. Although, these solutions can also run on similar ARM host platform only instructions for Raspberry Pi are provided. The document includes steps for installing dependencies, configuring CPCd, and starting the necessary services for Zigbee, OpenThread, and Bluetooth.
- **Running and Building Host Applications Locally**: Detailed instructions for running Zigbee, OpenThread and Bluetooth host applications. Explains how to build and run CPCd, Zigbeed, Z3Gateway, OpenThread Border Router, and a Bluetooth host.
- **Running Host Applications - Prebuilt Platforms**: Provides evaluation multiprotocol host application packages for OpenWRT, Debian, and Docker Multiprotocol Solution. These OpenWRT packages are pre-built for Raspberry Pi 4 running OpenWRT version 23.05.3.
- **Configuration Files**: Includes information on configuring CPCd and Zigbeed, how to modify UART or SPI configurations and enabling logging. It also covers the configuration of the OpenThread Border Router (OTBR) and detailed information on debugging techniques and tools available for these components.

###### Pre-Built Host Application Distribution

The following pre-built host application distribution methods support the following architectures:

- Debian Packages (*.deb for debian-bookworm) support architectures: armhf (arm32v7), arm64 (aarch64,arm64v8), i386, amd64 (x86_64)  
  > **Note**: When installing the ot-br-posix .deb file for on 32-bit bookworm systems such as armhf (arm32v7), make sure the dhcpcd package on your target system is higher than the recommended bookworm version of 9.4.1-24, which has a [known startup issue](https://github.com/NetworkConfiguration/dhcpcd/issues/323). The bookworm-backports apt source provides version 10.1.0 which is highly recommended. If not, Silicon Labs recommends updating your dhcpcd package version to at least 9.5.1.
- Multiprotocol Container support architectures: armhf **the Multiprotocol Container will be deprecated in 2025
- OpenWRT packages (*ipk for OpenWRT-23.05.3) support architectures: arm64

Below is a table that shows how each application is delivered:

|Applications|Debian Packages (*.deb)|Multiprotocol Container|OpenWRT Packages (*.ipk)|Built Natively|
|---|---|---|---|---|
|CPCD|X|X|X|X|
|Zigbeed|X|X|X|X|
|ot-br-posix|X|X|X|X|
|ot-cli| |X| |X|
|zigbee_z3_gateway*| |X| |X|
|zigbee_host_xncp_led| |X| |X|
|bt_host_empty| |X| |X|
|cpc-hci-bridge| |X| |X|
|pro-compliance-posix| |X| |X|

##### System Architecture

###### Overview

The system architecture includes various software and hardware components:

- Radio Board: A co-processor image that runs on the EFR32 co-processor. This can be either a RCP (Radio Co-Processor) or an NCP (Networking Co-Processor).
- Co-Processor Communication Daemon (CPCd): A Linux host process that communicates with the co-processor over a Universal Asynchronous Receiver/Transmitter (UART) or Serial Physical Interface (SPI) physical link, and multiplexes protocol streams.
- Zigbeed: A Linux daemon that runs the Zigbee Networking stack and sends and receives Spinel messages to CPCd over a socket when running in RCP mode.
- Zigbee host application (Z3Gateway): An application that communicates with Zigbeed using EmberZNet Serial Protocol / Asynchronous Serial Host (EZSP / ASH) over a virtual serial port, or directly to CPCd in the case of the Zigbee NCP/OpenThread RCP.
- OpenThread host application, like OpenThread Border Router (otbr-agent, OTBR): An application that includes the OpenThread protocol stack, and which connects to CPCd over a socket and operates on a separate PAN from the Zigbee network.
- BlueZ: Official Linux Bluetooth protocol stack that runs on the Linux host and communicates with the Bluetooth LE Controller on the RCP via the Host Controller Interface (HCI). `bluetoothd` is the BlueZ daemon for GAP/GATT applications that must be running to manage the Bluetooth devices.
- Bluetooth host applications: In the Zigbee NCP + BLE NCP case, the `bt_host_empty` application communicates with the NCP via CPCd using BGAPI over CPC. In a multiprotocol RCP case, the BlueZ stack uses a BlueZ CLI tool (`bluetoothctl`) controls the RCP by communicating with the CPCd through the CPC-HCI bridge over a virtual serial port.
- CPC-HCI bridge application: The bt_host_cpc_hci_bridge is a host applicatiion that serves as a communication bridge between the BlueZ host stack and CPCd, and creates a virtual serial interface.

The following figure illustrates the system architecture for the RCP:

![System Architecture for the Multiprotocol RCP](/multiprotocol-solution-linux/0.4/images/figure-1-1-host-rcp-configuration.png)

The following figure illustrates the system architecture for the Zigbee NCP/OpenThread RCP:

![System Architecture for the Zigbee NCP + OpenThread RCP](/multiprotocol-solution-linux/0.4/images/figure-1-2-host-ncp-rcp-configuration.png)

The following figure illustrates the system architecture for the Dynamic Multiprotocol (DMP) Zigbee + Bluetooth NCP.

![System Architecture for the Zigbee NCP + BLE NCP](/multiprotocol-solution-linux/0.4/images/figure-1-3-host-ncp-configuration.png)

###### EFR32 Software

The EFR32 co-processor image comes in four flavors:

1. Multi-PAN 802-15.4 RCP (Radio Co-Processor):  
   - Protocols Run: Openthread + Zigbee  
   - Project Name: rcp-uart-802154.slcp and rcp-spi-802154.slcp  
   - Description: The multipan RCP is based on the OpenThread 802.15.4 RCP with added multi-PAN and CPC support. It has a small flash footprint (~150K) and uses the Spinel protocol to serialize commands. The Spinel messages are further encapsulated by the CPC protocol before being sent over the physical link. Both UART and SPI links are supported. This is illustrated in system-architecture for the Multiprotocol RCP (ignoring the Bluetooth components).
2. Multiprotocol RCP  
   - Protocols Run: Openthread + Zigbee + Bluetooth  
   - Project Name: rcp-uart-802154-blehci.slcp and rcp-spi-802154-blehci.slcp  
   - Description: The multiprotocol RCP adds the Bluetooth Controller and FreeRTOS to the above 802.15.4 RCP. It has a larger flash footprint (~250k). HCI is used to serialize Bluetooth commands over CPC. Both UART and SPI links are supported. See system-architecture for the Multiprotocol RCP.
3. Zigbee NCP (Network Co-Processor) with OpenThread RCP  
   - Protocols Run: Zigbee + Openthread  
   - Project Name: zigbee_ncp-ot_rcp-uart.slcp and zigbee_ncp-ot_rcp-spi.slcp  
   - Description: This configuration runs the Zigbee Networking stack on the EFR32 alongside the OpenThread RCP. The Zigbee application still runs on the host and uses EZSP to send commands to the Zigbee NCP over CPC. Note that this solution does not make use of Zigbeed, because the Zigbee networking stack is running on the EFR32, not on the host. OpenThread runs on the host as in the other cases and uses Spinel over CPC to communicate with the OpenThread RCP. Both UART and SPI links are supported. See system-architecture for the Zigbee NCP + OpenThread RCP. **Due to a larger application footprint, it is recommended to choose parts with sufficient RAM (>64kB).**
4. Zigbee NCP with BLE NCP  
   - Protocols Run: Zigbee + BLE  
   - Project Name: zigbee_ncp-ble_ncp-uart.slcp zigbee_ncp-ble_ncp-spi.slcp  
   - Description: The Zigbee application (Z3GatewayCPC) runs on the Linux host and communicates with the NCP using EZSP over CPC (SPI and UART are both available options). The Bluetooth host app bt_host_empty is compiled with CPC=1 option to enable communication with the DMP NCP over CPC. See system-architecture for the Zigbee NCP + BLE NCP. **Due to a larger application footprint, it is recommended to choose parts with sufficient RAM (>64kB).**

###### Host Software Overview

The Co-Processor Communication Daemon (CPCd) enables users to have multiple stack protocols interact with the radio co-processor over a shared physical link. CPCd is distributed as three components: the daemon binary cpcd; a library libcpc.so that enables C applications to interact with the daemon; and a configuration file cpcd.conf. In CPC, data transfers between processors are segmented in sequential packets. Transfers are guaranteed to be error-free and sent in order. Multiple applications can send or receive on the same endpoint without worrying about collisions. A library libcpc.so is provided to simplify the interaction between the user application and the daemon via Unix domain sockets. Code to interface the OpenThread Spinel driver to libcpc.so is provided as part of this solution. For more information on CPCd, see [https://github.com/SiliconLabs/cpc-daemon/blob/main/readme.md](https://github.com/SiliconLabs/cpc-daemon/blob/main/readme.md).

The Zigbee Daemon (Zigbeed) runs the Zigbee networking stack on the host. It is built with a Spinel adaptation layer that translates between 802.15.4 MAC primitives and Spinel messages and uses the Spinel driver-to-libcpc.so interface code referred to above. Spinel messages from Zigbeed are sent to CPCd where they are encapsulated and forwarded to the RCP. Zigbeed also opens a virtual serial port for communicating with the host application using the EZSP/ASH protocol.

The socat command line utility (also known as zigbee-socat) is used to create two virtual serial ports (PTY) and link them to each other. This allows a Zigbee host application that was built for a Zigbee NCP co-processor to interface with Zigbeed unchanged, simply by supplying the proper device name to it.

Both Zigbeed and the OpenThread stack can connect to CPCd and use the multi-PAN RCP at one time. Spinel messages for each application are labelled with a Spinel Interface ID (IID) which is supplied to the application at startup via the OpenThread Radio URL command line argument. The fact that the RCP is being shared between multiple PANs is transparent to the host applications. The only requirement is that all PANs must operate on the same 802.15.4 channel, the only exception being if Concurrent Listening. Coordination must happen between applications and no mechanism is provided as part of this solution.

Zigbeed stores non-volatile Zigbee stack tokens in a file called host_token.nvm. This allows Zigbeed to retain network information across resets.

For the Zigbee NCP with OpenThread RCP, the Zigbee host app and the OpenThread stack can also connect to CPCd and use the co-processor at the same time. They must coordinate at the application layer to ensure that both operate on the same 802.15.4 channel, or enable Concurrent Listening to operate on different 802.15.4 channels.

###### Concurrent Listening

Concurrent Listening allows the Zigbee and Thread stacks to operate on independent 802.15.4 channels when used with the 802.15.4 RCP. This feature works on xG21, xG24 and xG26 parts only.

It functions by using the RX antenna diversity hardware block to switch extremely rapidly between two channels, approximately every 48 µsec. If a preamble is detected, it stays on the channel until completion of the packet. Because the switching is so rapid, packets on either channel are received. When enabling the concurrent listening feature the PHY performance is slightly degraded. The sensitivity of the IEEE 802.15.4 PHY in this mode drops to around -98 dBm for the xG21 and xG24, xG26.

SiSDK 2024.12.x includes rail_util_ieee802154_fast_channel_switching and rail_util_dma components which allows Zigbee and Thread networks to operate on 2 different radio channels. Note that the concurrent listening feature becomes active only when both networks are up. This feature can result in a decrease in receive sensitivity.

If this feature is not required, these components can be removed. However, if this component is removed it is imperative to make sure that both Zigbee and OpenThread networks are on the same channel.

Because concurrent listening makes use of the antenna diversity hardware block, antenna diversity is not available for the RCP on xG21, xG24, and xG26.

##### Co-Processor Configuration

Silicon Labs offers many different multiprotocol configurations (Multiprotocol RCP, Zigbee NCP with OT RCP, etc.). Depending on your multiprotocol configuration, you will need to choose the correct radio image for the EFR32 device.

###### EFR32 Co-processor Setup: Using Pre-Compiled Images

Simplicity Studio has precompiled demo's of NCP / RCP projects for certain boards along with sample projects. First make sure you have the latest SDK installed in Simplicity Studio.

1. On the toolbar, click **Install**. In the resulting Installation Manager dialog, click **Manage installed packages**.
2. On the Assets tab, turn off **Filter by connected product**. Expand the target release and browse for the file; for example `protocol\openthread\demos\rcp-uart-802154`.
3. Select the .s37 file compatible with your board and click **Install**.  
   ![Install Pre-Built RCP Images](/multiprotocol-solution-linux/0.4/images/figure-2-1-install-pre-built-rcp-images.png)

###### EFR32 Co-processor Setup: Building Radio Image

To build a co-processor image for any board in Simplicity Studio 6, navigate to the Projects panel and select **Create New Project**.

1. On the Example Project Selection, type _concurrent_ as the keyword filter to find RCP projects and _dynamic_ and _NCP_ as the keywords to filter the DMP NCP projects.  
   ![Concurrent RCP Projects](/multiprotocol-solution-linux/0.4/images/figure-2-2-project-selection.png)  
   ![Dynamic NCP Projects](/multiprotocol-solution-linux/0.4/images/figure-2-3-ncp-project-selection.png)
2. From the list of projects, select the appropriate image depending on the desired combination of protocols, and depending on whether your physical link is UART or SPI.
3. Click **Create** and select which device you will be using. Then select finish.  
   At this stage your project will have been generated. Since this is a local instance of the RCP Project you have the flexibility to configure the project to your exact specifications.
4. Once configured, right click on the project and select **Open in IDE**. The project should open up in VS Code. You can then click the build Icon to build the project. Once built, flash the project onto the board using Simplicity Commander. For more information, see the [Simplicity Commander Reference Guide](https://docs.silabs.com/simplicity-commander/latest/simplicity-commander-start/).  
   > **Note**: Multiprotocol, Multi-PAN and CPC support for the RCP is currently only available in Silicon Lab's SDK and not in the OpenThread GitHub repo.

###### Build the Bootloader for Co-Processor

For your application to boot, make sure that you have a bootloader flashed onto the co-processor. Users can create a bootloader project in Simplicity Studio by creating a new project and searching for the project: **Internal Storage Bootloader**. Once the project is generated, users can build the Bootloader Application and flash it onto the RCP.

###### Co-Processor CPC Security Configuration

If you are using a radio image with CPC, it is important to check the CPC Security setting to make sure it matches that set in cpcd.conf. **By default, CPC Security is enabled and is recommended for production**. However, in development it may be easier to have this security parameter disabled. To modify this parameter, go to your SLCP File and search for CPC Security component. If you would like to turn off CPC Security, uninstall the CPC SECURITY component and install CPC SECURITY NONE.

![CPC Security Component](/multiprotocol-solution-linux/0.4/images/figure-2-4-cpc-security-component.png)

###### Configure SPI RCP

Your RCP should have the **Internal Storage Bootloader** flashed onto the device. There is an example project: rcp-spi-802154-blehci project that is configured already for SPI communication.

In your co-processor application project, if you open the SLCP file and navigate to the CPC Secondary - SPI(USART) component, you should be able to manually configure your co-processor SPI pinout.

To verify that the SPI configuration is configured properly and match the bootloader configuration, you can check the Pin Tool of the rcp-spi-802154-blehci project. The following image show an example pinout of rcp-spi-802154-blehci project using a BRD4180B:

![RCP SPI Pin Configuration](/multiprotocol-solution-linux/0.4/images/figure-2-5-pin-tool.jpg)

Furthermore, in the EFR User Guide, you can look for the pinout diagram. Below, the User Guide for BRD4180B (EFR32MG21) shows the direct mappings of the radio pins to that of the WPK EXP headers. This can be useful in cases where you are manually wiring the WPK Expansion headers to your host.

![Expansion Header](/multiprotocol-solution-linux/0.4/images/figure-2-6-exp-header.jpg)

###### Zigbee Child Configuration for Multiprotocol (CMP) Builds

In CMP configurations where Zigbee and OpenThread run together, the Zigbee child count must remain consistent between the Zigbee host and the RCP builds.
If the Zigbee application changes its default child configuration, the same update must be applied to the RCP build, as this value is not synchronized automatically.

##### Prerequisites

This document will go over the hardware and software pre-requisites for building and running a multiprotocol solution locally. Note that this section will be using Raspberry Pi + Raspian OS, however, this is not necessarily required and you can build and run the multiprotocol solution on similar ARM host platforms.

###### Hardware

- Raspberry Pi 4
- SD Card 32GB+
- EFR32 Radio Board (For example BRD4180B)
- Either: WSTK / WPK (BRD4001 / BRD4002)
- PC with Simplicity Studio

> **Note: For SPI configuration, you can use a Pi Hat Expansion Header BRD8016A in replacement for a WSTK/WPK.**

###### Software

- Linux host OS: The following sections use **raspberry Pi OS Lite using Debian Bookworm Version 12**
- Silicon Labs SDK

For GSDK 4.4.0 and up (Including SiSDK) you can use raspberry Pi OS Lite using Debian Version 12 (Bookworm), which uses GCC v12.2.

> **Note: Zigbee Specific: Refer to [Building Zigbeed & Z3Gateway](./building-zigbee-hosts-locally) to find supported Zigbeed platforms.**

###### Install Required Prerequisite Packages

```bash
sudo apt-get update && sudo apt-get upgrade -y
sudo apt-get install cmake git libreadline-dev libmbedtls-dev socat
sudo apt-get install bluetooth bluez bluez-tools rfkill libbluetooth-dev
```

Some versions of operating systems for the Raspberry Pi have enabled a swapfile. This file creates a lot of unnecessary wear on the SD card and causes performance to degrade drastically over time. It is recommended to disable this with the following commands:

```bash
sudo dphys-swapfile swapoff
sudo dphys-swapfile uninstall
sudo apt remove dphys-swapfile 
```

###### SPI Configuration

This section is intended only for users running the co-processor with SPI, UART co-processor configurations can skip this section.

###### Configuring RasperryPi OS to run the SPI Driver (heading level 7)

1. Open /boot/config.txt and add the following configuration:  
   ```bash  
   # Enable SPI0 with 1 chip select  
   [all]  
   dtoverlay=spi0-1cs  
     
   # If the CS pin number must be changed, use and adapt this line **instead**:  
   dtoverlay=spi0-1cs,cs0_pin=8  
     
   # If you need two CS pins, use and adapt this line instead:  
   dtoverlay=spi0-2cs,cs0_pin=8,cs1_pin=7   
   ```
2. Reboot for changes to take effect.  
   ```bash  
   sudo reboot  
   ```
3. Validate that your changes are successful, by running:  
   ```bash  
   ls /dev | grep spi  
   ```

success will yield the following output:

```c
```bash
spidev0.0
```
```

1. To make sure the chip select GPIOs are proper, run: dtc -I fs /sys/firmware/devicetree/base and make sure you can find the following configuration:  
   ```bash  
   spi0_cs_pins{      
       brcm,function = <0x01>;      
       phandle = <0x0f>;  
       brcm,pins = <0x08>;  
   };  
   ```

In this configuration the CS pin is connected to GPIO 8 so this is the correct and expected pin configuration of the RasperryPi host.

###### SPI Pin mapping of Raspberry Pi to Co-Processor (heading level 7)

The following pinout diagram shows the GPIO mappings for a RaspberryPi 4:

![Raspberry Pi 4 Pin Mapping](/multiprotocol-solution-linux/0.4/images/figure-4-raspi-mapping.png)

The following table provides the pin mapping required between the RaspberryPi 4 GPIO, the WSTK Pin out, and the cpcd.conf file:

|Pin Name|Pin Tool Mapping|Radio Board Pin|EXP Header Pin|Raspi Config|
|---|---|---|---|---|
|IRQ|PB00|P4|EXP7|GPIO22 (Pin 15)|
|Bootloader Wake|PB01|P6|EXP9|GPIO24 (Pin 18)|
|MOSI|PC00|P1|EXP4|GPIO10 (Pin 19)|
|MISO|PC01|P3|EXP6|GPIO9 (Pin 21)|
|CLK|PC02|P5|EXP8|GPIO11 (Pin 23)|
|CS|PC03|P7|EXP10|GPIO8 (Pin 24)|
|Reset|EFR32_Reset|F4|NA|GPIO23 (Pin 16)|
|GND|-|-|EXP1|Ground (Pin 20)|

Using the WPK: The SPI signals can be manually wired from the WPK EXP pins to the RaspberryPi GPIO. Please note that these signals listed above are using the default cpcd.conf file configuration. If you are using different GPIO pins you will need to modify the various pin configurations in the cpcd.conf file using your text editor of choice. Specifically the MOSI, MISO, CLK, CS, GND pins are all defined by the SPI driver your RaspberryPi is utilizing.

Using the BRD8016A Wireless Expansion board: you will need to mount the expansion board on the RaspberryPi and make sure that the expansion board switch is set to the high power mode. By default, the SPI configuration on the co-processor and CPCD should match this configuration.

##### Co-Processor Communication Daemon

The Co-Processor Communication Daemon documentation and software can be found in the github repository: [https://github.com/SiliconLabs/cpc-daemon](https://github.com/SiliconLabs/cpc-daemon).

The Co-Processor Daemon (CPCd) enables users to have multiple stack protocols interact with a secondary processor (Either an RCP or NCP) over a shared physical link by using multiple endpoints.

CPCd runs with three main components:

1. Daemon Binary (CPCd)
2. libcpc.so which is a library that enables C applications to interact with the Daemon
3. CPCD.Conf configuration file to configure CPCd

The figure below shows how CPCd runs in the Host application.

![CPCd System Diagram](/multiprotocol-solution-linux/0.4/images/figure-3-1-cpc-system-diagram.png)

###### Building CPCd Locally

> **Note: It is important to use the git tag that corresponds to the SDK version of the RCP. For Example 4.1.0.0 GSDK should map to CPCD git commit 4.1.0.0.**

###### Install and Make CPCD on the System

```bash
git clone https://github.com/SiliconLabs/cpc_daemon.git
cd cpc_daemon
mkdir build
cd build
cmake ../
make
```

By default, the make install places libcpc.so in `/usr/local/lib/arm-linux-gnueabihf` and sl_cpc.h in `/usr/local/include`.

###### Install CPCD and the CPC Library (heading level 7)

```bash
sudo make install
sudo ldconfig
sudo cp ../cpcd.conf /usr/local/etc/.
```

###### CPCD Service File (heading level 7)

If you want CPCD to be included in a system service, create a CPCD Service file named: cpcd.service file. An example of the service file is shown below:

```bash
[Unit]
Description=Cpcd service
StartLimitIntervalSec=0

[Service]
Type=simple
Restart=always
RestartSec=1
User=root
ExecStart=/usr/bin/stdbuf -o0 /usr/local/bin/cpcd
ExecStop=/bin/kill -WINCH ${MAINPID}
PIDFile=/run/cpcd.pid
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=cpcd

[Install]
WantedBy=multi-user.target
```

After moving the cpcd.service file /etc/systemd/system, the cpc daemon can be started from anywhere with:

```bash
sudo systemctl start cpcd
```

###### CPCD Configuration

CPCD allows users to have flexibility in how to connect the secondary (radio) to the host processor. This includes configuring the SPI or UART communication, baudrates, GPIO pins, etc.

###### Modify CPCD.Conf File (heading level 7)

To modify your CPCd configuration, you can modify the file located in `/usr/local/etc/cpcd.conf`.

Some important Parameters to note:

- **bus_type**: This configures CPCd to expect UART or SPI communication with the RCP.
- **disable_encryption**: This parameter is set to false by default. It is very important that the CPCd encryption setting matches the CPC encryption setting on the co-processor (SL_CPC_SERCURITY_ENABLED component). By default, CPCd and the RCP **enable** encryption.
- **stdout_trace**: This parameter can be configured to print more verbose logging.

More information on CPCD Configuration can be found by following the instructions at [https://github.com/SiliconLabs/cpc-daemon/blob/main/readme.md](https://github.com/SiliconLabs/cpc-daemon/blob/main/readme.md).

###### CPCD in a Heavy Traffic Environment (heading level 7)

In heavy traffic environments, it is recommended to increase the host's socket buffers to be able to process all of the incoming network packets. An error message like so typically points to the socket buffers filling up on the host:

```bash
Write() at cpc_interface.cpp:242: Broken pipe
```

To mediate this issue, you can increase the socket buffer sizes in /etc/sysctl.conf.

###### CPCD Configuration for SPI Communication

This section shows how to configure the host and co-processor to run concurrent multiprotocol (CMP) application with a co-processor over SPI.

Some examples on this page were done with Silicon Labs Gecko SDK, the latest software is now being released as part of Silicon Labs Simplicity SDK which can be found: [https://github.com/SiliconLabsSoftware/sisdk-release](https://github.com/SiliconLabsSoftware/sisdk-release).

To check your co-processor configuration, refer to [Co-Processor Configuration for SPI](co-processor-configuration#configure-spi-rcp).

###### Running CPCD with SPI Configuration

In the CPCD.CONF file you will need to change **bus_type: SPI**. If you changed any host GPIO pin outs those will need to be reflected on the CPCD.CONF as well. Furthermore, by default CPCD has the bootloader_recovery_pins_enabled: false. If you would like to make use of using the bootloader wake pin and nReset pin then you will need to enable this parameter to true.

Assuming that CPCD has already been installed on the host you can run this command to set up connection between the Host and the RCP. Note you can see that the bus is now changed to **SPI**. For questions on how to configure the host to run SPI and how to connect the co-processor to the host please reference section 3: Local Host Configuration.

```bash
sudo /usr/local/bin/cpcd -c ~/cpc-daemon/cpcd.conf

WARNING in function 'main' in file /home/pi/cpc-daemon/main.c at line #186 : Running CPCd as 'root' is not recommended. Proceed at your own risk.
[21:55:49:125777] Info : [CPCd v4.3.1.0] [Library API v3] [RCP Protocol v4]
[21:55:49:126074] Info : Git commit: 133b29678b3d0bc7578e098d2f46b4d5bcd2ebb4 / branch:
[21:55:49:126115] Info : Sources hash: 1253de9aeadd9a3091781c41f4487219097dbd92209a7913d0818747b9a3da3c
[21:55:49:126157] WARNING : In function 'main' in file /home/pi/cpc-daemon/main.c at line #186 : Running CPCd as 'root' is not recommended. Proceed at your own risk.
[21:55:49:126288] Info : Reading cli arguments
[21:55:49:126340] Info : /usr/local/bin/cpcd -c /home/pi/cpc-daemon/cpcd.conf
[21:55:49:126968] Info : Reading configuration
[21:55:49:127008] Info :   file_path = /home/pi/cpc-daemon/cpcd.conf
[21:55:49:127037] Info :   instance_name = cpcd_0
[21:55:49:127064] Info :   socket_folder = /dev/shm
[21:55:49:127092] Info :   operation_mode = MODE_NORMAL
[21:55:49:127120] Info :   use_encryption = false
[21:55:49:127146] Info :   binding_key_file = /root/.cpcd/binding.key
[21:55:49:127173] Info :   stdout_tracing = false
[21:55:49:127199] Info :   file_tracing = false
[21:55:49:127225] Info :   lttng_tracing = false
[21:55:49:127251] Info :   enable_frame_trace = false
[21:55:49:127277] Info :   traces_folder = /dev/shm/cpcd-traces
[21:55:49:127303] Info :   bus = SPI
[21:55:49:127329] Info :   spi_file = /dev/spidev0.0
[21:55:49:127355] Info :   spi_bitrate = 1000000
[21:55:49:127381] Info :   spi_irq_chip = gpiochip0
[21:55:49:127408] Info :   spi_irq_pin = 22
[21:55:49:127434] Info :   fu_recovery_pins_enabled = true
[21:55:49:127460] Info :   fu_reset_chip = gpiochip0
[21:55:49:127487] Info :   fu_spi_reset_pin = 23
[21:55:49:127513] Info :   fu_wake_chip = gpiochip0
[21:55:49:127539] Info :   fu_spi_wake_pin = 24
[21:55:49:127565] Info :   fu_connect_to_bootloader = false
[21:55:49:127591] Info :   fu_enter_bootloader = false
[21:55:49:127617] Info :   restart_cpcd = false
[21:55:49:127643] Info :   application_version_validation = false
[21:55:49:127669] Info :   print_secondary_versions_and_exit = false
[21:55:49:127696] Info :   use_noop_keep_alive = false
[21:55:49:127722] Info :   reset_sequence = true
[21:55:49:127748] Info :   stats_interval = 0
[21:55:49:127774] Info :   rlimit_nofile = 2000
[21:55:49:127800] Info : ENCRYPTION IS DISABLED
[21:55:49:127826] Info : Starting daemon in normal mode
[21:55:49:130871] Info : Connecting to Secondary...
[21:55:49:308569] Info : RX capability is 256 bytes
[21:55:49:308614] Info : Connected to Secondary
[21:55:49:309227] Info : Secondary Protocol v4
[21:55:49:310603] Info : Secondary CPC v4.3.1
[21:55:49:311238] Info : Secondary bus bitrate is 0
[21:55:49:311893] Info : Secondary max bus bitrate is 4000000
[21:55:49:311905] Info : SPI bitrate from the config file is lesser than the maximum value returned by the secondary. For performance reason, consider raising it.
[21:55:49:311916] Info : The negotiated SPI bitrate will be 1000000
[21:55:49:312651] Info : Secondary APP vUNDEFINED
[21:55:49:312774] Info : Daemon startup was successful. Waiting for client connections
```

As you can see, the following CPCD configuration parameters will be printed to the console. If the CPCD connection is successful, you will see **Info: Daemon Startup was successful. Waiting for client connections**.

More information and documentation on CPCD can be found at [https://github.com/SiliconLabs/cpc-daemon](https://github.com/SiliconLabs/cpc-daemon).

##### Building the Zigbee Host Locally

This page describes how to build and configure the Zigbee Host Code on a Raspberry pi 4B or a similar ARM host platform. Note that all pre-compiled binaries have been compiled for arm32v7 and arm64v8 architectures.

###### Raspberry Pi Recommended OS

To build the Z3Gateway and Zigbeed, it is important to build the application using the correct GCC toolchain version meant for that release.

For GSDK 4.4.0 and up (Including SiSDK), you can use Raspberry Pi OS Lite using Debian Version 12 (Bookworm), which uses GCC v12.2.

###### Configuring Zigbee Host Software

As mentioned in [System Architecture](system-architecture), there are 3 different ways to configure the Zigbee Host Software depending on the configuration of the Radio Board:

![Zigbee Application Configurations](/multiprotocol-solution-linux/0.4/images/figure-5-1-zigbee-stack-configurations.png)

1. Running Radio Board as a Zigbee RCP:  
   - Z3Gateway (Application Layer)  
   - Zigbeed (Zigbee Networking Layer)  
   - Zigbeed-Socat (Communication Link between Zigbeed and CPCD)  
   - CPCD (Communication Link between Host and the Radio Board)  
   - Radio Board with a Zigbee RCP Application (Zigbee Radio)
2. Running the Radio Board as a Zigbee NCP (without CPCD):  
   - Z3Gateway (Application Layer)  
   - Radio Board with a Zigbee NCP Application (Zigbee Networking layer + Zigbee Radio)
3. Running the Radio Board as a Zigbee NCP (With CPCD):  
   - Z3GatewayCPC (Application Layer with CPC Connection)  
   - CPCD (Communication Link between Host and the Radio Board)  
   - Radio Board with Zigbee NCP Application with CPCD Enabled (Zigbee Networking Layer + Zigbee Radio)

###### Z3Gateway

###### Building Z3Gateway (heading level 7)

Regardless of the Radio Board Configuration, all multiprotocol Zigbee applications require you to build a Zigbee application program on the host. Silicon Labs offers two main Zigbee host applications: Z3Gateway or Z3GatewayCpc, both of which the project generation and build steps are the same.

![Create a new project](/multiprotocol-solution-linux/0.4/images/figure-5-2-project-for-linux-host.png)

1. Create a new Z3Gateway Project: **File > New > Silicon Labs Project Wizard**.
2. Select your Target Device: Linux 32 bit (Or Linux 64 bit depending on the host platform configuration).
3. Select your IDE / Toolchain: Makefile IDE.  
   ![Select Project](/multiprotocol-solution-linux/0.4/images/figure-5-3-project-names.png)
4. Select the Z3Gateway Project Required (Z3Gateway or Z3GatewayCpc).  
   ![Build Project](/multiprotocol-solution-linux/0.4/images/figure-5-4-copy-contents.png)
5. Before clicking **Finish**, it is recommended to select **Copy Contents**. This makes building the application simpler on the Raspberry Pi as it copies all required SDK files (rather than symlinking).
6. After clicking **Finish**, zip up the Z3Gateway Folder and SCP onto the Raspberry pi.  
   ```c  
   scp pi@<IP-Address>:/home/pi ./Z3Gateway.zip  
   ```
7. On the Raspberry Pi, unzip the Z3Gateway folder.
8. CD into the Z3Gateway folder and build the Z3Gateway with the following command: `make -f <Z3Gateway_Project_name>.Makefile`.
9. Upon a successful build, you should see in your folder the directory: `./build/debug/<Z3Gateway_project_name>` Executable.

###### Configuring Z3Gateway to use CPCD (Z3GatewayCPC) (heading level 7)

For Zigbee host applications that are required to use CPC, the Zigbee NCP/OpenThread RCP image should be built using the `zigbee_ezsp_cpc` component in place of the `zigbee_ezsp_uart` or `zigbee_ezsp_spi` component in the Z3GatewayHost SLCP file. This component allows the host app to connect directly to CPCd. The Z3GatewayCpc sample app provided in the GSDK already includes the proper components and can be generated and built as is for this purpose.

###### Zigbeed

###### Supported Zigbeed Platforms (heading level 7)

Zigbeed is supported on the following platforms:

- Debian-Bookworm: arm32v7, arm64v8, amd64 (x86_64), i386
- Tizen-0.1-13.1: arm32, aarch64
- Android 12: aarch64
- openwrt:23.05.3 (as .ipk): aarch64_cortex-a72_gcc-12.3.0_musl

###### Building Zigbeed in Studio (heading level 7)

Depending on your Radio board configuration, you may need to run Zigbeed. Specifically, if you are running a Zigbee RCP with CPCD you **must** build and install zigbeed to run the Zigbee Networking Stack. The steps to build and run zigbeed are quite similar to building Z3Gateway.

Prerequisite: If you are going to build Zigbeed, you **must** build CPCD and install the libcpc.so libraries properly in order for zigbeed to build properly. For instructions on how to build CPCD and install the corresponding libraries, see [Building CPCD Locally](building-cpcd-locally#building-cpcd-locally).

1. Create a new zigbeed project: **File > New > Silicon Labs Project Wizard**.
2. Select your Target Device: Linux 32 bit (Or Linux 64 bit depending on the OS).
3. Select your IDE / Toolchain: Makefile IDE.
4. Select the zigbeed project.
5. Before clicking **Finish**, it is recommended to select **Copy Contents**. This makes building the application simpler on the Raspberry Pi as it copies all required SDK files (rather than symlinking).  
   ![Select Library Component](/multiprotocol-solution-linux/0.4/images/figure-4-5-zigbee-arm32-component.png)
6. _Depending on the underlying Host Running Zigbeed_, you may need to select **Zigbee Arm32 Component**. This will pull in the proper library files to be built in the right host environment. Other components depending on your underlying host could be: zigbee_arm32, zigbee_arm64, or zigbee_x86_64.
7. After clicking **Finish**, zip up the zigbeed folder and SCP onto the Raspberry pi.
8. On the Raspberry Pi, unzip the zigbeed folder.
9. CD into the zigbeed folder and build the zigbeed with the following command: `make -f <zigbeed_project_name>.Makefile`.
10. Upon a successful build, you should see in your folder the directory: `./build/debug/<zigbeed_project_name>` Executable.

###### Building Zigbeed using SLC (heading level 7)

The zigbeed.slcp project file is found in `\<SDK Installation\>/protocol/zigbee/app/projects/zigbeed/zigbeed.slcp`. It includes all the components necessary to build Zigbeed. The Makefile can then be generated using SLC-CLI as follows:

```bash
slc generate -s=../.. –-with=linux_arch_32,zigbee_arm32 -p=app/projects/zigbeed/zigbeed.slcp -d=app/zigbeed/output
```

> **Note**: If you generated the app on your PC but want to build it on a Raspberry Pi, add the -cp option to copy the necessary files, including
> libraries, to the generation directory.

```bash
slc generate -cp -s=../.. –-with=linux_arch_32,zigbee_arm32 -p=app/projects/zigbeed/zigbeed.slcp -d=app/zigbeed/output
```

Copy the generation directory to your target ARM-based system. From within the generation directory, invoke make as follows `make -f zigbeed.Makefile`.

###### Configuring and Installing Zigbeed (heading level 7)

Zigbeed, unlike Z3Gateway also includes a configuration file. To Install the Zigbeed Configuration file, do the following:

1. Navigate inside your SDK and copy over the zigbeed.conf file to the Raspberry pi's directory here: `/usr/local/etc`. The `zigbeed.conf` can be found at [./app/multiprotocol/apps/zigbeed/usr/local/etc/zigbeed.conf](https://github.com/SiliconLabsSoftware/sisdk-release/blob/sisdk-2025.12/multiprotocol_app/app/multiprotocol/apps/zigbeed/usr/local/etc/zigbeed.conf).
2. Copy the zigbeed binary that was built to: `/usr/local/bin/`. Make sure that your zigbeed binary is called `zigbeed`.

###### Zigbeed Service File (heading level 7)

If you want Zigbeed to be included in a system service, create a Zigbeed Service file named: zigbeed.service. An example of the service file is shown below:

```bash
[Unit]
Description=Zigbeed service
StartLimitIntervalSec=0
BindsTo=cpcd.service zigbeed-socat.service
After=cpcd.service zigbeed-socat.service

[Service]
Type=simple
Restart=always
RestartSec=1
User=root
ExecStart=/usr/bin/stdbuf -o0 /usr/local/bin/zigbeed
ExecStop=/bin/kill -WINCH ${MAINPID}
PIDFile=/run/zigbeed.pid
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=zigbeed

[Install]
WantedBy=multi-user.target
```

Once you copy the Zigbeed.service file to `/etc/systemd/system`, this Service will allow your pi to start zigbeed upon reboot and you can start the zigbeed service by calling:

```bash
sudo systemctl start zigbeed
```

###### Understanding Zigbeed.conf File (heading level 7)

- `radio-url=spinel+cpc://cpcd_0?iid=1&iid-list=0`: The Radio URL is a set of parameters passed to the Spinel Driver. This is used for CPCD to connect to Zigbeed.
- `ezsp-interface=/tmp/ttyZigbeeNCP`: This is the URL used to create the Socat Link. This is used for Z3Gateway to connect to Zigbeed.
- `debug-level=5`: This allows you to have Zigbeed Logs from Log level 0 (None) up to level 5 (Debug).
- `verbose=`: Print out Spinel Driver log messages.

###### Configuring Zigbeed Project for Further Customization (heading level 7)

By default, the Zigbeed project includes all the components necessary to build Zigbeed. It also includes the `zigbee_xncp` component by default, which support custom messaging between Zigbeed and the Zigbee host application.

By default, the project also uses the `linux_arch_64` component to build for arm64v8. Select the `linux_arch_32` component to build for arm32v7. Lastly, specify the correct Zigbee library component for the host architecture. Supported components include zigbee_arm32, zigbee_arm64, and zigbee_x86_64.

###### Custom EZSP Messaging in Zigbeed (heading level 7)

The XNCP component (zigbee_xncp) allows custom EZSP messages to be added to Zigbeed in the same way that they can be added to the Zigbee NCP. See [Building a Customized NCP Application with Zigbee EmberZNet 7.x](https://docs.silabs.com/zigbee/latest/customized-ncp-zigbee7/) for more information on XNCP.

To implement custom messages between Zigbeed and the host app, you define and implement the format, parsing, and serialization of the message set. The serialized messages are conveyed between Zigbeed and host as opaque byte strings. This “extensible network co-processor” functionality is provided by the XNCP component. To send a custom message to the host, construct and serialize the message, and then send the resulting byte string to the host using the EmberZNet PRO API function `emberAfPluginXncpSendCustomEzspMessage()`. After enabling the XNCP Library component, the following callbacks are provided for custom 2-way messaging over EZSP:

- sl_zigbee_af_xncp_incoming_custom_frame_cb: Processing of custom incoming serial frames from the EZSP host.
- sl_zigbee_af_incoming_message_cb: Custom processing of received Zigbee application layer messages before passing these (through Incoming Message Callback frames) to the EZSP host. Note that custom outgoing serial frames from Zigbeed to the EZSP host should be provided as response frames to the host in reply to a Callbacks EZSP command or some custom host-to-Zigbeed EZSP command, where they can be handled by the following host-side (such as in zigbee_host_xncp_led host app) callback: void sl_zigbee_ezsp_custom_frame_handler(int8u payloadLength, int8u* payload).

The zigbeed.slcp project includes the source file `protocol/zigbee/app/projects/zigbeed/zigbeed_custom_ezsp_commands.c` that contains an example implementation for custom messaging on the zigbeed xncp side. The example implements a small set of custom messages that can be sent from zigbee_host_xncp_led app, which is supplied prebuilt in the docker container for convenience. This functionality can be tested as follows:

```c
$ zigbee_host_xncp_led -p ttyZigbeeNCP
zigbee_host_xncp_led>custom set-led 1
zigbee_host_xncp_led>custom get-led
custom get-led
Send custom frame: 0x00
Response (state): 1 (OFF)
```

This example from `zigbee_host_xncp_led.slcp` and `zigbeed_custom_ezsp_commands.c` can be followed to implement other custom EZSP messages between a host app and Zigbeed.

###### Configuring Zigbeed-socat (heading level 7)

Zigbeed Socat is used to connect Zigbeed to the Host Application (for Example Z3Gateway).

To use Zigbeed Socat, you need to make sure that you install the socat package.

You can run Zigbeed Socat from the command line by following this command:

```c
socat -x -v pty,link=/dev/ttyZigbeeNCP pty,link=/tmp/ttyZigbeeNCP
```

###### Creating Zigbeed-socat Service File

If you want Zigbeed-socat to be included in a system service, create a Zigbeed Service file named: `zigbeed-socat.service`. An example of the service file is shown below:

```bash
[Unit]
Description=Zigbeed socat helper service
StartLimitIntervalSec=0

[Service]
Type=simple
Restart=always
RestartSec=1
User=root
ExecStart=/usr/bin/socat -v pty,link=/dev/ttyZigbeeNCP pty,link=/tmp/ttyZigbeeNCP
ExecStop=/bin/kill -WINCH ${MAINPID}
PIDFile=/run/zigbeed-socat.pid
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=zigbeed-socat

[Install]
WantedBy=multi-user.target
```

Once you copy the Zigbeed-socat.service file to: `/etc/systemd/system` this Service will allow your pi to start zigbeed-socate upon reboot and you can start the zigbeed service by calling:

```bash
sudo systemctl start zigbeed-socat
```

##### Building OTBR Locally

The purpose of this document is to provide comprehensive steps and guidance on building an OpenThread Border Router (OTBR) that communicates with the Co-Processor Communication Daemon (CPCd). This guide aims to assist developers and engineers in setting up and configuring an OTBR to enhance network communication and management. By following the outlined procedures, you can use the OTBR in a multiprotocol context. For reference on hardware or software requirements, refer to the local host configuration and co-processor configuration.

###### Install OTBR Repository

OpenThread host applications can be built from Silicon Lab's SDK as well as GitHub repositories. Silicon Labs provides a vendor extension to build an OpenThread Linux host application with multi-PAN and CPC support.

Start by cloning the SDK source code on your Raspberry Pi:

```shell
git clone https://github.com/SiliconLabsSoftware/sisdk-release.git

```

```shell
sudo apt-get install bind9
```

For steps on setting up and running CPCd, refer to [Building CPCd locally](building-cpcd-locally).

###### Building OTBR

Before proceeding to build the OTBR, first, ensure:

The OpenThread repo is in ~/simplicity_sdk/openthread_stack/util/third_party/openthread. This folder must be symlinked under ~/simplicity_sdk/openthread_stack/util/third_party/ot-br-posix/third_party/openthread/repo:

```shell
ln -s ~/simplicity_sdk/openthread_stack/util/third_party/openthread/ ~/simplicity_sdk/openthread_stack/util/third_party/ot-br-posix/third_party/openthread/repo
```

> **Note**: The specific flags shown in the following commands are recommended for 1.4 certifiable OTBR, but may require review for your use case.

Now, install the dependencies by running the bootstrap script:

```shell
cd ~/simplicity_sdk/openthread_stack/util/third_party/ot-br-posix/
sudo ./script/bootstrap
```

To configure the OTBR for multi-PAN and CPC support, you can use Silicon Labs specific configuration settings for border-router and ot-cli. Use the special configuration header hosted in the GSDK/SiSDK under `protocol/openthread/platform-abstraction/posix/openthread-core-silabs-posix-config.h`.

```shell
sudo cp ~/simplicity_sdk/openthread/platform-abstraction/posix/openthread-core-silabs-posix-config.h ~/simplicity_sdk/openthread_stack/util/third_party/openthread/src/posix/platform/
```

To build a Thread certifiable otbr-agent, use the following reference commands, which should be run from under the `util/third_party/otbr-posix` directory. Make sure to provide the absolute path to `$SDK_DIR` and `$CPCD_DIR` variables, for example:

```shell
export SDK_DIR=<An absolute path to SDK directory> 
export CPCD_DIR=<An absolute path to CPCD directory> 
```

Run the setup script to complete building OTBR:

```shell
sudo INFRA_IF_NAME=eth0 OTBR_OPTIONS="-DOT_THREAD_VERSION=1.4 -DOT_MULTIPAN_RCP=ON -DCPCD_SOURCE_DIR=$CPCD_DIR -DOT_POSIX_RCP_HDLC_BUS=ON -DOT_POSIX_RCP_SPI_BUS=ON -DOT_POSIX_RCP_VENDOR_BUS=ON -DOT_POSIX_CONFIG_RCP_VENDOR_DEPS_PACKAGE=$SDK_DIR/protocol/openthread/platform-abstraction/posix/posix_vendor_rcp.cmake -DOT_POSIX_CONFIG_RCP_VENDOR_INTERFACE=$SDK_DIR/protocol/openthread/platform-abstraction/posix/cpc_interface.cpp -DOT_PLATFORM_CONFIG=openthread-core-silabs-posix-config.h -DOTBR_DUA_ROUTING=ON -DOTBR_DHCP6_PD=ON" ./script/setup
```

Some of the important Build Flags are the following:

- OT_POSIX_CONFIG_RCP_VENDOR_INTERFACE: This needs to point to vendor interface source files. For Multiprotocol solutions this is the cpc_interface.cpp file.
- OT_POSIX_CONFIG_RCP_VENDOR_DEPS_PACKAGE: This points to the vendor interface CMake package file. For Multiprotocol solutions this is the posix_vendor_rcp.cmake file. (Note in earlier releases this was referred to as SilabsRcpDeps).
- DOT_MULTIPAN_RCP=ON: Compiles the multi-PAN RCP Code.
- OT_POSIX_RCP_VENDOR_BUS=ON
- DOT_PLATFORM_CONFIG: This should point to our openthread-core-silabs-posix-config.h file.
- DOT_THREAD_VERSION: This is the Thread Version. This can be modified for testing Thread 1.4.

OTBR supports a reference implementation of DHCPv6 Prefix Delegation client using the flag DHCPV6_PD_REF=1 which can be passed to the bootstrap and setup commands above. This utilizes PD implementation using external utilities like dhcpcd and radvd. Border router products may have their own implementation of this feature they might choose to use.

To link against the cpc library and header, it is recommended that cpcd is first built and installed. The openthread CMake projects will automatically resolve these dependencies using pkgconfig or by searching in standard installation directories. If cpcd is not installed, the library path and include directory can be specified as CMake arguments (-DCpc_LIBRARY and -DCpc_INCLUDE_DIR respectively), or the root path to the cpc-daemon project can be specified as a CMake argument (-DCPCD_SOURCE_DIR).

**Note**:

- Host applications can be built using 'CMake' only; 'make' is unsupported.
- If you are looking to clear or update your build space, ensure to do the following command `sudo rm -rf build/`.

###### OTBR Configuration Changes for Child and Source Match Tables (heading level 7)

Starting with the 2025.12.0 release, the sizing logic for child and source match tables has been **decoupled**. The number of entries in the source match table now depends on a combination of:

- The configured **maximum number of children** (`OPENTHREAD_CONFIG_MLE_MAX_CHILDREN`)
- The **build type** (single-instance, multi-instance, or CMP)
- The **number of OpenThread network instances or protocol stacks** included in the build (for example, OpenThread and Zigbee in a CMP setup)

The total number of source-match entries is sized to support all child devices across the enabled protocol stacks. In practice, this corresponds to the number of OpenThread children across all OT instances, plus any Zigbee end-device children when CMP is enabled:

```shell
total entries ≈ (MLE children × OT instances) + (Zigbee children, only when CMP is enabled)
```

This can be adjusted at build time using:

```shell
-DOPENTHREAD_CONFIG_MLE_MAX_CHILDREN=10

-DSL_ZIGBEE_MAX_END_DEVICE_CHILDREN=6

-DOPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE=1
-DOPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM=2

```

**Note**: Use SL_ZIGBEE_MAX_END_DEVICE_CHILDREN only if Zigbee runs alongside OT (CMP).

**Note**: Use OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_* only if you’re intentionally running multiple OT instances in the same host.

###### Configuration of the OTBR

Once the OTBR has been successfully built, you need to modify the Radio URL to point to the correct CPCd iid. Note these instructions use: -DOT_MULTIPAN_RCP=ON meaning the OTBR was built for a multi-PAN RCP, this requires us to include both iid and iid-list values. Both Zigbeed and the OpenThread stack can connect to CPCd and use the multi-PAN RCP at one time. Spinel messages for each application are labeled with a Spinel Interface ID (IID), which is supplied to the application at startup via the OpenThread Radio URL command line argument. The fact that the RCP is being shared between multiple PANs is transparent to the host applications.

-Multi-PAN RCP (ex: Zigbee RCP + OT RCP):  `OTBR_AGENT_OPTS="-I wpan0 -B eth0 spinel+cpc://cpcd_0?iid=2&iid-list=0"`. This indicates the OT RCP interface will respond to IID=2.

- Zigbee NCP + OT RCP:  `OTBR_AGENT_OPTS="-I wpan0 -B eth0 spinel+cpc://cpcd_0?iid=0&iid-list=0"`. The Zigbee NCP does not use Spinel thus the multi-PAN interface (IID) can be set to 0.

`/etc/default/otbr-agent` is the configuration file where you can update the Radio URL as follows:

```shell
sudo vi /etc/default/otbr-agent 
OTBR_AGENT_OPTS="-I wpan0 -B eth0 spinel+cpc://cpcd_0?iid=2&iid-list=0"
```

To confirm that the OTBR is correctly running, you can access its CLI utility by executing `sudo ot-ctl`.

###### OTBR Build Commands for SDK Versions

The following table contains the reference OTBR build commands for a few previous releases.

Common steps for following SDK releases:

###### SiSDK 2025.12 - Thread 1.4 (heading level 7)

```shell
sudo INFRA_IF_NAME=eth0 OTBR_OPTIONS="-DOT_THREAD_VERSION=1.4 -DOT_MULTIPAN_RCP=ON -DCPCD_SOURCE_DIR=$CPCD_DIR -DOT_POSIX_RCP_HDLC_BUS=ON -DOT_POSIX_RCP_SPI_BUS=ON -DOT_POSIX_RCP_VENDOR_BUS=ON -DOT_POSIX_CONFIG_RCP_VENDOR_DEPS_PACKAGE=$SDK_DIR/protocol/openthread/platform-abstraction/posix/posix_vendor_rcp.cmake -DOT_POSIX_CONFIG_RCP_VENDOR_INTERFACE=$SDK_DIR/protocol/openthread/platform-abstraction/posix/cpc_interface.cpp -DOT_PLATFORM_CONFIG=openthread-core-silabs-posix-config.h -DOTBR_DUA_ROUTING=ON -DOTBR_DHCP6_PD=ON" ./script/setup
```

###### SiSDK 2025.06 - Thread 1.4 (heading level 7)

```shell
sudo INFRA_IF_NAME=eth0 RELEASE=1 REFERENCE_DEVICE=0 BACKBONE_ROUTER=1 BORDER_ROUTING=1 NAT64=1 DHCPV6_PD=0 WEB_GUI=0 REST_API=0 OTBR_OPTIONS="-DOT_THREAD_VERSION=1.4 -DOT_MULTIPAN_RCP=ON -DCPCD_SOURCE_DIR=$CPCD_DIR -DOT_POSIX_RCP_HDLC_BUS=ON -DOT_POSIX_RCP_SPI_BUS=ON -DOT_POSIX_RCP_VENDOR_BUS=ON -DOT_POSIX_CONFIG_RCP_VENDOR_DEPS_PACKAGE=$SDK_DIR/protocol/openthread/platform-abstraction/posix/posix_vendor_rcp.cmake -DOT_POSIX_CONFIG_RCP_VENDOR_INTERFACE=$SDK_DIR/protocol/openthread/platform-abstraction/posix/cpc_interface.cpp -DOT_PLATFORM_CONFIG=openthread-core-silabs-posix-config.h -DOTBR_DUA_ROUTING=ON -DOTBR_DNSSD_DISCOVERY_PROXY=ON -DOTBR_SRP_ADVERTISING_PROXY=ON -DOTBR_TREL=ON -DOTBR_DHCP6_PD=ON" ./script/setup
```

###### SiSDK 2024.12 - Thread 1.4 (heading level 7)

```shell
sudo INFRA_IF_NAME=eth0 RELEASE=1 REFERENCE_DEVICE=0 BACKBONE_ROUTER=1 BORDER_ROUTING=1 NETWORK_MANAGER=0 NAT64=1 DNS64=1 DHCPV6_PD=0 WEB_GUI=0 REST_API=0 OTBR_OPTIONS="-DOT_THREAD_VERSION=1.4 -DOT_MULTIPAN_RCP=ON -DCPCD_SOURCE_DIR=$CPCD_DIR  -DOT_POSIX_RCP_HDLC_BUS=ON -DOT_POSIX_RCP_SPI_BUS=ON -DOT_POSIX_RCP_VENDOR_BUS=ON -DOT_POSIX_CONFIG_RCP_VENDOR_DEPS_PACKAGE=$GSDK_DIR/protocol/openthread/platform-abstraction/posix/posix_vendor_rcp.cmake -DOT_POSIX_CONFIG_RCP_VENDOR_INTERFACE=$GSDK_DIR/protocol/openthread/platform-abstraction/posix/cpc_interface.cpp -DOT_PLATFORM_CONFIG=openthread-core-silabs-posix-config.h -DOTBR_DUA_ROUTING=ON -DOTBR_DNSSD_DISCOVERY_PROXY=ON -DOTBR_SRP_ADVERTISING_PROXY=ON -DOTBR_TREL=ON -DOTBR_DHCP6_PD=ON" ./script/setup
```

###### SiSDK 2024.06 - Thread 1.3 (heading level 7)

```shell
sudo INFRA_IF_NAME=eth0 RELEASE=1 REFERENCE_DEVICE=0 BACKBONE_ROUTER=1 BORDER_ROUTING=1 NETWORK_MANAGER=0 NAT64=1 DNS64=1 DHCPV6_PD=0 WEB_GUI=0 REST_API=0 OTBR_OPTIONS="-DOT_THREAD_VERSION=1.3 -DOT_MULTIPAN_RCP=ON -DCPCD_SOURCE_DIR=$CPCD_DIR -DOT_POSIX_RCP_HDLC_BUS=ON -DOT_POSIX_RCP_SPI_BUS=ON -DOT_POSIX_RCP_VENDOR_BUS=ON -DOT_POSIX_CONFIG_RCP_VENDOR_DEPS_PACKAGE=$SDK_DIR/protocol/openthread/platform-abstraction/posix/posix_vendor_rcp.cmake -DOT_POSIX_CONFIG_RCP_VENDOR_INTERFACE=$SDK_DIR/protocol/openthread/platform-abstraction/posix/cpc_interface.cpp -DOT_PLATFORM_CONFIG=openthread-core-silabs-posix-config.h -DOTBR_DUA_ROUTING=ON -DOTBR_DNSSD_DISCOVERY_PROXY=ON -DOTBR_SRP_ADVERTISING_PROXY=ON -DOTBR_TREL=ON -DOTBR_DHCP6_PD=ON" ./script/setup
```

###### GSDK v4.4.3 (heading level 7)

```shell
sudo INFRA_IF_NAME=eth0 RELEASE=1 REFERENCE_DEVICE=1 BACKBONE_ROUTER=1 BORDER_ROUTING=1 NAT64=1 DNS64=1 OTBR_OPTIONS="-DOT_THREAD_VERSION=1.3 -DOT_MULTIPAN_RCP=ON -DCPCD_SOURCE_DIR=$CPCD_DIR -DOT_POSIX_RCP_VENDOR_BUS=ON -DOT_POSIX_CONFIG_RCP_VENDOR_DEPS_PACKAGE=$GSDK_DIR/protocol/openthread/platform-abstraction/posix/posix_vendor_rcp.cmake -DOT_POSIX_CONFIG_RCP_VENDOR_INTERFACE=$GSDK_DIR/protocol/openthread/platform-abstraction/posix/cpc_interface.cpp -DOT_PROJECT_CONFIG=openthread-core-silabs-posix-config.h -DOTBR_DUA_ROUTING=ON -DOTBR_DNSSD_DISCOVERY_PROXY=ON -DOTBR_SRP_ADVERTISING_PROXY=ON" ./script/setup
```

###### GSDK v4.4.2 (heading level 7)

```shell
sudo INFRA_IF_NAME=eth0 RELEASE=1 REFERENCE_DEVICE=1 BACKBONE_ROUTER=1 BORDER_ROUTING=1 NAT64=1 DNS64=1 OTBR_OPTIONS="-DOT_THREAD_VERSION=1.3 -DOT_MULTIPAN_RCP=ON -DCPCD_SOURCE_DIR=$CPCD_DIR -DOT_POSIX_CONFIG_RCP_BUS=VENDOR -DOT_POSIX_CONFIG_RCP_VENDOR_DEPS_PACKAGE=$GSDK_DIR/protocol/openthread/platform-abstraction/posix/posix_vendor_rcp.cmake -DOT_POSIX_CONFIG_RCP_VENDOR_INTERFACE=$GSDK_DIR/protocol/openthread/platform-abstraction/posix/cpc_interface.cpp -DOT_CONFIG=openthread-core-silabs-posix-config.h -DOTBR_DUA_ROUTING=ON -DOTBR_DNSSD_DISCOVERY_PROXY=ON -DOTBR_SRP_ADVERTISING_PROXY=ON" ./script/setup
```

###### GSDK v4.4.1, v4.4.0 (heading level 7)

```shell
sudo INFRA_IF_NAME=eth0 RELEASE=1 REFERENCE_DEVICE=1 BACKBONE_ROUTER=1 BORDER_ROUTING=1 NAT64=1 DNS64=1 OTBR_OPTIONS="-DOT_THREAD_VERSION=1.3 -DOT_MULTIPAN_RCP=ON -DCPCD_SOURCE_DIR=$CPCD_DIR -DOT_POSIX_RCP_VENDOR_BUS=ON -DOT_POSIX_CONFIG_RCP_VENDOR_DEPS_PACKAGE=$GSDK_DIR/protocol/openthread/platform-abstraction/posix/posix_vendor_rcp.cmake -DOT_POSIX_CONFIG_RCP_VENDOR_INTERFACE=$GSDK_DIR/protocol/openthread/platform-abstraction/posix/cpc_interface.cpp -DOT_CONFIG=openthread-core-silabs-posix-config.h -DOTBR_DUA_ROUTING=ON -DOTBR_DNSSD_DISCOVERY_PROXY=ON -DOTBR_SRP_ADVERTISING_PROXY=ON" ./script/setup
```

###### GSDK v4.3.3, v4.3.2 (heading level 7)

```shell
sudo INFRA_IF_NAME=eth0 RELEASE=1 REFERENCE_DEVICE=1 BACKBONE_ROUTER=1 BORDER_ROUTING=1 NAT64=1 DNS64=1 OTBR_OPTIONS="-DOT_THREAD_VERSION=1.3 -DOT_MULTIPAN_RCP=ON -DCMAKE_MODULE_PATH=$GSDK_DIR/protocol/openthread/platform-abstraction/posix -DCPCD_SOURCE_DIR=$CPCD_DIR -DOT_POSIX_CONFIG_RCP_BUS=VENDOR -DOT_POSIX_CONFIG_RCP_VENDOR_DEPS_PACKAGE=SilabsRcpDeps -DOT_POSIX_CONFIG_RCP_VENDOR_INTERFACE=$GSDK_DIR/protocol/openthread/platform-abstraction/posix/cpc_interface.cpp -DOT_CONFIG=openthread-core-silabs-posix-config.h -DOTBR_DUA_ROUTING=ON -DOTBR_DNSSD_DISCOVERY_PROXY=ON -DOTBR_SRP_ADVERTISING_PROXY=ON" ./script/setup 
```

###### Building OT-CLI

This command will build the posix ot-cli app using CMake (which is a different OT Host application than the OTBR). The command to build the posix ot-cli from the `util/third_party/openthread` directory of the SDK using CMake:

```shell
sudo ./script/cmake-build posix -DOT_MULTIPAN_RCP=ON -DOT_POSIX_RCP_HDLC_BUS=ON -DOT_POSIX_RCP_SPI_BUS=ON -DOT_POSIX_RCP_VENDOR_BUS=ON -DCPCD_SOURCE_DIR=$CPCD_DIR -DOT_POSIX_CONFIG_RCP_VENDOR_INTERFACE=$GSDK_DIR/protocol/openthread/platform-abstraction/posix/cpc_interface.cpp -DOT_PLATFORM_CONFIG=openthread-core-silabs-posix-config.h -DOT_POSIX_CONFIG_RCP_VENDOR_DEPS_PACKAGE=$SDK_DIR/protocol/openthread/platform-abstraction/posix/posix_vendor_rcp.cmake
```

Build ‘otbr-agent’ and ‘ot-cli’ host applications using Silicon Labs SDK artifacts provides OT_PLATFORM_CONFIG, CPCD_SOURCE_DIR, and DOT_POSIX_CONFIG_RCP_VENDOR_INTERFACE configurations.

##### Building the Bluetooth Hosts Locally

This page describes how to configure and build the Bluetooth Host applications on a Raspberry pi 4B or a similar ARM host platform.

###### Prerequisites

Clone the Silicon Labs SDK source code on the Raspberry Pi:

git clone [https://github.com/SiliconLabsSoftware/sisdk-release.git](https://github.com/SiliconLabsSoftware/sisdk-release.git)

###### Bluetooth Host Configuration for Multiprotocol NCP

The default Bluetooth NCP host application (bt_host_empty) is configured to communicate with the NCP target using BGAPI via UART. In order to enable CPC, the application needs to be built with the following command line option:

```bash
make CPC=1
```

The makefile for `bt_host_empty` is in the following SiSDK directory: <path_to_sdk>/bluetooth_le_app/example_host/bt_host_empty.

> **Note**: CPCd must first be installed on the system before building the host application.

###### Bluetooth Host Configuration for Multiprotocol RCP

###### BlueZ - Official Linux Bluetooth Stack (heading level 7)

In newer versions of Raspberry Pi OS, BlueZ is already installed by default and the bluetooth service is enabled.

- Check the BlueZ stack version using the command `bluetoothd --version` and if not the latest, download the latest version from [https://www.kernel.org/pub/linux/bluetooth/](https://www.kernel.org/pub/linux/bluetooth/) and follow the instructions in the README file.
- The bluetooth service status can be checked with the command `systemctl status bluetooth`, which should give the following output:

```bash
● bluetooth.service - Bluetooth service
     Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2024-10-15 20:24:27 BST; 4 weeks 2 days ago
       Docs: man:bluetoothd(8)
   Main PID: 932 (bluetoothd)
     Status: "Running"
      Tasks: 1 (limit: 3933)
        CPU: 5.179s
     CGroup: /system.slice/bluetooth.service
             └─932 /usr/libexec/bluetooth/bluetoothd
```

If the bluetooth service is disabled, manually start the service by running `systemctl start bluetooth`.

*As of Nov 2024, the latest BlueZ version is 5.79.

###### CPC HCI Bridge (heading level 7)

The CPC-HCI bridge application connects to the CPCd and exposes a virtual serial device on the Linux host in `/dev/pts`. The source code and makefile are located in the SiSDK at `<path_to_sdk>/app/bluetooth/example/example_host/bt_host_cpc_hci_bridge`. Go to the `bt_host_cpc_hci_bridge` folder and run the following command to build the CPC-HCI bridge application:

```bash
make
```

##### Running CPCd, Zigbeed, Z3Gateway, OTBR Concurrently on Raspbian

The purpose of this guide is to provide a hands-on example of running the previously discussed multiprotocol processes concurrently. This guide aims to demonstrate the process of setting up and configuring the Co-Processor Communication Daemon (CPCd), Zigbee Daemon (zigbeed), Zigbee Host application (Z3Gateway), and the OpenThread Border Router (OTBR). This guide assumes that the previous sections: _Building CPCd on Raspbian_, _Building zigbeed and Z3Gateway on Raspbian (UART)_, _Building Zigbee host_, and _Building OTBR on Raspbian_ have been completed.

###### Running CPCd

This step must be done before before running the Zigbee or OpenThread processes. CPCd can either be run from _systemd_ or via the binary in the build folder, this guide will cover both. Note that by default CPCd encryption is turned on. This means you will have an additional CPCd binding step. **The steps below have disabled CPC Security on both CPCd and the RCP**.

###### Running CPCd from the Binary (heading level 7)

This method runs the CPCd binary directly in the terminal. If logging is enabled, debug logs will appear as shown below. The log level can be adjusted in the `cpcd.conf` file.

```bash
From the cpcd/build/debug/out folder:

./cpcd -c [/path_to_cpcd_conf] -[options]

or

cpcd/build/debug/out/cpcd -c [/path_to_cpcd_conf] -[options]
```

**If you are running CPCd with security enabled you will first need to run:**

```bash
./cpcd -c cpcd.conf --bind ecdh
```

###### Running CPCd as a Service in the Background (heading level 7)

If you chose to run CPCd as a service in the background, you can create a `cpcd.service` file and move it to the following location: `/etc/systemd/system`. Now with this file installed, you can simply start CPCd by running the following command:

```bash
systemctl start cpcd
```

To view the status of this process:

```bash
systemctl status cpcd
```

Should yield an output like the following:

```bash
● cpcd.service - Cpcd service
     Loaded: loaded (/etc/systemd/system/cpcd.service; disabled; vendor preset: enabled)
     Active: active (running) since Tue 2024-09-23 20:31:18 UTC; 4s ago
   Main PID: 1170 (cpcd)
      Tasks: 6 (limit: 9238)
        CPU: 32ms
     CGroup: /system.slice/cpcd.service
             └─1170 /usr/local/bin/cpcd
```

###### CPCd Log Example (heading level 7)

```bash
[2024-09-20T19:28:38.039762Z] Info : [CPCd v4.5.0.0] [Library API v3] [RCP Protocol v5]
[2024-09-20T19:28:38.040027Z] Info : Git commit: f7cb761e18f338f09b4a59f07f6458aefb0a0f05 / branch: refs/heads/main
[2024-09-20T19:28:38.040047Z] Info : Reading cli arguments
[2024-09-20T19:28:38.040067Z] Info : ./cpcd 
[2024-09-20T19:28:38.040999Z] Info : Reading configuration
[2024-09-20T19:28:38.041019Z] Info :   file_path = /usr/local/etc/cpcd.conf
[2024-09-20T19:28:38.041029Z] Info :   instance_name = cpcd_0
[2024-09-20T19:28:38.041038Z] Info :   socket_folder = /dev/shm
[2024-09-20T19:28:38.041048Z] Info :   operation_mode = MODE_NORMAL
[2024-09-20T19:28:38.041057Z] Info :   use_encryption = false
[2024-09-20T19:28:38.041066Z] Info :   binding_key_file = /home/orkevlar/.cpcd/binding.key
[2024-09-20T19:28:38.041075Z] Info :   stdout_tracing = false
[2024-09-20T19:28:38.041084Z] Info :   file_tracing = false
[2024-09-20T19:28:38.041092Z] Info :   lttng_tracing = false
[2024-09-20T19:28:38.041100Z] Info :   enable_frame_trace = false
[2024-09-20T19:28:38.041108Z] Info :   traces_folder = /dev/shm/cpcd-traces
[2024-09-20T19:28:38.041117Z] Info :   bus = UART
[2024-09-20T19:28:38.041125Z] Info :   uart_baudrate = 115200
[2024-09-20T19:28:38.041134Z] Info :   uart_hardflow = true
[2024-09-20T19:28:38.041142Z] Info :   uart_file = /dev/ttyACM0
[2024-09-20T19:28:38.041151Z] Info :   fwu_recovery_pins_enabled = false
[2024-09-20T19:28:38.041160Z] Info :   fwu_connect_to_bootloader = false
[2024-09-20T19:28:38.041168Z] Info :   fwu_enter_bootloader = false
[2024-09-20T19:28:38.041177Z] Info :   restart_cpcd = false
[2024-09-20T19:28:38.041185Z] Info :   application_version_validation = false
[2024-09-20T19:28:38.041194Z] Info :   print_secondary_versions_and_exit = false
[2024-09-20T19:28:38.041202Z] Info :   use_noop_keep_alive = false
[2024-09-20T19:28:38.041210Z] Info :   reset_sequence = true
[2024-09-20T19:28:38.041218Z] Info :   stats_interval = 0
[2024-09-20T19:28:38.041226Z] Info :   rlimit_nofile = 2000
[2024-09-20T19:28:38.041236Z] Info : ENCRYPTION IS DISABLED 
[2024-09-20T19:28:38.052580Z] Info : Starting daemon in normal mode
[2024-09-20T19:28:38.063960Z] Info : Connecting to Secondary...
[2024-09-20T19:28:38.230299Z] Info : RX capability is 256 bytes
[2024-09-20T19:28:38.230329Z] Info : Connected to Secondary
[2024-09-20T19:28:38.236297Z] Info : Secondary Protocol v5
[2024-09-20T19:28:38.249294Z] Info : Secondary CPC v4.5.0
[2024-09-20T19:28:38.261343Z] Info : Secondary bus bitrate is 115200
[2024-09-20T19:28:38.273286Z] Info : Secondary APP vUNDEFINED
[2024-09-20T19:28:38.273546Z] Info : Daemon startup was successful. Waiting for client connections
```

When either the `otbr-agent` or `zigbeed` are connected to `cpcd`, the following will appear in the `cpcd` logs:

```bash
[2024-09-23T18:09:26.299013Z] Info : New client connection using library v4.5.2.0
[2024-09-23T18:09:26.305764Z] Info : Opened connection socket for ep#12
[2024-09-23T18:09:26.313604Z] Info : Endpoint socket #12: Client connected. 1 connection(s)
```

When both are connected, the logs will show:

```bash
[2024-09-23T20:01:07.851455Z] Info : New client connection using library v4.5.2.0
[2024-09-23T20:01:07.858154Z] Info : Endpoint socket #12: Client connected. 2 connection(s)
```

When the CPC-HCI bridge is connected to CPCd, the log will show:

```bash
[2024-11-15T20:43:46.043660Z] Info : New client connection using library v4.5.2.0
[2024-11-15T20:43:46.056109Z] Info : Opened connection socket for ep#14
[2024-11-15T20:43:46.065940Z] Info : Endpoint socket #14: Client connected. 1 connection(s)
```

###### Running Zigbeed

This must be done **after** CPCd is running.

###### Running Socat (heading level 7)

You can run Socat from the command line by following this command:

```bash
socat -x -v pty,link=/dev/ttyZigbeeNCP pty,link=/tmp/ttyZigbeeNCP
```

This command creates a virtual serial port for Zigbee Communication.

###### Running Socat as a Service in the Background

Similarly, Socat can be run as a service with `systemd`.

This service requires the `zigbeed-socat.service` file to be created and copied into the `/etc/systemd/system/..` directory. Now you can start zigbeed-socat with:

```bash
sudo systemctl start zigbeed-socat
```

To view the status of this process:

```bash
systemctl status zigbeed-socat
```

Should yield an output like the following:

```bash
● zigbeed-socat.service - Zigbeed socat helper service
     Loaded: loaded (/etc/systemd/system/zigbeed-socat.service; disabled; vendor preset: enabled)
     Active: active (running) since Tue 2024-09-23 20:31:18 UTC; 4s ago
   Main PID: 1171 (socat)
      Tasks: 1 (limit: 9238)
        CPU: 14ms
     CGroup: /system.slice/zigbeed-socat.service
             └─1171 /usr/bin/socat -v pty,link=/dev/ttyZigbeeNCP pty,link=/tmp/ttyZigbeeNCP
```

###### Running Zigbeed from the Binary (heading level 7)

This method runs the Zigbeed binary directly in the terminal. If logging is enabled, debug logs will appear as shown below. The log level can be adjusted in the `zigbeed.conf` file.

```bash
From the zigbeed/build/debug/out folder:

./zigbeed -[options]

or

zigbeed/build/debug/out/zigbeed -[options]
```

The zigbeed logs will appear in the terminal as shown below:

```bash
By using this software, you are agreeing to the Silicon Labs MSLA found at https://www.silabs.com/about-us/legal/master-software-license-agreement.
zigbeed[5479]: [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:0, cmd:PROP_VALUE_IS, key:LAST_STATUS, status:RESET_SOFTWARE
zigbeed[5479]: [I] P-SpinelDrive-: co-processor reset: RESET_SOFTWARE
zigbeed[5479]: [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:1, tid:0, cmd:RESET
zigbeed[5479]: [D] P-SpinelDrive-: Waiting response: key=0
zigbeed[5479]: [C] P-SpinelDrive-: Software reset co-processor successfully
zigbeed[5479]: [D] P-SpinelDrive-: Waiting response: key=1
zigbeed[5479]: [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:1, tid:1, cmd:PROP_VALUE_IS, key:PROTOCOL_VERSION, major:4, minor:3
zigbeed[5479]: [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:0, cmd:PROP_VALUE_IS, key:LAST_STATUS, status:RESET_SOFTWARE
zigbeed[5479]: [I] P-SpinelDrive-: co-processor reset: RESET_SOFTWARE
zigbeed[5479]: [D] P-SpinelDrive-: Waiting response: key=2
zigbeed[5479]: [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:1, tid:1, cmd:PROP_VALUE_IS, key:NCP_VERSION, version:SL-OPENTHREAD/2.5.1.0_GitHub-1fceb225b;
zigbeed[5479]: [D] P-SpinelDrive-: Waiting response: key=5
zigbeed[5479]: [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:1, tid:1, cmd:PROP_VALUE_IS, key:CAPS, caps:COUNTERS UNSOL_UPDATE_FILTER 802_15_4_2450MHZ_OQP
zigbeed[5479]: [D] Platform------: instance init:0xcd178 - iid = 1
zigbeed[5479]: [D] P-RadioSpinel-: Wait response: tid=1 key=8
zigbeed[5479]: [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:1, tid:1, cmd:PROP_VALUE_IS, key:HWADDR, eui64:90395efffee405e1
zigbeed[5479]: [D] P-RadioSpinel-: RCP supports crash dump logging. Requesting crash dump.
zigbeed[5479]: [D] P-RadioSpinel-: Wait response: tid=2 key=178
zigbeed[5479]: [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:1, tid:2, cmd:PROP_VALUE_IS, key:LAST_STATUS, status:OK
zigbeed[5479]: [D] P-RadioSpinel-: Wait response: tid=3 key=176
zigbeed[5479]: [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:1, tid:3, cmd:PROP_VALUE_IS, key:RCP_API_VERSION, version:10
zigbeed[5479]: [D] P-RadioSpinel-: Wait response: tid=4 key=4619
zigbeed[5479]: [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:1, tid:4, cmd:PROP_VALUE_IS, key:RADIO_CAPS, caps:255
zigbeed[5479]: Zigbeed started
zigbeed[5479]: RCP version: SL-OPENTHREAD/2.5.1.0_GitHub-1fceb225b; EFR32; Sep 20 2024 15:27:16
zigbeed[5479]: Zigbeed Version: GSDK 8.0.1 - Jul 22 2024 - 21:58:48
```

To record the zigbeed logs to a file, you can use a command like this:

```bash
./zigbeed 2>&1  | sudo tee [path-to-out-file]
```

###### Running Zigbeed as a Service in the Background (heading level 7)

Again, running Zigbeed as a service in the background will require you to install the `zigbeed.service` file into `/etc/systemd/system` directory.

```bash
systemctl start zigbeed
```

To view the status of this process:

```bash
systemctl status zigbeed
```

Should yield an output like the following:

```bash
● zigbeed.service - Zigbeed service
     Loaded: loaded (/etc/systemd/system/zigbeed.service; disabled; vendor preset: enabled)
     Active: active (running) since Tue 2024-09-23 20:31:18 UTC; 4s ago
   Main PID: 1173 (zigbeed)
      Tasks: 1 (limit: 9238)
        CPU: 17ms
     CGroup: /system.slice/zigbeed.service
             └─1173 /usr/local/bin/zigbeed
```

###### Running the Zigbee Host Application

This must be done **after** Zigbeed has been started. The Zigbee Host application is run directly in the terminal via the binary in the build folder.

```bash
From the zigbee_z3_gateway/build/debug/out folder:

./zigbee_z3_gateway -n 0 -p /dev/ttyZigbeeNCP

or

zigbee_z3_gateway/build/debug/out/z3gateway -n 0 -p /dev/ttyZigbeeNCP
```

Z3Gateway CLI

```bash
Reset info: 11 (SOFTWARE)
ezsp ver 0x0E stack type 0x02 stack ver. [8.0.1 GA build 270]
Ezsp Config: set address table size to 0x0002:Success: set
Ezsp Config: set TC addr cache to 0x0002:Success: set
Ezsp Config: set MAC indirect TX timeout to 0x1E00:Success: set
Ezsp Config: set max hops to 0x001E:Success: set
Ezsp Config: set tx power mode to 0x8000:Success: set
Ezsp Config: set supported networks to 0x0001:Success: set
Ezsp Config: set stack profile to 0x0002:Success: set
Ezsp Config: set security level to 0x0005:Success: set
Ezsp Value : set end device keep alive support mode to 0x00000003:Success: set
Ezsp Policy: set binding modify to "allow for valid endpoints & clusters only":Success: set
Ezsp Policy: set message content in msgSent to "return":Success: set
Ezsp Value : set maximum incoming transfer size to 0x00000052:Success: set
Ezsp Value : set maximum outgoing transfer size to 0x00000052:Success: set
Ezsp Value : set default timeout for transient device table to 0x00002710:Success: set
Ezsp Config: set binding table size to 0x0002:Success: set
Ezsp Config: set key table size to 0x0004:Success: set
Ezsp Config: set max end device children to 0x0006:Success: set
Ezsp Config: set aps unicast message count to 0x000A:Success: set
Ezsp Config: set broadcast table size to 0x000F:Success: set
Ezsp Config: set neighbor table size to 0x0010:Success: set
Ezsp Config: set end device poll timeout to 0x0008:Success: set
Ezsp Config: set zll group addresses to 0x0000:Success: set
Ezsp Config: set zll rssi threshold to 0xFFD8:Success: set
Ezsp Config: set transient key timeout to 0x012C:Success: set
Ezsp Config: set retry size to 0x0010:Success: set
Ezsp Endpoint 1 added, profile 0x0104, in clusters: 8, out clusters 17
Ezsp Endpoint 242 added, profile 0xA1E0, in clusters: 0, out clusters 1
Starting identifying on endpoint 0x01, identify time is 0 sec
Stopping identifying on endpoint 0x01
No endpoints identifying; stopping identification feedback.
Found 0 files
zigbee_z3_gateway>
```

###### Running the OTBR

In the Multiprotocol context, this must be done **after** CPCd is running. The `otbr-agent` can be run as a service with `systemd` or directly in the terminal via the binary.

###### Running the otbr-agent Binary (heading level 7)

The binary is located in: `~/simplicity_sdk/util/third_party/ot-br-posix/build/otbr/src/agent`. The following command will start the otbr-agent, and logs will appear in the terminal window.

```bash
sudo ./otbr-agent -d 6 -v -I wpan0 -B eth0 'spinel+cpc://cpcd_0?iid=2&iid-list=0'
```

If the Radio URL is **not** configured properly, the OTBR will respond with the following error message:

```bash
Aug 14 14:18:21 raspberrypi otbr-agent[8037]: 49d.19:21:57.018 [C] Platform------: Init() at radio.cpp:107: InvalidArgument
Aug 14 14:18:21 raspberrypi systemd[1]: otbr-agent.service: Main process exited, code=exited, status=2/INVALIDARGUMENT
```

The OTBR logs will appear similar to the following if it is running properly:

```bash
otbr-agent[1892]: [NOTE]-AGENT---: Running 0.3.0-v2024.6.1-0-1-g36e12f01
otbr-agent[1892]: [NOTE]-AGENT---: Thread version: 1.4
otbr-agent[1892]: [NOTE]-AGENT---: Thread interface: wpan0
otbr-agent[1892]: [NOTE]-AGENT---: Radio URL: spinel+cpc://cpcd_0?iid=2&iid-list=0
otbr-agent[1892]: [NOTE]-ILS-----: Infra link selected: eth0
otbr-agent[1892]: [INFO]-NCP-----: OpenThread log level changed to 4
otbr-agent[1892]: 49d.17:11:58.417 [I] P-SpinelDrive-: co-processor reset: RESET_SOFTWARE
otbr-agent[1892]: 49d.17:12:00.447 [C] P-SpinelDrive-: Software reset co-processor successfully
otbr-agent[1892]: 49d.17:12:00.504 [I] P-Netif-------: Sent request#1 to set addr_gen_mode to 1
otbr-agent[1892]: 00:00:00.087 [I] ChildSupervsn-: Timeout: 0 -> 190
otbr-agent[1892]: 00:00:00.088 [I] TrelInterface-: Enabled interface, local port:60358
otbr-agent[1892]: 00:00:00.088 [I] RoutingManager: Initializing - InfraIfIndex:2
otbr-agent[1892]: 00:00:00.089 [I] InfraIf-------: Init infra netif 2
otbr-agent[1892]: 00:00:00.089 [I] Settings------: Read BrUlaPrefix fd8b:b68:2457::/48
otbr-agent[1892]: 00:00:00.089 [N] RoutingManager: BR ULA prefix: fd8b:b68:2457::/48 (loaded)
otbr-agent[1892]: 00:00:00.089 [I] RoutingManager: Generated local OMR prefix: fd8b:b68:2457:1::/64
otbr-agent[1892]: 00:00:00.089 [I] RoutingManager: Generated local NAT64 prefix: fd8b:b68:2457:2:0:0::/96
otbr-agent[1892]: 00:00:00.089 [N] RoutingManager: Local on-link prefix: fdde:ad00:beef:cafe::/64
otbr-agent[1892]: 00:00:00.089 [I] InfraIf-------: State changed: NOT RUNNING -> RUNNING
otbr-agent[1892]: 00:00:00.089 [I] RoutingManager: Enabling
otbr-agent[1892]: 00:00:00.089 [I] Nat64---------: IPv4 CIDR for NAT64: 192.168.255.0/24 (actual address pool: 192.168.255.1 - 192.168.255.254, 254 addresses)
otbr-agent[1892]: 00:00:00.089 [I] Nat64---------: NAT64 translator is now Disabled
otbr-agent[1892]: 00:00:00.090 [I] P-Resolver----: Got nameserver #0: 192.168.0.1
otbr-agent[1892]: [INFO]-UTILS---: Set state callback: OK
otbr-agent[1892]: 00:00:00.090 [I] Nat64---------: NAT64 translator is now NotRunning
otbr-agent[1892]: [INFO]-BA------: Start Thread Border Agent
otbr-agent[1892]: [INFO]-BA------: Publish meshcop service OpenThread BorderRouter #102C._meshcop._udp.local.
otbr-agent[1892]: 00:00:00.091 [I] Settings------: Read BorderAgentId {id:27497e430c9fd5c4b52cb337e06f77a7}
otbr-agent[1892]: [INFO]-MDNS----: Registering service OpenThread BorderRouter #102C._meshcop._udp
otbr-agent[1892]: 00:00:00.097 [I] BbrLocal------: Add Domain Prefix: ::/0, NotFound
otbr-agent[1892]: 00:00:00.098 [I] BbrLocal------: Add BBR Service: seqno (73), delay (5s), timeout (3600s), InvalidState
otbr-agent[1892]: [INFO]-ADPROXY-: Started
otbr-agent[1892]: [INFO]-DPROXY--: Started
otbr-agent[1892]: [INFO]-APP-----: Thread Border Router started on AIL eth0.
otbr-agent[1892]: 00:00:00.108 [I] Notifier------: StateChanged (0x42038210) [MLAddr NetData PanId NetName ExtPanId BbrState Nat64]
otbr-agent[1892]: 00:00:00.108 [I] Bbr-----------: Start listening on port 61631
otbr-agent[1892]: 00:00:00.108 [I] Bbr-----------: Backbone TMF subscribes ff32:40:fdde:ad00:beef:0:0:3: OK
otbr-agent[1892]: 00:00:00.109 [I] BbrManager----: Start Backbone TMF agent: OK
otbr-agent[1892]: 00:00:00.114 [I] Platform------: Execute command `ipset flush otbr-ingress-allow-dst-swap` = 0
otbr-agent[1892]: 00:00:00.118 [I] Platform------: Execute command `ipset flush otbr-ingress-deny-src-swap` = 0
otbr-agent[1892]: 00:00:00.122 [I] Platform------: Execute command `ipset add otbr-ingress-deny-src-swap fdde:ad00:beef:0::/64 -exist` = 0
otbr-agent[1892]: 00:00:00.130 [I] Platform------: Execute command `ipset swap otbr-ingress-deny-src-swap otbr-ingress-deny-src` = 0
otbr-agent[1892]: 00:00:00.134 [I] Platform------: Execute command `ipset swap otbr-ingress-allow-dst-swap otbr-ingress-allow-dst` = 0
otbr-agent[1892]: 00:00:00.134 [I] P-Netif-------: NAT64 CIDR updated to 192.168.255.0/24.
otbr-agent[1892]: 00:00:00.135 [I] P-Netif-------: Deleting route for NAT64
otbr-agent[1892]: 00:00:00.135 [I] P-McastRtMgr--: Disable: OK
otbr-agent[1892]: [INFO]-BA------: Publish meshcop service OpenThread BorderRouter #102C._meshcop._udp.local.
otbr-agent[1892]: 00:00:00.135 [I] RouterTable---: Route table
otbr-agent[1892]: 00:00:00.135 [I] TrelInterface-: Registering DNS-SD service: port:60358, txt:"xa=f2385e307513102c, xp=dead00beef00cafe"
otbr-agent[1892]: 00:00:00.142 [I] P-Netif-------: Host netif is down
otbr-agent[1892]: 00:00:00.143 [I] P-Netif-------: Succeeded to process request#1
otbr-agent[1892]: 00:00:00.143 [W] P-Netif-------: Failed to process request#2: No such process
otbr-agent[1892]: [INFO]-MDNS----: Successfully registered service OpenThread BorderRouter #102C._meshcop._udp
otbr-agent[1892]: [INFO]-BA------: Result of publish meshcop service OpenThread BorderRouter #102C._meshcop._udp.local: OK
otbr-agent[1892]: [INFO]-BA------: Result of publish meshcop service OpenThread BorderRouter #102C._meshcop._udp.local: OK
```

You should now be able to interact with the OT CLI:

```bash
sudo ot-ctl state
enabled
```

###### Running the OTBR as a Service (heading level 7)

OTBR Service can be started as by calling:

```bash
sudo systemctl start otbr-agent 
```

###### Running the Bluetooth Host Applications

For both multiprotocol RCP and NCP architectures, CPCd must be started before running the Bluetooth host applications.

###### Bluetooth Host Applications for Multiprotocol RCP (heading level 7)

Follow the steps below after CPCd starts up successfully.

1. Run the CPC-HCI bridge application (bt_host_cpc_hci_bridge). It connects to CPCd using the standard instance name cpcd_0, opens a CPC endpoint to the BLE RCP running on the EFR, and creates a numbered virtual serial device on the host, for example `/dev/pts/2`. The actual number may vary. For convenience, `bt_host_cpc_hci_bridge` also creates a symlink to the device from `pts_hci` in the working directory.  
   ```bash  
   [I] Silicon Labs | CPC-HCI bridge  
   [I] CPC successfully initialized with 'cpcd_0'  
   [I] CPC Bluetooth endpoint opened  
   [I] PTY device file opened at '/dev/pts/6'  
   ```
2. Use the following command to attach the Bluetooth stack to the newly created virtual serial device, where <device> is the name of the virtual serial device:  
   ```bash  
   sudo btattach -B <device> -S 115200  
   ```
3. Run `sudo bluetoothctl` to start the Bluetooth CLI utility and use commands to control the Bluetooth controller device.
4. A utility that comes with the standard Bluetooth tools, called `btmon`, can be used to view the HCI traffic between the Bluetooth host and the controller.  
   - To view the HCI traces for live debugging, run `sudo btmon &`.  
   - To save the HCI traces and debug later using tools such as Wireshark, run `sudo btmon --write ~/hcitrace.snoop`.

###### Bluetooth Host Application for Multiprotocol NCP (heading level 7)

Run the example Bluetooth host application `bt_host_empty` with the following command:

```bash
./bt_host_empty -C cpcd_0
```

The -C option is used to specify the CPC instance name (ex: -C cpcd_0).

##### Debugging with Logs and Silicon Labs Components

###### Introduction

This is a comprehensive overview of how to enable and read logs for each component in a Concurrent Multiprotocol Radio Co-Processor (CMP RCP) setup. It also provides general debugging advice for Thread and Zigbee protocols.

###### Details

Debugging a CMP RCP setup can be difficult due to the many components that make up the system. For a Zigbee and OpenThread setup, for example, the components that enable the protocols are:

- **Radio Co-Processor (RCP)**: Implements the physical layer and connects to the Co-Processor Communication Daemon (CPCd) as the secondary.
- **CPCd**: Multiplexes protocol streams and communicates with the RCP over a serial link such as UART or SPI.
- **OTBR Agent**: Host program that implements the OpenThread (OT) Stack.
- **Zigbeed**: Host program that implements the Zigbee Stack.
- **Zigbee Host Application (Z3Gateway)**: Communicates with Zigbeed using EmberZNet Serial Protocol (EZSP) over a virtual serial port.

###### Debugging RCP

The RCP is responsible for receiving packets over the air and filtering data for the host-side processes to receive.

Here are several ways to debug with an RCP:

- Use the [packet trace interface (PTI)](https://community.silabs.com/s/article/why-should-i-enable-the-pti-peripheral-x?language=en_US) to view data received by the RCP along with RAIL messages.
- Configure [SWO](https://community.silabs.com/s/article/how-to-send-string-through-the-swo-interface-by-calling-printf-function-x?language=en_US) debug to print out logs.
- Use Ozone for debugging if the issue is reproduced with a Wireless Starter Kit (WSTK).
- Check the CPCd logs for RCP connection status (systemctl status cpcd).
- RCP logs with OT stack (see [Debugging OTBR](debugging-local-processes#debugging-otbr)).

When using an OTBR + RCP setup, you can enable [efr32RadioCounters](https://github.com/SiliconLabsSoftware/sisdk-release/blob/sisdk-2025.12/openthread/platform-abstraction/include/radio_counters.h) by following the steps below:

1. Add the "EFR32 Platform extension" component to your RCP project.  
   - `ot_platform_ext` component if generating project with slc-cli
2. Set SL_CATALOG_OPENTHREAD_EFR32_CLI_PRESENT = 1.
3. Build OTBR with -DSL_OT_EFR32_CLI=ON to enable the host-side API.
4. Use cli command `ot-ctl efr32 counters radio` or call `otPlatRadioExtensionGetRadioCounters()` on the host to get the counter data.

###### Debugging CPCd

[CPCd](https://github.com/SiliconLabs/cpc-daemon) is a key component in a multiprotocol architecture, bridging the physical bits received by the RCP and forwarding the information to the host stack process. The `cpcd.conf` file contains fields to enable tracing:

```c
##### Prints tracing information to stdout
##### Optional, defaults to 'false'
##### Allowed values are 'true' or 'false'
stdout_trace: true

##### Prints tracing information to a file located under traces_folder
##### Optional, defaults to 'false'
##### Allowed values are 'true' or 'false'
trace_to_file: true

##### Traces folder
##### Optional, defaults to '/dev/shm/cpcd-traces'
##### Folder mounted on a tmpfs is preferred
traces_folder: /dev/shm/cpcd-traces

##### Enable frame trace
##### Optional, defaults to 'false'
##### Allowed values are 'true' or 'false'
enable_frame_trace: true
```

> **Note**: These are not enabled by default so they must enabled to see the [spinel](https://github.com/openthread/spinel-spec/blob/master/core/intro-frames.md) frame traces. This is beneficial if CPC security is disabled in both the RCP and CPCd. In enabling these traces, the raw spinel frames being transferred along with the [CPC headers](https://github.com/SiliconLabs/cpc-daemon/blob/133b29678b3d0bc7578e098d2f46b4d5bcd2ebb4/doc/uart.md?plain=1#L23) can be debugged.

The CPCd process itself will output information to syslog about the state of connection of the secondary (RCP), along with information about how many connections and endpoint has. If a stack process, such as Zigbeed, is disconnected something like the following will be printed:

```c
Info : Endpoint socket #12: Client connected. 2 connections
Info : Endpoint socket #12: Client disconnected. 1 connections
```

If the RCP is stuck or CPCd is unable to connect, you can view the live CPCd syslogs with:

```c
tail -F /var/log/syslog | grep cpcd
```

First, you must enable the Zigbee logs in the zigbeed.conf file:

```c
##### Debug level
##### Optional
##### The debug level for the Spinel driver.
##### Message are sent to syslog if the syslog service is running.
##### All messages with level less than or equal to the supplied value are logged.
##### For example, setting debug-level=5 logs all messages.
##### 0=NONE, 1=CRIT, 2=WARN, 3=NOTE, 4=INFO, 5=DEBG
##### Uncomment to enable.

##### debug-level=5
```

Note that this is not enabled by default so you must remove the '#' in the `zigbeed.conf` file to obtain the Zigbeed logs.

The Zigbeed logs will display vital information such as network data, spinel transactions, EmberZNet API prints (such as `emberSendUnicast()` information), user custom syslog prints, RCP disconnections, and Zigbeed crashes/resets.

A few Silicon Labs components can be added to a Zigbeed project to help debug:

- [Counters](https://docs.silabs.com/zigbee/latest/zigbee-af-api/counters): This component provides support for reading and manipulating counters that record different events in the EmberZnet stack.
- [Stack Diagnostics](https://docs.silabs.com/d/zigbee-af-api/6.9/plugin-stack-diagnostics): This component provides support for reading and manipulating counters that record different events in the stack.
- [Packet Handoff](https://community.silabs.com/s/article/packet-handoff-plugin-how-to-guide?language=en_US#:~:text=Details-,The%20packet%20handoff%20functionality%20allows%20the%20application%20to%20get%20access,journey%20through%20the%20Zigbee%20stack.): This component hooks directly into the stack and provides a mechanism to intercept packets and hand them off to other components as a flat buffer.

In the Zigbeed code, you can make use of the APIs from these components to print out helpful debug information. The information can then be found in the syslogs. For example, you can use the packet handoff in combination with debug prints to display a packet:

```bash
// packetHandoff
sl_zigbee_packet_action_t sl_zigbee_af_incoming_packet_filter_cb(sl_zigbee_zigbee_packet_type_t packetType, uint8_t* packetData, uint8_t* size_p, void* data) {
  switch (packetType)
    {
    //case <packetType>:
      //code
      //break;
    case SL_ZIGBEE_ZIGBEE_PACKET_TYPE_APS_DATA: // APS packet
      syslog(LOG_INFO, "APS_DATA ----> %u", packetType);
      break;
    default:
      break;
    }

  syslog(LOG_INFO, "Packet data bytes ---->");
  for (int i = 0; i < *size_p; ++i)
    {
      syslog(LOG_INFO, "%x", packetData[i]);
    }

  return SL_ZIGBEE_ACCEPT_PACKET;
}
```

###### Debugging OTBR

If there is any issue with the Thread network and the RCP is working fine, then you can view the OT process syslogs by running:

```c
tail -F /var/log/syslog | grep otbr-agent
```

Unlike Zigbeed, the Thread stack does not have a .conf file to enable logging. Logging can be enabled by setting the log level of your OTBR agent to 5:

```c
Sudo ot-ctl log level 5
```

For more information, refer to [OpenThread CLI Commands](https://openthread.io/reference/cli/commands).

Before doing this, the OTBR on the host must be built with the following configuration flags while calling the setup script:

```c
OTBR_OPTIONS='-DOT_LOG_LEVEL=DEBG -DOT_FULL_LOGS=ON'
```

The OT agent logs, when set to level 5, are helpful for debugging network information (Rx and Tx), spinel data, and OTBR agent state.

The OT stack also allows for users to view RCP logs. To enable these:

1. OTBR must be built with the debug configs above.
2. In platform-abstraction/efr32/board_config.h, set RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT to 1 to enable debug counters in radio.c.
3. Rebuild RCP with the following configuration: OPENTHREAD_CONFIG_LOG_OUTPUT:OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED, OPENTHREAD_CONFIG_LOG_LEVEL:5, OPENTHREAD_FULL_LOGS_ENABLE:1.

Once these are followed, you can view any RCP logs in RTT or printed in the OTBR syslog (OPENTHREAD_CONFIG_LOG_OUTPUT_APP).

You should be able to see:

- Any errors surfaced on the RCP.
- Rx and Tx data, along with failures.
- Users can use otLogInfoPlat("") to print any data, such as efr32RadioCounters.

> Important note for RAIL tx counters / generally any RCP debug output: there is a limit to the amount of information that can be printed if OPENTHREAD_CONFIG_LOG_OUTPUT is set to OPENTHREAD_CONFIG_LOG_OUTPUT_APP. Printing too much information will result in the RCP being unresponsive, it will surface a NOMEM error in the syslog.

###### Zigbee Host Application (Z3Gateway)

The Z3Gateway has no logs but there are prints in the console which may be helpful. For example, you can see the configuration data to ensure that zigbeed was configured properly (pay close attention to messages containing 'FAIL' while Z3Gateway configures Zigbeed).

You should also be able to see APS layer messages that are received and transmitted. If there are any issues with transmitting, a [tx 0x66](https://community.silabs.com/s/question/0D51M00007xeTcLSAU/what-cause-result-in-tx-66-error-print?language=en_US) error will be printed in the host console. You can also view the raw EZSP traces between Zigbeed and the host program, as well as any EZSP errors between zigbeed and Z3Gateway. APIs like `sl_zigbee_app_debug_print()` are useful for customer debug prints.

One of the more useful ways to debug with Z3Gateway is to use the CLI commands, most of which map to Zigbeed functions (so you can also implement these in Zigbeed). These commands can be found by typing 'help' in the Z3Gateway console. Inputting 'plugin' will print all of the plugins that you can use. 'counters', 'mfglib', and 'stack-diagnostics' can be really useful for debugging.

###### Miscellaneous Network Debugging

Silicon Labs offers helpful debugging tools through Simplicity Studio. Some examples:

- Simplicity Commander: v1.17.4 - [User's Guide](https://docs.silabs.com/simplicity-commander/latest/simplicity-commander-start/)
- Network Analyzer: Network Analyzer - v5.7.1 - Simplicity Studio 5 Users Guide Silicon Labs
- Energy Profiler: Energy Profiler - v5.7.1 - Simplicity Studio 5 Users Guide Silicon Labs
- Many sample apps such as Zigbee StandardizedRfTesting and RAIL SoC RAILtest
- Studio tools: Tools Overview - v5.7.1 - Simplicity Studio 5 Users Guide Silicon Labs

Refer to the release notes page to check for known bugs and workarounds.

###### Common Issues

_CPC won't connect to the secondary_

Look over the CPC syslogs or state. This will output obvious mistakes such as mismatching settings or versions. If the CPC logs show the RCP is unresponsive, double-check the following:

- UART/SPI baud & settings for the secondary
- UART baud for the WSTK (if used), console -> admin -> 'serial vcom'
- A bootloader has been flashed to the RCP

Refer to [Common CPCD Issues & Debugging](https://community.silabs.com/s/article/Common-CPCD-Issues-Debugging?language=en_US) for more information.

_My OTBR agent won't start_

- Look over the syslogs for the OTBR agent
- Ensure the URL in /etc/default/otbr-agent is correct
- If using CPCd, make sure CPCd is connected before starting the service

_Tx timeout reported, 'RCP reset' messages after normal operation_

If these messages are printed the RCP has crashed or is stuck. Follow the "OTBR" section to capture what assert the RCP is hitting.

###### Running the OTBR CLI Utility (heading level 7)

```bash
 sudo ot-ctl
```

For a complete list of OTBR CLI commands, refer to the [OpenThread CLI Command Reference](https://openthread.io/reference/cli/commands).

##### Running Host Applications with Pre-Built Packages

###### Running Host Applications with Debian-Bookworm Packages

As mentioned before, you can install pre-built host applications. The packages can be acquired from the Silicon Labs Github Page: [https://github.com/SiliconLabsSoftware/sisdk-release/releases](https://github.com/SiliconLabsSoftware/sisdk-release/releases) in the debian-bookworm.zip folder.

Install all packages, making sure to match the right version to your system architecture.

First, install the cpc packages.

```bash
apt-get install -y ./debian-bookworm/deb/libcpc3_<version>.deb
apt-get install -y ./debian-bookworm/deb/libcpc-dev_<version>.deb
apt-get install -y ./debian-bookworm/deb/cpcd_<version>.deb
```

Then, install the ot-br-posix and zigbeed packages.

> **Note**: When installing the ot-br-posix .deb file on 32-bit bookworm systems such as armhf (arm32v7), make sure the dhcpcd package on your target system is higher than the recommended bookworm version of 9.4.1-24, which has a [known startup issue](https://github.com/NetworkConfiguration/dhcpcd/issues/323). The bookworm-backports apt source provides version 10.1.0 which is highly recommended. If not, Silicon Labs recommends updating your dhcpcd package version to at least 9.5.1. To use the backports source, you can create the following two files and then run `sudo apt update`.

```bash
/etc/apt/sources.list.d/bookworm-backports.list:

deb [trusted=yes] http://deb.debian.org/debian bookworm-backports main

/etc/apt/preferences.d/bookworm-backports:

Package: dhcpcd
Pin: release n=bookworm-backports
Pin-Priority: 1001

Package: dhcpcd-base
Pin: release n=bookworm-backports
Pin-Priority: 1001
```

```bash
apt-get install -y ./debian-bookworm/deb/ot-br-posix_<version>.deb
apt-get install -y ./debian-bookworm/deb/zigbeed_<version>.deb
```

###### Running Host Applications with OpenWRT Packages

Silicon Labs provides evaluation multiprotocol host application packages for OpenWRT. These packages are pre-built for Raspberry Pi 4 – bcm2711 devices running OpenWRT version 23.05.3.

###### Installation (heading level 7)

To use these packages, download the release tar file containing the ipk packages from [https://github.com/SiliconLabsSoftware/sisdk-release/releases](https://github.com/SiliconLabsSoftware/sisdk-release/releases).

The openwrt.tar.gz should contain:

- openthread-br_<version>.ipk
- cpcd_<version>.ipk
- libcpc_<version>.ipk
- zigbeed_<version>.ipk

Install the packages with `opkg install silabs/\*.ipk`.

To register a uart device with a `/dev/ttyACM` device filepath, it is recommended to download the kmod-usb-acm package.

`opkg update`

`opkg install kmod-usb-acm`

As described in [Running Local Processes Concurrently: Running Zigbeed](running-local-processes-concurrently#running-zigbeed), the socat utility is needed for zigbeed. Make sure it is also installed on the system.

`opkg install socat`

###### Install CPCD (heading level 8)

```c
opkg update
opkg install libcpc_<version>.ipk
opkg install cpcd_<version>.ipk
```

###### Install OTBR (heading level 8)

```c
opkg update
opkg install openthread-br_<version>.ipk
```

###### Install Zigbeed (heading level 8)

```c
opkg update
opkg install zigbeed_<version>.ipk
```

###### Build Bluetooth and Zigbee Host Applications

Both the Bluetooth host and Z3Gateway host applications can be built for OpenWRT. Instructions to build applications for OpenWRT can be found on the OpenWRT.org website.

The `cpcd.conf` and `zigbeed.conf` files (described in section [Configuring CPCD](building-cpcd-locally#cpcd-configuration)) are both populated during installation.

There are two setup instructions recommended for the `openthread-br` package, updating the firewall options, and updating the radio-url in the `otbr-agent` configuration file.

OpenWRT has a firewall that is independent of the Silicon Labs multiprotocol host apps. However, the firewall can interfere with the host apps as far as the generic processing of IP traffic is concerned, such as with the openthread border router application. One simple way to ensure that all IP traffic can be processed is by setting the policy for each firewall chain to ‘ACCEPT’; other alternatives can be configured on a case by case basis using the OpenWRT Firewall configuration user guide: [https://openwrt.org/docs/guide-user/start#firewall_configuration](https://openwrt.org/docs/guide-user/start#firewall_configuration).

The firewall service can be updated through the OpenWRT Unified Configuration Interface (UCI) with the following commands:

`uci set firewall.@defaults[0].input=’ACCEPT’`

`uci set firewall.@defaults[0].output=’ACCEPT’`

`uci set firewall.@defaults[0].forward=’ACCEPT’`

`uci commit`

`service firewall restart`

The other recommended setup step needed for the openthread-br package is to update the radio-url in the otbr-agent configuration file, /etc/init.d/otbr-agent with the radio-url specified in [OpenThread Host Applications](building-otbr-locally#configuration-of-the-otbr) in order to use the multiprotocol supported spinel interface using cpc-daemon.

###### Startup

To start up the installed and configured multiprotocol host applications, perform the following sequence:

1. Run the cpc-daemon with `/usr/bin/cpcd`.  
   1. Bind to the RCP if not previously done with `cpcd –bind ecdh`.  
   2. Launch the `cpcd` program in the background or in a separate terminal with `cpcd&`.
2. For `openthread-br`:  
   1. Launch the otbr-firewall service with `service otbr-firewall start`.  
   2. Launch the otbr-agent service with `service otbr-agent start`.  
   3. Launch the `ot-ctl` program to connect to the OpenThread cli through the agent socket.
3. For Zigbee Host applications:  
   1. Run zigbeed with `/usr/bin/zigbeed`.  
   2. Run Z3Gateway as in any other native configuration ie. `{Z3Gateway_build_path}/zigbee_z3_gateway -p ttyZigbeeNCP`.

##### Running Multiprotocol Solution on Docker

> **NOTICE: The Multiprotocol Docker Container is now deprecated**

###### Raspberry Pi / ARM Host with Docker Setup

The quickest method to set up a multiprotocol host is to use the pre-configured Multiprotocol Docker container on the Raspberry Pi. This container includes everything necessary to easily run the Z3Gateway Zigbee application, the OpenThread Border Router (OTBR) and the ot-cli application, and the BlueZ Bluetooth stack and bluetoothctl, an open source interactive CLI utility for sending commands to the BlueZ stack.

The Multiprotocol Docker container is hosted on DockerHub (hub.docker.com) in the `siliconlabsinc/multiprotocol` repository. Pulling the latest tag is generally the best way to get the latest release by issuing this command:

```bash
docker pull siliconlabsinc/multiprotocol:latest
```

If necessary, you can replace the latest tag with a specific version. To run the Docker container, a helpful _run.sh_ script is provided in the SDK in the `app/host/multiprotocol/zigbeed/multiprotocol-container` directory. The script is meant to simplify the docker setup process. Copy it to your Raspberry Pi’s home directory. Make sure the RCP device is flashed and attached to the Raspberry Pi before starting the Docker container.

The Docker container by default uses CPCd security, make sure your RCP has CPC Security Enabled. Before running the docker container, a CPCd security commissioning step is required. CPC is now configured by default to encrypt the data over the SPI or UART serial connection between the host and the EFR32. For detailed information, see [https://github.com/SiliconLabs/cpc-daemon/blob/main/readme.md](https://github.com/SiliconLabs/cpc-daemon/blob/main/readme.md).

The _run.sh_ script provides a convenient shortcut to perform the security commissioning. After flashing the EFR32 with the desired image, simply execute `run.sh -K` on the host. This will pull and run the docker container, use cpcd inside the container to commission security and generate a binding key file, then copy the binding key file outside of the container and place it at /etc/binding-key.key on the host filesystem. This file will then be used for all subsequent runs of the container to establish a secure connection with the EFR32.

After CPCd security is commissioned, to start the Docker container, execute the run.sh script without any arguments. The container will start systemd, which is a process management utility. This will make sure that all the components are started in the proper sequence and are kept running. At startup, only CPCd is started.

The `/tmp/multiprotocol-container/log/` directory on the host will be mounted to `/var/log/` inside the Docker container. It contains the file syslog, which will contain log output from cpcd, zigbeed, zigbeed-socat, otbr-agent, and ot-cli, among other programs. Monitor the file live by issuing the following command:

```bash
tail -f /tmp/multiprotocol-container/log/syslog
```

Use the standard `journalctl` utility to monitor the logs for a given systemd service. For example, to monitor the cpcd log from outside of the container, use the following command:

```bash
docker exec -it multiprotocol journalctl -fexu cpcd
```

As a convenience, the command can also be executed by typing `run.sh -l`. Note that by default, verbose cpcd logging is disabled. To enable it, see **TODO LINK TO CPCD FILE CONFIGURATION**. After starting the container, it is a good idea to verify that CPCd successfully connected to the EFR32 by running `run.sh -l` and looking for the log line “Daemon startup was successful”.

A special file `/accept_silabs_msla` will be created in the Docker container to indicate acceptance of the Silicon Labs Master Software License Agreement (MSLA) located at [https://www.silabs.com/about-us/legal/master-software-license-agreement](https://www.silabs.com/about-us/legal/master-software-license-agreement).

Once the Docker container is running, you can open a shell by issuing the command:

```bash
run.sh -o
```

The Zigbeed configuration file is located: `/usr/local/etc/zigbeed.conf` and the CPCd configuration file is located: `/usr/local/etc/cpcd.conf`. For more information, see **TODO Link to CPCD CONF**.

The cpcd, zigbeed, zigbee_z3_gateway, zigbee_z3_gateway_cpc, zigbee_host_xncp_led, otbr-agent, ot-ctl, ot-cli, and cpc-hci-bridge binaries are all installed in `/usr/local/bin`. There are systemd configuration files to start up CPCd, zigbeed and socat for Zigbee, OTBR for OpenThread, and cpc-hci-bridge and hciattach for Bluetooth. The definition files for those are in `/etc/systemd/system`. Use systemd commands to start the services from within the Docker container shell as follows. Note that the otbr service takes an iid argument.

```bash
systemctl start zigbeed
systemctl start otbr
systemctl start hciattach
```

From outside of the container, as follows:

```bash
docker exec -it multiprotocol systemctl start zigbeed
docker exec -it multiprotocol systemctl start otbr
docker exec -it multiprotocol systemctl start hciattach
```

For Bluetooth, to avoid interference with Bluetooth running in the container, first disable Bluetooth on the native Raspi host using the
following commands:

```bash
sudo systemctl stop bluetooth
sudo systemctl mask bluetooth.service
```

After starting the container and opening a shell, issue the command:

```bash
service hciattach start
```

This uses the systemd utility to start the necessary processes for the BlueZ stack to connect to the RCP via CPCd.

To interact with the Bluetooth network, issue the command:

```bash
bluetoothctl
```

Once at the bluetoothctl prompt, use the bluetoothctl commands such as list, advertise, connect, and scan to exercise the host Bluetooth stack and RCP. Additional documentation for bluetoothctl and BlueZ is available online.

Note that the zigbeed service automatically starts the zigbeed-socat service, and the hciattach service automatically starts the cpc-hcibridge service.

For convenience, the `run.sh` script has arguments to start each of these services after the container is running:

```bash
-Z starts zigbeed and launches a terminal with zigbee_z3_gateway
-T starts OTBR and opens a terminal with ot-ctl
-L starts Bluetooth and opens a terminal with bluetoothctl
-C launches a terminal with zigbee_z3_gateway_cpc (for use with the Zigbee NCP/OpenThread RCP)
```

You can see the processes by running `ps aux` inside the Docker container. Alternatively, use systemd commands to see the state of the various services as follows:

```bash
systemctl status zigbeed
```

To stop the docker container issue:

```bash
run.sh -s. 
```

###### Zigbee Host Applications in Docker

For convenience, a precompiled zigbee_z3_gateway host sample application is included in the Docker container. To run it with Zigbeed, issue the following commands from inside the Docker container shell (or on the host, depending on your installation):

```c
Systemctl start zigbeed
/usr/local/bin/zigbee_z3_gateway -p ttyZigbeeNCP
```

The ttyZigbeeNCP refers to the file `/dev/ttyZigbeeNCP`. The zigbee_z3_gateway application automatically prepends /dev to a relative path in the -p argument. The `run.sh -Z` is a script that will run these commands on an already running container. Any Zigbee host application built with EZSP/ASH for communicating with a Zigbee NCP over a UART can also be used with Zigbeed by passing it the PTY device name. This is because to the host application, the PTY device appears exactly like a normal serial device. If theZigbee host application was built for EZSP/SPI, it will have to rebuild for EZSP/ASH to work with Zigbeed.

Some EZSP commands that are not supported with zigbeed:

```c
• Set/Get Manufacturing Tokens
• EZSP_SET_MFG_TOKEN
• EZSP_GET_MFG_TOKEN
• Secure EZSP Commands
• EZSP_SET_SECURITY_KEY
• EZSP_SET_SECURITY_PARAMETERS
• EZSP_RESET_TO_FACTORY_DEFAULTS
• EZSP_GET_SECURITY_KEY_STATUS
• Bootloader Commands
• EZSP_LAUNCH_STANDALONE_BOOTLOADER
• EZSP_SEND_BOOTLOADER_MESSAGE
• EZSP_GET_STANDALONE_BOOTLOADER_VERSION_PLAT_MICRO_PHY
• EZSP_INCOMING_BOOTLOAD_MESSAGE_HANDLER
• EZSP_BOOTLOAD_TRANSMIT_COMPLETE_HANDLER
• EZSP_AES_ENCRYPT
```

If using the Zigbee NCP/OpenThread RCP, do not run Zigbeed on the host. Instead use a host app that has been built with the zigbee_ezsp_cpc component. For convenience, a precompiled zigbee_z3_gateway_cpc host sample application has been included in the Docker container. No arguments are required to run it, as it connects directly to CPCd. An optional -c argument can be supplied to specify the CPC daemon instance name to connect to. The default instance connected to is cpcd_0.

###### OT CLI Application in Docker

A precompiled OpenThread ot-cli sample application with multi-PAN and CPCd support is also included in the Docker container and SDK. ot-cli is a stand-alone sample host application that includes the OpenThread stack and exposes the standard OpenThread CLI. You can run by issuing this command:

```c
/usr/local/bin/ot-cli 'spinel+cpc://cpcd_0?iid=2&iid-list=0'
```

The RCP uses the interface id parameter of the radio-url argument (iid=2) to distinguish between the OpenThread and Zigbeed applications. By default, the `/usr/local/etc/zigbeed.conf` file uses a radio-url argument with iid=1. The iid-list argument in radiourl is to allow spinel frames other than the host interface id. RCP uses the interface id zero to broadcast a spinel frame to all the hosts. The string ‘cpcd_0’ in the radio-url is the default CPCd instance name, which is defined in the `/usr/local/etc/cpcd.conf` file. To enable otcli debugging output, use the command line argument -d <level> to enable logging at the desired level 1-5. By default, log messages are printed to syslog. Add the argument -v as well to echo log messages to stdout as well. The `run.sh -O` command can be used as a shortcut to start this application on a running container and open a shell to it.

###### OpenThread Border Router (OTBR) Application in Docker

The Multiprotocol Docker container is based on the OpenThread Border Router Docker container. It has all the necessary dependencies to run OTBR over CPC. To run OTBR with iid 2, issue the following commands from inside the Multiprotocol Docker container shell (or on the host, depending on your installation):

```c
systemctl start otbr
ot-ctl
```

For convenience, this can be run with `run.sh -T` on a running container and open a shell to it. Note that OTBR uses ot-ctl instead of ot-cli. These two utilities offer similar CLI to the OpenThread network. But ot-ctl is a utility that connects to otbr-agent, which is the process that runs the OpenThread stack for OTBR. **TODO POINT TO WHERE WE DISCUSS CHANING OTBR RADIO URL**

To use OTBR with Zigbee NCP/OpenThread RCP application, OTBR must set iid value to zero in the radio-url. Otherwise, OpenThread RCP will reject the spinel frames for all non-zero iid values. To run OTBR with iid 0, issue the following command from inside the Multiprotocol Docker container:

```c
systemctl start otbr
ot-ctl
```

For convenience, this can be run with `run.sh -T 0` on an already running container to run OTBR with iid value zero.

###### Bluetooth Host Applications on Docker

The multiprotocol solution uses the standard Linux BlueZ Bluetooth stack on the host. BlueZ and associated utilities are documented extensively online. Once Bluetooth is running as described in section 8: Bluetooth Host Setup, you can use standard Bluetooth tools such as bluetoothctl for a CLI utility and btmon to view the traffic going through the Bluetooth device.

##### Zigbee Host+NCP to Host+Zigbeed+RCP Migration Note

This section describes one method of migrating a Zigbee Host+NCP system to a Host+Zigbeed+RCP system by backing up and restoring the token data. This method is similar to backing up and restoring Z3Gateway explained in [Backing Up and Restoring a Z3 Green Power Combo Gateway](https://docs.silabs.com/zigbee/latest/gp-combo-gateway-backup-restore/), with certain differences as noted here. One of the major differences is that in this case the same NCP hardware is used as RCP hardware.

The non-volatile Zigbee network stack context on an NCP is stored using the on-chip token system. By moving that stack context from the NCP to Zigbeed on the host, we can migrate a Zigbee Host+NCP application to Host+Zigbeed+RCP application.

The migration procedure requires the NCP using the NVM3 token system and the `zigbee\_token\_interface` component. If it is not already using these, then the NCP must first be updated to do so. Similarly, the Zigbee host app needs to be updated to add the `zigbee\_trust\_center\_backup` component with EMBER_AF_PLUGIN_TRUST_CENTER_BACKUP_POSIX_FILE_BACKUP_SUPPORT configuration set to 1.

With the above upgrades to NCP and host, the host can read the stack tokens from NCP and save them to a file.

![NCP host](/multiprotocol-solution-linux/0.4/images/figure-6-1-ncp-host.png)

The host then reads the saved tokens and updates the tokens on Zigbeed. Zigbeed’s default configuration includes the `zigbee\_token\_interface` component, which allows the host to write the saved network stack tokens to it.

![Zigbeed host](/multiprotocol-solution-linux/0.4/images/figure-6-2-host-zigbeed.png)

For simplicity, the `trust\_center\_backup` component provides command line interfaces to read and save ncp tokens and write to Zigbeed tokens. They are, respectively:

```C
plugin trust-center-backup backup-tokens <file name to save the tokens>

plugin trust-center-backup update-zigbeed <the file that has saved the tokens above>
```

There are certain limitations:

- This method only works for migrating from NCPs with NVM3 tokens.
- It migrates the stack tokens from NCP to Zigbeed. It presently does not migrate the custom tokens.
- This method assumes the same NCP hardware is used as RCP, and therefore retains the IEEE address (EUI64).
- Since the network key frame counter (NWK key FC) value is stored truncated and only incremented after initialization, you may need to perform an extra reset cycle of the Z3Gateway/Zigbeed to obtain a working value for the FC. Otherwise, the starting value after migrating may be lower than the last sent value and therefore the gateway’s packets might be ignored until the FC value exceeds the value before migration. Please note that, on initialization, the stack code will read the truncated FC and increment it by 0x1000. This incremented value will only be used at the next initialization (after a reset of the Z3Gateway).

#### Running Matter with OpenThread, Zigbee, and Bluetooth Concurrently on a System-on-Chip

##### Running Matter with OpenThread, Zigbee, and Bluetooth Concurrently on a System-on-Chip

> **Note: This section replaces _AN1418: Running Zigbee, OpenThread, and Bluetooth Concurrently on a System-on-Chip_. Further updates to this application note will be provided here**.

This application note describes how to run a combination of Zigbee, Matter with OpenThread, and Bluetooth networking stacks and the Zigbee application layer on a System-on-Chip (SoC). One of the main functions of a Concurrent Multiprotocol (CMP) device is to act as a bridge between Zigbee and OpenThread networks.

Note that, depending on the chip, memory size restrictions may prevent running Matter on SoC devices.

##### Concurrent Multiprotocol (CMP) Sample Application (z3-light_ot-ftd)

![Zigbee + OpenThread Concurrent Multiprotocol Application](/concurrent-mp-soc/0.1/images/sld678-image1.png)

![Zigbee + Bluetooth + OpenThread Concurrent Multiprotocol Application](/concurrent-mp-soc/0.1/images/sld678-image2.png)

The CMP sample application consists of a Z3Light, which is a Zigbee router, and an OpenThread FTD (Full Thread Device). Both protocol stacks operate by multiplexing a single radio.

> **Note**: SiSDK 2024.12 allows inclusion of `rail_util_ieee802154_fast_channel_switching` and `rail_util_dma` components. This allows Zigbee and Thread networks to operate on 2 different radio channels. Note that the concurrent listening feature becomes active only when both networks are up. This feature can result in a decrease in receive sensitivity.

If this feature is not required, these components can be removed. However, it is imperative to make sure that both Zigbee and OpenThread networks are on the same channel.

###### RTOS

Within the CMP application, scheduling is managed using a Real Time Operating System (RTOS). Each protocol runs in a dedicated RTOS task. The Zigbee and OpenThread tasks operate at the same priority while the Command Line Interface (CLI) is made available using a CLI RTOS task that operates at a lower priority.

> **CAUTION for SDK 4.4 and older**: It is critical to note that Zigbee and OpenThread APIs are not thread-safe. Calling them from different threads can result in unexpected behavior. In addition, any references to EmberMessageBuffer must be contained within the Zigbee task.

As of SiSDK 2024.6, Zigbee stack APIs are thread-safe. Application framework APIs are protected by a mutex in the CLI task context. If they are to be called from a custom RTOS task, you are expected to surround the calls with `sl_zigbee_af_acquire_lock` and `sl_zigbee_af_release_lock`. It is also important to note that this version also introduced the standardization of all function names. Refer to the [Zigbee API Reference Manual](https://docs.silabs.com/zigbee/latest/zigbee-api-ref-v7-vs-v8/02-renaming-changes-in-zigbee) for more details. It is important to note that any references or allocations of zigbee buffers (for example, `sli_legacy_buffer_manager_allocate_buffer`) must be contained within the Zigbee task to avoid unexpected operation.

###### Command Line Interface

This application supports all CLI commands that can be found in the Z3Light sample application. A subset of the OpenThread CLI has been ported to demonstrate form, join and ping operations. This functionality can be extended further, if necessary, by following the example commands in the ot_up_cli.c file from the ot_up_cli component. Note that before SiSDK 2024.06, OpenThread APIs should only be invoked from `sl_ot_rtos_application_tick` since they are not thread-safe.

###### OpenThread Commissioning (heading level 7)

This device can be commissioned on to an OpenThread network out-of-band using CLI commands. Setting the OpenThread network parameters, such as network key and channel, before starting the network allows the CMP device to join a Thread network as a child or router device. The table below lists the OpenThread CLI commands.

|CLI Command|Description|
|---|---|
|dataset|View OpenThread network configuration.|
|dataset_new|Creates a new OpenThread dataset.|
|dataset_commit_active|Commits dataset to NVM.|
|factory_reset|Removes all NVM OpenThread settings.|
|dataset_networkkey|Presets the network key on the device to help with joining an existing OpenThread network out-of-band.|
|dataset_channel|Presets the radio channel used by the OpenThread network. This command can be used to force both Zigbee and OpenThread networks to use the same radio channel.|
|dataset_pan_id|Presets the PAN ID on the device to help with joining the OpenThread network out-of-band.|
|dataset_extended_pan_id|Presets the extended PAN ID on the device to help with joining the OpenThread network out-of-band.|
|ifconfig_up|Enables OpenThread interface.|
|thread_start|Enables and attaches OpenThread protocol operation.|
|thread_state|Reads current status: offline, disabled, detached, child, router, or leader.|

##### Concurrent Multiprotocol (CMP) Sample Application Matter Zigbee Lighting Example

This sample app demonstrates the usage of the Matter Stack over Thread, alongside the Silicon Labs Zigbee stack. As such, the lighting device represented by select EFR parts are able to receives On/Off commands from both a Zigbee Switch ([Z3 Switch](https://www.silabs.com/support/training/Zigbee-application-layer-concepts/building-a-Zigbee-3-0-switch-and-light-from-scratch) sample app) and a Matter Controller (Google, Apple, Chip-tool) as represented in the diagram below.

![image info](/concurrent-mp-soc/0.1/images/sld678-image28.png)

###### Building

###### Prerequisites (heading level 7)

The following prerequisites need to be installed on the host machine:

- ARM GCC 12.2
- ZAP (version 2024.05.07 or greatest)
- SLC-CLI
- Various environment variables set like:  
  - ARM_GCC_DIR  
  - TOOLDIR  
  - STUDIO_ADAPTER_PACK_PATH

###### Build Variants (heading level 7)

As of now, this sample app supports two different build variants:

- Sequential Zigbee & Matter
- Concurrent Zigbee & Matter

###### Sequential Zigbee and Matter (heading level 8)

In this scenario, the node will initially act as a Zigbee device with the full set of working Zigbee features. Once the device is commissioned onto a Matter fabric, the device will switch into being a full Matter Node on the network.

###### Concurrent Zigbee and Matter (heading level 8)

In this scenario, the node will act as both a Zigbee device and a Matter device capable of receiving commands from both protocols at the same time.

###### Known Limitations

- CLI and Log outputs are defaulted to the uart interface. Instead of having logs on RTTViewer and CLI/ Matter shell on the UART, everything is forwarded to the uart to prevent weird routing of log messages and uart commands. Issue is present with SISDK 2024.6.0.
- Single Channel listening: In concurrent mode, with SISDK 2024.6.0, the radio mux only supports a single channel for listening. This means that when the device is commissioned on the Matter network, both Zigbee and OpenThread need to operate on the same channel. Since the OTBR can be a fully closed product like a Google Nest Hub, an Apple TV, or an Amazon Echo, there is no control over which channel is going to be selected. This makes channel steering features, like Touchlink, incompatible with this sample app because of this limitation in concurrent mode.

> **Note**: As of SiSDK 2024.12.0 for MG26 parts and SiSDK 2025.6.1 for SiMG301 parts, support for Fast Channel Switching is available. This allows that both Zigbee and OpenThread can operate on different channels.

###### Expected Behavior

Once the application is build and flashed onto the device, you should see the Matter QR code displayed and if you're using a BLE sniffer like the EFRConnect app you should be able to see the Silabs-Light being advertised and ready to be commissioned into a Matter network.

###### Sequential Zigbee and Matter (heading level 7)

With this build variant, your device will act as a Zigbee device as long as no Matter fabric is present on the device. Once the device is successfully commissioned with Matter, the Zigbee network will be shut down until the next factory reset.

Features like Touchlink will work with this build variant since there is no need to listen on multiple channels at once.

###### Concurrent Zigbee and Matter (heading level 7)

With this build variant, the light can be controlled simultaneously from the Matter side or the Zigbee side. For the best user experience, it is advised to commission the Matter side **first** as the OTBR will trigger a channel switch on the Zigbee side. Should Zigbee be commissioned first, then upon completion of the Matter commissioning process, a network leave followed by a network start will be issued to the Zigbee stack in order to achieve a successful channel switch without missing any packets from the Matter side. As such, all previously paired devices on the Zigbee side will have to be re-paired with the device. Again, this is a limitation present in SiSDK 2024.6.0.

###### Building Command

You can build this application from going to the Matter Extension repo by running the following commands:

```c
./slc/build.sh slc/sample-app/zigbee-matter-light/efr32/zigbee-matter-light.slcp brd4116a,matter_zigbee_sequential\;matter

./slc/build.sh slc/sample-app/zigbee-matter-light/efr32/zigbee-matter-light.slcp brd4187c,matter_zigbee_concurrent\;matter
```

Building it without any extra component will default to the concurrent version:

```c
./slc/build.sh slc/sample-app/zigbee-matter-light/efr32/zigbee-matter-light.slcp brd4187c
```

If you are building through Simplicity Studio, you need to install the latest SiSDK along with including the Matter Extension. You can then select a supported radio part (ie. MG24, MG26, SiMG301) and select the Zigbee Matter Light Application. You can then generate the project and build the application.

###### Customizing

This sample app can be customized to fit most of your needs.

###### Matter (heading level 7)

Just like any other Matter sample app, you can add and remove the Matter extension components that you need.

###### Zigbee (heading level 7)

Just like any other Zigbee sample app, you can add and remove the Matter extension components that you need. However, if you want to support install code, you need to actually modify the configuration file present within this project to set `SL_MATTER_CMP_SECURE_ZIGBEE` to 1. This will disable TouchLink and use the provided install code and EUI64 present in the same configuration file (`sl_cmp_config.h`).

###### DataModel

Since the Matter and Zigbee data models are quite similar, this project only needs one single zap file for both protocols. For the best user experience, the endpoint configuration should match as closely as possible on the two protocols (except for endpoint 0 which is protocol specific).

##### Converting a Zigbee Application into a Zigbee-OpenThread CMP Application

This section describes the steps involved in converting a Z3Light into a Concurrent Multiprotocol application that includes the OpenThread stack.

1. Use the Simplicity Studio **Create New Project** wizard to create a Zigbee – SoC Light project for your board of choice.
2. Open the **Software Components** tab of the generated project to add the OpenThread > **Stack (FTD)** component. Note that the addition of this component automatically adds a Real Time Operating System (RTOS) to the project.  
   ![image](/concurrent-mp-soc/0.1/images/sld678-image3.png)
3. Select the IO Stream > Driver > IOS Stream: USART > **vcom** component and configure it by increasing **Receive buffer size** to 128.  
   ![image](/concurrent-mp-soc/0.1/images/sld678-image4.png)  
   ![image](/concurrent-mp-soc/0.1/images/sld678-image5.png)
4. Search for the **Memory Manager region** component and increase stack size to 4608 to account for the OpenThread stack usage. Heap size is set automatically to use up any remaining RAM.  
   ![image](/concurrent-mp-soc/0.1/images/sld678-image6.png)  
   ![image](/concurrent-mp-soc/0.1/images/sld678-image7.png)

> **Note**: On Series 3 Platforms all sample application are required to be run by an RTOS. Therefore, if you are using a Series 3 part you will not need to follow Step 5.

1. The RTOS selection defaults to FreeRTOS with an option to use Micrium RTOS instead. If your project uses Micrium, select Micrium > Common > **Micrium OS Common Module Core** component and configure it to decrease “size of heap memory” to 0 to prevent Micrium RTOS from allocating its own heap memory.  
   ![image](/concurrent-mp-soc/0.1/images/sld678-image8.png)  
   ![image](/concurrent-mp-soc/0.1/images/sld678-image9.png)
2. Add OpenThread CLI commands by installing the Zigbee > Zigbee 3.0 > **OpenThread CLI using Silabs unified platform** (ot_up_cli) component.  
   ![image](/concurrent-mp-soc/0.1/images/sld678-image10.png)
3. Select the OpenThread > **Platform Abstraction** component and configure the following:  
   - OpenThread stack task priority is set to 49 to match the Zigbee stack task RTOS priority.  
   - OpenThread app task priority is set to 48 to match Zigbee app framework RTOS priority  
   ![image](/concurrent-mp-soc/0.1/images/sld678-image11.png)  
   ![image](/concurrent-mp-soc/0.1/images/sld678-image12.png)
4. Search for **Circular Queue** and configure queue size to 16. This will ensure proper operation of the OpenThread radio Rx buffers.  
   ![image](/concurrent-mp-soc/0.1/images/sld678-image13.png)  
   ![image](/concurrent-mp-soc/0.1/images/sld678-image14.png)
5. Right click the Z3Light project in Simplicity Studio’s Project Explorer view and click Properties. Open C/C++ Build Settings and Under GNU ARM C Compiler, select Preprocessor. Add two preprocessor define symbols:  
   - For Micrium OS Only - OS_CFG_COMPAT_INIT (Used in conjunction with LIB_MEM_CFG_HEAP_SIZE to allow the application to handle heap allocation)
6. Click **Apply and Close** to save.  
   ![image](/concurrent-mp-soc/0.1/images/sld678-image15.png)
7. Open app.c file in the project folder and add the code below to the beginning of the file to initialize OpenThread. Save file and build the project.  
   ```C  
   #if defined(OPENTHREAD_FTD)  
     #include <assert.h>  
     #include <openthread-core-config.h>  
     #include <openthread/config.h>  
     
     #include <openthread/ncp.h>  
     #include <openthread/diag.h>  
     #include <openthread/tasklet.h>  
     
     #include "openthread-system.h"  
     
   static otInstance *     sInstance       = NULL;  
     
   void sl_ot_create_instance(void)  
   {  
     #if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE  
     size_t   otInstanceBufferLength = 0;  
     uint8_t *otInstanceBuffer       = NULL;  
     
     // Call to query the buffer size  
     (void)otInstanceInit(NULL, &otInstanceBufferLength);  
     
     // Call to allocate the buffer  
     otInstanceBuffer = (uint8_t *)malloc(otInstanceBufferLength);  
     assert(otInstanceBuffer);  
     
     // Initialize OpenThread with the buffer  
     sInstance = otInstanceInit(otInstanceBuffer, &otInstanceBufferLength);  
     #else  
     sInstance = otInstanceInitSingle();  
     #endif  
     assert(sInstance);  
   }  
     
   otInstance *otGetInstance(void)  
   {  
     return sInstance;  
   }  
     
   #endif //#if defined(OPENTHREAD_FTD)  
   ```
8. Force generate the project and build project.  
   ![image](/concurrent-mp-soc/0.1/images/sld678-image16.png)

This application can now form a distributed Zigbee network or join any Zigbee network (centralized or distributed). It can also function as a leader, child, or router on the OpenThread network.

###### Optional - Adding Bluetooth to the Concurrent Multiprotocol Application

This section describes the steps involved in adding Bluetooth to the above application. Search for and install the following components:

- `bluetooth_stack` in the software components  
  ![image](/concurrent-mp-soc/0.1/images/sld678-image17.png)
- `gatt_configuration`  
  ![image](/concurrent-mp-soc/0.1/images/sld678-image18.png)
- `bluetooth_feature_legacy_advertiser`  
  ![image](/concurrent-mp-soc/0.1/images/sld678-image19.png)
- `bluetooth_feature_connection`  
  ![image](/concurrent-mp-soc/0.1/images/sld678-image20.png)
- `bluetooth_feature_gatt` – Install the GATT Client and GATT Server  
  ![image](/concurrent-mp-soc/0.1/images/sld678-image21.png)  
  ![image](/concurrent-mp-soc/0.1/images/sld678-image22.png)
- `bluetooth_feature_legacy_scanner`  
  ![image](/concurrent-mp-soc/0.1/images/sld678-image23.png)
- `bluetooth_feature_sm`  
  ![image](/concurrent-mp-soc/0.1/images/sld678-image24.png)
- `bluetooth_feature_system`  
  ![image](/concurrent-mp-soc/0.1/images/sld678-image25.png)
- `zigbee_ble_dmp_cli`  
  ![image](/concurrent-mp-soc/0.1/images/sld678-image26.png)

In addition to the above, add the following snippet of code in the project app.c file. This sample code provides an implementation for the Bluetooth event handler (`sl_bt_on_event` function).

```C
#ifdef SL_CATALOG_BLUETOOTH_PRESENT

//------------------------------------------------------------------------------
// Bluetooth Event handler

#include "zigbee_app_framework_event.h"
#include "sl_zigbee_system_common.h"
#include "sl_bluetooth.h"
#include "sl_bluetooth_advertiser_config.h"
#include "sl_bluetooth_connection_config.h"
#include "sl_component_catalog.h"
static uint8_t cli_adv_handle;
static uint8_t activeBleConnections = 0;
void zb_ble_dmp_print_ble_address(uint8_t *address)
{
  sl_zigbee_app_debug_print("\nBLE address: [%02X %02X %02X %02X %02X %02X]\n",
                            address[5], address[4], address[3],
                            address[2], address[1], address[0]);
}
struct {
  bool inUse;
  bool isMaster;
  uint8_t connectionHandle;
  uint8_t bondingHandle;
  uint8_t remoteAddress[6];
} bleConnectionTable[SL_BT_CONFIG_MAX_CONNECTIONS];

void bleConnectionInfoTableInit(void)
{
  uint8_t i;
  for (i = 0; i < SL_BT_CONFIG_MAX_CONNECTIONS; i++) {
    bleConnectionTable[i].inUse = false;
  }
}
uint8_t bleConnectionInfoTableFindUnused(void)
{
  uint8_t i;
  for (i = 0; i < SL_BT_CONFIG_MAX_CONNECTIONS; i++) {
    if (!bleConnectionTable[i].inUse) {
      return i;
    }
  }
  return 0xFF;
}

bool bleConnectionInfoTableIsEmpty(void)
{
  uint8_t i;
  for (i = 0; i < SL_BT_CONFIG_MAX_CONNECTIONS; i++) {
    if (bleConnectionTable[i].inUse) {
      return false;
    }
  }
  return true;
}

uint8_t bleConnectionInfoTableLookup(uint8_t connHandle)
{
  uint8_t i;
  for (i = 0; i < SL_BT_CONFIG_MAX_CONNECTIONS; i++) {
    if (bleConnectionTable[i].inUse
        && bleConnectionTable[i].connectionHandle == connHandle) {
      return i;
    }
  }
  return 0xFF;
}

void bleConnectionInfoTablePrintEntry(uint8_t index)
{
  assert(index < SL_BT_CONFIG_MAX_CONNECTIONS
        && bleConnectionTable[index].inUse);
  sl_zigbee_app_debug_println("**** Connection Info index[%d]****", index);
  sl_zigbee_app_debug_println("connection handle 0x%x",
                              bleConnectionTable[index].connectionHandle);
  sl_zigbee_app_debug_println("bonding handle = 0x%x",
                              bleConnectionTable[index].bondingHandle);
  sl_zigbee_app_debug_println("local node is %s",
                               (bleConnectionTable[index].isMaster) ? "master" : "slave");
  sl_zigbee_app_debug_print("remote address: ");
  zb_ble_dmp_print_ble_address(bleConnectionTable[index].remoteAddress);
  sl_zigbee_app_debug_println("");
  sl_zigbee_app_debug_println("*************************");
}

void sl_bt_on_event(sl_bt_msg_t* evt)
{
  switch (SL_BT_MSG_ID(evt->header)) {
    case sl_bt_evt_system_boot_id: {
      bd_addr ble_address;
      uint8_t type;
      sl_status_t status = sl_bt_system_hello();
      sl_zigbee_app_debug_println("BLE hello: %s",
                                   (status == SL_STATUS_OK) ? "success" : "error");
      #define SCAN_WINDOW 5
      #define SCAN_INTERVAL 10

      status = sl_bt_scanner_set_parameters(sl_bt_scanner_scan_mode_active,
                                            (uint16_t)SCAN_INTERVAL,
                                            (uint16_t)SCAN_WINDOW);
      status = sl_bt_system_get_identity_address(&ble_address, &type);
      zb_ble_dmp_print_ble_address(ble_address.addr);

      status = sl_bt_advertiser_create_set(&cli_adv_handle);
      if (status) {

        sl_zigbee_app_debug_println("sl_bt_advertiser_create_set status 0x%02x", status);
      }
    }
    break;

    case sl_bt_evt_connection_opened_id: {
      sl_zigbee_app_debug_println("sl_bt_evt_connection_opened_id \n");
      sl_bt_evt_connection_opened_t *conn_evt =
        (sl_bt_evt_connection_opened_t*) &(evt->data);
      uint8_t index = bleConnectionInfoTableFindUnused();
      if (index == 0xFF) {
        sl_zigbee_app_debug_println("MAX active BLE connections");
        assert(index < 0xFF);
      } else {
        bleConnectionTable[index].inUse = true;
        bleConnectionTable[index].isMaster = (conn_evt->role > 0);
        bleConnectionTable[index].connectionHandle = conn_evt->connection;
        bleConnectionTable[index].bondingHandle = conn_evt->bonding;
        (void) memcpy(bleConnectionTable[index].remoteAddress,
                      conn_evt->address.addr, 6);

        activeBleConnections++;
        sl_bt_connection_set_preferred_phy(conn_evt->connection, sl_bt_test_phy_1m, 0xff);
        sl_zigbee_app_debug_println("BLE connection opened");
        bleConnectionInfoTablePrintEntry(index);
        sl_zigbee_app_debug_println("%d active BLE connection",
                                    activeBleConnections);
      }
    }
    break;
    case sl_bt_evt_connection_phy_status_id: {
      sl_bt_evt_connection_phy_status_t *conn_evt =
         (sl_bt_evt_connection_phy_status_t *)&(evt->data);
      // indicate the PHY that has been selected
      sl_zigbee_app_debug_println("now using the %dMPHY\r\n",
                                  conn_evt->phy);
    }
    break;
    case sl_bt_evt_connection_closed_id: {
      sl_bt_evt_connection_closed_t *conn_evt =
        (sl_bt_evt_connection_closed_t*) &(evt->data);

      uint8_t index = bleConnectionInfoTableLookup(conn_evt->connection);
      assert(index < 0xFF);

      bleConnectionTable[index].inUse = false;
      if  ( activeBleConnections ) {
        --activeBleConnections;
      }

      sl_zigbee_app_debug_println(
        "BLE connection closed, handle=0x%02x, reason=0x%02x : [%d] active BLE connection",
        conn_evt->connection, conn_evt->reason, activeBleConnections);
    }
    break;

    case sl_bt_evt_scanner_legacy_advertisement_report_id: {
      sl_zigbee_app_debug_print("Scan response, address type=0x%02x",
                                evt->data.evt_scanner_legacy_advertisement_report.address_type);
      zb_ble_dmp_print_ble_address(evt->data.evt_scanner_legacy_advertisement_report.address.addr);
      sl_zigbee_app_debug_println("");
    }
    break;

    case sl_bt_evt_connection_parameters_id: {
      sl_bt_evt_connection_parameters_t* param_evt =
        (sl_bt_evt_connection_parameters_t*) &(evt->data);
      sl_zigbee_app_debug_println(
        "BLE connection parameters are updated, handle=0x%02x, interval=0x%02x, latency=0x%02x, timeout=0x%02x, security=0x%02x",
        param_evt->connection,
        param_evt->interval,
        param_evt->latency,
        param_evt->timeout,
        param_evt->security_mode);
    }
    break;

    case sl_bt_evt_gatt_service_id: {
      sl_bt_evt_gatt_service_t* service_evt =
        (sl_bt_evt_gatt_service_t*) &(evt->data);
      uint8_t i;
      sl_zigbee_app_debug_println(
        "GATT service, conn_handle=0x%02x, service_handle=0x%04x",
        service_evt->connection, service_evt->service);
      sl_zigbee_app_debug_print("UUID=[");
      for (i = 0; i < service_evt->uuid.len; i++) {
        sl_zigbee_app_debug_print("0x%04x ", service_evt->uuid.data[i]);
    }
    sl_zigbee_app_debug_println("]");
  }
  break;

  default:
  break;
  }
}
void zb_ble_dmp_print_ble_connections(void)
{
  uint8_t i;
  for (i = 0; i < SL_BT_CONFIG_MAX_CONNECTIONS; i++) {
    if (bleConnectionTable[i].inUse) {
      bleConnectionInfoTablePrintEntry(i);
    }
  }
}
#endif //SL_CATALOG_BLUETOOTH_PRESENT
```

#### Using the Co-Processor Communication Daemon (CPCd)

##### Using the Co-Processor Communication Daemon (CPCd)

> **Note: This section replaces _AN1351: Using the Co-Processor Communication Daemon (CPCd)_. Further updates to this application note will be provided here**.

This application note guides the user through the steps needed to properly configure and run the CPC daemon (CPCd) on Linux or Android. It does not discuss how to use the CPC Library to write applications that interact with CPCd.

###### Key Points

- Theory of operation
- Compiling and installing CPCd
- Configuring CPCd
- Troubleshooting
- Considerations

##### Introduction

Co-Processor Communication (CPC) enables one host system to communicate with a Network co-processor device (NCP), also named the secondary device or secondary, by physical transport (UART, SPI, and so on).

In CPC, data transfers between processors are segmented in sequential packets. Transfers are guaranteed to be error-free and sent in order. Multiple applications can send or receive on the same endpoint without worrying about collisions.

A CPC daemon (CPCd) is provided to allow applications on Linux to interact with a secondary running CPC.

The CPC daemon (CPCd) is distributed as three components:

- The daemon binary **(cpcd)**
- A library and associated header files that enable C applications to interact with the daemon **(libcpc.so)**
- A configuration file **(cpcd.conf)**

##### Theory of Operation

CPCd uses Unix sockets configured as sequential packets to transfer data with the Linux host applications. Data is then forwarded to the co-processor over a serial link. The Unix sockets, used to transfer data with applications that use the CPC Library (libcpc.so), are instantiated in the /tmp/cpcd folder. A description of the CPC library usage is out of scope for this application note.

![Co-Processor Communication Overview](/using-co-processor-communication-daemon/0.1/images/sld804-image1.png)

##### Compiling, Installing, and Configuring CPCd

###### Downloading

Download the daemon source files from Silicon Labs GitHub project:

[https://github.com/SiliconLabs/cpc_daemon](https://github.com/SiliconLabs/cpc_daemon)

The main branch contains the latest official versions. Early access versions are available in the specific version branches.

###### Compiling CPCd and the CPC Library

The build essential and CMake packages in Linux are required for this step. Compile the CPC daemon in the source folder using the following commands:

```sh
mkdir build
cd build
cmake ../
make
```

###### Installing CPCd

Super-user permissions are required to install the daemon, cpclib, and the configuration file. These can be installed with the following commands:

```sh
make install
```

The following components will be installed:

- /usr/local/lib/libcpc.so.0.1
- /usr/local/lib/libcpc.so.1
- /usr/local/lib/libcpc.so
- /usr/local/include/sl_enum.h
- /usr/local/include/sl_cpc.h
- /usr/local/bin/cpcd
- /etc/cpcd.conf

Once installed, CPCd can be executed by invoking the cpcd command.

###### Configuring CPCd

When running the daemon without arguments, it starts with the default configuration file installed in the previous step. To specify a different configuration file, use the --conf argument. For example:

```sh
cpcd --conf <configuration file path>
```

###### Obtaining the Version of CPCd

If CPCd is started with the -v or --version argument, the daemon first prints the version of CPCd and exit. For example:

```sh
cpcd –version
```

###### Available Configurations

CPCd is configured in a key/value manner in the **cpcd.conf** file.

**Table**: CPCd configuration

|Description|Key|Possible Values|Default Value|Mandatory|
|---|---|---|---|---|
|Instance Name|INSTANCE_NAME|string|cpcd_0|No|
|Bus type selection|BUS_TYPE|UART SPI|UART|Yes|
|SPI device file|SPI_DEVICE_FILE|Any path to SPI device file|/dev/spidev0.0|Yes if BUS_TYPE is SPI|
|SPI Chip Select GPIO #|SPI_CS_GPIO|Any GPIO # (1)|24|Yes if BUS_TYPE is SPI|
|SPI RX IRQ GPIO|SPI_RX_IRQ_GPIO|Any GPIO # (1)|23|Yes if BUS_TYPE is SPI|
|SPI Bitrate|SPI_DEVICE_BITRATE|Any (2)|1000000|Yes if BUS_TYPE is SPI|
|SPI Mode|SPI_DEVICE_MODE|SPI_MODE_0 (3) SPI_MODE_1 SPI_MODE_2 SPI_MODE_3|SPI_MODE_0|Yes if BUS_TYPE is SPI|
|UART Device File|UART_DEVICE_FILE|Any (4)|/dev/serial0|Yes if BUS_TYPE is UART|
|UART Baud Rate|UART_DEVICE_BAUD|1200 (5) 2400 4800 19200 38400 57600 115200|115200|Yes if BUS_TYPE is UART|
|UART Hardware Flow Control|UART_HARDFLOW|True or false|False|Yes if BUS_TYPE is UART|
|Trace to stdout|STDOUT_TRACE|True or false|False|No|
|Trace to a file located under TRACES_FOLDER|TRACE_TO_FILE|True or False|False|No|
|Destination folder when TRACE_TO_FILE is enabled|TRACES_FOLDER|Any path that the CPCd can access|./cpcd-traces|No|
|The maximum number of open file descriptors.|RLIMIT_NOFILE|Depends on the OS limit|2000|No|
|Disable the encryption over CPC endpoints|DISABLE_ENCRYPTION|True or false|False|No|

(1) Make sure the CPC daemon has enough permissions to access this GPIO.

(2) This setting depends on various factors. The bitrate needs to satisfy both side requirements.

(3) Refer to section 2.6 for additional details.

(4) This setting depends on the Linux SOC.

(5) These baud rates are typical, but any value that meets both requirements can be used.

###### Available SPI Modes

The SPI_DEVICE_MODE configuration allows SPI clock polarity and phase to be configured, as shown in the following table.

**Table**: SPI Mode configuration

|Mode|Clock Polarity (CPOL)|Clock Phase (CPHA)|
|---|---|---|
|SPI_MODE_0|0|0|
|SPI_MODE_1|0|1|
|SPI_MODE_2|1|0|
|SPI_MODE_3|1|1|

##### Troubleshooting

If an error or a warning occurs during CPCd runtime, it prints to the console STDERR. If additional debugging is required, tracing can be enabled.

> **Note**: Enabling traces may impact performance.

###### Tracing to the Standard Output (stdout)

When the configuration STDOUT_TRACE is enabled, the CPC daemon prints traces to the console.

###### Tracing to a File

When the configuration TRACE_TO_FILE is enabled. the CPC daemon prints traces to a file. The tracing file name contains the date and timestamp. This file is placed in the folder specified in the configuration TRACES_FOLDER.

The trace file has the following format:

```sh
trace-<year>-<month>-<day>-<hour>-<minute>-<second>.txt
```

The timestamp uses the operating system’s local time zone.

> **Note**: Only enable tracing to a file when debugging, as log file size increases over time.

##### Considerations

- The SPI driver uses a **sysfs** class GPIO as a chip select. Make sure the daemon has the proper permissions to access this GPIO.
- If the provided GPIO for the SPI chip select is already used by another driver, it needs to be deactivated and enabled as standard GPIO. In Linux this is usually done via the device tree.
- CPCd uses Unix sockets to exchange information with the Linux applications that use the CPC library. These sockets are stored under /etc/cpcd. Only users with the appropriate permissions should be able to access these sockets. CPCd inherits the permission of the user who starts the CPC daemon.
- Make sure no other application is using the serial bus at the same time as CPCd.
- Sensitive information can be exposed when tracing to a file is enable. Only enable tracing during development, for debugging purposes only. Refer to the TRACE_TO_FILE and STDOUT_TRACE configurations.

### Bootloading

#### Bootloading Embedded Applications

Bootloading allows you to update application firmware images on your devices. This section provides background information about bootloading using the Silicon Labs Gecko Bootloader.

- [**Bootloader Fundamentals**](/openthread/3.1.0/bootloader-fundamentals): Introduces bootloading for Silicon Labs networking devices. Discusses the Gecko Bootloader as well as legacy Ember and Bluetooth bootloaders, and describes the file formats used by each.
- [**Silicon Labs Gecko Bootloader User’s Guide for Series 3 and Higher**](/openthread/3.1.0/bootloader-user-guide-series3-and-higher): Describes the high-level implementation of the Silicon Labs Gecko Bootloader for Series 3 microcontrollers, SoCs (System on Chips), and NCPs (Network Co-Processors), and provides information on different aspects of configuring the Gecko Bootloader.
- [**Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)**](/openthread/3.1.0/bootloader-user-guide-gsdk-4): Describes the high-level implementation of the Silicon Labs Gecko Bootloader for EFR32 SoCs and NCPs, and provides information on how to get started using the Gecko Bootloader with Silicon Labs wireless protocol stacks in GSDK 4.0 and higher.
- [**Series 2 and Series 3 Secure Boot with RTSL**](/openthread/3.1.0/series2-secure-boot-with-rtsl): Describes the design of Secure Boot with RTSL (Root of Trust and Secure Loader) on Series 2 and Series 3 devices. It also provides examples of how to implement the Secure Boot process.
- [**Transitioning to the Updated Gecko Bootloader in GSDK 4.0 and Higher**](/openthread/3.1.0/bootloader-transitioning-guide-gsdk-v40-and-higher): Gecko Bootloader v2.x, introduced in GSDK 4.0, contains a number of changes compared to Gecko Bootloader v1.x. This document describes the differences between the versions, including how to configure the new Gecko Bootloader in Simplicity Studio.

#### Bootloader Fundamentals

##### Bootloader Fundamentals

> **Note: This section replaces _UG103.06: Bootloader Fundamentals_. Further updates to this user guide will be provided here**.

This guide introduces bootloading for Silicon Labs networking devices. It describes the concepts of standalone and application bootloaders and discusses their relative strengths and weaknesses. In addition, it looks at design and implementation details for each method. Finally, it describes the bootloader file format.

Silicon Labs’ _Fundamentals_ series covers topics that project managers, application designers, and developers should understand before beginning to work on an embedded networking solution using Silicon Labs chips, networking stacks such as EmberZNet PRO or Silicon Labs Bluetooth, and associated development tools. These guides can be used as a starting place for anyone needing an introduction to developing wireless networking applications, or who is new to the Silicon Labs development environment.

##### Introduction

The bootloader is a program stored in reserved flash memory that can initialize a device, update firmware images, and possibly perform some integrity checks. Firmware image update occurs on demand, either by serial communication or over the air. Production-level programming is typically done during the product manufacturing process yet it is desirable to be able to reprogram the system after production is complete. More importantly, it is valuable to be able to update the device's firmware with new features and bug fixes after deployment. The firmware image update capability makes that possible.

Silicon Labs supports devices that do not use a bootloader, but this requires external hardware such as a Debug Adapter (Silicon Labs ISA3 or Wireless Starter Kit (WSTK)) or third-party SerialWire/JTAG programming device to update the firmware. Devices without a bootloader have no supported way of updating the firmware once they are deployed, which is why Silicon Labs strongly advocates implementing a bootloader.

In March of 2017, Silicon Labs introduced the Gecko Bootloader, a code library configurable through Simplicity Studio’s IDE to generate bootloaders that can be used with a variety of Silicon Labs protocol stacks. The Gecko Bootloader can be used with EFM32 and EFR32 Series 1 and later devices. The Gecko Bootloader was restructured into a component-based design and released with Gecko SDK Suite (GSDK) 4.0 in December of 2021. This new version is documented in [Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/) or [Silicon Labs Gecko Bootloader User’s Guide for Series 3 and Higher](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-series3-and-higher/), along with other documents. Documentation for older versions is installed with their respective SDKs.

The Gecko Bootloader uses a customized update image file format. The update image file consumed by a Gecko Bootloader-generated application bootloader is a GBL (Gecko BootLoader) file, and is described in [Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/) or [Silicon Labs Gecko Bootloader User’s Guide for Series 3 and Higher](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-series3-and-higher/).

Bootloading a firmware update image can be accomplished in two ways. The first is Over-The-Air (OTA), that is, through the wireless network, as shown in the following figure.

![OTA Bootloading Use Case](/bootloader-fundamentals/0.1/images/ota-bootloading-use-case.jpg)

The second is through a hardwired link to the device. The following figure represents the serial bootloader use cases for SoCs (System on Chips) using either a UART (Universal Asynchronous Receiver/Transmitter), SPI (Serial Protocol Interface), or USB (Universal Serial Bus) interface, and for NCPs (Network Coprocessors) using either UART or SPI.

![Serial Bootloading Use Cases](/bootloader-fundamentals/0.1/images/serial-bootloading-use-cases.jpg)

Silicon Labs networking devices use bootloaders that perform firmware updates in two different modes: standalone (also called standalone bootloaders) and application (also called application bootloaders). Application bootloaders are further divided into those that use external storage for the download update image, and those that use local storage. These bootloader types are discussed in the next two sections.

The firmware update situations described in this document assume that the source node (the device sending the firmware image to the target through a serial or OTA link) acquires the new firmware through some other means. For example, if a device on the local Zigbee network has an Ethernet gateway attached, this device could get or receive these firmware updates over the Internet. This necessary part of the firmware update process is system-dependent and beyond the scope of this document.

###### Standalone Bootloader

A standalone bootloader is a program that uses an external communication interface, such as UART or SPI, to get an application image. Standalone firmware update is a single-stage process that allows the application image to be placed into flash memory, overwriting the existing application image, without the participation of the application itself. Very little interaction occurs between the standalone bootloader and the application running in flash. In general, the only time that the application interacts with the bootloader is when it requests a reboot into the bootloader. Once the bootloader is running, it receives firmware update packets containing the (new) firmware image either by physical connections such as UART or SPI, or by the radio (over-the-air).

When the firmware update process is initiated, the new code overwrites the existing stack and application code. If any errors occur during this process, the application cannot be recovered, and the process must start over. For information about configuring the Gecko Bootloader as a standalone bootloader, see [Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/) or [Silicon Labs Gecko Bootloader User’s Guide for Series 3 and Higher](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-series3-and-higher/).

###### Application Bootloader

An application bootloader begins the firmware update process after the running application has completely downloaded the update image file. The application bootloader expects that the image either lives in external memory accessible by the bootloader or in a portion of main flash memory (if the chip has sufficient memory to support this local storage model).

The application bootloader relies on the application to acquire the new firmware image. This image can be downloaded by the application in any way that is convenient (UART, over-the-air, etc.) but it must be stored into a region referred to as the download space. The download space is typically an external memory device such as an EEPROM or dataflash, but it can also be a section of the chip’s internal flash when using a local storage variant of the application bootloader. Once the new image has been stored, the application bootloader is then called to validate the new image and copy it from the download space to flash.

Since the application bootloader does not participate in acquiring the image, and the entire image is downloaded before the firmware update process is started, download errors do not adversely affect the running image. The download process can be restarted or paused to acquire the image over time. The integrity of the downloaded update image can be verified before initiating the firmware update process, to prevent a corrupt or non-functional image from being applied.

The Gecko Bootloader can be configured to accept a list of multiple upgrade images to attempt to verify and apply. This allows the Gecko Bootloader to store what is in effect a backup copy of the update image, which it can access if the first image is corrupt.

Note that the EmberZNet NCP platform does not utilize an application bootloader because the application code resides on the host rather than on the NCP directly. Instead a device acting as a serial coprocessor would utilize a standalone bootloader designed to accept code over the same serial interface as the expected NCP firmware uses. However, the host application (residing on a separate MCU from the NCP) can utilize whatever bootloading scheme is appropriate.

For more information on application bootloaders, see [Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/) or [Silicon Labs Gecko Bootloader User’s Guide for Series 3 and Higher](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-series3-and-higher/).

##### About the Gecko Bootloader

The Silicon Labs Gecko Bootloader is a configurable code library that can be used with all the newer Silicon Labs Gecko MCUs and wireless MCUs. It uses a specially-formatted update image file called a GBL file. The Gecko Bootloader has a two-stage design on Series 1 devices, where a minimal first stage bootloader is used to update the main bootloader. On Series 2 devices, the first stage bootloader is replaced by a Secure Engine and the Gecko Bootloader consists only of the main bootloader. The Secure Engine may be hardware-based, or virtual (software). If hardware-based, the implementation may be either with or without Secure Vault. Throughout this document, the following conventions will be used.

- **HSE**: Hardware Secure Engine, either with or without Secure Vault if not specified
- **VSE**: Virtual Secure Engine
- **SE**: Secure Engine (either HSE or VSE, in general)

Having a first stage bootloader or SE allows for field updates of the main bootloader, including adding new capabilities, changing communication protocols, adding new security features and fixes, and so on. The Gecko Bootloader consists of three component parts:

**Core:** The bootloader core contains the main function of both bootloader stages. It also contains functionality to write to the internal main flash, to perform a bootloader update, and to reset into the application flagging applicable reset reasons.

**Driver:** Different bootloading applications require different hardware drivers for use by the other components of the bootloader.

**Component/Plugin:** All parts of the main bootloader that are either optional or selectable for different configurations are implemented as components (in GSDK 4.0 and higher) or previously in plugins. Each component/plugin has a generic header file, and one or more implementations. The current release contains components for functionality like UART and SPI communication protocols, SPI flash storage, internal flash storage, and different cryptographic operations.

###### Features

Gecko Bootloader features include:

- Field-updatable
- Secure boot
- Signed GBL firmware update image file
- Encrypted GBL firmware update image file

These features are summarized in the following sections and described in more detail in [Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/) or [Silicon Labs Gecko Bootloader User’s Guide for Series 3 and Higher](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-series3-and-higher/). Protocol-specific information about using the Gecko Bootloader may be found in the following documents:

- [Using the Gecko Bootloader with EmberZNet](https://docs.silabs.com/zigbee/latest/using-gecko-bootloader-with-zigbee/)
- [Bootloading and OTA with Silicon Labs Connect SDK v3.x](https://docs.silabs.com/connect-stack/latest/bootloading-and-ota-with-connect-v3x)
- [Using the Gecko Bootloader with Silicon Labs Bluetooth Applications](https://docs.silabs.com/bluetooth/latest/using-gecko-bootloader-with-bluetooth-apps/)

###### Field-Updatable (heading level 7)

###### Series 1 (heading level 8)

On EFM32 and EFR32 Series 1 devices, field update capability for the Gecko bootloader is provided by a two-stage design where the bootloader has a first stage and a main stage. The minimal first stage of the bootloader is not field updatable, and can only update the main bootloader by reading from and writing to fixed addresses in internal flash memory. To perform a main bootloader update, the running main bootloader verifies the integrity and authenticity of the bootloader update image, writes it to internal flash, and issues a reboot into the first stage bootloader. The first stage bootloader verifies the integrity of the main bootloader update image before copying it to the main bootloader location, completing the update

###### Series 2 (heading level 8)

On Series 2 devices, field update capability for the Gecko bootloader is provided by the SE. To perform a main bootloader update, the running main bootloader verifies the integrity and authenticity of the bootloader update image, writes it to internal flash, and requests that the SE installs the update. The SE optionally verifies the authenticity of the main bootloader update image before copying it to the main bootloader location, completing the update. The same mechanism can be used to update the SE itself.

###### Secure Boot (heading level 7)

Secure boot is designed to prevent an untrusted application from running on the device. When Secure Boot is enabled, the bootloader enforces cryptographic signature verification of the application image on every boot using asymmetric cryptography. The signature algorithm used is ECDSA-P256-SHA256. The public key is written to the device during manufacturing, while the private key is kept secret. This ensures that the application was created and signed by a trusted party.

###### Signed GBL Update Image File (heading level 7)

The Gecko Bootloader supports enforcing cryptographic signature verification of the update image file in addition to Secure Boot. This allows the bootloader and application to verify that the application or bootloader update comes from a trusted source before starting the update process. The signature algorithm used is ECDSA-P256-SHA256. The public key is the same key as for secure boot, written to the device during manufacturing, while the private key is never distributed. This ensures that the GBL file was created and signed by a trusted party.

###### Encrypted GBL Update File (heading level 7)

The GBL update file can also be encrypted, to prevent eavesdroppers from getting hold of the plaintext firmware image. The encryption algorithm used is AES-CTR-128, and the encryption key is written to the device during manufacturing.

##### Memory Space for Bootloading

The first stage of the Gecko Bootloader on Series 1 devices takes up a single flash page. On devices with 2 kB flash pages, like EFR32MG1, this means that the first stage takes 2 kB.

The size of the main bootloader is dependent on the functionality required. With a typical bootloader configuration, the main bootloader for Series 1 devices takes up 14 kB of flash, bringing the total bootloader size to 16 kB.

Silicon Labs recommends reserving 16 kB for the bootloader for Series 1 and EFR32xG21 devices and 24 kB for EFR32xG22 devices.

On EFR32xG1 devices (Mighty Gecko, Flex Gecko, and Blue Gecko families), the bootloader resides in main flash.

- First stage bootloader @ 0x0
- Main bootloader @ 0x800
- Application @ 0x4000

On EFR32xG12 and later Series 1 devices, the bootloader resides in the bootloader area in the Information Block.

- Application @ 0x0
- First stage bootloader @ 0x0FE10000
- Main bootloader @ 0x0FE10800

On EFR32xG21, the main bootloader resides in main flash:

- Main bootloader @ 0x0
- Application @ 0x4000

On EFR32xG22, the main bootloader resides in main flash:

- Main bootloader @ 0x0
- Application @ 0x6000

On EFR32xG23, the main bootloader resides in main flash:

- Main bootloader @ 0x08000000
- Application @ 0x08006000

On EFR32xG24, the main bootloader resides in main flash:

- Main bootloader @ 0x08000000
- Application @ 0x08006000

##### Design Decisions

The decision of what bootloader type to deploy depends on many factors. Note that the platform type and available flash memory may limit bootloader choices.

Some questions related to this are:

- Where does the device get the new update image? Is this over-the-air via the networking protocol? Using a separate interface connected to the Internet?
- Will the device have an external memory chip to store a new update image? If not, is there enough internal flash memory to store both a current and a newly downloaded copy of the largest expected application image?
- If the device receives the new image over-the-air, will it be multiple hops away from the server holding the download image?
- What kind of image security is needed?
- What communications driver will be used (in the single protocol case)?
- Does the use case require more than one protocol?

The configurable design of the Gecko Bootloader platform means that developers can create bootloaders to fit almost any design choice. See [Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/) or [Silicon Labs Gecko Bootloader User’s Guide for Series 3 and Higher](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-series3-and-higher/).

#### Gecko Bootloader User's Guide for Series 3 and Higher

##### Silicon Labs Gecko Bootloader User’s Guide for Series 3 and Higher

This guide describes the high-level implementation of the Silicon Labs Gecko Bootloader for Series 3 microcontrollers, SoCs (System on Chips), and NCPs (Network Co-Processors), and provides information on different aspects of configuring the Gecko Bootloader.

The Silicon Labs Gecko Bootloader is a common bootloader for all the newer MCUs and wireless MCUs from Silicon Labs. This guide is specific to Series 3 devices and above. The Gecko Bootloader can be configured to perform a variety of functions, from device initialization to firmware upgrades. Key features of the bootloader are:

- Usable across Silicon Labs Gecko microcontroller and wireless microcontroller families
- In-field upgradeable
- Configurable
- Enhanced security features, including:
- **Secure Boot**: When Secure Boot is enabled, the bootloader enforces cryptographic signature verification of the application image on every boot, using asymmetric cryptography. This ensures that the application was created and signed by a trusted party.
- **Signed upgrade image file**: The Gecko Bootloader supports enforcing cryptographic signature verification of the upgrade image file. This allows the bootloader and application to verify that the application or bootloader upgrade comes from a trusted source before starting the upgrade process, ensuring that the image file was created and signed by a trusted party.
- **Encrypted upgrade image**: The image file can also be encrypted to prevent eavesdroppers from acquiring the plaintext firmware image.

For Series 3 devices and above, Gecko Bootloader uses a proprietary format for its upgrade images, called GBL V4 (Gecko Bootloader file). These files have the file extension “.gbl”. See the _Gecko Bootloader File Format_ page for more details. Series 3 and above devices do not support GBL versions lesser than v4.

On Series 3 devices, the Gecko bootloader consist of the main stage bootloader. The main bootloader is upgradable through the Secure Engine.

The Secure Engine provides functionality to install an image to code region 0 in the external flash.

To perform a bootloader upgrade, the running bootloader verifies the integrity and authenticity of the bootloader upgrade image file. The bootloader then writes the upgrade image to the upgrade location in external flash and requests that the Secure Engine installs it. On some devices, the Secure Engine is also capable of verifying the authenticity of the main bootloader update image against a root of trust. The Secure Engine itself is upgradable using the same mechanism. See the _Gecko Bootloader Operation - Secure Engine Upgrade_ page for more details.

The bootloader consists of a common core, drivers, and a set of components that give the bootloader specific capabilities. The common bootloader core is provided as a full-source delivery. The common bootloader core contains functionality to parse GBL v4 files and flash their contents to the device.

The Gecko Bootloader can be configured to perform firmware upgrades in standalone mode (also called a standalone bootloader) or in application mode (also called an application bootloader), depending on the component configuration. Components can be installed in and configured through the Simplicity Studio IDE.

A standalone bootloader uses a communications channel to get a firmware upgrade image. NCP (network co-processor) devices always use standalone bootloaders. Standalone bootloaders perform firmware image upgrades in a single-stage process that allows the application image to be placed into flash memory, overwriting the existing application image, without the participation of the application itself. In general, the only time that the application interacts with a standalone bootloader is when it requests to reboot into the bootloader. Once the bootloader is running, it receives packets containing the firmware upgrade image by a physical connection such as UART or SPI. To function as a standalone bootloader with a physical connection, a component providing a communication interface such as UART or SPI must be configured.

An application bootloader relies on the application to acquire the firmware upgrade image. The application bootloader performs a firmware image upgrade by writing the firmware upgrade image to a region of flash memory referred to as the download space. The application transfers the firmware upgrade image to the download space in any way that is convenient (UART, over-the-air, Ethernet, USB, and so on). The download space is data flash or a section of the device’s flash. The Gecko Bootloader can partition the download space into multiple storage slots and store multiple firmware upgrade images simultaneously. To function as an application bootloader, a component providing a bootloader storage implementation must be configured.

Silicon Labs provides example bootloaders that come with a preconfigured set of installed components for configuration in either standalone or application mode. See the _Configuring the Gecko Bootloader_ page. The following sections provide an overview of the Gecko Bootloader common core, drivers, and components. For details, including details on error codes and conditions, see the Gecko Bootloader API Reference, shipped with the SDK in the platform/bootloader/documentation folder.

###### Core

The bootloader core contains the bootloader’s main functions. It also contains functionality to write to the external flash, an image parser to parse and act upon the contents of GBL v4 upgrade files, and functionality to boot the application in main flash.

A version of the GBL v4 image parser without support for encrypted upgrade images is also available. This version can be used in flash space constrained bootloader applications where encryption of the upgrade image is not required.

###### Shared Memory (heading level 7)

To exchange information between the bootloader and application, a section of SRAM is used. The contents of SRAM are preserved through a software reset, making SRAM suitable as a communication channel between bootloader and application.

The shared memory has a size of 4 bytes, and is located at the first address of SRAM, 0x20000000. It is used to store a single word containing the reason for a reset. The structure of the reset cause word is defined in the Reset Information part of the Application Interface, in the file **btl_reset_info.h**, as 16 bits containing the reason, and 16 bits of signature indicating if the word is valid or not. If the signature reads 0xF00F, the reset reason is valid.

All 16-bit reset reasons used by Silicon Labs have the most significant bit set to zero. If custom reset reasons are desired, it is recommended to set the most significant bit to avoid conflicting definitions.

###### Drivers

Different applications for firmware upgrade require different hardware drivers for use by the other components of the bootloader.

Driver modules include:

- Delay: Simple delay routines for use with components that require small delays or timeouts.
- SPI Slave: Flexible SPI Slave driver implementation for use in communication components implementing SPI protocols. This driver supports both blocking and non-blocking operation, with DMA (Direct Memory Access) backing the background transfers to support non-blocking operation.
- UART: Flexible serial UART driver implementation for use in communication components implementing UART protocols. This driver supports both blocking and non-blocking operations, with DMA backing the background transfers to support non-blocking operation. Additionally, support for hardware flow control (RTS/CTS) is included.

###### Components

All parts of the bootloader that are either optional or that may be exchanged for different configurations are implemented as components. Each component may have a configuration header file, and one or more implementations. Components include:

- Communication
- UART: XMODEM
- UART: BGAPI
- Compression
- Debug
- GPIO Activation
- Security
- Storage

###### Communication (heading level 7)

Communication components provide an interface for implementing communication with a host device, such as a computer or a micro-controller. Several components implement the communication interface, using different transports and protocols.

- BGAPI UART DFU: By enabling the BGAPI communication component, the bootloader communication interface implements the UART DFU protocol using BGAPI commands.
- UART XMODEM: By enabling the UART XMODEM communication component, the bootloader communication interface implements the XMODEM-CRC protocol over UART. This component makes the bootloader compatible with the legacy serial-uart-bootloader that was previously released with the EmberZNet wireless stack. See _AN760: Using the Ember Standalone Bootloader_ for more information about legacy Ember standalone bootloaders.

###### Compression (heading level 7)

Compression components provide capability for the bootloader GBL file parser to handle compressed GBL v4 upgrade images. Each compression component provides support for one (de)compression algorithm. At the time of writing, decompression of data compressed with the LZ4 and LZMA algorithms is supported, through the _GBL Compression (LZ4)_ and _GBL Compression (LZMA)_ components.

###### Debug (heading level 7)

This component provides the bootloader with support for debugging output. If the component is configured to enable debug prints, short debug messages will be printed over Serial Wire Output (SWO), which can be accessed in multiple ways, including using Simplicity Commander, and by connecting to port 4900 of the Wireless Starter Kit TCP/IP interface.

To turn on debug prints, enable the Debug component and select **Debug prints**. Select **Debug asserts** to enable assertions in the source code.

###### GPIO Activation (heading level 7)

This component provides functionality to enter firmware upgrade mode automatically after reset if a GPIO pin is active during boot. The GPIO pin location and polarity are configurable.

- GPIO: By enabling the GPIO activation component, the firmware upgrade mode can be activated by push buttons.

###### Security (heading level 7)

Security components provide implementations of cryptographic operations as well as functionality to compute checksums and to read cryptographic keys from manufacturing tokens.

Modules include:

- AES: AES decryption functionality
- CRC16: CRC16 functionality
- CRC32: CRC32 functionality
- ECDSA: ECDSA signature verification functionality
- SHA-256: SHA-256 digest functionality

###### Storage (heading level 7)

These components provide the bootloader with multiple storage options for SoCs. All storage implementations must provide an API to access image files to be upgraded. This API is based on the concept of dividing the download space into storage slots, where each slot has a predefined size and location in memory and can be used to store a single upgrade image. Some storage implementations also support a raw storage API to access the underlying storage medium. This can be used by applications to store other data in parts of the storage medium that are not used for storing firmware upgrade images. Implementations include:

- **External Flash**: The external flash storage implementation uses the external flash of the device for upgrade image storage. Note that this storage area is only a download space and is separate from the portion of external flash used to hold the active application code.

##### Gecko Bootloader File Format v4

The Gecko Bootloader supports GBL File format v4 for Series 3 device. This format has significant differences as compared to version 3, The file formats described in this section are generated by [Simplicity Commander](https://docs.silabs.com/simplicity-commander/latest/simplicity-commander-start/) commands.

###### File Structures

The GBL v4 file format is composed of nested TLV (Type-Length-Value) elements. The format of the TLV is as follows:

|Field|Length (Bytes)|Description|
|---|---|---|
|Type|4|Type indicates the content of the Value field|
|Length|4|Length of the Value field in bytes|
|Value|N|Length size field containing either more TLVs or data|

There are two types of TLVs, data and container. Data TLVs value field contains data whose format is specified in the type field and the container TLVs value filed contains more TLVs, which in turn again may contain data or container TLVs.

###### TLV Descriptions

This section describes each of the TLVs.

###### GBLV4 (heading level 7)

This mandatory TLV is used to identify the GBLv4 file when parsing and tell the length of the file.

|Type|Length|Value|
|---|---|---|
|0x84A617EB|Variable|Contains all the TLVs for the file|

###### MANIFEST (heading level 7)

MANIFEST TLV is present inside the GBLV4 TLV. It has a collection, TLVs, that describes the metadata of the GBLv4 file that is used during the update process.

|Type|Length|Value|
|---|---|---|
|0xAA01012A|Variable|Contains metadata TLVs for applying the file.|

###### MANIFEST_INFO (heading level 7)

The MANIFEST_INFO TVL contains the `ManifestInfo_t` structure that describes the version dependency and the features of the GBL file.

|Type|Length|Value|
|---|---|---|
|0x2B03032B|Variable|manifest_info structure|

The `ManifestInfo_t` structure is as follows:

|Data Type|Field|Purpose|
|---|---|---|
|uint32_t|version|Minimum version of the GLBv4 parser required, used for future extensions.|
|uint32_t|features|Features requested from the parser (encryption, compression etc.)|

###### BUNDLE_VERSION (heading level 7)

This is the child TVL of MANIFEST TLV, required to identify the bundle/file and is used for versioning and rejection of downgrades.

|Type|Length|Value|
|---|---|---|
|0x2B04042B|Fixed|bundle_version structure|

The `BundleVersion_t` structure is as follows:

|Data Type|Field|Purpose|
|---|---|---|
|Uint8_t|productId|128 bit unique product ID|
|uint32_t|bundleVersion|Reject if smaller than this exists|
|Uint32_t|minVersion|The minimum previous version to which this update file can be applied|

###### CONTENT_HASH (heading level 7)

An optional TLV that provides integrity checking on the remainder of the file. This TLV is applicable to batch updates only.

|Type|Length|Value|
|---|---|---|
|0x0x2B05052BUL|Fixed|content_hash structure|

The `content_hash` structure is as follows:

|Data Type|Field|Purpose|
|---|---|---|
|uint32_t|hashType|The hash function used to validate (1=SHA, etc.)|
|uint8_t|sha[]|The hash of all contents following the manifest|

###### MANIFEST_CERTIFICATE (heading level 7)

|Type|Length|Value|
|---|---|---|
|0x2B01012B|Fixed|ApplicationCertificate_t|

The `ApplicationCertificate_t` structure is as follows:

|Data Type|Field|Purpose|
|---|---|---|
|uint8_t|structVersion|Vesion of the certificate structure|
|uint8_t|flags|Reserved flags|
|uint8_t|key|Public key used to verify GBL file|
|uint32_t|version|Version number of this certificate|
|uint8_t|signature|The signature of the certificate itself (not the GBL file)|

###### MANIFEST_SIGNATURE (heading level 7)

|Type|Length|Value|
|---|---|---|
|0x2B02022BUL|Variable|Manifest signature structure|

The `Manifest_signature` structure is as follows:

|Data Type|Field|Purpose|
|---|---|---|
|uint32_t|Signature function|The function used for signing|
|uint8_t[]|Signature|The signature for authentication|

###### UPDATE_PROCESS (heading level 7)

This is a collection of TLVs that describes the update process. Parsing this TLV does not happen until the MANIFEST_TLV is authenticated. Each TLV contained in this is parsed and processed one at a time in the order they are listed.

|Type|Length|Value|
|---|---|---|
|0xAB06062BUL|Variable|TLVs describing the steps to update the system|

###### UPDATE_SE (heading level 7)

This is an optional TLV that is used to update the SE.

|Type|Length|Value|
|---|---|---|
|0x2C02022CUL|Fixed|UpdateSe_t structure|

The `UpdateSe_t` structure is as follows:

|Data Type|Field|Purpose|
|---|---|---|
|uint32_t|version|Version number of the SE update image.|
|uint32_t|tlvPosition|The absolute position of the SE update image Blob TLV|

###### UPDATE_MEMORY_SECTION (heading level 7)

The memory section’s content is present in the file and needs to be updated.

|Type|Length|Value|
|---|---|---|
|0x2C03032CUL|Fixed|UpdateMemorySection_t structure|

The `UpdateMemorySection_t` structure is as follows:

|Data Type|Field|Purpose|
|---|---|---|
|uint8_t|targetMemory|Which memory this definition is for (0=main memory, 0x1-0x7f = reserved, 0x80-0xff=defined by the user)|
|uint8_t|plainImageSize|Represents the size of the plain image in 3 bytes|
|uint32_t|targetAddr|The target memory address for the update|
|uint32_t|type|Bitfield representing the type of application|
|uint32_t|version|Version number for this application (customer-defined)|
|uint32_t|capabilities|Bitfield representing the capabilities of this application|
|uint32_t|memorySectionPos|The absolute position of the MEMORY_SECTION_TLV in the file|
|HashVaule_t|memSecHash|A hash value for verifying the integrity of the memory section|

###### MANIFEST_FINISH (heading level 7)

TLV to indicate the end of an update.

|Type|Length|Value|
|---|---|---|
|0x2C04042CUL|Fixed|Empty|

###### MEMORY_SECTION (heading level 7)

This TLV contains TLVs for a memory section update.

|Type|Length|Value|
|---|---|---|
|0xBA01013AUL|Variable|TLVs for the region|

###### MEMORY_SECTION_INFO (heading level 7)

This TLV contains information about how the blob data is read and where the data is to be written to. This is a common wrapper for all data as it allows the data to be compressed, verified and processed in a unified manner.

The data can be divided into blocks, with each block having its own hash. This can then be compared with the hashes in the memory section info to authenticate the block. This is useful during streaming updates to avoid writing unauthenticated data to flash.

|Type|Length|Value|
|---|---|---|
|0x3B01013BUL|Fixed|MemSectionPartialInfo_t|

The `MemSectionPartialInfo_t` structure is as follows:

<table>
    <thead>
        <tr>
            <th>Data Type</th>
            <th>Field</th>
            <th>Purpose</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>uint8_t</td>
            <td>compressionScheme</td>
            <td>
                <p>0 = No compression</p>
                <p>1 = LZ4 compression</p>
                <p>2 = LMZA compression</p>
            </td>
        </tr>
        <tr>
            <td>uint8_t</td>
            <td>encryptionScheme</td>
            <td>
                <p>0 = No encryption</p>
                <p>1 = AES CCM</p>
            </td>
        </tr>
        <tr>
            <td>uint8_t</td>
            <td>secureBootScheme</td>
            <td>Reserved for future use.</td>
        </tr>
        <tr>
            <td>uint8_t</td>
            <td>reserved</td>
            <td></td>
        </tr>
        <tr>
            <td>uint16_t</td>
            <td>signBlockSize</td>
            <td>Block size if DFU authentication is done for each block.</td>
        </tr>
        <tr>
            <td>uint16_t</td>
            <td>numBlocks</td>
            <td>Number of blocks inside the BLOB.</td>
        </tr>
        <tr>
            <td>uint8_t[]</td>
            <td>nonce</td>
            <td>Nonce for encryption</td>
        </tr>
        <tr>
            <td>uint8_t[]</td>
            <td>finalImageHash</td>
            <td>Reserved for future use.</td>
        </tr>
        <tr>
            <td>uint8_t[]</td>
            <td>secureBootSign</td>
            <td>Reserved for future use.</td>
        </tr>
    </tbody>
</table>

###### BLOB (heading level 7)

This TLV contains variable length binary data.

|Type|Length|Value|
|---|---|---|
|0x3B02023BUL|Variable|Binary Data|

The figure below shows the structure of a GBL v4 file.

![Structure of GBL V4 file](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image1.png)

##### Gecko Bootloader Operation - Application Upgrade

This section summarizes Gecko Bootloader operation for updating application firmware, first if the Gecko Bootloader is configured in standalone mode and then if it is configured in application mode. The _Gecko Bootloader Operation - Bootloader Upgrade_ provides the same information for updating the bootloader firmware.

###### Standalone Bootloader Operation

Standalone bootloader operation is illustrated in the following figure:

![Standalone Bootloader Operation](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image2.png)

1. The device reboots into the bootloader.
2. A GBL file containing an application image is transmitted from the host to the device. If image encryption is enabled in the  bootloader and the image is encrypted, decryption is performed during the process of receiving and parsing the GBL file.
3. The bootloader applies the application upgrade from the GBL upgrade file on-the-fly. If image authentication is enabled in the bootloader and the GBL file contains a signature, the authenticity of the image is verified before completing the process.
4. The device boots into the application. Application upgrade is complete.

###### Rebooting Into the Bootloader (heading level 7)

The Gecko Bootloader supports multiple mechanisms for triggering the bootloader. If the **GPIO activation** component is installed, the host device can keep this pin low/high (depending on configuration) through reset to make the device enter the bootloader. The bootloader can also be entered through software. The `bootloader_rebootAndInstall` API first signals to the bootloader that it should enter firmware upgrade mode by writing a command to the shared memory location at the bottom of SRAM, and then performs a software reset. If the bootloader finds the correct command in shared memory upon boot, it will enter firmware upgrade mode instead of booting the existing application.

###### Downloading and Applying a GBL Upgrade File (heading level 7)

When the bootloader enters firmware upgrade mode, it enters a receive loop waiting for data from the host device. The specifics of the receive loop depend on the protocol. Received packets are passed to the image parser, a state machine that parses the data and returns a callback containing any data that should be acted upon. The bootloader core implements this callback and flashes the data to external flash at the specified address. If GBL file authentication or encryption is enabled, the image parser will enforce this, and abort the image upgrade if the authentication fails The bootloader prevents a newly uploaded image from being bootable by holding back parts of the application vector table until the GBL file hash and GBL signature (if required) have been verified.

###### Booting Into the Application (heading level 7)

When an application upgrade is completed, the bootloader triggers a reboot with a message in shared memory at the bottom of SRAM signaling that an application upgrade has been successfully completed. The application can use this reset information to learn that an application upgrade was just performed.

Before jumping to the main application, the bootloader verifies that the application is ready to run. This includes verifying if the Program counter and Stack Pointer are valid and also if the lock bit is set. If secure boot is enabled, the bootloader expects a signed application and attempts to validate the signature of the application. In scenarios where secure boot is not enabled, the bootloader attempts to validate if the Application properties pointer points to valid app properties structure in the flash. If valid app properties struct is found, the bootloader proceeds based on the signature type indicated by the application properties struct or else the bootloader assumes that the Application properties pointer points to the Reset Handler of the application (an app without application properties) and proceeds to boot into the application. In case the verification of the application fails at any stage, the bootloader enters the bootload mode instead of booting into the application.

###### Error Handling (heading level 7)

If the application upgrade is interrupted at any time, the device will be without a working application. The bootloader then resets the device, and re-enters firmware upgrade mode. The host device can easily restart the application upgrade process, to try loading the upgrade image again.

###### Application Bootloader Operation

The following figure illustrates the application bootloader operation both for a single image/single storage slot, and multiple images/ multiple storage slots.

![Application Bootloader Operation: Single Storage Slot](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image3.png)

![Application Bootloader Operation: Multiple Storage Slots](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image4.png)

1. A GBL file is downloaded onto the storage medium of the device, as described below, and the presence of an upgrade image is indicated.
2. The device reboots into the bootloader, and the bootloader enters firmware upgrade mode.
3. The device applies the application upgrade.
4. The device boots into the application. Application upgrade is complete.

###### Downloading and Storing a GBL Upgrade Image File (heading level 7)

To prepare for receiving an upgrade image, the application finds an available storage slot or erases an existing one using `bootloader_eraseStorageSlot`. If the bootloader only supports a single storage slot, a value of 0 should be used for the slot ID.

The application then receives a GBL file using an applicable protocol, such as Ethernet, USB, Zigbee, OpenThread or Bluetooth, and stores it in the slot by calling `bootloader_writeStorage`.

When the download is complete, the application can optionally verify the integrity of the GBL file by calling `bootloader_verifyImage`. This is also done by the bootloader before applying the image but can be done from the application to avoid rebooting into the bootloader if the received image was corrupt.

If multiple storage slots are supported, the application should write a bootload list by calling `bootloader_setImageToBootload`. The list is written to the two bootload info pages as shown in the figure above. The bootload list is a prioritized list of slots indicating the order the bootloader should use when attempting to perform a firmware upgrade. The bootloader attempts to verify the images in these storage slots in sequence and applies the first image to pass verification. If only a single storage slot is supported, the bootloader treats the entire download space as a single storage slot.

###### Rebooting and Applying a GBL Upgrade File (heading level 7)

The bootloader can be entered through software. The `bootloader_rebootAndInstall` API signals to the bootloader that it should enter firmware upgrade mode by writing a command to the shared memory location at the bottom of SRAM, and then performs a software reset. If the bootloader finds the correct command in shared memory upon boot, it enters firmware upgrade mode instead of booting the existing application.

The bootloader iterates over the list of storage slots marked for bootload and attempts to verify the image stored in each. Once it finds a valid GBL upgrade file, firmware upgrade is attempted from this GBL file. If the upgrade fails, the bootloader moves to the next image in the list. If no images pass verification, the bootloader reboots back into the existing application with a message in the shared memory location in SRAM indicating that no good upgrade images were found.

###### Booting Into the Application (heading level 7)

When an application upgrade is completed, the bootloader triggers a reboot with a message in shared memory at the bottom of SRAM signaling that an application upgrade has been successfully completed. The application can use this reset information to learn that an application upgrade was just performed.

Before jumping to the main application, the bootloader verifies that the application is ready to run. This includes verifying if the Program counter and Stack Pointer are valid as well as if the lock bit is set. If secure boot is enabled, the bootloader expects a signed application and attempts to validate the signature of the application. In scenarios where secure boot is not enabled, the bootloader attempts to validate if the Application properties pointer points to valid app properties structure in the flash. If valid app properties struct is found, the bootloader proceeds based on the signature type indicated by the application properties struct or else the bootloader assumes that the Application properties pointer points to the Reset Handler of the application (an app without application properties) and proceeds to boot into the application. In case the verification of the application fails at any stage, the bootloader enters the bootload mode instead of booting into the application.

##### Gecko Bootloader Operation - Bootloader Upgrade

Bootloader upgrade functionality is provided by the Secure Engine on Series 3 devices. The Secure Engine itself is also upgradable. For more details, see the _Gecko Bootloader Operation - Secure Engine Upgrade_ page.

Requirements for upgrading the main bootloader vary depending on the bootloader configuration:

- Application bootloader with storage: Upgrading the main bootloader requires a single GBL file containing both bootloader and application upgrade images.
- Standalone bootloader with communication interface: Upgrading the bootloader requires a GBL file with only the bootloader upgrade image.

Security of the bootloader upgrade process is provided by signing the GBL file. See _Creating a Signed and Encrypted GBL Upgrade Image File from an Application_ on the _Gecko Bootloader Security Features_ page.

###### Bootloader Upgrade on Bootloaders with Communication Interface (Standalone Bootloaders)

The process is illustrated in the following figure:

![Standalone Bootloader: Bootloader Upgrade](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image5.png)

1. The device reboots into the bootloader.
2. A GBL file containing only a bootloader upgrade image is transmitted from the host to the device.
3. The contents of the GBL Bootloader tag are written to the data region in flash
4. The device reboots into the Secure Engine.
5. The Secure Engine replaces the main bootloader with the new version found in the bootloader upgrade GBL.
6. The device boots into the new main bootloader. Bootloader upgrade is complete.

A bootloader upgrade is started in the same way as an application upgrade.

###### Downloading and Applying a Bootloader GBL Upgrade File (heading level 7)

When the bootloader has entered the receive loop, a GBL upgrade file containing a bootloader upgrade is transmitted to the bootloader. When a packet is received, it is passed to the image parser. The image parser parses the data and returns bootloader upgrade data in a callback. The bootloader core implements this callback and flashes the data to the flash at the bootloader upgrade location.

The bootloader prevents a newly uploaded bootloader upgrade image from being interpreted as valid by holding back parts of the bootloader upgrade vector table until the GBL file hash and GBL signature (if required) have been verified.

When a complete bootloader upgrade image is received, the bootloader signals the Secure Engine that it should enter firmware upgrade mode. Secure Engine communication is used to signal that bootloader upgrade is ready to be performed.

The authenticity of the main bootloader optionally can be verified before applying the bootloader upgrade. See _Setting a Version Number_ on the _Configuring the Gecko Bootloader_ page for more information about versioning bootloader images.

###### Upgrading Bootloaders without Secure Boot to Bootloaders with Secure Boot (heading level 7)

A bootloader without the secure boot feature can be upgraded to a bootloader with the secure boot feature, using the following procedure:

1. Prepare a Gecko Bootloader image with secure boot enabled. The version of the bootloader needs to be higher than the bootloader on the device.  
   - Turn on secure boot in Simplicity Studio by going to the **Bootloader Core** component and selecting the **Enable secure boot** option.  
   - (Optional) In the **Bootloader Core** component, select the **Require signed firmware upgrade files** option. This means that the Gecko Bootloader will only accept signed GBL files.
2. Generate a public/private Signing Key pair. See the _Generating Keys_ section on the _Gecko Bootloader Security Features_ page for more information on creating a Signing Key pair.
3. Write the public key generated from the previous step to the device. The public key is stored as a manufacturing token in the device by default. Key locations are defined in the bootloader project file btl_security_tokens.c.
4. Create a GBL file using the Gecko Bootloader image. The GBL file needs to be signed/unsigned depending on the current configuration of the Gecko Bootloader running on the device. For more details on creating a GBL file, see the _Creating a Signed and Encrypted GBL Upgrade Image File from an Application_ section on the _Gecko Bootloader Security Features_ page.
5. Upload the GBL file. For more details on the upgrade process, see the _Bootloader Upgrade on Bootloaders with Communication Interface (Standalone Bootloaders)_ section.

###### Enabling Secure Boot RTSL on Series 3 Devices (heading level 7)

Secure Boot RTSL (Root of Trust and Secure Loader) can be enabled using the following procedure:

1. Prepare a Gecko Bootloader image with secure boot enabled. The version of the Gecko Bootloader needs to be higher than the Gecko Bootloader on the device.  
   - Turn on secure boot in Simplicity Studio by going to the **Bootloader Core** component and selecting the **Enable secure boot** option.  
   - (Optional) In the **Bootloader Core** component, select the **Require signed firmware upgrade files** option. This means that the Gecko Bootloader will only accept signed GBL files.
2. Generate a public/private Signing Key pair. See the _Generating Keys_ section on the _Gecko Bootloader Security Features_ page for more information on creating a Signing Key pair.
3. Prepare an application that installs the public key generated from step 2 to the Secure Engine One-time Programmable memory. Installing a key in the VSE requires a reset routine. Make sure that the application does not end up in the reset loop. Create an unsigned GBL file from this application and upload it. For more information on installing public keys, see the _Creating a Signed and Encrypted GBL Upgrade Image File from an Application_ section on the _Gecko Bootloader Security Features_ page.
4. Sign the Gecko Bootloader image generated from step 1 using the private key generated in step 2. See the _Signing an Application Image for Secure Boot_ section on the _Gecko Bootloader Security Features_ page for more information on signing binaries.
5. Make a custom application that turns on secure boot on the Secure Engine and sign this application binary with the private key generated from step 2.
6. Create a GBL file using the Gecko Bootloader image from step 4.
7. Create a GBL file using the application from step 5. The GBL file need to be signed if the **Bootloader Core** component option **Require signed firmware upgrade files** was selected in step 1.
8. Upload the GBL file containing the Gecko Bootloader image.
9. Upload the GBL file containing the application.

###### Bootloader Upgrade on Application Bootloaders with Storage

The process is illustrated in the following figure.

![Application Bootloader: Bootloader Upgrade](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image6.png)

1. A single GBL file containing a bootloader upgrade image is downloaded onto the storage medium of the device.
2. The device reboots into the main bootloader.
3. The main bootloader verifies the integrity of the upgrade image and then resets the device with reset reason BOOTLOADER_RESET_REASON_UPGRADE to apply the upgrade.
4. The device reboots into the Secure Engine.
5. The Secure Engine replaces the main bootloader with the new version.
6. The device boots into the new main bootloader.

A bootloader upgrade is started in the same way as an Application Upgrade. A GBL file containing a bootloader is written to storage by the application, and the bootloader is entered.

The bootloader iterates over the list of storage slots marked for bootload and attempts to verify the GBL file stored within. Verification returns information about whether the GBL file contains a bootloader, or both a bootloader and an application. The image parser parses the file. If the GBL file contains a bootloader, the bootloader upgrade data is returned in a callback. The bootloader core implements this callback and flashes the data to internal flash at the bootloader upgrade location.

The bootloader prevents a newly uploaded bootloader upgrade image from being interpreted as valid by holding back parts of the bootloader upgrade vector table until the GBL file hash  and GBL signature (if required) have been verified.

Secure Engine communication interface is used to signal the Secure Engine that a bootloader upgrade is ready to be performed.

On Series 3 devices, the authenticity of the main bootloader optionally can be verified before applying the bootloader upgrade. See the _Setting a Version Number_ section on the _Configuring the Gecko Bootloader_ page for more information about versioning bootloader images.

The new main bootloader is entered, and the images in the list of storage slots marked for bootload are verified. When the image parser parses the slot containing the GBL file with the bootloader + application upgrade, the version number of the bootloader upgrade is equal to the running main bootloader version, so another bootloader upgrade will not be performed. Instead, the application upgrade data are returned in a callback. Bootloading of the new application proceeds as described in the _Application Bootloader Operation_ section on the _Gecko Bootloader Operation Application Upgrade_ page.

###### Storage Space Size Configuration (heading level 7)

The storage space size must be configured to have enough space to store the upgrade images.

###### Upgrading Bootloaders without Secure Boot to Bootloaders with Secure Boot (heading level 7)

A bootloader without the secure boot feature can be upgraded to a bootloader with the secure boot feature. The procedure is as follows:

1. Prepare a Gecko Bootloader image with secure boot enabled. The version of the bootloader needs to be higher than the bootloader on the device.  
   - Turn on secure boot from the **Bootloader Core** component in Simplicity Studio by selecting the **Enable secure boot** option.
2. Generate a public/private Signing Key pair. See the _Generating Keys_ section on the _Gecko Bootloader Security Features_ page for more information on creating a Signing Key pair.
3. Write the public key generated from the previous step to the device. The public key is stored as a manufacturing token in the device by default. This key can be written by application code running on the device. The Gecko Bootloader prepared from step 1 can be modified to look for the decryption and signature keys in a different location. Key locations are defined in the bootloader project file `btl_security_tokens.c`.
4. Prepare a signed application image using the private key generated in step 2. See the _Signing an Application Image for Secure Boot_ section on the _Gecko Bootloader Security Features_ page for more information on signing an application.
5. Create a GBL file using the Gecko Bootloader image and the signed application image. The GBL file needs to be signed/unsigned depending on the configuration of the Gecko Bootloader running on the device. For more details on creating a GBL file, see the _Creating a Signed and Encrypted GBL Upgrade Image File from an Application_ section on the _Gecko Bootloader Security Features_ page.
6. Upload the GBL file. For more details on the upgrade process, see the _Bootloader Upgrade on Application Bootloaders with Storage_ section.

###### Enabling Secure Boot RTSL on Series 3 Devices (heading level 7)

Secure Boot RTSL can be enabled by using the following procedure:

1. Prepare a Gecko Bootloader image with secure boot enabled. The version of the Gecko Bootloader needs to be higher than the Gecko Bootloader on the device.  
   - Turn on secure boot from the **Bootloader Core** component in Simplicity Studio by selecting the **Enable secure boot** option.  
   - (Optional) In the **Bootloader Core** component, select the **Require signed firmware upgrade files** option. This means that the Gecko Bootloader will only accept signed GBL files.
2. Generate a public/private Signing Key pair. See the _Generating Keys_ section on the _Gecko Bootloader Security Features_ page for more information on creating a Signing Key pair.
3. Prepare an application that installs the public key generated from step 2 to the Secure Engine One-time Programmable memory. Installing a key in VSE requires a reset routine. Make sure that the application does not end up in the reset loop. Create an unsigned GBL file from this application and upload it. For more details on creating a GBL file, see the _Creating a Signed and Encrypted GBL Upgrade Image File from an Application_ section on the _Gecko Bootloader Security Features_ page.
4. Sign the Gecko Bootloader image generated from step 1 using the private key generated in step 2. See the _Signing an Application Image for Secure Boot_ section on the _Gecko Bootloader Security Features_ page for more information on signing binaries.
5. Make a custom application that turns on secure boot on the Secure Engine and sign this application binary with the private key generated from step 2. For more details on how to turn on secure boot on the Secure Engine.
6. Create a GBL file using the Gecko Bootloader image from step 4 and the application from step 5. The GBL file must be signed if the **Bootloader Core** component option **Require signed firmware upgrade files** was selected in step 1. For more details on creating a GBL file, see the _Creating a Signed and Encrypted GBL Upgrade Image File from an Application_ section on the _Gecko Bootloader Security Features_ page.
7. Upload the GBL file containing the Gecko Bootloader image and the application.

##### Gecko Bootloader Operation - Secure Engine Upgrade

The Secure Engine is upgradable and for both application and standalone type of bootloader a GBL file containing the Secure Engine Upgrade image has to be flashed or sent to the bootloader.

A bootloader upgrade can also be included in the same GBL file in application mode, or as a second GBL file in standalone mode. The figures that illustrate Gecko Bootloader operation in this section do not provide information about the bootloader memory layouts for different devices. For more details refer to the _Memory Space for Bootloading_ section in [Bootloader Fundamentals](https://docs.silabs.com/bootloader/latest/bootloader-fundamentals/).

Signed and encrypted Secure Engine upgrade images are provided by Silicon Labs through Simplicity Studio. Upgrade images with the same or lower version number than the running Secure Engine will be ignored.

To download Secure Engine firmware images, connect a Series 3 device and select a preferred SDK. The Secure Firmware **Update to x.x.x** link appears in the Launcher Perspective, as shown in the following figure.

![screenshot](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image7.jpg)

Click **Update to x.x.x** next to Secure FW: x.x.x. A warning dialog box appears. Click **Yes** to continue.

![screenshot](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image8.jpg)

The Launcher Perspective is then updated so that the current Secure Firmware version and link version are the same.

![screenshot](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image9.jpg)

The Secure Engine firmware images can be found in the _util/se_release/public_ directory under the Gecko SDK. Simplicity Studio displays the SE firmware version available in the Gecko SDK selected.

###### Secure Engine Upgrade on Bootloaders with Communication Interface (Standalone Bootloaders)

The process is illustrated in the following figure.

![Standalone Bootloader: Secure Engine Bootloader Upgrade](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image10.png)

1. The device reboots into the bootloader.
2. A GBL file containing only a Secure Engine upgrade image is transmitted from the host to the device.
3. The device reboots into the Secure Engine.
4. The Secure Engine is replaced by the new version found in the pre-configured upgrade location.
5. The device boots into the bootloader.

###### Downloading and Applying a Secure Engine GBL Upgrade File (heading level 7)

When the bootloader has entered the receive loop, a GBL upgrade file containing a Secure Engine upgrade is transmitted to the bootloader. When a packet is received, it is passed to the image parser. The image parser parses the data and returns Secure Engine upgrade data in a callback. The bootloader core implements this callback and flashes the data to flash at the pre-configured storage data region.

When a complete Secure Engine upgrade image is received, the bootloader signals the Secure Engine that it should enter firmware upgrade mode. This is done by the Secure Engine communication interface that is used to signal that bootloader upgrade is ready to be performed.

###### Secure Engine Upgrade on Application Bootloaders with Storage

The process is illustrated in the following figure.

![Application Bootloader: Secure Engine Upgrade](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image11.png)

1. A single GBL file containing a Secure Engine upgrade image is downloaded onto the storage medium of the device.
2. The device reboots into the bootloader.
3. The upgrade image will be fetched directly from the GBL file in storage instead of first copying the image to the pre-configured upgrade location.
4. The device reboots into the Secure Engine.
5. The Secure Engine is replaced by the new version found in the pre-configured upgrade location (or directly from storage, ref. 3b).
6. The device boots into the main bootloader.
7. The bootloader applies the application image from the GBL upgrade file.
8. The device boots into the application. Secure Engine upgrade is complete.

##### Getting Started with the Gecko Bootloader

This section describes how to build a Gecko Bootloader from one of the provided examples. These instructions assume that you have installed Simplicity Studio 5, the SiSDK and associated utilities as described in the SDK’s quick start guide, and that you are familiar with generating, compiling, and flashing an example application in the relevant version.

1. Create a project based on the Gecko Bootloader example of your choice. The project opens with a tab describing the example.  
   ![screenshot](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image12.png)
2. Click the project (*.slcp) tab to move to the Project Configurator interface.  
   ![screenshot](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image13.png)
3. The Software Components tab shows the list of available components that can be installed in the project.  
   ![screenshot](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image14.png)
4. The **Storage Slot Setup** component allows you to configure storage slots to be used if a storage component is also installed. The default configuration matches the target part and bootloader type. This component supports a maximum of three storage slots.  
   ![screenshot](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image15.png)
5. Click the **Build** (hammer) icon.
6. After the build is complete, the bootloader binaries are available in the **artifact** folder as depicted in the image below.  
   ![screenshot](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image16.png)

The image containing only a bootloader must be used to create a GBL file for bootloader upgrade.

##### Configuring the Gecko Bootloader

###### Configuring Storage

Gecko Bootloaders configured as application bootloaders must include an API to store and access image files. This API is based on the concept of storage slots, where each slot has a predefined size and location in memory and can be used to store a single upgrade image. Slots are configured in the **Bootloader Storage Slot Setup** component.

When multiple storage slots are configured, a bootload list is used to indicate the order in which the bootloader should access slots to find upgrade images. If multiple storage slots are supported, the application should write the bootload list by calling bootloader_setImageToBootload before rebooting into the bootloader to initiate a firmware upgrade process. The bootloader attempts to verify the images in these storage slots in sequence and applies the first image to pass verification. If only a single storage slot is supported, the bootloader uses this slot implicitly. A maximum of three slots may be configured in the **Bootloader Storage Slot Setup** component.

###### Storage Configuration (heading level 7)

When configuring a Gecko Bootloader to obtain images fromflash, modify the following.

The **base address of the storage area** should be configured in the **Common Storage** component. This is the address at which the bootloader will place the prioritized list of storage slots to attempt to bootload from, if more than one storage slot is configured. In the default configuration, only a single storage slot is configured, so this value is set to 0, and isn’t used. If more than one storage slot is configured, this value needs to be configured too.

The **location and size of the storage slots** can be configured using the **Bootloader Storage Slot Setup** Component (supports a maximum of three configurable storage slots). The addresses input here are absolute addresses (they are not offsets from the base address). If more than a single slot is configured, enough space must be reserved between the base address as configured in the **Common Storage** component and the first storage slot configured in the **Bootloader Storage Slot Setup** component. Enough space to fit two copies of the bootload list must be reserved. These two copies need to reside on different flash pages, to provide redundancy in case of power loss during writing. Two full flash pages therefore need to be reserved. The following figure illustrates how the storage area can be partitioned.

![Internal Storage Area Configurations](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image17.png)

> **Note**: The storage area partitioning in the example for two storage slots above does not take any NVM system into account. If using an NVM3 , take care to place and size the storage area in such a way that bootloader storage does not overlap with NVM3.

###### Compressed Upgrade Images

The Gecko Bootloader optionally supports compressed GBL files. In a compressed GBL file, only the application upgrade data and bootloader upgrade data is compressed, any metadata (if present) stays uncompressed.  Signature and encryption operations on a compressed GBL work identically to on an uncompressed GBL.

To be able to use compressed upgrade images, a decompressor for the relevant compression algorithm must be added to the Gecko bootloader. The following table shows which compression algorithms are supported by the Gecko Bootloader, and which Bootloader component should be added to enable the feature. The table also shows how much space the decompressor takes up in the bootloader, and how big of a size reduction to expect for the compressed application upgrade image. Be aware of the bootloader size requirement. The bootloader space might be too small to fit the decompressors, depending on the device and enabled components.

|Compression Algorithm|Component|Bootloader Size Requirement|Application Upgrade Size Reduction (typical)|
|---|---|---|---|
|LZ4|GBL Compression (LZ4)|< 1 kB|~ 10%|
|LZMA|Bootloader Compression (LZMA)|~5 kB flash, 18 kB RAM|~ 30%|

It is important to note that the compressed GBL file stays compressed while being transferred to the device, and while it is stored in the upgrade area. It is decompressed by the bootloader when the upgrade is applied. This means that the running application in main flash will be identical to one that was installed using an uncompressed (normal) GBL file.

Compressed GBL files can only be decompressed by the bootloader when running standalone, not through the Application Interface. This means that upgrade image verification performed by the application prior to reboot will not attempt to decompress the application upgrade, it will only verify the signature of the compressed payload. After rebooting into the bootloader, it will decompress the image as part of the upgrade process.

###### LZMA Compression Settings (heading level 7)

LZMA decompression is only supported for images compressed with certain compression settings. Simplicity Commander automatically uses these settings during gbl4 creation.

- Probability model counters: lp + lc <= 2. Simplicity Commander uses lp=1, lc=1.
- Dictionary size no greater than 8 kB. Simplicity Commander uses 8 kB.

Together, these settings cause the decompressor to require 18 kB of RAM for decompression – 10 kB for the counters and 8 kB for the dictionary.

The Gecko bootloader only supports decompressing payloads that contain the end mark as the last 8 bytes of the compressed stream.

###### Bootloader Example Configurations

The following sections describe the key configuration options for the example bootloader applications.

> **Note**: Security features are disabled for all example configurations. In development, Silicon Labs strongly recommends enabling security features to prevent unauthorized parties from uploading untrusted program code. See the _Using Application Image Security Features_ section on the _Gecko Bootloader Security Features_ page to learn how to configure the security features of the Gecko Bootloader.

###### UART XMODEM Bootloader (heading level 7)

Standalone bootloader for Series 3 devices running the EmberZNet PRO and Silicon Labs Connect protocol stacks, using XMODEM-CRC over UART.

In this configuration, the **UART XMODEM** communication component, **XMODEM Parser** component, and **Bootloader UART Driver** component are installed. For the example application to run on a custom board, the GPIO ports and pins used for UART need to be configured in the **Bootloader UART Driver** component. Here, Hardware Flow Control can be enabled or disabled, and the baud rate and pinout can be configured.

The **GPIO activation** component is also installed by default, allowing bootloader entry into firmware upgrade mode by activating a GPIO through reset. The GPIO pin used can be configured here. This component can be uninstalled if this functionality is not desired.

###### BGAPI UART DFU Bootloader (heading level 7)

Standalone bootloader for the Bluetooth protocol stack, using the BGAPI protocol for UART DFU. This bootloader should be used for all NCP-mode Bluetooth applications.

In this configuration, the **BGAPI UART DFU** communication component and **Bootloader UART Driver** component are installed. For the example application to run on a custom board, the GPIO ports and pins used for UART need to be configured in the **Bootloader UART Driver** component. Here, Hardware Flow Control can be enabled or disabled, and the baud rate and pinout can be configured.

The GPIO activation component is also installed by default, allowing bootloader entry by activating a GPIO through reset. The GPIO pin used can be configured here. This component can be uninstalled if this functionality is not desired.

###### Storage Bootloader (heading level 7)

Application bootloader for all wireless protocol stacks, using flash to store upgrade images received over the air by the application.

Multiple examples are provided, including configurations for 2 MB, 3 MB, 4 MB & 8 MB flash memory devices . **The storage layout should be modified before running the bootloader on any other devices**. In this configuration, the flash and common storage components are installed. The base address of the storage area is configured in the **Common Storage** component. The location and size of the storage slots can be configured using the **Bootloader Storage Slot Setup** component (provides up to three configurable storage slots). Default example applications are provided with configurations for both single storage slot and multiple storage slots.

The default storage slot configurations provided by the Gecko Bootloader **must** be configured to match the use-case-specific application configurations, as shown below.

|Sample Applications|Storage Offset|Storage Size|
|---|---|---|
|Storage Bootloader (Single OTA Image of size 792kB)|0x010E0000|0x000C6000 (811,008 bytes)|
|Storage Bootloader (Single OTA Image of size 1180kB)|0x0128F000|0x00127000 (1,210,368 bytes)|
|Storage Bootloader (Single OTA Image of size 1540kB)|0x01379000|0x00181000 (1,577,984 bytes)|

###### Image Acquisition Application Example Configuration

These examples illustrate applications that acquire and store a GBL upload image for an application bootloader. For the running bootloader to accept an application upgrade, the new application version must be higher than the existing version.

###### Setting a Version Number

To distinguish between different builds of the Gecko Bootloader, it is useful to set a version number. To perform a bootloader upgrade, not only must the running bootloader pass its integrity checks (see the _Downloading and Applying a Bootloader GBL Upgrade File_ section on the _Gecko Bootloader Operation Bootloader Upgrade_ page), but the bootloader upgrade image must also have a higher version number than the running bootloader image. A version number can be set using Simplicity Studio by configuring the **Bootloader Version Main Customer** option of the **Bootloader Core** component. This macro will be picked up by the config file **btl_config.h**, where it is combined with the version number of the Gecko Bootloader files provided by Silicon Labs.

###### Hardware Configuration

The Gecko Bootloader uses the Pin Tool for configuration of pinout and other hardware-related settings. When Pin Tool configuration is available for a bootloader component, the relevant settings are shown in the Component Editor for that component.

![Example of USART Configuration for UART Driver](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image18.png)

The standalone Pin Tool User Interface can also be used to configure settings for Gecko Bootloader if desired.

While the Pin Tool provides configuration for many different peripherals, the Gecko Bootloader uses only the following Pin Tool modules:

- SERIAL is used by the UART Driver component to configure baud rate, flow control mode and pinout.
- VCOM is used by the UART Driver component to enable the serial interface if necessary (only on Silicon Labs Wireless Starter Kits).
- BTL_BUTTON is used by the GPIO Activation component.
- CMU HFXO frequency setting is used by the delay driver to calibrate timing if the core is running from the HFXO.

Other settings, like CMU oscillator configuration or DCDC configuration, are not taken into consideration by the default bootloader code. If using these configuration settings is desired, the required code must be added in btl_main.c.

> **Note**: While the delay driver uses the HFXO frequency setting from Pin Tool, the HFXO enable setting is not used to initialize the HFXO on startup. This setting is only used when calling the bootloader through the Application Interface, and the application has switched to the HFXO prior to calling the Bootloader Application Interface API.

##### Simplicity Commander and the Gecko Bootloader

Simplicity Commander is a single, all-purpose tool to be used in a production environment. It is invoked using a simple CLI (Command Line Interface) that is also scriptable. You can use Simplicity Commander to perform these essential tasks:

- Generating key files for signing and encryption
- Signing application images for Secure Boot
- Creating GBL images (encrypted or unencrypted, signed, or unsigned)
- Parsing GBL images

Simplicity Commander is used throughout the examples in the following sections. For more information on executing commands to complete these tasks, see the [Simplicity Commander User Guide](https://docs.silabs.com/simplicity-commander/latest/simplicity-commander-start/).

> **Note**: Simplicity Commander also offers a GUI (Graphical User Interface) that can be used in the lab for typical tasks such as flashing device images. The functions described in this User Guide are performed from the CLI.

###### Creating GBL V4 Files Using Simplicity Commander

To create an unsigned GBL V4 file from an application **myapp.s37**, execute `commander gbl4 create myapp.gbl -–config configfile.yaml`.

Example of a config file is as follows:

```c
manifest:
  product_id: “00000000000000000000000000000000”
updates:
  - data: myapp.s37
    block_size: 0
```

To create an unsigned GBL V4 file from a bootloader upgrade **mybootloader.s37**, execute `commander gbl4 create mybootloader.gbl –-config confligfile.yaml`.

Example of a config file is as follows:

```c
manifest:
  product_id: “00000000000000000000000000000000”
updates:
  - data: mybootloader.s37
    block_size: 0
```

This file can be used with the standalone bootloader configurations of the Gecko Bootloader.

To create an unsigned GBL file from a Secure Engine, upgrade **mySecureElement.seuv2**, and execute `commander gbl4 create mySecureElement.gbl –config configfile.yaml`.

Example of a config file is as follows:

```c
manifest:
  product_id: “00000000000000000000000000000000”
se_upgrade:
  se_file: mysecureElement.seuv2
```

The Secure Engine images, .seuv2, are provided by Silicon Labs and can be found through Simplicity Studio. See the _Gecko Bootloader Operation - Secure Engine Upgrade_ section on the _Gecko Bootloader Operation Secure Engine Upgrade_ page.

The command can also be used to create a single upgrade image, suitable for use with application bootloader configurations of the Gecko Bootloader: `commander gbl4 create myupgrade.gbl –config configfile.yaml`.

Example of a config file is as follows:

```c
manifest:
  product_id: “00000000000000000000000000000000”
updates:
  - data: myapp.s37
  - data:mybootloader.s37

se_upgrade:
  se_file: mysecureElement.seuv2
```

##### Gecko Bootloader Security Features

###### About Bootloader Image Security

Secure Boot and Secure Firmware Upgrade, discussed in the following sections, enables Gecko Bootloader to provide authenticity and integrity checks on the Application image, which provides a sufficient level of security for many applications. However, in systems without a hardware root of trust, no process checks the authenticity or integrity of the Gecko Bootloader itself. Its security is provided solely by the device hardware and the robustness of the software running on the device.

The native behavior of Firmware Upgrade will prevent accidental version rollback of Gecko Bootloader under normal usage conditions.

###### About Application Image Security

The Gecko Bootloader can enforce security on two levels:

- Secure Boot refers to the verification of the authenticity of the application image in main flash on every boot of the device.
- Secure Firmware Upgrade refers to the verification of the authenticity of an upgrade image before performing a bootload, and optionally enforcing that upgrade images are encrypted.

###### Secure Boot Procedure (heading level 7)

When Secure Boot is enabled, the cryptographic signature of the application image in flash is verified on every boot before the application is allowed to run. Secure Boot is not enabled by default in the example configurations provided by Silicon Labs, but enabling it is highly recommended to ensure the validity and integrity of firmware images.

**Signature Algorithms**

The Gecko Bootloader supports the ECDSA-P256-SHA256 cryptographic signature algorithm. This is the ECDSA (elliptical curve digital signature algorithm) of the SHA-256 digest of the application firmware image, using the NIST P-256 (secp256r1) curve.

**Summary of Operation**

1. On boot, the bootloader checks the application image for information about whether it is signed.
2. The type of signature and signature location is determined.
3. If the type of signature does not match the requirements of the bootloader, the bootloader enters device firmware upgrade mode and prevents the application from running.
4. According to the chosen signature algorithm, the signature of the contents of flash from the beginning of the application to the location of the signature is compared to the signature at the signature location.
5. If the signatures do not match, the bootloader enters device firmware upgrade mode and prevents the application from running.

**Secure Boot using ECDSA-P256-SHA256**

For an image to be signed for Secure Boot, the application needs to contain a copy of the **ApplicationProperties_t** struct. This struct contains information about which signature algorithm is used, and where to find the signature.

On every boot, the bootloader calculates the SHA-256 digest of the application image, from the beginning of the application to the start of the signature. The signature of the SHA-256 digest is then verified using ECDSA-P256.

If the signature is valid, the application is allowed to boot. Else, the bootloader is entered, and an application upgrade is attempted if one is available.

Simplicity Commander can be used to generate a key pair and write the public key to the device. See the [Simplicity Commander User Guide](https://docs.silabs.com/simplicity-commander/latest/simplicity-commander-start/) for more information.

**Secure Boot with Application Rollback Protection**

On every boot, the application version in the **ApplicationData_t** struct is stored at the end of the bootloader area in flash, which is used to prevent applications from being downgraded. The application version can remain the same for upgrades.

If application rollback prevention component is installed in the bootloader project, then before applying the upgrade image, the bootloader will check the application version in the application properties structure that resides inside the signed/encrypted GBL file and will only apply the OTA image to application area if the application version in ApplicationData structure is equal or higher than the highest application version last seen.

The application rollback prevention feature can be enabled in the **Bootloader Core** component by selecting the **Enable application rollback protection** option. The **Minimum application version allowed** option can be used to configure the minimum application version that should be allowed to boot.

The application versions are stored in the SE OTP area.

**Secure Boot Using a Certificate**

On Series 3 devices, a certificate-based secure boot operation is supported. The Certificate contains:

- Struct version: The version of the certificate structure.
- Public key: ECDSA-P256 public key, X and Y coordinates concatenated, used to validate the image.
- Certificate version: The version of the running certificate.
- Signature: ECDSA-P256 signature, used for the authentication of the public key and the certificate version.

The definition of the certificate struct can be found in `api/application_properties.h`.

To utilize certificate-based secure boot, configure Secure Engine to authenticate the bootloader image by configuring the certificate-based secure boot option in the Secure Engine OTP. Configure the Gecko Bootloader to enable certificate-based secure boot in the **Bootloader Core** component by selecting the **Enable certificate support** option. The Gecko Bootloader certificate must be signed by the private key pair of the public key stored in the Secure Engine OTP. For more information on the key storage, see the _Key Storage_ section.

The certificate-based secure boot procedure is illustrated in the following figure.

![Certificate-Based Secure Boot Procedure](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image19.jpg)

Once the certificate-based secure boot option on Secure Engine is turned on, Secure Engine verifies the Gecko Bootloader certificate. The public key stored in the certificate is used to validate the signature of the Gecko Bootloader. Secure Engine will not accept bootloader images without a certificate.

If only the secure boot option is enabled (not certificate-based) on Secure Engine, and Secure Engine identifies a certificate, the certificate will be used to validate the bootloader image. If the certificate version from the bootloader image is higher than 0 and it gets verified once, Secure Engine will never again accept direct signed bootloader images without a certificate.

The Gecko Bootloader will authenticate the direct signed application using the public key stored in the Gecko Bootloader certificate. If the application contains a certificate, Gecko Bootloader will authenticate it. The procedure is illustrated in the following figure.

![Advanced Certificate-Based Secure Boot Procedure](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image20.jpg)

After authentication of the application certificate, Gecko Bootloader verifies the signature of the application using the public key from the application certificate. In addition, Gecko Bootloader compares the Gecko Bootloader certificate version against the application certificate version. All application images with certificate version lower than the certificate version of the Gecko Bootloader will be rejected. Gecko Bootloader can be configured to only allow applications with certificates to boot by configuring the **Bootloader Core** component by selecting the **Reject direct signed images** option.

The **ApplicationProperties_t** struct contains the certificate struct **ApplicationCertificate_t**. The certificate struct can be injected to images that contain an **ApplicationProperties_t** with **ApplicationCertificate_t**. To inject a certificate to an image, issue the following command from Simplicity Commander:

```C
commander convert <image file> --secureboot --keyfile <signing key> --certificate <certificate>
--outfile <signed image file with certificate>
```

###### Secure Firmware Upgrade (heading level 7)

The Gecko Bootloader supports a secure firmware upgrade process. This is achieved by using symmetric encryption to encrypt the upgrade image, and asymmetric cryptography to sign the upgrade image. Symmetric encryption provides confidentiality, and asymmetric cryptography provides integrity and authenticity. Note that encryption alone is not enough to provide authenticity.

**Encryption Algorithms**

The Gecko Bootloader supports the AES-CTR-128 encryption algorithm. The GBL upgrade file is encrypted using 128-bit AES in Counter mode with a random nonce as the initial counter value.

To make use of the OTA decryption key stored in the Secure Engine OTP, the **Use symmetric key stored in Secure Engine storage** option in the **Bootloader Core** component must be selected. Simplicity Commander can be used to generate an OTA decryption key and write the key to the device. For more information on storing the OTA decryption key on Series 3 devices, see [Simplicity Commander User Guide](https://docs.silabs.com/simplicity-commander/latest/simplicity-commander-start/).

The Secure Engine OTP key support depends on the SE Manager component, which is enabled by default.

**Signature Algorithms**

The Gecko Bootloader supports the ECDSA-P256-SHA256 cryptographic signature algorithm. This is the ECDSA signature of the SHA-256 digest of the GBL upgrade file, using the NIST P-256 (secp256r1) curve.

**Summary of Operation**

Before starting a firmware upgrade process, the application can verify an image in storage by calling into the bootloader verification functions. For bootloaders with Communication Interface, the host device should verify the image before sending it to the NCP or RCP.

During firmware upgrade, the GBL file is parsed, and if encrypted, decrypted on-the-fly. A GBL Signature Tag in the GBL file indicates to the bootloader that the file is signed, and the signature is verified. If signature verification fails, the firmware upgrade process is aborted.

On Series 3 devices, Gecko Bootloader will authenticate the GBL signature tag using the public key stored in the bootloader certificate if the **Enable certificate support** option is selected in the **Bootloader Core** component. A GBL Certificate tag in the GBL file indicates to the bootloader that the GBL certificate tag needs to be authenticated using the public key stored in the bootloader certificate. The certificate version in the GBL certificate tag is compared with the bootloader certificate and only a version equal or higher than the bootloader certificate is accepted. Once the GBL certificate tag is authenticated, the GBL file's signature is verified using the authenticated public key from the GBL certificate tag.

###### Using Application Image Security Features

This example assumes that a bootloader called **bootloader-uart-xmodem** has been built in Simplicity Studio.The relevant version can be flashed to the device using the Flash Programmer in Simplicity Studio or using Simplicity Commander.

This example provides two ways of signing the upgrade images. The first option uses Simplicity Commander to generate key material and sign data. This is suitable for development. The second option uses an external signer, such as a dedicated Hardware Security Module (HSM) to protect private key material and perform signing operations. Silicon Labs recommends using an HSM to safeguard private keys.

###### Generating Keys (heading level 7)

To use the security features of the Gecko Bootloader, encryption and signing keys need to be generated. These keys must then be written to the device. The encryption key is used with the GBL file for secure firmware upgrade. The signing keys are used both with the GBL file for secure firmware upgrade and to sign the application image for Secure Boot.

**Generating a Signing Key Using Simplicity Commander**

```C
commander util genkey --type ecc-p256 --privkey signing-key --pubkey signing-key.pub
```

This creates an ECDSA-P256 key pair for signing; `signing-key` contains the private key in PEM format and **must be kept secret from third parties**. This key will later be used to sign images and GBL files. `signing-key.pub` contains the public key in PEM format and can be used to verify GBL files using commander gbl4 info.

**Generating a Signing Key Using a Hardware Security Module**

When using a Hardware Security Module, the private key is kept secret inside the HSM. According to the instructions from your HSM vendor, have it generate an ECDSA-P256 key pair and export the public key in PEM format to the file **signing-key.pub**. Then use Simplicity Commander to convert the key to token format, suitable for writing to the Series 3 device.

```C
commander gbl keyconvert --type ecc-p256 signing-key.pub
```

**Generating an Encryption Key**

```C
commander util genkey --type aes-ccm --outfile encryption-key
```

This creates an AES-128 key for encryption in the file **encryption-key**.

**Writing Keys to the Device**

> **Note**: Refer to the sections, _Writing the AES Decryption Key_ and _Writing the Public Key to the Device_ in the [Simplicity Commander User Guide](https://docs.silabs.com/simplicity-commander/latest/simplicity-commander-start/).

###### Signing an Application Image for Secure Boot (heading level 7)

If the bootloader enforces Secure Boot, the application needs to be signed to pass verification. On every boot, a SHA-256 digest of the application is calculated. The signature is verified using ECDSA-P256, with the same public key as for the GBL file signing. Signature verification failure prevents the application from booting.

Application images should contain an **ApplicationProperties_t** struct declaring the application version, capabilities, and other metadata. If **ApplicationProperties_t** is missing, the application image cannot be signed. For more details on adding **ApplicationProperties_t**, see the _Application Properties_ section on the _Application Interface_ page.

**Using Simplicity Commander**

Signing the application can be done with the command:

```C
commander convert myapp.s37 --secureboot --keyfile signing-key --outfile myapp-signed.s37
```

**Using a Hardware Security Module**

The application can be prepared for signing by issuing the command:

```C
commander convert myapp.s37 --secureboot --extsign --outfile myapp-for-signing.s37
```

Using an HSM, sign the output file **myapp-for-signing.s37**, and supply the resulting DER-formatted signature file **signature.der** back to Simplicity Commander:

```C
commander convert myapp-for-signing.s37 --secureboot --signature signature.der --verify signing-key.pub

--outfile myapp-signed.s37
```

###### Creating a Signed and Encrypted GBL Upgrade Image File from an Application (heading level 7)

To create a GBL file from an application, use `commander gbl4 create`.

Note that, as of this writing, secure application images can only be constructed through Simplicity Commander, not through the configuration options available through Simplicity Studio.

Application images should contain an **ApplicationProperties_t** struct declaring the application version, capabilities, and other metadata. If **ApplicationProperties_t** is missing, the application image cannot be signed. For more details on adding **ApplicationProperties_t**, see the _Application Properties_ section on the _Application Interface_ page.

**Using Simplicity Commander to Sign**

For an application called **myapp.s37**, use:

```C
commander gbl4 create myapp.gbl -–config configfile.yaml

```

Example of a config file is as follows:

```C
manifest:
 product_id: "[16 B ID] (default from app or 00000000000000000000000000000000)"
 bundle_version: "[version] (default 0x00000000)"
 min_version: "[version] (default 0x00000000)"
 hash_function: "[none | sha256] (default sha256)"
 certificate: "[path to file] (optional)"
 signing_key: "[path to file] (optional)"
se_upgrade:
 se_file: "[path to file] (optional)"
updates:
 - data: <path to file>
  app_type: "[uint32] (default from app or 0x00000000)"
  app_capabilities: "[uint32] (default from app or 0x00000000)"
  version: "[uint32] (default from app or 0x00000000)"
  compression_scheme: "[none | lz4 | lzma] (default none)"
  encryption_scheme: "[none | aes-ctr-128] (default none)"
  block_size: "[size in bytes] (default 0/disabled)"
```

This single command performs three actions:

- Creates a GBL file
- Encrypts the GBL file
- Signs the GBL file

If Secure Boot is also desired, the application must be signed using `commander convert --secureboot` prior to creating the GBL.

###### System Security Considerations

The Gecko bootloader security features can be used to create a secure device, but do not create a secure system by themselves. This section goes over considerations that need to be taken when designing a secure system where the Gecko Bootloader is a component.

###### Key Storage (heading level 7)

On Series 3 devices, the decryption key and the sign key used by the Gecko Bootloader in the Secure Engine OTP. The decryption key can be provisioned in the Secure Engine OTP using Simplicity Commander or using the Secure Engine Mailbox interface. Once a key value has been programmed into the Secure Engine OTP, it cannot be changed.

##### Application Interface

The bootloader has an application interface exposed through a function table in the bootloader. The application interface provides APIs to use bootloader functions for storing and retrieving upgrade images and verifying their integrity. APIs to reboot into the bootloader are also provided. For details see the Gecko Bootloader API Reference, at [https://docs.silabs.com/gecko-platform/5.1.2/platform-bootloading-overview/folder](https://docs.silabs.com/gecko-platform/5.1.2/platform-bootloading-overview/folder).

If you are not using a protocol stack from Silicon Labs, the **api/btl_interface.h** header provides the bootloader application interface API. If you are using a protocol stack from Silicon Labs, the recommended bootloader interface API for the specific protocol stack should be used instead. The following files provide the implementation of the bootloader interface:

**api/btl_interface.c** (common interface)

**api/btl_interface_storage.c** (interface to storage functionality)

The application interface consists of functions that can be included in the customer application, and that communicate with the bootloader through the **MainBootloaderTable_t**. This table contains function pointers into the bootloader. The 10th word of the bootloader contains a pointer to this structure, allowing any application to easily locate it. Using the wrapper functions provided in the Bootloader Interface API is preferred over accessing the bootloader table directly. Modules include:

- **Application Storage Interface**: Application interface for interfacing with bootloader storage. The Storage Interface is only available on bootloaders that support the storage interface.
- **Common Application Interface**: Generic application interface available on all versions of the bootloader, independently of which components are present.

###### Application Properties

Application images should contain an **ApplicationProperties_t** struct declaring the application version, capabilities, and other metadata. The Simplicity Commander extracts the metadata contained in this structure from the application and places it in the UPDATE_MEMORY_SECTION tag in the GBL upgrade file. If the structure is not present in the application, Simplicity Commander will raise an error. The **ApplicationProperties_t** struct is added to the application on installing the **bootloader_interface** component to the application. The **bootloader_interface** component installs **bootloader_app_properties** component which adds an instance of **ApplicationProperties_t** named **sl_app_properties** to the project. The component adds a source file named **app_properties.c** and a configuration file named **app_properties_config.h**. This configuration file allows users to configure the application version via Simplicity Studio’s Component Editor. To open the Component Editor, locate the **App Properties** component under **Platform > Bootloader** as shown below.

![screenshot](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image21.png)

Click **Configure** to open the Component Editor.

![screenshot](/bootloader-user-guide-series3-and-higher/0.2/images/sld737-image22.png)

The application type is automatically populated based on the wireless stack used in building the project. The value of the application type is automatically set during the project generation step and can be found in **autogen/sl_application_type.h** file.

The contents of the **GBL Application Tag** can be extracted from a GBL file by a running application using the Application Storage interface. Note that the **GBL Application Tag** will only be added if the GBL file contains an application image, not if the GBL file only contains a bootloader upgrade or metadata.

The structure in the application is also used to declare whether the application image is signed, and what type of signature is used. This information is added by Simplicity Commander when signing the image using `commander convert (--secureboot, --extsign or -- signature)`. For the bootloader to locate the **ApplicationProperties_t** struct, if not already done by the linker, Simplicity Commander modifies word 13 of the application to insert a pointer to the **ApplicationProperties_t** struct when signing the application image for Secure Boot.

###### Error Codes

Most Gecko bootloader APIs return error codes. The following table lists the groups of error codes that may be returned. The full list of error codes within each group can be found in _api/btl_errorcode.h_ in the platform/bootloader directory of the SDK, as well as in the API Reference.

|ID|Description|
|---|---|
|0x0|OK|
|0x01xx|Initialization error|
|0x02xx|Image verification error|
|0x04xx|Storage error|
|0x05xx|Bootload error|
|0x06xx|Security error|
|0x07xx|Communication error|
|0x09xx|XMODEM parser error|
|0x10xx|GBL file parser error|
|0x11xx|SPI slave driver error|
|0x12xx|UART driver error|
|0x13xx|Compression error|
|0x14xx|RTOS error|

#### Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher

##### Silicon Labs Gecko Bootloader User’s Guide for GSDK 4.0 and Higher

> **Note: This section replaces _UG489: Silicon Labs Gecko Bootloader User’s Guide for GSDK 4.0 and Higher_. Further updates to this user guide will be provided here**.

This document describes the high-level implementation of the Silicon Labs Gecko Bootloader for EFM32 and EFR32 Series 1 and Series 2 microcontrollers, SoCs (System on Chips) and NCPs (Network Co-Processors), and provides information on different aspects of configuring the Gecko Bootloader. If you are not familiar with the basic principles of performing a firmware upgrade or want more information about upgrade image files, refer to [Bootloader Fundamentals](https://docs.silabs.com/mcu-bootloader/latest/bootloader-fundamentals/). For more information on using the Gecko Bootloader with different wireless stacks, see the following:

- [Using the Gecko Bootloader with EmberZNet](https://docs.silabs.com/zigbee/latest/using-gecko-bootloader-with-zigbee/)
- [Bootloading and OTA with Silicon Labs Connect v3.x](https://docs.silabs.com/connect-stack/latest/bootloading-and-ota-with-connect-v3x)
- [Using the Gecko Bootloader with Silicon Labs Bluetooth Applications](https://docs.silabs.com/bluetooth/latest/using-gecko-bootloader-with-bluetooth-apps/)

For more information on Series 2 device security, see:

- [Series 2 and Series 3 Secure Boot with RTSL](https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/)
- [Series 2 Secure Debug](https://docs.silabs.com/iot-security/latest/series2-secure-debug/)
- [Production Programming of Series 2 and Series 3 Devices](https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/)

For more information on security using Series 2 devices with Secure Vault, see:

- [Anti-Tamper Protection Configuration and Use](https://docs.silabs.com/iot-security/latest/efr32-secure-vault-tamper/)
- [Authenticating Silicon Labs Devices using Device Certificates](https://docs.silabs.com/iot-security/latest/authenticating-devices-using-device-certificates/)
- [Secure Key Storage](https://docs.silabs.com/iot-security/latest/efr32-secure-key-storage/)

##### Overview

The Silicon Labs Gecko Bootloader is a common bootloader for all the newer MCUs and wireless MCUs from Silicon Labs. The Gecko Bootloader can be configured to perform a variety of functions, from device initialization to firmware upgrades. Key features of the bootloader are:

- Useable across Silicon Labs Gecko microcontroller and wireless microcontroller families
- In-field upgradeable
- Configurable
- Enhanced security features, including:  
  - **Secure Boot**: When Secure Boot is enabled, the bootloader enforces cryptographic signature verification of the application image on every boot, using asymmetric cryptography. This ensures that the application was created and signed by a trusted party.  
  - **Signed upgrade image file**: The Gecko Bootloader supports enforcing cryptographic signature verification of the upgrade image file. This allows the bootloader and application to verify that the application or bootloader upgrade comes from a trusted source before starting the upgrade process, ensuring that the image file was created and signed by a trusted party.  
  - **Encrypted upgrade image file**: The image file can also be encrypted to prevent eavesdroppers from acquiring the plaintext firmware image.

The Gecko Bootloader uses a proprietary format for its upgrade images, called GBL (Gecko Bootloader file). These files have the file extension “.gbl”. See section [Gecko Bootloader File Format](02-gecko-bootloader-file-format#gecko-bootloader-file-format) for more details.

On Series 1 devices, the Gecko Bootloader has a two-stage design, first stage and main stage, where a minimal first stage bootloader is used to upgrade the main bootloader. The first stage bootloader only contains functionality to read from and write to fixed addresses in internal flash. To perform a main bootloader upgrade, the running main bootloader verifies the integrity and authenticity of the bootloader upgrade image file. The running main bootloader then writes the upgrade image to a fixed location in internal flash and issues a reboot into the first stage bootloader. The first stage bootloader verifies the integrity of the main bootloader firmware upgrade image, by computing a CRC32 checksum before copying the upgrade image to the main bootloader location.

On Series 2 devices, the Gecko bootloader consists only of the main stage bootloader. The main bootloader is upgradable through the Secure Engine. The Secure Engine may be hardware-based, or virtual (software). If hardware-based, the implementation may be either with or without Secure Vault. Throughout this document, the following conventions will be used.

- HSE - Hardware Secure Engine, either with or without Secure Vault if not specified
- VSE - Virtual Secure Engine
- SE - Secure Engine (either HSE or VSE, in general)

The Secure Engine provides functionality to install an image to address 0x0 in internal flash, by copying from a configurable location in internal flash. This makes it possible to have a 2-stage design, where the main bootloader is not present. However, the presence of a main bootloader is assumed throughout this document.

To perform a main bootloader upgrade, the running main bootloader verifies the integrity and authenticity of the bootloader upgrade image file. The running main bootloader then writes the upgrade image to the upgrade location in flash and requests that the Secure Engine installs it. On some devices, the Secure Engine is also capable of verifying the authenticity of the main bootloader update image against a root of trust. The Secure Engine itself is upgradable using the same mechanism. See [Gecko Bootloader Operation - Secure Engine Upgrade](05-gecko-bootloader-operation-secure-engine-upgrade#gecko-bootloader-operation-secure-engine-upgrade) for more details.

The main bootloader consists of a common core, drivers, and a set of components that give the bootloader specific capabilities. The common bootloader core is now provided as a full-source delivery, as opposed to previously being a combination of compiled libraries and source code while the components continue to be delivered as source code. The common bootloader core contains functionality to parse GBL files and flash their contents to the device.

The Gecko Bootloader can be configured to perform firmware upgrades in standalone mode (also called a standalone bootloader) or in application mode (also called an application bootloader), depending on the component configuration. Components can be installed in and configured through the Simplicity Studio IDE.

A standalone bootloader uses a communications channel to get a firmware upgrade image. NCP (network co-processor) devices always use standalone bootloaders. Standalone bootloaders perform firmware image upgrades in a single-stage process that allows the application image to be placed into flash memory, overwriting the existing application image, without the participation of the application itself. In general, the only time that the application interacts with a standalone bootloader is when it requests to reboot into the bootloader. Once the bootloader is running, it receives packets containing the firmware upgrade image over-the-air using Bluetooth or by a physical connection such as UART or SPI. To function as a standalone bootloader with a physical connection, a component providing a communication interface such as UART or SPI must be configured.

An application bootloader relies on the application to acquire the firmware upgrade image. The application bootloader performs a firmware image upgrade by writing the firmware upgrade image to a region of flash memory referred to as the download space. The application transfers the firmware upgrade image to the download space in any way that is convenient (UART, over-the-air, Ethernet, USB, and so on). The download space is either an external memory device such as an EEPROM or dataflash or a section of the device’s internal flash. The Gecko Bootloader can partition the download space into multiple storage slots and store multiple firmware upgrade images simultaneously. To function as an application bootloader, a component providing a bootloader storage implementation has to be configured.

Silicon Labs provides example bootloaders that come with a preconfigured set of installed components for configuration in either standalone or application mode. See section [Configuring the Gecko Bootloader](07-configuring-the-gecko-bootloader#configuring-the-gecko-bootloader). The Silicon Labs Gecko SDK Suite also includes precompiled bootloader images for several different EFR32 devices. As of this writing, the images shown in the following table are provided.

> **Note**: The bootloader security features are not enabled in these precompiled images.

Table Prebuilt Bootloader Images

<table>
    <thead>
        <tr>
            <th>Use</th>
            <th>Wireless Stack</th>
            <th>Image Name</th>
            <th>Mode</th>
            <th>Interface</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>SoC</p>
            </td>
            <td>
                <p>EmberZNet PRO</p>
            </td>
            <td>
                <p>SPI Flash Storage Bootloader</p>
            </td>
            <td>
                <p>Application</p>
            </td>
            <td>
                <p>SPI Serial Flash</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SoC</p>
            </td>
            <td>
                <p>Bluetooth</p>
            </td>
            <td>
                <p>Bluetooth In-Place OTA DFU Bootloader</p>
            </td>
            <td>
                <p>Application</p>
            </td>
            <td>
                <p>OTA/internal flash</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>NCP</p>
            </td>
            <td>
                <p>EmberZNet PRO</p>
            </td>
            <td>
                <p>UART XMODEM Bootloader</p>
            </td>
            <td>
                <p>Standalone</p>
            </td>
            <td>
                <p>UART (EZSP)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>NCP</p>
            </td>
            <td>
                <p>Bluetooth</p>
            </td>
            <td>
                <p>BGAPI UART DFU Bootloader</p>
            </td>
            <td>
                <p>Standalone</p>
            </td>
            <td>
                <p>UART (BGAPI)</p>
            </td>
        </tr>
    </tbody>
</table>

Note that on devices with a dedicated bootloader area (EFR32xG12 and later Series 1 devices), if the device is configured to boot to the bootloader area (that is, if bit 1 of the Config Lock Word 0 CLW0[1] is set), an image always must be present in the bootloader area. The device is factory-programmed with a dummy bootloader that simply jumps directly to the application in main flash. This means that when flashing a bootloader to a device with a dedicated bootloader area, this dummy bootloader is replaced. If later during development using the bootloader is no longer desired, CLW0[1] must be cleared or the dummy bootloader needs to be re-flashed. Platform-specific prebuilt dummy bootloader images are located in _./platform/bootloader/util/bin/_. Note that since the dummy bootloader only consists of a few instructions and doesn't pad out the remainder of the bootloader area, only the first flash page (where the first-stage bootloader resides) is overwritten, so the main stage bootloader would likely remain intact after programming the dummy bootloader. If desired, the rest of the flash pages in the bootloader area can then be erased separately.

On devices that do not have a dedicated bootloader area (EFR32xG1 and EFR32 Series 2), a dummy bootloader is not needed.

The following sections provide an overview of the Gecko Bootloader common core, drivers, and components. For details, including details on error codes and conditions, see the Gecko Bootloader API Reference, shipped with the SDK in the platform/bootloader/documentation folder.

The bootloader area can be fully erased using the `commander device pageerase --region @bootloader` command with Simplicity Commander. In this state, the device will not boot until CLW0[1] is cleared or the dummy bootloader is flashed. For more information on how to use Simplicity Commander with Gecko bootloader, see section [Simplicity Commander and the Gecko Bootloader](08-simplicity-commander-and-the-gecko-bootloader#simplicity-commander-and-the-gecko-bootloader).

###### Core

The bootloader core contains the bootloader’s main functions. It also contains functionality to write to the internal flash, an image parser to parse and act upon the contents of GBL upgrade files, and functionality to boot the application in main flash.

The image parser can also optionally support the legacy Ember Bootloader (EBL) file format, but none of the security features offered by the Gecko Bootloader are supported if support for legacy EBL files is enabled.

A version of the GBL image parser without support for encrypted upgrade images is also available. This version can be used in flash space constrained bootloader applications where encryption of the upgrade image is not required.

###### Shared Memory (heading level 7)

To exchange information between the bootloader and application, a section of SRAM is used. The contents of SRAM are preserved through a software reset, making the SRAM suitable as a communication channel between bootloader and application.

The shared memory has a size of 4 bytes, and is located at the first address of SRAM, 0x20000000. It is used to store a single word containing the reason for a reset. The structure of the reset cause word is defined in the Reset Information part of the Application Interface, in the file **btl_reset_info.h**, as 16 bits containing the reason, and 16 bits of signature indicating if the word is valid or not. If the signature reads 0xF00F, the reset reason is valid.

All 16-bit reset reasons used by Silicon Labs have the most significant bit set to zero. If custom reset reasons are desired, it is recommended to set the most significant bit to avoid conflicting definitions.

In addition to the reset causes defined in the Reset Information documentation, the bootloader will enter firmware upgrade mode if the shared memory contains the value 0x00000001. This value is supported for compatibility with certain legacy Bluetooth applications.

###### Drivers

Different applications of firmware upgrade require different hardware drivers for use by the other components of the bootloader.

Driver modules include:

- Delay: Simple delay routines for use with components that require small delays or timeouts.
- SPI: Simple, blocking SPI master implementation for communication with external devices such as SPI flashes.
- SPI Slave: Flexible SPI Slave driver implementation for use in communication components implementing SPI protocols. This driver supports both blocking and non-blocking operation, with DMA (Direct Memory Access) backing the background transfers to support non-blocking operation.
- UART: Flexible serial UART driver implementation for use in communication components implementing UART protocols. This driver supports both blocking and non-blocking operation, with DMA backing the background transfers to support non-blocking operation. Additionally, support for hardware flow control (RTS/CTS) is included.

###### Components

All parts of the bootloader that are either optional or that may be exchanged for different configurations are implemented as components. Each component may have a configuration header file, and one or more implementations. Components include:

- Communication  
  - UART: XMODEM  
  - UART: BGAPI  
  - SPI: EZSP  
  - Bluetooth: AppLoader
- Compression
- Debug
- GPIO Activation
- Security
- Storage  
  - Internal flash  
  - External SPI flash
- SMP Switch (Series 2)  
  - Two-Page Switch Record

###### Communication (heading level 7)

The Communication components provide an interface for implementing communication with a host device, such as a computer or a micro-controller. Several components implement the communication interface, using different transports and protocols.

- BGAPI UART DFU: By enabling the BGAPI communication component, the bootloader communication interface implements the UART DFU protocol using BGAPI commands. See _AN1053: Bluetooth® Device Firmware Update over UART for EFR32xG1 and BGM11x Series Products_ for more information about this legacy bootloader.
- Bluetooth:AppLoader: By enabling the Bluetooth AppLoader communication component, the bootloader communication interface implements over-the-air device firmware upgrade functionality using Bluetooth. See [Using the Gecko Bootloader with the Silicon Labs Bluetooth Applications](https://docs.silabs.com/bluetooth/latest/using-gecko-bootloader-with-bluetooth-apps/) for more information.
- EZSP-SPI: By enabling the EZSP-SPI communication component, the bootloader communication interface implements the EZSP protocol over SPI. This component makes the bootloader compatible with the legacy ezsp-spi-bootloader that was previously released with the EmberZNet wireless stack. See _AN760: Using the Ember Standalone Bootloader_ for more information about legacy Ember standalone bootloaders.
- UART XMODEM: By enabling the UART XMODEM communication component, the bootloader communication interface implements the XMODEM-CRC protocol over UART. This component makes the bootloader compatible with the legacy serial-uart-bootloader that was previously released with the EmberZNet wireless stack. See _AN760: Using the Ember Standalone Bootloader_ for more information about legacy Ember standalone bootloaders.

###### Compression (heading level 7)

The Compression components provide capability for the bootloader GBL file parser to handle compressed GBL upgrade images. Each compression component provides support for one (de)compression algorithm. At the time of writing, decompression of data compressed with the LZ4 and LZMA algorithms is supported, through the _GBL Compression (LZ4)_ and _GBL Compression (LZMA)_ components.

###### Debug (heading level 7)

This component provides the bootloader with support for debugging output. If the component is configured to enable debug prints, short debug messages will be printed over Serial Wire Output (SWO), which can be accessed in multiple ways, including using Simplicity Commander, and by connecting to port 4900 of the Wireless Starter Kit TCP/IP interface.

To turn on debug prints, enable the Debug component and select **Debug prints**. Select **Debug asserts** to enable assertions in the source code.

On Series 1 devices, also select the GPIO peripheral in the Pin Tool.

###### GPIO Activation (heading level 7)

This component provides functionality to enter firmware upgrade mode automatically after reset if a GPIO pin is active during boot. The GPIO pin location and polarity are configurable.

- GPIO: By enabling the GPIO activation component, the firmware upgrade mode can be activated by the push buttons.
- EZSP GPIO: The EZSP communication protocol over SPI can be used together with this component. By enabling the EZSP GPIO component, the firmware upgrade mode can be entered by activating the _nWake_ pin.

###### Security (heading level 7)

Security components provide implementations of cryptographic operations as well as functionality to compute checksums and to read cryptographic keys from manufacturing tokens.

Modules include:

- AES: AES decryption functionality
- CRC16: CRC16 functionality
- CRC32: CRC32 functionality
- ECDSA: ECDSA signature verification functionality
- SHA-256: SHA-256 digest functionality

###### Storage (heading level 7)

These components provide the bootloader with multiple storage options for SoCs. All storage implementations have to provide an API to access image files to be upgraded. This API is based on the concept of dividing the download space into storage slots, where each slot has a predefined size and location in memory and can be used to store a single upgrade image. Some storage implementations also support a raw storage API to access the underlying storage medium. This can be used by applications to store other data in parts of the storage medium that are not used for storing firmware upgrade images. Implementations include:

- **Internal Flash**: The internal flash storage implementation uses the internal flash of the device for upgrade image storage. Note that this storage area is only a download space and is separate from the portion of internal flash used to hold the active application code.
- **SPI Flash**: Two components are available for SPI Flash storage implementation.  
  1. **SPI Flash Storage**: The SPI flash storage implementation supports a variety of SPI flash parts. The subset of devices supported can be configured in this component in Gecko Bootloader. (The default configuration if no devices are selected is to include drivers for all supported parts.) Including support for multiple devices requires more flash space in the bootloader. The SPI flash storage implementation does not support any write protection functionality. Supported SPI flash parts are shown in Table 1.2.  
  2. **SPI Flash Storage SFDP**: The SPI Flash storage SFDP implementation supports all the SPI flash parts that support SFDP (Serial Flash Description Parameter) Standard from JEDEC. The SPI Flash type is detected automatically by querying the SFDP Parameter table present within the flash memory. All the properties of the SPI Flash are accessed from this parameter table. This component is not configurable since the flash is automatically detected. Any flash that supports the SFDP standard can be used with the Gecko Bootloader. However, performance delays might occur when compared to using the **SPI Flash Storage** component.

> **Note**: Low power devices are recommended for battery-operated applications. Use of the other listed devices will decrease battery life due to higher quiescent current, but this can be mitigated with external shutdown FET circuitry, if desired.

Table Supported Serial Dataflash/EEPROM External Memory Parts

<table>
    <thead>
        <tr>
            <th>Manufacturer Part Number</th>
            <th>Size (kB)</th>
            <th>Quiescent Current (µA Typical)*</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Macronix MX25R8035F (low power)</p>
            </td>
            <td>
                <p>1024</p>
            </td>
            <td>
                <p>0.007</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Macronix MX25R6435SF (low power)</p>
            </td>
            <td>
                <p>8192</p>
            </td>
            <td>
                <p>0.007</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Macronix MX25R3235F (low power)</p>
            </td>
            <td>
                <p>4096</p>
            </td>
            <td>
                <p>0.007</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Spansion S25FL208K</p>
            </td>
            <td>
                <p>1024</p>
            </td>
            <td>
                <p>15</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Winbond W25X20BVSNIG (W25X20CVSNJG for high-temperature support)</p>
            </td>
            <td>
                <p>256</p>
            </td>
            <td>
                <p>1</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Winbond W25Q80BVSNIG (W25Q80BVSNJG for high-temperature support)</p>
            </td>
            <td>
                <p>1024</p>
            </td>
            <td>
                <p>1</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Macronix MX25L2006EM1I-12G (MX25L2006EM1R-12G for high-temperature support)</p>
            </td>
            <td>
                <p>256</p>
            </td>
            <td>
                <p>2</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Macronix MX25L4006E</p>
            </td>
            <td>
                <p>512</p>
            </td>
            <td>
                <p>2</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Macronix MX25L8006EM1I-12G (MX25L8006EM1R-12G for high-temperature support)</p>
            </td>
            <td>
                <p>1024</p>
            </td>
            <td>
                <p>2</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Macronix MX25L1606E</p>
            </td>
            <td>
                <p>2048</p>
            </td>
            <td>
                <p>2</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Macronix MX25U1635E (2V)</p>
            </td>
            <td>
                <p>2048</p>
            </td>
            <td>
                <p>2</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Atmel/Adesto AT25DF041A</p>
            </td>
            <td>
                <p>512</p>
            </td>
            <td>
                <p>15</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Atmel/Adesto AT25DF081A</p>
            </td>
            <td>
                <p>1024</p>
            </td>
            <td>
                <p>5</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Atmel/Adesto AT25SF041</p>
            </td>
            <td>
                <p>512</p>
            </td>
            <td>
                <p>2</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Micron (Numonyx) M25P20</p>
            </td>
            <td>
                <p>256</p>
            </td>
            <td>
                <p>1</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Micron (Numonyx) M25P40</p>
            </td>
            <td>
                <p>512</p>
            </td>
            <td>
                <p>1</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Micron (Numonyx) M25P80</p>
            </td>
            <td>
                <p>1024</p>
            </td>
            <td>
                <p>1</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Micron (Numonyx) M25P16</p>
            </td>
            <td>
                <p>2048</p>
            </td>
            <td>
                <p>1</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>ISSI IS25LQ025B</p>
            </td>
            <td>
                <p>32</p>
            </td>
            <td>
                <p>8</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>ISSI IS25LQ512B</p>
            </td>
            <td>
                <p>64</p>
            </td>
            <td>
                <p>8</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>ISSI IS25LQ010B</p>
            </td>
            <td>
                <p>126</p>
            </td>
            <td>
                <p>8</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>ISSI IS25LQ020B</p>
            </td>
            <td>
                <p>256</p>
            </td>
            <td>
                <p>8</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>ISSI IS25LQ040B</p>
            </td>
            <td>
                <p>512</p>
            </td>
            <td>
                <p>8</p>
            </td>
        </tr>
    </tbody>
</table>

* Quiescent current values are as of December 2017; check the latest part specifications for any changes.

###### Compatibility

1. Silicon Labs recommends building the Gecko Bootloader and the application using the same GSDK. This implies that, if the Gecko Bootloader has been built using GSDK v4.0, it is recommended that the application also be built using GSDK v4.0.
2. Backward compatibility is supported wherein the Gecko Bootloader is built using a previous SDK version and the application is built using a newer SDK version.
3. In general, Silicon Labs does not recommend building the Gecko Bootloader using a newer SDK version with the application built using an older SDK version.

The below table attempts to explain the above-mentioned guidelines:

<table>
    <thead>
        <tr>
            <th>
                <p>SDK version X &lt; SDK version Y</p>
            </th>
            <th>
                <p>Application built using SDK version X</p>
            </th>
            <th>
                <p>Application built using SDK version Y</p>
            </th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Gecko Bootloader built using SDK version X</p>
            </td>
            <td>
                <p>Recommended</p>
            </td>
            <td>
                <p>Compatible</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Gecko Bootloader built using SDK version Y</p>
            </td>
            <td>
                <p>Not recommended</p>
            </td>
            <td>
                <p>Recommended</p>
            </td>
        </tr>
    </tbody>
</table>

###### SMP Switch

The Switch Multi-Protocol (SMP) Switch component provides the bootloader with **dual-application boot selection** on a single device. The bootloader stores a small **switch record** in two dedicated flash erase pages and uses it to select which of two application images (App 1 or App 2) to boot. No NVM3 is required for this selection.

The component is currently available on **Series 2 devices** (EFR32ZG28).

When enabled, the bootloader:

- Reads a `btl_smp_switch_record_t` from each of two configured flash pages and validates magic, app id, version, and CRC32.
- Boots the application identified by the **newest valid** record (higher 16-bit sequence number wins; ties resolve to page 2).
- Uses `BTL_SMP_DEFAULT_APP_ID` when both records are invalid (e.g., first boot).
- Falls back to the **alternate application slot** if image verification of the SMP-selected app fails — for example when the SMP-selected app does not pass Secure Boot verification.

The application can request that the **next reset** runs the other application by calling `btl_smp_switch_request_next_boot_app()`. The application discovers the switch page bases at runtime via the bootloader storage vtable (`getSmpSwitchPageBases`); it does not include the bootloader's `btl_smp_cfg.h`.

The bootloader advertises this capability through the `BOOTLOADER_CAPABILITY_SMP_SWITCH` bit in `BootloaderInformation_t.capabilities`.

Implementations include:

- **SMP Switch (Series 2)** — adds the `bootloader_smp_switch` component, defines `BTL_SMP_SUPPORT`, and configures page and application base addresses through `btl_smp_cfg.h` (see [Configuring the Gecko Bootloader](https://docs.silabs.com/shared-content/latest/bootloader-user-guide-gsdk-4/07-configuring-the-gecko-bootloader). For the application-side API and switch-record format, see [Application Interface](https://docs.silabs.com/shared-content/latest/bootloader-user-guide-gsdk-4/12-application-interface) and the dedicated SMP chapter.

##### Gecko Bootloader File Format

The GBL file format is used by the Gecko Bootloader. File formats described in this section are generated by Simplicity Commander commands. For more information, see [Simplicity Commander Reference Guide](https://docs.silabs.com/simplicity-commander/latest/simplicity-commander-start/).

###### File Structures

The GBL file format is composed of several tags that indicate a format of the subsequent data and the length of the entire tag. The format of a tag is as follows:

<table>
    <thead>
        <tr>
            <th>
                <p>Tag ID</p>
            </th>
            <th>
                <p>Tag Length</p>
            </th>
            <th>
                <p>Tag Payload</p>
            </th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>4 bytes</p>
            </td>
            <td>
                <p>4 bytes</p>
            </td>
            <td>
                <p>Variable (according to tag length)</p>
            </td>
        </tr>
    </tbody>
</table>

###### Plaintext Tag Description

<table>
    <thead>
        <tr>
            <th>
                <p>Tag Name</p>
            </th>
            <th>
                <p>ID</p>
            </th>
            <th>
                <p>Description</p>
            </th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>GBL Header Tag</p>
            </td>
            <td>
                <p>0x03A617EB</p>
            </td>
            <td>
                <p>This must be the first tag in the file. The header tag contains the version number of the GBL file specification, and flags indicating the type of GBL file - whether it is signed or encrypted.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>GBL Version Dependency Tag</p>
            </td>
            <td>
                <p>0x76A617EB</p>
            </td>
            <td>
                <p>This optional tag contains encoded version dependencies that the software currently running on the device must satisfy before an upgrade can be attempted. Only available on Series 2 devices.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>GBL Application Info Tag</p>
            </td>
            <td>
                <p>0xF40A0AF4</p>
            </td>
            <td>
                <p>This tag contains information about the application update image that is contained in this GBL file</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>GBL SE Upgrade Tag</p>
            </td>
            <td>
                <p>0x5EA617EB</p>
            </td>
            <td>
                <p>This tag contains a complete encrypted Secure Element update image. Only applicable on Series 2 devices.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>GBL Bootloader Tag</p>
            </td>
            <td>
                <p>0xF50909F5</p>
            </td>
            <td>
                <p>This tag contains a complete bootloader update image.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>GBL Program Data Tag</p>
            </td>
            <td>
                <p>0xFE0101FE / 0xFD0303FD</p>
            </td>
            <td>
                <p>This tag contains information about what application data to program at a specific address into the main flash memory.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>GBL Delta Tag</p>
            </td>
            <td>
                <p>0xF80A0AF8UL</p>
            </td>
            <td>
                <p>This tag contains the information about the delta patch that should be used to create the new app.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>GBL Program LZ4 Compressed Data Tag</p>
            </td>
            <td>
                <p>0xFD0505FD</p>
            </td>
            <td>
                <p>This tag contains LZ4 compressed information about what application data to program at a specific address into the main flash memory.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>GBL Delta LZ4 Compressed Data Tag</p>
            </td>
            <td>
                <p>0xF80B0BF8UL</p>
            </td>
            <td>
                <p>This tag contains LZ4 compressed information about the delta patch that should be used to create the new app.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>GBL Program LZMA Compressed Data Tag</p>
            </td>
            <td>
                <p>0xFD0707FD</p>
            </td>
            <td>
                <p>This tag contains LZMA compressed information about what application data to program at a specific address into the main flash memory.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>GBL Delta LZMA Compressed Data Tag</p>
            </td>
            <td>
                <p>0xF80C0CF8UL</p>
            </td>
            <td>
                <p>This tag contains LZMA compressed information about the delta patch that should be used to create the new app.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>GBL Metadata Tag</p>
            </td>
            <td>
                <p>0xF60808F6</p>
            </td>
            <td>
                <p>This tag contains metadata that the bootloader does not parse but can be returned to the application through a callback.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>GBL Certificate Tag</p>
            </td>
            <td>
                <p>0xF30B0BF3</p>
            </td>
            <td>
                <p>This tag contains a certificate that will be used to verify the authenticity of the GBL file.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>GBL Signature Tag</p>
            </td>
            <td>
                <p>0xF70A0AF7</p>
            </td>
            <td>
                <p>This tag contains the ECDSA-P256 signature of all preceding data in the file.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>GBL End Tag</p>
            </td>
            <td>
                <p>0xFC0404FC</p>
            </td>
            <td>
                <p>This tag indicates the end of the GBL file. It contains a 32-bit CRC for the entire file as an integrity check. The CRC is a non-cryptographic check. This must be the last tag.</p>
            </td>
        </tr>
    </tbody>
</table>

The allowed sequence of GBL tags in a GBL file is shown in the following figure.

![GBL Tag Sequence](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image1.png)

###### Encrypted Tag Descriptions

The encrypted GBL file format is like the unencrypted version. It introduces several new tags.

<table>
    <thead>
        <tr>
            <th>
                <p>Tag Name</p>
            </th>
            <th>
                <p>ID</p>
            </th>
            <th>
                <p>Description</p>
            </th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>GBL Header Tag</p>
            </td>
            <td>
                <p>0x03A617EB</p>
            </td>
            <td>
                <p>The GBL header is the same as for a plaintext GBL file, but the flag indicating that the GBL file is encrypted must be set.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>GBL Encryption Init Header</p>
            </td>
            <td>
                <p>0xFA0606FA</p>
            </td>
            <td>
                <p>This contains information about the image encryption such as the Nonce and the amount of encrypted data.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>GBL Encrypted Program Data</p>
            </td>
            <td>
                <p>0xF90707F9</p>
            </td>
            <td>
                <p>This contains an encrypted payload containing a plaintext GBL tag, one of Application Info, Bootloader, Metadata or Program Data. The data is encrypted using AES-CTR-128.</p>
            </td>
        </tr>
    </tbody>
</table>

The allowed sequence of GBL tags in an encrypted GBL file is shown in the following figure.

![Encrypted GBL Tag Sequence](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image2.png)

###### GBL Tag Data Structures and Definitions

**GBL Tag Header**

```C
typedef struct {

uint32_t  tagId;  // Tag ID representing the type of tag (GBL Header Tag, GBL Bootloader Tag, etc.).

uint32_t  length; // Length of the subsequent tag data in bytes.

} GblTagHeader_t;
```

**GBL Header Tag**

```C
typedef struct {

GblTagHeader_t header;  // Tag header.

uint32_t       version; // Version of the GBL file format specification. E.g. 0x03000000.

uint32_t       type;    // Flags indicating whether the GBL file is encrypted and/or signed.

// See definitions below.

} GblHeader_t;
```

```C
// GBL types

#define GBL_TYPE_NONE                 0x00000000UL

#define GBL_TYPE_ENCRYPTION_AESCCM    0x00000001UL

#define GBL_TYPE_SIGNATURE_ECDSA      0x00000100UL
```

**GBL Version Dependency Tag**

```C
typedef struct {

uint8_t  imageType;  // Type of image (application, bootloader, SE)

uint8_t  statement;  // Encoded dependency statement (ex. appVersion > (0).1.2.3)

uint16_t reserved;   // Reserved

uint32_t version;    // The version number used in the statement (ex. (0).1.2.3)

} VersionDependency_t;

// Image types

#define GBL_VERSION_DEPENDENCY_TYPE_APPLICATION             0x01U

#define GBL_VERSION_DEPENDENCY_TYPE_BOOTLOADER              0x02U

#define GBL_VERSION_DEPENDENCY_TYPE_SE                      0x03U

// Operator encoding

#define GBL_VERSION_DEPENDENCY_OPERATOR_MASK                0x0FU

#define GBL_VERSION_DEPENDENCY_OPERATOR_SHIFT               0x00U

#define GBL_VERSION_DEPENDENCY_OPERATOR_TYPE_MASK           0x0EU

#define GBL_VERSION_DEPENDENCY_OPERATOR_NEGATOR_BIT_MASK    0x01U

#define GBL_VERSION_DEPENDENCY_OPERATOR_LT                  0x00U

#define GBL_VERSION_DEPENDENCY_OPERATOR_LEQ                 0x02U

#define GBL_VERSION_DEPENDENCY_OPERATOR_EQ                  0x04U

#define GBL_VERSION_DEPENDENCY_OPERATOR_GEQ                 0x06U

#define GBL_VERSION_DEPENDENCY_OPERATOR_GT                  0x08U

// Connective encoding

#define GBL_VERSION_DEPENDENCY_CONNECTIVE_MASK              0xF0U

#define GBL_VERSION_DEPENDENCY_CONNECTIVE_SHIFT             0x04U

#define GBL_VERSION_DEPENDENCY_CONNECTIVE_TYPE_MASK         0x0EU

#define GBL_VERSION_DEPENDENCY_CONNECTIVE_NEGATOR_BIT_MASK  0x01U

#define GBL_VERSION_DEPENDENCY_CONNECTIVE_AND               0x00U

// SE version mask for ignoring the compatibility byte when comparing versions

#define GBL_VERSION_DEPENDENCY_SE_VERSION_MASK              0x00FFFFFFUL
```

**GBL Application Info Tag**

```C
typedef struct {

GblTagHeader_t    header;  // Tag header.

ApplicationData_t appInfo; // Application information structure. See definition below.

} GblApplication_t;
```

```C
typedef struct ApplicationData {

uint32_t type;           // Bitfield representing the type of application.

// See definitions below for possible values.

uint32_t version;        // Version number for this application (customer-defined).

uint32_t capabilities;   // Bitfield representing the capabilities of this application.

uint8_t  productId[16];  // Unique ID (UUID or GUID) for the product this application is built for.

} ApplicationData_t;
```

```C
// Application types

#define APPLICATION_TYPE_ZIGBEE          (1UL << 0UL)

#define APPLICATION_TYPE_THREAD          (1UL << 1UL)

#define APPLICATION_TYPE_FLEX            (1UL << 2UL)

#define APPLICATION_TYPE_BLUETOOTH       (1UL << 3UL)

#define APPLICATION_TYPE_MCU             (1UL << 4UL)

#define APPLICATION_TYPE_BLUETOOTH_APP   (1UL << 5UL)

#define APPLICATION_TYPE_BOOTLOADER      (1UL << 6UL)

#define APPLICATION_TYPE_ZWAVE           (1UL << 7UL)
```

**GBL SE Upgrade Tag**

```C
typedef struct {

GblTagHeader_t header;   // Tag header.

uint32_t       blobSize; // Size of the SE upgrade data blob.

uint32_t       version;  // Version of the SE image.

uint8_t        data[];   // Array of data containing the SE upgrade blob.

} GblSeUpgrade_t;
```

**GBL Bootloader Tag**

```C
typedef struct {

GblTagHeader_t header;            // Tag header.

uint32_t       bootloaderVersion; // Version number of the bootloader.

uint32_t       address;           // Base address of the bootloader image.

uint8_t        data[];            // Array of data containing the bootloader upgrade image.

} GblBootloader_t;
```

**GBL Program Data Tag**

```C
typedef struct {

GblTagHeader_t header;            // Tag header.

uint32_t       flashStartAddress; // Address at which to start flashing data.

uint8_t        data[];            // Array of data to be flashed

// (compressed data in the LZ4 and LZMA variants of the tag).

} GblProg_t;
```

**GBL Metadata Tag**

```C
typedef struct {

GblTagHeader_t header;     // Tag header.

uint8_t        metadata[]; // Array containing the metadata.

} GblMetadata_t;
```

**GBL Certificate Tag**

```C
typedef struct {

GblTagHeader_t           header;      // Tag header.

ApplicationCertificate_t certificate; // Certificate used to verify the GBL file. See definition below.

} GblCertificateEcdsaP256_t;

typedef struct ApplicationCertificate {

uint8_t  structVersion;     // Version of the certificate structure.

uint8_t  flags[3];          // Reserved flags.

uint8_t  key[64];           // Public key used to verify the GBL file.

uint32_t version;           // Version number of this certificate.

uint8_t  signature[64];     // The signature of the certificate itself (not the GBL file).

} ApplicationCertificate_t;
```

**GBL Signature Tag**

```C
typedef struct {

GblTagHeader_t header; // Tag header.

uint8_t        r[32];  // The r value of the ECDSA signature.

uint8_t        s[32];  // The s value of the ECDSA signature.

} GblSignatureEcdsaP256_t;
```

**GBL End Tag**

```C
typedef struct {

GblTagHeader_t header; // Tag header.

uint32_t       gblCrc; // CRC32 checksum of the entire GBL file.

} GblEnd_t;
```

**GBL Encryption Init Header Tag**

```C
typedef struct {

GblTagHeader_t header;    // Tag header.

uint32_t       msgLen;    // Length of the ciphertext in bytes.

uint8_t        nonce[12]; // Random AES-CTR nonce.

} GblEncryptionInitAesCcm_t;
```

**GBL Encrypted Data Tag**

```C
typedef struct {

GblTagHeader_t header;             // Tag header.

uint8_t        encryptedGblData[]; // Array containing the ciphertext (encrypted GBL data).

// Note that the corresponding plaintext must contain one or

// more complete GBL tags. Put differently: A single plaintext GBL

// tag cannot be split across multiple encrypted tags.

} GblEncryptionData_t;
```

##### Gecko Bootloader Operation - Application Upgrade

This section summarizes Gecko Bootloader operation for updating application firmware, first if the Gecko Bootloader is configured in standalone mode and then if it is configured in application mode. Section [Gecko Bootloader Operation - Bootloader Upgrade](04-gecko-bootloader-operation-bootloader-upgrade#gecko-bootloader-operation-bootloader-upgrade) provides the same information for updating the bootloader firmware.

The figures that illustrate Gecko Bootloader operation in this section do not provide information about the bootloader memory layouts for different devices. For more details refer to the section "Memory Space for Bootloading" in [Bootloader Fundamentals](https://docs.silabs.com/mcu-bootloader/latest/bootloader-fundamentals/).

###### Standalone Bootloader Operation

Standalone bootloader operation is illustrated in the following figure:

![Standalone Bootloader Operation](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image3.png)

1. The device reboots into the bootloader.
2. A GBL file containing an application image is transmitted from the host to the device. If image encryption is enabled in the main stage bootloader and the image is encrypted, decryption is performed during the process of receiving and parsing the GBL file.
3. The bootloader applies the application upgrade from the GBL upgrade file on-the-fly. If image authentication is enabled in the main stage bootloader and the GBL file contains a signature, the authenticity of the image is verified before completing the process.
4. The device boots into the application. Application upgrade is complete.

###### Rebooting Into the Bootloader (heading level 7)

The Gecko Bootloader supports multiple mechanisms for triggering the bootloader. If the **GPIO activation** component is installed, the host device can keep this pin low/high (depending on configuration) through reset to make the device enter the bootloader. The bootloader can also be entered through software. The `bootloader_rebootAndInstall` API first signals to the bootloader that it should enter firmware upgrade mode by writing a command to the shared memory location at the bottom of SRAM, and then performs a software reset. If the bootloader finds the correct command in shared memory upon boot, it will enter firmware upgrade mode instead of booting the existing application.

###### Downloading and Applying a GBL Upgrade File (heading level 7)

When the bootloader enters firmware upgrade mode, it enters a receive loop waiting for data from the host device. The specifics of the receive loop depend on the protocol. Received packets are passed to the image parser, a state machine that parses the data and returns a callback containing any data that should be acted upon. The bootloader core implements this callback and flashes the data to internal flash at the address specified. If GBL file authentication or encryption is enabled, the image parser will enforce this, and abort the image upgrade if the authentication fails.

The bootloader prevents a newly uploaded image from being bootable by holding back parts of the application vector table until the GBL file CRC and GBL signature (if required) have been verified.

###### Booting Into the Application (heading level 7)

When an application upgrade is completed, the bootloader triggers a reboot with a message in shared memory at the bottom of SRAM signaling that an application upgrade has been successfully completed. The application can use this reset information to learn that an application upgrade was just performed.

Before jumping to the main application, the bootloader verifies that the application is ready to run. This includes verifying if the Program counter and Stack Pointer are valid. If secure boot is enabled, the bootloader expects a signed application and attempts to validate the signature of the application. In scenarios where secure boot is not enabled, the bootloader attempts to validate if the Application properties pointer points to valid app properties structure in the flash. If a valid app properties struct is found, the bootloader proceeds based on the signature type indicated by the application properties struct or else the bootloader assumes that the Application properties pointer points to the Reset Handler of the application (an app without application properties) and proceeds to boot into the application. In case the verification of the application fails at any stage, the bootloader enters the bootload mode instead of booting into the application.

###### Error Handling (heading level 7)

If the application upgrade is interrupted at any time, the device will be without a working application. The bootloader then resets the device, and re-enters firmware upgrade mode. The host device can easily restart the application upgrade process, to try loading the upgrade image again.

###### Application Bootloader Operation

The following figure illustrates the application bootloader operation both for a single image/single storage slot, and multiple images/ multiple storage slots.

![Application Bootloader Operation: Single Storage Slot](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image4.png)

![Application Bootloader Operation: Multiple Storage Slots](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image5.png)

1. A GBL file is downloaded onto the storage medium of the device (internal flash or external dataflash), as described below, and the presence of an upgrade image is indicated.
2. The device reboots into the bootloader, and the bootloader enters firmware upgrade mode.
3. The bootloader applies the application upgrade from the GBL upgrade file.
4. The device boots into the application. Application upgrade is complete.

###### Downloading and Storing a GBL Upgrade Image File (heading level 7)

To prepare for receiving an upgrade image, the application finds an available storage slot, or erases an existing one using `bootloader_eraseStorageSlot`. If the bootloader only supports a single storage slot, a value of 0 should be used for the slot ID.

The application then receives a GBL file using an applicable protocol, such as Ethernet, USB, Zigbee, OpenThread, or Bluetooth, and stores it in the slot by calling `bootloader_writeStorage`.

When download is complete, the application can optionally verify the integrity of the GBL file by calling `bootloader_verifyImage`. This is also done by the bootloader before applying the image but can be done from the application in order to avoid rebooting into the bootloader if the received image was corrupt.

If multiple storage slots are supported, the application should write a bootload list by calling `bootloader_setImageToBootload`. The list is written to the two bootload info pages as shown in Figure The bootload list is a prioritized list of slots indicating the order the bootloader should use when attempting to perform a firmware upgrade. The bootloader attempts to verify the images in these storage slots in sequence and applies the first image to pass verification. If only a single storage slot is supported, the bootloader treats the entire download space as a single storage slot.

###### Rebooting and Applying a GBL Upgrade File (heading level 7)

The bootloader can be entered through software. The `bootloader_rebootAndInstall` API signals to the bootloader that it should enter firmware upgrade mode by writing a command to the shared memory location at the bottom of SRAM, and then performs a software reset. If the bootloader finds the correct command in shared memory upon boot, it enters firmware upgrade mode instead of booting the existing application.

The bootloader iterates over the list of storage slots marked for bootload and attempts to verify the image stored in each. Once it finds a valid GBL upgrade file, firmware upgrade is attempted from this GBL file. If the upgrade fails, the bootloader moves to the next image in the list. If no images pass verification, the bootloader reboots back into the existing application with a message in the shared memory location in SRAM indicating that no good upgrade images were found.

###### Booting Into the Application (heading level 7)

When an application upgrade is completed, the bootloader triggers a reboot with a message in shared memory at the bottom of SRAM signaling that an application upgrade has been successfully completed. The application can use this reset information to learn that an application upgrade was just performed.

Before jumping to the main application, the bootloader verifies that the application is ready to run. This includes verifying if the Program counter and Stack Pointer are valid. If secure boot is enabled, the bootloader expects a signed application and attempts to validate the signature of the application. In scenarios where secure boot is not enabled, the bootloader attempts to validate if the Application properties pointer points to valid app properties structure in the flash. If valid app properties struct is found, the bootloader proceeds based on the signature type indicated by the application properties struct or else the bootloader assumes that the Application properties pointer points to the Reset Handler of the application (an app without application properties) and proceeds to boot into the application. In case the verification of the application fails at any stage, the bootloader enters the bootload mode instead of booting into the application.

##### Gecko Bootloader Operation - Bootloader Upgrade

Bootloader upgrade functionality is provided by the first stage bootloader on Series 1 devices, or the Secure Engine on Series 2 devices. The Secure Engine itself is also upgradable. For more details, see section [Gecko Bootloader Operation - Secure Engine Upgrade](05-gecko-bootloader-operation-secure-engine-upgrade#gecko-bootloader-operation-secure-engine-upgrade). On Series 1 devices, the first stage bootloader is not upgradable.

Requirements for upgrading the main bootloader vary depending on the bootloader configuration:

- Application bootloader with storage: Upgrading the main bootloader requires a single GBL file containing both bootloader and application upgrade images.
- Standalone bootloader with communication interface: Upgrading the bootloader requires two GBL files, one with only the bootloader upgrade image, and one with only the application upgrade image.

Security of the bootloader upgrade process is provided by signing the GBL file. See section [Creating a Signed and Encrypted GBL Upgrade Image File from an Application](09-gecko-bootloader-security-features#creating-a-signed-and-encrypted-gbl-upgrade-image-file-from-an-application).

The figures that illustrate Gecko Bootloader operation in this section do not provide information about the bootloader memory layouts for different devices. For more details refer to the “Memory Space for Bootloading” section in [Bootloader Fundamentals](https://docs.silabs.com/mcu-bootloader/latest/bootloader-fundamentals/). For convenience, the figures do not distinguish between SE and VSE.

###### Bootloader Upgrade on Bootloaders with Communication Interface (Standalone Bootloaders)

The process is illustrated in the following figure:

![Standalone Bootloader: Bootloader Upgrade](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image6.png)

1. The device reboots into the bootloader.
2. A GBL file containing only a bootloader upgrade image is transmitted from the host to the device.
3. The contents of the GBL Bootloader tag are written to the bootloader upgrade location in internal flash, overwriting the existing application.
4. The device reboots into the first stage bootloader / Secure Engine.
5. The first stage bootloader / Secure Engine replaces the main bootloader with the new version found in the bootloader upgrade location.
6. The device boots into the new main bootloader.
7. A GBL file containing only an application image is transmitted from the host to the device.
8. The bootloader applies the application image from the GBL upgrade file on-the-fly.
9. The device boots into the application. Bootloader upgrade is complete.

A bootloader upgrade is started in the same way as an application upgrade.

###### Downloading and Applying a Bootloader GBL Upgrade File (heading level 7)

When the bootloader has entered the receive loop, a GBL upgrade file containing a bootloader upgrade is transmitted to the bootloader. When a packet is received, it is passed to the image parser. The image parser parses the data and returns bootloader upgrade data in a callback. The bootloader core implements this callback and flashes the data to internal flash at the bootloader upgrade location.

The bootloader prevents a newly uploaded bootloader upgrade image from being interpreted as valid by holding back parts of the bootloader upgrade vector table until the GBL file CRC and GBL signature (if required) have been verified.

When a complete bootloader upgrade image is received, the main bootloader signals the first stage bootloader / Secure Engine that it should enter firmware upgrade mode. On Series 1 devices, this is done by writing a command to the shared memory location at the bottom of SRAM, and then performing a software reset. On Series 2 devices, Secure Engine communication is used to signal that bootloader upgrade is ready to be performed.

On Series 1 devices, the first stage bootloader verifies the CRC on the current main bootloader and verifies the CRC of the bootloader upgrade present in the bootloader upgrade location in internal flash.

- If the CRC in the upgrade image location and the CRC in the current main bootloader are both valid, then the upgrade image will be copied over the main bootloader if the version number of the upgrade is higher than the current main bootloader version.
- If the CRC in the upgrade image location is valid and the CRC in the current main bootloader location is not valid, the upgrade image will be copied over the main bootloader regardless of the version. This is because the version of the main bootloader cannot be relied upon if the main bootloader image is corrupt.
- If the CRC in the upgrade location is not valid, the upgrade will not be applied.

On Series 2 devices, the authenticity of the main bootloader optionally can be verified before applying the bootloader upgrade. See section [Setting a Version Number](07-configuring-the-gecko-bootloader#setting-a-version-number) for more information about versioning bootloader images.

###### Upgrading Bootloaders Without Secure Boot to Bootloaders with Secure Boot (heading level 7)

A bootloader without the secure boot feature can be upgraded to a bootloader with the secure boot feature, using the following procedure:

1. Prepare a Gecko Bootloader image with secure boot enabled. The version of the bootloader needs to be higher than the bootloader on the device.  
   - Turn on secure boot in Simplicity Studio by going to the **Bootloader Core** component and selecting the **Enable secure boot** option.  
   - (Optional) In the **Bootloader Core** component, select the **Require signed firmware upgrade files** option. This means that the Gecko Bootloader will only accept signed GBL files.
2. Generate a public/private Signing Key pair. See section [Generating Keys](09-gecko-bootloader-security-features#generating-keys) for more information on creating a Signing Key pair.
3. Write the public key generated from the previous step to the device. The public key is stored as a manufacturing token in the device by default. This key can be written by application code running on the device as long as the Lock Bits page is configured to allow flash writes. If the Lock Bits page is locked, it can only be erased by the debugger. Therefore, signing/decryption keys residing in the Lock Bits page cannot be erased from firmware. This means that, for devices in the field, those areas in flash cannot be replaced with new ones. However, the Gecko Bootloader prepared from step 1 can be modified to look for the decryption and signature keys in a different location. Key locations are defined in the bootloader project file `btl_security_tokens.c`.
4. Create a GBL file using the Gecko Bootloader image. The GBL file needs to be signed/unsigned depending on the current configuration of the Gecko Bootloader running on the device. For more details on creating a GBL file, see section [Creating a Signed and Encrypted GBL Upgrade Image File from an Application](09-gecko-bootloader-security-features#creating-a-signed-and-encrypted-gbl-upgrade-image-file-from-an-application).
5. Upload the GBL file. For more details on the upgrade process, see section [Bootloader Upgrade on Bootloaders with Communication Interface (Standalone Bootloaders)](#bootloader-upgrade-on-bootloaders-with-communication-interface-standalone-bootloaders).

###### Enabling Secure Boot RTSL on Series 2 Devices (heading level 7)

Secure Boot RTSL (Root of Trust and Secure Loader) can be enabled using the following procedure:

1. Prepare a Gecko Bootloader image with secure boot enabled. The version of the Gecko Bootloader needs to be higher than the Gecko Bootloader on the device.  
   - Turn on secure boot in Simplicity Studio by going to the **Bootloader Core** component and selecting the **Enable secure boot** option.  
   - For EFR32xG21, in the **Bootloader Core** component, disable the **Allow use of public key from manufacturing token storage** option. This means that the Gecko Bootloader will never make use of the public key stored in the last page of the main flash  
   - (Optional) In the **Bootloader Core** component, select the **Require signed firmware upgrade files** option. This means that the Gecko Bootloader will only accept signed GBL files.
2. Generate a public/private Signing Key pair. See section [Generating Keys](09-gecko-bootloader-security-features#generating-keys) for more information on creating a Signing Key pair.
3. Prepare an application that installs the public key generated from step 2 to the Secure Engine One-time Programmable memory. Installing a key in the VSE requires a reset routine. Make sure that the application does not end up in the reset loop. Create an unsigned GBL file from this application and upload it. For more information on installing public keys, see section [Creating a Signed and Encrypted GBL Upgrade Image File from an Application](09-gecko-bootloader-security-features#creating-a-signed-and-encrypted-gbl-upgrade-image-file-from-an-application).
4. Sign the Gecko Bootloader image generated from step 1 using the private key generated in step 2. See section [Signing an Application Image for Secure Boot](09-gecko-bootloader-security-features#signing-an-application-image-for-secure-boot) for more information on signing binaries.
5. Make a custom application that turns on secure boot on the Secure Engine and sign this application binary with the private key generated from step 2. For more details on how to turn on secure boot on the Secure Engine, see [Series 2 and Series 3 Secure Boot with RTSL](https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/).
6. Create a GBL file using the Gecko Bootloader image from step 4.
7. Create a GBL file using the application from step 5. The GBL file need to be signed if the **Bootloader Core** component option **Require signed firmware upgrade files** was selected in step 1.
8. Upload the GBL file containing the Gecko Bootloader image.
9. Upload the GBL file containing the application.

###### Downloading and Applying an Application GBL Upgrade File (heading level 7)

Once the bootloader upgrade is completed, the existing application is rendered invalid, since the bootloader upgrade location overlaps with the application. A GBL upgrade file containing an application upgrade is transmitted to the bootloader. The application upgrade process follows that in section [Standalone Bootloader Operation](03-gecko-bootloader-operation-application-upgrade#standalone-bootloader-operation).

###### Bootloader Upgrade on Application Bootloaders with Storage

The process is illustrated in the following figure.

![Application Bootloader: Bootloader Upgrade](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image7.png)

1. A single GBL file containing both a bootloader upgrade image and an application image is downloaded onto the storage medium of the device (internal flash or external SPI flash).
2. The device reboots into the main bootloader.
3. The main bootloader parses its upgrade image into internal flash at the bootloader upgrade location. Depending on the application size as well as how the “scratch space” is configured for the bootloader on Series 2 devices, the existing application may be overwritten.
4. The main bootloader verifies the integrity of the upgrade image and then resets the device with reset reason BOOTLOADER_RESET_REASON_UPGRADE to apply the upgrade.
5. The device reboots into the first stage bootloader / Secure Engine.
6. The first stage bootloader / Secure Engine replaces the main bootloader with the new version.
7. The device boots into the new main bootloader.
8. The bootloader applies the application image from the GBL upgrade file.
9. The device boots into the application. Bootloader upgrade is complete.

A bootloader upgrade is started in the same way as an Application Upgrade. A single GBL file containing both a bootloader and an application upgrade is written to storage by the application, and the bootloader is entered.

The bootloader iterates over the list of storage slots marked for bootload and attempts to verify the GBL file stored within. Verification returns information about whether the GBL file contains an application, or both a bootloader and an application. The image parser parses the file. If the GBL file contains a bootloader, the bootloader upgrade data is returned in a callback. The bootloader core implements this callback and flashes the data to internal flash at the bootloader upgrade location.

The bootloader prevents a newly uploaded bootloader upgrade image from being interpreted as valid by holding back parts of the bootloader upgrade vector table until the GBL file CRC and GBL signature (if required) have been verified.

On Series 1 devices, the main bootloader signals the first stage bootloader that it should enter firmware upgrade mode by writing a command to the shared memory location at the bottom of SRAM, and then performing a software reset. On Series 2 devices, Secure Engine communication interface is used to signal the Secure Engine that a bootloader upgrade is ready to be performed.

On Series 1 devices, the first stage bootloader verifies the CRC of the bootloader upgrade present in the bootloader upgrade location in internal flash and copies the bootloader upgrade over the main bootloader if the version number of the upgrade is higher than the version number of the existing main bootloader. On Series 2 devices, the authenticity of the main bootloader optionally can be verified before applying the bootloader upgrade. See section [Setting a Version Number](07-configuring-the-gecko-bootloader#setting-a-version-number) for more information about versioning bootloader images.

The new main bootloader is entered, and the images in the list of storage slots marked for bootload are verified. When the image parser parses the slot containing the GBL file with the bootloader + application upgrade, the version number of the bootloader upgrade is equal to the running main bootloader version, so another bootloader upgrade will not be performed. Instead, the application upgrade data are returned in a callback. Bootloading of the new application proceeds as described in section [Application Bootloader Operation](03-gecko-bootloader-operation-application-upgrade#application-bootloader-operation).

###### Storage Space Size Configuration (heading level 7)

The storage space size must be configured to have enough space to store the upgrade images. Depending on the configuration, the bootloader size can vary. For size requirements of the bootloader, see section [Size Requirements for Different Bootloader Configurations for Series 1 Devices](07-configuring-the-gecko-bootloader#size-requirements-for-different-bootloader-configurations-for-series-1-devices).

###### Upgrading Bootloaders without Secure Boot to Bootloaders with Secure Boot (heading level 7)

A bootloader without the secure boot feature can be upgraded to a bootloader with the secure boot feature. The procedure is as follows:

1. Prepare a Gecko Bootloader image with secure boot enabled. The version of the bootloader needs to be higher than the bootloader on the device.  
   - Turn on secure boot from the **Bootloader Core** component in Simplicity Studio by selecting the **Enable secure boot** option.
2. Generate a public/private Signing Key pair. See section [Generating Keys](09-gecko-bootloader-security-features#generating-keys) for more information on creating a Signing Key pair.
3. Write the public key generated from the previous step to the device. The public key is stored as a manufacturing token in the device by default. This key can be written by application code running on the device as long as the Lock Bits page is configured to allow flash writes. If the Lock Bits page is locked, it can only be erased by the debugger. Therefore, signing/decryption keys residing in the Lock Bits page cannot be erased from firmware. This means that, for devices in the field, those areas in flash cannot be replaced with new ones. However, the Gecko Bootloader prepared from step 1 can be modified to look for the decryption and signature keys in a different location. Key locations are defined in the bootloader project file `btl_security_tokens.c`.
4. Prepare a signed application image using the private key generated in step 2. See section [Signing an Application Image for Secure Boot](09-gecko-bootloader-security-features#signing-an-application-image-for-secure-boot) for more information on signing an application.
5. Create a GBL file using the Gecko Bootloader image and the signed application image. The GBL file needs to be signed/unsigned depending on the configuration of the Gecko Bootloader running on the device. For more details on creating a GBL file, see section [Creating a Signed and Encrypted GBL Upgrade Image File from an Application](09-gecko-bootloader-security-features#creating-a-signed-and-encrypted-gbl-upgrade-image-file-from-an-application).
6. Upload the GBL file. For more details on the upgrade process, see section [Bootloader Upgrade on Application Bootloaders with Storage](#bootloader-upgrade-on-application-bootloaders-with-storage).

###### Enabling Secure Boot RTSL on Series 2 Devices (heading level 7)

Secure Boot RTSL can be enabled by using the following procedure:

1. Prepare a Gecko Bootloader image with secure boot enabled. The version of the Gecko Bootloader needs to be higher than the Gecko Bootloader on the device.  
   - Turn on secure boot from the **Bootloader Core** component in Simplicity Studio by selecting the **Enable secure boot** option.  
   - For EFR32xG21, in the **Bootloader Core** component, disable the **Allow use of public key from manufacturing token storage** option. This means that the Gecko Bootloader will never make use of the public key stored in the last page of the main flash.  
   - (Optional) In the **Bootloader Core** component, select the **Require signed firmware upgrade files** option. This means that the Gecko Bootloader will only accept signed GBL files.
2. Generate a public/private Signing Key pair. See section [Generating Keys](09-gecko-bootloader-security-features#generating-keys) for more information on creating a Signing Key pair.
3. Prepare an application that installs the public key generated from step 2 to the Secure Engine One-time Programmable memory. Installing a key in VSE requires a reset routine. Make sure that the application does not end up in the reset loop. Create an unsigned GBL file from this application and upload it. For more information on installing public keys, see [Series 2 and Series 3 Secure Boot with RTSL](https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/). For more details on creating a GBL file, see section [Creating a Signed and Encrypted GBL Upgrade Image File from an Application](09-gecko-bootloader-security-features#creating-a-signed-and-encrypted-gbl-upgrade-image-file-from-an-application).
4. Sign the Gecko Bootloader image generated from step 1 using the private key generated in step 2. See section [Signing an Application Image for Secure Boot](09-gecko-bootloader-security-features#signing-an-application-image-for-secure-boot) for more information on signing binaries.
5. Make a custom application that turns on secure boot on the Secure Engine and sign this application binary with the private key generated from step 2. For more details on how to turn on secure boot on the Secure Engine, see [Series 2 and Series 3 Secure Boot with RTSL](https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/).
6. Create a GBL file using the Gecko Bootloader image from step 4 and the application from step 5. The GBL file must be signed if the **Bootloader Core** component option **Require signed firmware upgrade files** was selected in step 1. For more details on creating a GBL file, see section [Creating a Signed and Encrypted GBL Upgrade Image File from an Application](09-gecko-bootloader-security-features#creating-a-signed-and-encrypted-gbl-upgrade-image-file-from-an-application).
7. Upload the GBL file containing the Gecko Bootloader image and the application.

##### Gecko Bootloader Operation - Secure Engine Upgrade

The Secure Engine is upgradable and requirements for upgrading the Secure Engine vary depending on the bootloader configuration:

- **Application bootloader with storage**: Upgrading the Secure Engine requires a single GBL file containing both Secure Engine and application upgrade images.
- **Standalone bootloader with communication interface**: Upgrading the Secure Engine requires two GBL files, one with only the Secure Engine upgrade image, and one with only the application upgrade image and optionally a third image containing only the Main bootloader upgrade.

A bootloader upgrade can also be included in the same GBL file in application mode, or as a third GBL file in standalone mode. The figures that illustrate Gecko Bootloader operation in this section do not provide information about the bootloader memory layouts for different devices. For more details refer to the section “Memory Space for Bootloading” in [Bootloader Fundamentals](https://docs.silabs.com/mcu-bootloader/latest/bootloader-fundamentals/).

Signed and encrypted Secure Engine upgrade images are provided by Silicon Labs through Simplicity Studio. Upgrade images with the same or lower version number than the running Secure Engine will be ignored.

To download Secure Engine firmware images, connect a Series 2 device and select a preferred SDK. The Secure Firmware **Update to x.x.x** link appears in the Launcher Perspective, as shown in the following figure.

![Launcher Perspective](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image8.jpg)

Click **Update to x.x.x** next to Secure FW: x.x.x. A warning dialog box appears. Click **[Yes]** to continue.

![Warning dialog box when click Update to x.x.x](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image9.jpg)

The Launcher Perspective is then updated so that the current Secure Firmware version and link version are the same.

![Launcher Perspective after clicking Update to x.x.x](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image10.jpg)

The Secure Engine firmware images can be found in the _util/se_release/public_ directory under the Gecko SDK. Simplicity Studio displays the SE firmware version available in the Gecko SDK selected.

###### Secure Engine Upgrade on Bootloaders with Communication Interface (Standalone Bootloaders)

The process is illustrated in the following figure.

![Standalone Bootloader: Secure Engine Bootloader Upgrade](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image11.png)

1. The device reboots into the bootloader.
2. A GBL file containing only a Secure Engine upgrade image is transmitted from the host to the device.
3. The contents of the GBL Secure Engine tag are written to the pre-configured upgrade location in internal flash, overwriting the existing application.
4. The device reboots into the Secure Engine.
5. The Secure Engine is replaced by the new version found in the pre-configured upgrade location.
6. The device boots into the main bootloader.
7. A GBL file containing only an application image is transmitted from the host to the device.
8. The bootloader applies the application image from the GBL upgrade on the fly.
9. The device boots into the application. Secure Engine upgrade is complete.

###### Downloading and Applying a Secure Engine GBL Upgrade File (heading level 7)

When the bootloader has entered the receive loop, a GBL upgrade file containing a Secure Engine upgrade is transmitted to the bootloader. When a packet is received, it is passed to the image parser. The image parser parses the data and returns Secure Engine upgrade data in a callback. The bootloader core implements this callback and flashes the data to internal flash at the pre-configured bootloader upgrade location.

When a complete Secure Engine upgrade image is received, the main bootloader signals the Secure Engine that it should enter firmware upgrade mode. This is done by the Secure Engine communication interface that is used to signal that bootloader upgrade is ready to be performed.

###### Downloading and Applying an Application GBL Upgrade File (heading level 7)

Once the Secure Engine upgrade is completed, the existing application is rendered invalid if the Secure Engine upgrade location overlaps with the application. A GBL upgrade file containing an application upgrade is transmitted to the bootloader. The application upgrade process follows that. For more information, see section [Standalone Bootloader Operation](03-gecko-bootloader-operation-application-upgrade#standalone-bootloader-operation).

###### Secure Engine Upgrade on Application Bootloaders with Storage

The process is illustrated in the following figure.

![Application Bootloader: Secure Engine Upgrade](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image12.png)

1. A single GBL file containing both a Secure Engine upgrade image and an application image is downloaded onto the storage medium of the device (internal flash or external SPI flash).
2. The device reboots into the bootloader.
3. a) The main bootloader copies its upgrade image into internal flash at the pre-configured upgrade location.  
   b) Alternatively, if the no-staging Secure Engine upgrade option has been enabled, the upgrade image will be fetched directly from the GBL file in storage instead of first copying the image to the pre-configured upgrade location.
4. The device reboots into the Secure Engine.
5. The Secure Engine is replaced by the new version found in the pre-configured upgrade location (or directly from storage, ref. 3b).
6. The device boots into the main bootloader.
7. The bootloader applies the application image from the GBL upgrade file.
8. The device boots into the application. Secure Engine upgrade is complete.

###### Storage Space Size Configuration (heading level 7)

The storage space size must be configured to have enough space to store the upgrade images. The following table shows the reserved SE upgrade image sizes.

<table>
    <thead>
        <tr>
            <th>
                <p>Device Family</p>
            </th>
            <th>
                <p>Reserved Flash for SE Upgrade Image</p>
            </th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>EFR32xG21</p>
            </td>
            <td>
                <p>48 kB</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>EFR32xG22</p>
            </td>
            <td>
                <p>24 kB</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>EFR32xG23</p>
            </td>
            <td>
                <p>96 kB</p>
            </td>
        </tr>
    </tbody>
</table>

Depending on the configuration, the bootloader size can vary. For size requirements of the bootloader, see section [Size Requirements for Different Bootloader Configurations for Series 1 Devices](07-configuring-the-gecko-bootloader#size-requirements-for-different-bootloader-configurations-for-series-1-devices). The bootloader size for EFR32xG21 devices can be up to 16 kB and for EFR32xG22, EFR32xG23, and EFR32xG24 devices the bootloader size can be up to 24 kB. For more details, see [Bootloader Fundamentals](https://docs.silabs.com/mcu-bootloader/latest/bootloader-fundamentals/).

##### Getting Started with the Gecko Bootloader

This section describes how to build a Gecko Bootloader from one of the provided examples. Simplicity Studio 5, used with Gecko SDK Suite (GSDK) v3.x and higher, differs from Simplicity Studio 4, used with GSDK v2.x, in how sample applications are selected and how projects are created. Refer to the documentation provided with your SDK for details. These instructions assume that you have installed Simplicity Studio 5, the GSDK and associated utilities as described in the SDK’s quick start guide, and that you are familiar with generating, compiling, and flashing an example application in the relevant version.

- [Bluetooth Getting Started Guide](https://docs.silabs.com/bluetooth/latest/bluetooth-getting-started-overview/)
- [Proprietary Flex SDK v3.x Quick-Start Guide](https://docs.silabs.com/connect-stack/latest/proprietary-flex-sdk-v3x-quick-start-guide/)
- [Silicon Labs OpenThread SDK Quick-Start Guide](https://docs.silabs.com/openthread/latest/openthread-quick-start-guide/)
- [Zigbee EmberZNet Quick-Start Guide for SDK 7.0 and Higher](https://docs.silabs.com/zigbee/latest/zigbee-7x-quick-start-guide/)

1. Create a project based on the Gecko Bootloader example of your choice. The project opens with a tab describing the example.  
   ![Project based on the Gecko Bootloader example](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image13.png)
2. Click the project (*.slcp) tab to move to the Project Configurator interface.  
   ![Project Configurator interface](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image14.png)
3. The Software Components tab shows the list of available components that can be installed in the project.  
   ![Software Components tab](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image15.png)
4. The **Storage Slot Setup** component allows you to configure storage slots to be used if a storage component is also installed. The default configuration matches the target part and bootloader type. This component supports a maximum of three storage slots.  
   ![Storage Slot Setup](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image16.png)
5. Click the **Build** (hammer) icon.
6. After the build is complete, the bootloader binaries are available in the **artifact** folder as depicted in the image below.  
   ![Bootloader binaries files in the artifact folder](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image17.png)

On Series 1 devices, three bootloader images are generated into the build directory: a main bootloader, a main bootloader with CRC32 checksum, and a combined first stage and main bootloader with CRC32 checksum. The main bootloader image is called **<projectname>.s37**, the main bootloader with CRC32 checksum is called **<projectname>-crc.s37**, while the combined first stage image + main bootloader image with a CRC32 checksum is called **<projectname>-combined.s37**. The first time a device is programmed, whether during development or manufacturing, the combined image needs to be programmed. For subsequent programming, when a first stage bootloader is already present on the device, it is okay to download an image containing just the main bootloader. In this case, the main bootloader with CRC32 should be used.

The requirement is that any main bootloader image that is programmed via serial wire must contain the CRC32 in the image. Files downloaded via serial wire are “s37” files. Most often, the **<projectname>-combined.s37** file is the one downloaded during production programming. However, it is possible to download only the main bootloader over serial wire, in which case **<projectname>-crc.s37** should be used.

Any main bootloader that is upgraded with the OTA or host method should already contain CRC32 because bootloader-initiated upgrades use GBL files (not “s37” files) and Simplicity Commander adds the CRC32 when it constructs the GBL file. The input files to Simplicity Commander can (and should) use the non-CRC “s37” file.

On Series 2 devices, the combined image is not present, since the first stage bootloader does not exist. The image containing only a main bootloader is the image that must be used to create a GBL file for bootloader upgrade.

##### Configuring the Gecko Bootloader

###### Configuring Storage

Gecko Bootloaders configured as application bootloaders must include an API to store and access image files. This API is based on the concept of storage slots, where each slot has a predefined size and location in memory and can be used to store a single upgrade image. Slots are configured in the **Bootloader Storage Slot Setup** component.

When multiple storage slots are configured, a bootload list is used to indicate the order in which the bootloader should access slots to find upgrade images. If multiple storage slots are supported, the application should write the bootload list by calling bootloader_setImageToBootload before rebooting into the bootloader to initiate a firmware upgrade process. The bootloader attempts to verify the images in these storage slots in sequence and applies the first image to pass verification. If only a single storage slot is supported, the bootloader uses this slot implicitly. A maximum of three slots may be configured in the **Bootloader Storage Slot Setup** component.

###### SPI Flash Storage Configuration (heading level 7)

When configuring a Gecko Bootloader to obtain images from SPI flash, modify the following.

The **base address of the storage area** should be configured in the **Common Storage** component. This is the address at which the bootloader places the bootload list, if more than one storage slot is configured. In the default configuration, this address is set to 0. If only a single storage slot is configured, the bootload list is not used, so configuring it may be omitted.

The **location and size of the storage slots** can be configured using the **Bootloader Storage Slot Setup** component (supports a maximum of three configurable storage slots). The addresses input here are absolute addresses (they are not offsets from the base address). If more than a single slot is configured, space must be reserved between the base address as configured in the **Common Storage** component and the first storage slot configured in the **Bootloader Storage Slot Setup** component. Enough space to fit two copies of the bootload list must be reserved. These two copies need to reside on different flash pages, to provide redundancy in case of power loss during writing. Two full flash pages therefore need to be reserved. In the default example application, a SPI flash part with 4 kB flash sectors is used. This means that 8 kB must be reserved before the first storage slot. The following figure illustrates how the storage area can be partitioned, where the numbers in the top row represent the starting addresses.

![SPI Flash Storage Area Configuration](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image18.png)

###### Internal Storage Configuration (heading level 7)

When configuring a Gecko Bootloader to obtain images from internal flash, modify the following.

The **base address of the storage area** should be configured in the **Common Storage** component. This is the address at which the bootloader will place the prioritized list of storage slots to attempt to bootload from, if more than one storage slot is configured. In the default configuration, only a single storage slot is configured, so this value is set to 0, and isn’t used. If more than one storage slot is configured, this value needs to be configured too.

The **location and size of the storage slots** can be configured using the **Bootloader Storage Slot Setup** Component (supports a maximum of three configurable storage slots). The addresses input here are absolute addresses (they are not offsets from the base address). If more than a single slot is configured, enough space must be reserved between the base address as configured in the **Common Storage** component and the first storage slot configured in the **Bootloader Storage Slot Setup** component. Enough space to fit two copies of the bootload list must be reserved. These two copies need to reside on different flash pages, to provide redundancy in case of power loss during writing. Two full flash pages therefore need to be reserved. The following figure illustrates how the storage area can be partitioned.

![Internal Storage Area Configurations](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image19.png)

> **Note**: The storage area partitioning in the example for two storage slots above does not take any NVM system into account. If using an NVM system like SimEE or PS Store, take care to place and size the storage area in such a way that bootloader storage does not overlap with NVM.

###### Compressed Upgrade Images

The Gecko Bootloader optionally supports compressed GBL files. In a compressed GBL file, only the application upgrade data is compressed, any metadata and bootloader upgrade data (if present) stay uncompressed. This means that a compressed GBL file is identical to a normal (uncompressed) GBL file, except that the GBL Programming Tag containing the application upgrade image (as described in [Bootloader Fundamentals](https://docs.silabs.com/mcu-bootloader/latest/bootloader-fundamentals/)) has been replaced by a GBL LZ4 Compressed Programming Tag or GBL LZMA Compressed Programming Tag. Signature and encryption operations on a compressed GBL work identically to on an uncompressed GBL.

To be able to use compressed upgrade images, a decompressor for the relevant compression algorithm must be added to the Gecko bootloader. The following table shows which compression algorithms are supported by the Gecko Bootloader, and which Bootloader component should be added to enable the feature. The table also shows how much space the decompressor takes up in the bootloader, and how big of a size reduction to expect for the compressed application upgrade image. Be aware of the bootloader size requirement. The bootloader space might be too small to fit the decompressors, depending on the device and enabled components.

<table>
    <thead>
        <tr>
            <th>
                <p>Compression Algorithm</p>
            </th>
            <th>
                <p>Component</p>
            </th>
            <th>
                <p>Bootloader Size Requirement</p>
            </th>
            <th>
                <p>Application Upgrade Size Reduction (typical)</p>
            </th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>LZ4</p>
            </td>
            <td>
                <p>GBL Compression (LZ4)</p>
            </td>
            <td>
                <p>&lt; 1 kB</p>
            </td>
            <td>
                <p>~ 10%</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>LZMA</p>
            </td>
            <td>
                <p>Bootloader Compression (LZMA)</p>
            </td>
            <td>
                <p>~5 kB flash, 18 kB RAM</p>
            </td>
            <td>
                <p>~ 30%</p>
            </td>
        </tr>
    </tbody>
</table>

It is important to note that the compressed GBL file stays compressed while being transferred to the device, and while it is stored in the upgrade area. It is decompressed by the bootloader when the upgrade is applied. This means that the running application in main flash will be identical to one that was installed using an uncompressed (normal) GBL file.

Compressed GBL files can only be decompressed by the bootloader when running standalone, not through the Application Interface. This means that upgrade image verification performed by the application prior to reboot will not attempt to decompress the application upgrade, it will only verify the signature of the compressed payload. After rebooting into the bootloader, it will decompress the image as part of the upgrade process.

> **Note**: The above means that Bluetooth in-place application upgrades cannot be compressed, as they are processed by the Bluetooth Supervisor or AppLoader using functionality in the bootloader through the Application Interface. Supervisor/stack and AppLoader updates can be compressed, but the user application cannot.

###### LZMA Compression Settings (heading level 7)

LZMA decompression is only supported for images compressed with certain compression settings. Simplicity Commander automatically uses these settings when using the `commander gbl create --compress lzma` command.

- Probability model counters: lp + lc <= 2. Simplicity Commander uses lp=1, lc=1.
- Dictionary size no greater than 8 kB. Simplicity Commander uses 8 kB.

Together, these settings cause the decompressor to require 18 kB of RAM for decompression – 10 kB for the counters and 8 kB for the dictionary.

The GBL LZMA Compressed Programming Tag contains a full LZMA file, containing the LZMA header, raw stream, and end mark. The Gecko bootloader only supports decompressing payloads that contain the end mark as the last 8 bytes of the compressed stream.

###### Bootloader Example Configurations

The following sections describe the key configuration options for the example bootloader applications.

> **Note**: Security features are disabled for all example configurations. In development, Silicon Labs strongly recommends enabling security features to prevent unauthorized parties from uploading untrusted program code. See section [Using Application Image Security Features](09-gecko-bootloader-security-features#using-application-image-security-features) to learn how to configure the security features of the Gecko Bootloader.

###### UART XMODEM Bootloader (heading level 7)

Standalone bootloader for EFM32 and EFR32 devices running the EmberZNet PRO and Silicon Labs Connect protocol stacks, using XMODEM-CRC over UART.

In this configuration, the **UART XMODEM** communication component, **XMODEM Parser** component, and **Bootloader UART Driver** component are installed. For the example application to run on a custom board, the GPIO ports and pins used for UART need to be configured in the **Bootloader UART Driver** component. Here, Hardware Flow Control can be enabled or disabled, and the baud rate and pinout can be configured.

The **GPIO activation** component is also installed by default, allowing bootloader entry into firmware upgrade mode by activating a GPIO through reset. The GPIO pin used can be configured here. This component can be uninstalled if this functionality is not desired.

###### BGAPI UART DFU Bootloader (heading level 7)

Standalone bootloader for the Bluetooth protocol stack, using the BGAPI protocol for UART DFU. This bootloader should be used for all NCP-mode Bluetooth applications.

In this configuration, the **BGAPI UART DFU** communication component and **Bootloader UART Driver** component are installed. For the example application to run on a custom board, the GPIO ports and pins used for UART need to be configured in the **Bootloader UART Driver** component. Here, Hardware Flow Control can be enabled or disabled, and the baud rate and pinout can be configured.

The GPIO activation component is also installed by default, allowing bootloader entry by activating a GPIO through reset. The GPIO pin used can be configured here. This component can be uninstalled if this functionality is not desired.

###### EZSP SPI Bootloader (heading level 7)

Standalone bootloader for EmberZNet PRO and Silicon Labs Connect protocol stacks using EZSP for SPI.

In this configuration, the **EZSP-SPI** communication component, **XMODEM Parser** component, and **Bootloader SPI Peripheral Driver** component are installed. For the example application to run on a custom board, the GPIO ports and pins used for SPI and EZSP signaling need to be configured in the **Bootloader SPI Peripheral Driver** and **EZSP-SPI** components, respectively.

The **EZSP GPIO activation** component is also installed by default, allowing bootloader entry into firmware upgrade mode by activating a GPIO through reset. The GPIO pin used can be configured here. This component can be uninstalled if this functionality is not desired.

###### Bluetooth AppLoader OTA DFU Bootloader (heading level 7)

Standalone bootloader for the Bluetooth protocol stack, using Bluetooth for Over-The-Air DFU.

In this configuration, the **Bluetooth AppLoader** communication component is installed.

###### SPI Flash Storage Bootloader (heading level 7)

Application bootloader for all wireless protocol stacks, using an external SPI flash to store upgrade images received over the air by the application.

In this configuration, the **SPI Flash Storage** and **Common Storage** components, as well as the **Bootloader SPI Controller Driver** component, are installed. For the example application to run on a custom board, the GPIO ports and pins used for SPI communication with the external flash need to be configured in the **Bootloader SPI Controller Driver** component, and the type of SPI flash needs to be configured in the **SPI Flash Storage** component. The base address of the storage area can be configured in the **Common Storage** component. The location and size of the storage slots themselves can be configured using the **Bootloader Storage Slot Setup** component (supports up to three configurable storage slots).

The SPI Flash storage bootloader provides customizable callback functions, which can be found in _bootloader-callback-stubs.c_. The callback functions _storage_customInit_ and _storage_customShutdown_, for example, can be used for custom hardware setups. Those callback functions can be interfaced from the applications using the bootloader interface functions such as _bootloader_init_ and _bootloader_deinit_ from _api/btl_interface.h_.

Currently, Silicon Labs supports the following SPI Flash storage bootloaders:

1. SPI Flash storage bootloader (multiple images)
2. SPI Flash storage bootloader (single image)
3. SPI Flash storage bootloader (single image with slot size of 1MB). **Note: This bootloader should only be used for systems with an external SPI Flash size >= 1 MB**.

###### SPI Flash Storage Bootloader SFDP (heading level 7)

Application bootloader for all wireless protocol stacks, using an external SPI flash to store upgrade images received over the air by the application.

In this configuration, the **SPI Flash Storage SFDP** and **Common Storage** components, as well as the **Bootloader SPI Controller Driver** component, are installed. For the example application to run on a custom board, the GPIO ports and pins used for SPI communication with the external flash need to be configured in the **Bootloader SPI Controller Driver** component. The type of SPI flash is detected at runtime. The base address of the storage area can be configured in the **Common Storage** component. The location and size of the storage slots themselves can be configured using the **Bootloader Storage Slot Setup** component (supports up to three configurable storage slots).

The SPI Flash storage bootloader SFDP provides customizable callback functions, which can be found in _bootloader-callback-stubs.c_. The callback functions _storage_customInit_ and _storage_customShutdown_, for example, can be used for custom hardware setups. Those callback functions can be interfaced from the applications using the bootloader interface functions such as _bootloader_init_ and _bootloader_deinit_ from _api/btl_interface.h_.

###### Internal Storage Bootloader (heading level 7)

Application bootloader for all wireless protocol stacks, using internal flash to store upgrade images received over the air by the application.

Multiple examples are provided, including configurations for 512 kB flash memory devices like EFR32xG13, 1024 kB flash memory devices like EFR32xG12, and 2048 kB flash memory devices like EFM32GG11. **The storage layout should be modified before running the bootloader on any other devices**. In this configuration, the internal flash and common storage components are installed. The base address of the storage area is configured in the **Common Storage** component. The location and size of the storage slots can be configured using the **Bootloader Storage Slot Setup** component (provides up to three configurable storage slots). Default example applications are provided with configurations for both single storage slot and multiple storage slots.

The default storage slot configurations provided by the Gecko Bootloader **must** be configured to match the use-case-specific application configurations.

**Table**: Internal Storage Bootloader Default Storage Configurations

<table>
    <thead>
        <tr>
            <th>
                <p>Sample Applications</p>
            </th>
            <th>
                <p>Storage Offset</p>
            </th>
            <th>
                <p>Storage Size</p>
            </th>
            <th>
                <p>Additional Notes</p>
            </th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Internal Storage Bootloader (single image on 256 kB device)</p>
            </td>
            <td>
                <p>0x21800 (137216)</p>
            </td>
            <td>
                <p>88064</p>
            </td>
            <td>
                <p>This configuration is available for devices where flash base address is 0x00</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>0x8021800 (134354944)</p>
            </td>
            <td>
                <p>88064</p>
            </td>
            <td>
                <p>This configuration is only available for devices where the flash base address is 0x08000000</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Internal Storage Bootloader (single image on 352 kB device)</p>
            </td>
            <td>
                <p>0x28000 (163840)</p>
            </td>
            <td>
                <p>147456</p>
            </td>
            <td>
                <p>This configuration is available for devices where flash base address is 0x00</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>0x8028000 (134381568)</p>
            </td>
            <td>
                <p>147456</p>
            </td>
            <td>
                <p>This configuration is only available for devices where the flash base address is 0x08000000</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Internal Storage Bootloader (single image on 512 kB device)</p>
            </td>
            <td>
                <p>0x44000 (278528)</p>
            </td>
            <td>
                <p>196608</p>
            </td>
            <td>
                <p>This configuration is available for devices where flash base address is 0x00</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>0x8044000 (134496256)</p>
            </td>
            <td>
                <p>196608</p>
            </td>
            <td>
                <p>This configuration is only available for devices where the flash base address is 0x08000000</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Internal Storage Bootloader (single image on 1 MB device)</p>
            </td>
            <td>
                <p>0x84000 (540672)</p>
            </td>
            <td>
                <p>458752</p>
            </td>
            <td>
                <p>This configuration is available for devices where flash base address is 0x00</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>0x8084000 (134758400)</p>
            </td>
            <td>
                <p>458752</p>
            </td>
            <td>
                <p>This configuration is only available for devices where the flash base address is 0x08000000</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Internal Storage Bootloader (single image on 2 MB device)</p>
            </td>
            <td>
                <p>0x10000 (1048576)</p>
            </td>
            <td>
                <p>1011712</p>
            </td>
            <td>
                <p>This configuration is available for devices where flash base address is 0x00</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>0x8100000 (135266304)</p>
            </td>
            <td>
                <p>1011712</p>
            </td>
            <td>
                <p>This configuration is only available for devices where the flash base address is 0x08000000</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Internal Storage Bootloader (multiple images on 1 MB device)</p>
            </td>
            <td>
                <p>0x5A000 (368640)</p>
            </td>
            <td>
                <p>337920</p>
            </td>
            <td>
                <p>This configuration is available for devices where flash base address is 0x00</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>0xAC800 (706560)</p>
            </td>
            <td>
                <p>337920</p>
            </td>
            <td>
                <p>This configuration is available for devices where flash base address is 0x00</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Internal Storage Bootloader (single image on 1536kB device)</p>
            </td>
            <td>
                <p>0xC0000 (786432)</p>
            </td>
            <td>
                <p>737280</p>
            </td>
            <td>
                <p>This configuration is available for devices where flash base address is 0x00</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>0x80C0000 (135004160)</p>
            </td>
            <td>
                <p>737280</p>
            </td>
            <td>
                <p>This configuration is only available for devices where the flash base address is 0x08000000</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Internal Storage Bootloader (single image on 1920kB device)</p>
            </td>
            <td>
                <p>0x80E8000 (135168000)</p>
            </td>
            <td>
                <p>966656</p>
            </td>
            <td>
                <p>This example application is available for device families with flash size of 1920kB or higher (for example xG25 device family)</p>
            </td>
        </tr>
    </tbody>
</table>

###### Image Acquisition Application Example Configuration

These examples illustrate applications that acquire and store a GBL upload image for an application bootloader. For the running bootloader to accept an application upgrade, the new application version must be higher than the existing version. The version number can be set in Appbuilder by defining the macro “APP_PROPERTIES_VERSION” in the **Additional Macros** field on the **Other** tab.

###### Setting a Version Number

To distinguish between different builds of the Gecko Bootloader, it is useful to set a version number. To perform a bootloader upgrade, not only must the running bootloader pass its integrity checks (see section [Downloading and Applying a Bootloader GBL Upgrade File](04-gecko-bootloader-operation-bootloader-upgrade#downloading-and-applying-a-bootloader-gbl-upgrade-file)), but the bootloader upgrade image must also have a higher version number than the running bootloader image. A version number can be set using Simplicity Studio by configuring the **Bootloader Version Main Customer** option of the **Bootloader Core** component. This macro will be picked up by the config file **btl_config.h**, where it is combined with the version number of the Gecko Bootloader files provided by Silicon Labs.

###### Placing Bootloader in Main Flash

For device families xG13 and xG14, the entire main stage bootloader might not fit into the bootloader flash if the user installs some extra components. In such scenarios, the main stage bootloader can be placed in the main flash by installing the Bootloader in Main Flash core component. This component internally provides a MACRO named MAIN_BOOTLOADER_IN_MAIN_FLASH which is used to set the appropriate address required to place the main bootloader in main flash. The MACRO is used to generate the linker file from the common linker template with the appropriate addresses to place the main stage bootloader in the main flash.

###### Hardware Configuration

The Gecko Bootloader uses the Pin Tool for configuration of pinout and other hardware-related settings. When Pin Tool configuration is available for a bootloader component, the relevant settings are shown in the Component Editor for that component.

![Example of USART Configuration for UART Driver](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image20.png)

The standalone Pin Tool User Interface can also be used to configure settings for Gecko Bootloader if desired.

While the Pin Tool provides configuration for many different peripherals, the Gecko Bootloader uses only the following Pin Tool modules:

- SERIAL is used by the UART Driver component to configure baud rate, flow control mode and pinout.
- VCOM is used by the UART Driver component to enable the serial interface if necessary (only on Silicon Labs Wireless Starter Kits).
- EXTFLASH is used by the SPI Driver to configure frequency and pinout.
- SPINCP is used by the SPI Slave Driver to configure pinout.
- BTL_BUTTON is used by the GPIO Activation component.
- CMU HFXO frequency setting is used by the delay driver to calibrate timing if the core is running from the HFXO.

Other settings, like CMU oscillator configuration or DCDC configuration, are not taken into consideration by the default bootloader code. If using these configuration settings is desired, the required code must be added in btl_main.c.

> **Note**: While the delay driver uses the HFXO frequency setting from Pin Tool, the HFXO enable setting is not used to initialize the HFXO on startup. This setting is only used when calling the bootloader through the Application Interface (see section [Gecko Bootloader and TrustZone](10-gecko-bootloader-and-trustzone#gecko-bootloader-and-trustzone)), and the application has switched to the HFXO prior to calling the Bootloader Application Interface API.

###### Size Requirements for Different Bootloader Configurations for Series 1 Devices

Enabling different configuration options for the Gecko Bootloader changes the size of the resulting image. The following table shows a list of example bootloader configurations and the resulting approximate size of the main bootloader. Note that any size above 14 kB will be too large to fit in the bootloader area of flash of EFR32xG13, and that any size above 16 kB will be too large to fit in the bootloader area of flash on EFR32xG14 and EFM32TG11. This table was last updated for Gecko SDK Suite 4.0 (Gecko Bootloader 2.0), and no guarantees are made that a configuration of a specific size with one SDK version will maintain that size in future releases.

**Table**: Bootloader Size Requirements

<table>
    <thead>
        <tr>
            <th>
                <p>Base Configuration</p>
            </th>
            <th>
                <p>Enabled Options</p>
            </th>
            <th>
                <p>Size (kB)</p>
            </th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>XMODEM UART</p>
            </td>
            <td>
                <p>Default configuration</p>
            </td>
            <td>
                <p>12.8</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade</p>
            </td>
            <td>
                <p>12.8</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade, LZ4 compression</p>
            </td>
            <td>
                <p>13.7</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade, LZMA compression</p>
            </td>
            <td>
                <p>17.9</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Internal storage, single slot</p>
            </td>
            <td>
                <p>Default configuration</p>
            </td>
            <td>
                <p>11.5</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade</p>
            </td>
            <td>
                <p>11.5</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade, LZ4 compression</p>
            </td>
            <td>
                <p>12.4</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade, LZMA compression</p>
            </td>
            <td>
                <p>16.6</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Internal storage, multiple slots</p>
            </td>
            <td>
                <p>Default configuration</p>
            </td>
            <td>
                <p>12.1</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade</p>
            </td>
            <td>
                <p>12.1</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade, LZ4 compression</p>
            </td>
            <td>
                <p>13</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade, LZMA compression</p>
            </td>
            <td>
                <p>17.1</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SPI flash, single slot</p>
            </td>
            <td>
                <p>Default configuration</p>
            </td>
            <td>
                <p>12.6</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade</p>
            </td>
            <td>
                <p>12.6</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade, LZ4 compression</p>
            </td>
            <td>
                <p>13.5</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade, LZMA compression</p>
            </td>
            <td>
                <p>17.7</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SPI flash, multiple slots</p>
            </td>
            <td>
                <p>Default configuration</p>
            </td>
            <td>
                <p>13.1</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade</p>
            </td>
            <td>
                <p>13.1</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade, LZ4 compression</p>
            </td>
            <td>
                <p>14</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade, LZMA compression</p>
            </td>
            <td>
                <p>18</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SPI flash using SFDP, single slot</p>
            </td>
            <td>
                <p>Default configuration</p>
            </td>
            <td>
                <p>12.8</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade</p>
            </td>
            <td>
                <p>12.8</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade, LZ4 compression</p>
            </td>
            <td>
                <p>13.8</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade, LZMA compression</p>
            </td>
            <td>
                <p>18</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>BGAPI UART</p>
            </td>
            <td>
                <p>Default configuration</p>
            </td>
            <td>
                <p>11.8</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade</p>
            </td>
            <td>
                <p>11.8</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade, LZ4 compression</p>
            </td>
            <td>
                <p>12.7</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade, LZMA compression</p>
            </td>
            <td>
                <p>16.9</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>EZSP SPI</p>
            </td>
            <td>
                <p>Default configuration</p>
            </td>
            <td>
                <p>13.1</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade</p>
            </td>
            <td>
                <p>13.1</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade, LZ4 compression</p>
            </td>
            <td>
                <p>13.9</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>'</p>
            </td>
            <td>
                <p>Secure boot, signed and encrypted upgrade, LZMA compression</p>
            </td>
            <td>
                <p>18.1</p>
            </td>
        </tr>
    </tbody>
</table>

###### EM4 GPIO Retention for OTA Upgrades

The EM4 GPIO Retention feature allows Series 2 devices to retain GPIO pin states during Over-The-Air (OTA) firmware upgrades by entering EM4 sleep mode instead of performing a full system reset. This is particularly useful for applications that must preserve GPIO states, such as LED indicators, relay outputs, or control signals throughout the upgrade process.

Normally, an OTA upgrade triggers a system reset that clears all GPIO configurations and output states. EM4 GPIO Retention avoids this by saving the GPIO configuration in Backup RAM (BURAM), which remains powered through EM4 sleep and wake cycles. When the device wakes, the GPIO configuration and output levels are automatically restored, ensuring uninterrupted operation during the upgrade.

When an OTA upgrade with GPIO retention is initiated, the bootloader performs the following sequence:

- Saves GPIO configurations to **BURAM**, which retains data during EM4 sleep.
- Configures **EM4 GPIO retention mode** to `SWUNLATCH`.
- Sets up the **Backup Real-Time Counter (BURTC)** for the wake event.
- Enters **EM4 sleep mode** instead of issuing a system reset.
- On wakeup, validates BURAM contents and restores GPIO states.
- Continues with the OTA process according to the reset reason stored in BURAM.

To protect against repeated boot failures, the bootloader uses a reset counter stored in BURAM. The counter increments whenever the device cannot find or validate a valid application image. After three failed wakeups, the system halts in a controlled infinite loop, requiring user intervention (e.g., a manual reset or power cycle) to recover.

The reason for boot failure is stored in BURAM using one of the following codes:

- `BADAPP`: Invalid application
- `BADIMAGE`: Invalid or missing upgrade image
- `FATAL`: Other unrecoverable error

###### Enabling and Usage of EM4 GPIO Retention (heading level 7)

To use the EM4 GPIO Retention during OTA upgrades, complete the following two steps:

###### Step-1: Install the EM4 GPIO Retention Compoenent (heading level 8)

Install the following component in the application via Simplicity Studio Configurator.

Component: **BTL_EM4_GPIO_RETENTION**

Path: **Platform → Bootloader → EM4 GPIO Retention**

This component enables the EM4 GPIO retention capability in the bootloader, to manage save and restore the GPIO states during OTA updates.

###### Step-2: Call the API from Application (heading level 8)

After the OTA image has been received and stored, `bootloader_rebootAndInstallWithGpioRetention()` API must be called in the application.

This API saves the current GPIO configurations to BURAM and instructs the bootloader to perform OTA upgrade using EM4 sleep instead of a system reset. GPIO settings are defined with the `gpio_em4_retention_t` structure, which specifies the port, pin number, and output state. A maximum of 16 GPIO pins can be retained as mentioned in `MAX_EM4_RETAINED_PINS`.

For detailed API usage, parameter descriptions, and return values, refer to the README file located at `<Simplicity SDK>/platform/bootloader/em4-gpio-retention/`

###### Limitations and Considerations (heading level 7)

**Upgrade Type Support**

- GPIO retention is supported only for **application-level upgrades**.
- Upgrades involving the Secure Engine (SE) or the Bootloader are not supported because they involve multiple reset cycles that clear GPIO states.

**GPIO Configuration Limits**

- Maximum of **16 GPIO pins** can be retained.
- Supported ports: **A, B, C, and D** (ports 0–3).
- Pins are restored as **push-pull outputs**.
- Only **output states** are retained; input states are not preserved.

**Power Requirements**

- BURAM retains data only while the device remains powered.
- If power is lost during EM4, BURAM contents and GPIO states will be cleared.
- Continuous power must be maintained throughout the OTA process to ensure successful restoration of GPIO states.

###### Configuring the SMP Two-Page Switch

The SMP Switch component (bootloader_smp_switch) lets a single bootloader boot one of two application images: App 1 or App 2. The active application image is selected using a small switch record stored in two dedicated flash pages.
To enable SMP support, install the Bootloader SMP Switch component in the bootloader project. This component defines BTL_SMP_SUPPORT and exposes the BOOTLOADER_CAPABILITY_SMP_SWITCH capability bit at runtime.

> SMP support is currently available on **EFR32 Series 2** devices (EFR32ZG28).

###### Configuration symbols (`btl_smp_cfg.h`) (heading level 7)

|Symbol|Description|
|---|---|
|`BTL_SMP_PAGE_1_BASE`|Switch record page 1 base address. Must be the start of a flash erase page (`FLASH_PAGE_SIZE`-aligned, see `em_device.h`).|
|`BTL_SMP_PAGE_2_BASE`|Switch record page 2 base address. Must differ from page 1 and not overlap. Typical layout: `BTL_SMP_PAGE_1_BASE + FLASH_PAGE_SIZE`.|
|`BTL_SMP_APP_1_BASE`|Application 1 base address. Defaults to `BTL_APPLICATION_BASE`; not normally overridden.|
|`BTL_SMP_APP_2_BASE`|Application 2 base address (start of the App 2 vector table in flash).|
|`BTL_SMP_DEFAULT_APP_ID`|Application booted when both switch records are invalid (1 = App 1, 2 = App 2).|

The two switch pages and both application images must fit within the device memory map without overlapping other regions, such as the bootloader, NVM3, OTA storage, or manufacturing tokens.
On many EFR32 Series 2 devices, FLASH_PAGE_SIZE is 8192 bytes (8 KiB). However, always verify the value for the device in use.

Application-side Configuration

Application projects do **not** include `btl_smp_cfg.h`. They obtain switch page bases at runtime by calling `get_smp_page_bases_from_storage()`, which uses the bootloader storage vtable callback `getSmpSwitchPageBases`. Application 1 and Application 2 are built as two separate images linked at `BTL_SMP_APP_1_BASE` and `BTL_SMP_APP_2_BASE` respectively.

Example Flash Memory Map (EFR32ZG28, 1 MB Internal Flash)

The following layout illustrates a working SMP configuration:

|Address|Region|
|---|---|
|`0x08000000`|Bootloader|
|`0x08006000`|App 1 (`BTL_APPLICATION_BASE` / `BTL_SMP_APP_1_BASE`)|
|`0x08016000`|App 2 (`BTL_SMP_APP_2_BASE`)|
|`0x08074000`|SMP switch page 1 (`BTL_SMP_PAGE_1_BASE`, 8 KiB)|
|`0x08076000`|SMP switch page 2 (`BTL_SMP_PAGE_2_BASE`, 8 KiB)|
|`0x08084000`|Bootloader storage slot|

Adjust the addresses to match your part's flash size, application sizes, storage requirements, and any reserved regions. Both switch pages must be erase-page-aligned and disjoint from every other region.

![Bootloader_smp screenshot](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image27.png)

Interactions with Other Features:

Secure Boot: When SMP is enabled, an image verification failure in the SMP-selected application automatically triggers a fallback to the alternate slot by using btl_smp_switch_get_alternate_app_base(). For more information, see Gecko Bootloader Security Features.
Storage: The bootloader storage backend must implement BootloaderStorageFunctions_t.getSmpSwitchPageBases. Storage backends that do not implement this callback do not support SMP.
TrustZone: This revision does not expose the SMP switch across the TrustZone boundary.

For information about the Flash API used by applications, see [Application Interface](https://docs.silabs.com/shared-content/latest/bootloader-user-guide-gsdk-4/12-application-interface)

##### Simplicity Commander and the Gecko Bootloader

Simplicity Commander is a single, all-purpose tool to be used in a production environment. It is invoked using a simple CLI (Command Line Interface that is also scriptable. You can use Simplicity Commander to perform these essential tasks:

- Generating key files for signing and encryption
- Signing application images for Secure Boot
- Creating GBL images (encrypted or unencrypted, signed, or unsigned)
- Parsing GBL images

Simplicity Commander is used throughout the examples in the following sections. For more information on executing the commands to complete these tasks, see [Simplicity Commander Reference Guide](https://docs.silabs.com/simplicity-commander/latest/simplicity-commander-start/).

> **Note**: Simplicity Commander also offers a GUI (Graphical User Interface) that can be used in the lab for typical tasks such as flashing device images. The functions described in this User Guide are performed from the CLI.

###### Creating GBL Files Using Simplicity Commander

To create an unsigned GBL file from an application **myapp.s37**, execute `commander gbl create myapp.gbl --app myapp.s37`.

To create an unsigned GBL file from a main bootloader upgrade **mybootloader.s37**, execute `commander gbl create mybootloader.gbl --bootloader mybootloader.s37`. This file can be used with the standalone bootloader configurations of the Gecko Bootloader.

To create an unsigned GBL file from a Secure Engine, upgrade **mySecureElement.seu**, and execute `commander gbl create mySecureElement.gbl --seupgrade mySecureElement.seu`. The Secure Engine images, .seu, are provided by Silicon Labs and  can be found through Simplicity Studio. See section [Gecko Bootloader Operation - Secure Engine Upgrade](05-gecko-bootloader-operation-secure-engine-upgrade#gecko-bootloader-operation-secure-engine-upgrade).

These commands can also be combined to create a single upgrade image, suitable for use with application bootloader configurations of the Gecko Bootloader: `commander gbl create myupgrade.gbl --app myapp.s37 --bootloader mybootloader.s37 --seupgrade mySecureElement.seu`.

##### Gecko Bootloader Security Features

###### About Bootloader Image Security

Secure Boot and Secure Firmware Upgrade, discussed in the following sections, enable Gecko Bootloader to provide authenticity and integrity checks on the Application image, which provides a sufficient level of security for many applications. However, in systems without a hardware root of trust, no process checks the authenticity or integrity of the Gecko Bootloader itself. Its security is provided solely by the device hardware and the robustness of the software running on the device.

The native behavior of Firmware Upgrade will prevent accidental version rollback of Gecko Bootloader under normal usage conditions. However, without a hardware root of trust, intentional downgrade attacks may be feasible. If a higher level of security assurance is required by the application, using a Series 2 device with hardware root of trust and anti-rollback protection is recommended. For more information, refer to [Series 2 and Series 3 Secure Boot with RTSL](https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/).

###### About Application Image Security

The Gecko Bootloader can enforce security on two levels:

- Secure Boot refers to the verification of the authenticity of the application image in main flash on every boot of the device.
- Secure Firmware Upgrade refers to the verification of the authenticity of an upgrade image before performing a bootload, and optionally enforcing that upgrade images are encrypted.

###### Secure Boot Procedure (heading level 7)

When Secure Boot is enabled, the cryptographic signature of the application image in flash is verified on every boot before the application is allowed to run. Secure Boot is not enabled by default in the example configurations provided by Silicon Labs, but enabling it is highly recommended to ensure the validity and integrity of firmware images.

**Signature Algorithms**

The Gecko Bootloader supports the ECDSA-P256-SHA256 cryptographic signature algorithm. This is the ECDSA (elliptical curve digital signature algorithm) of the SHA-256 digest of the application firmware image, using the NIST P-256 (secp256r1) curve.

**Summary of Operation**

1. On boot, the bootloader checks the application image for information about whether it is signed.
2. The type of signature and signature location is determined.
3. If the type of signature does not match the requirements of the bootloader, the bootloader enters device firmware upgrade mode and prevents the application from running.
4. According to the chosen signature algorithm, the signature of the contents of flash from the beginning of the application to the location of the signature is compared to the signature at the signature location.
5. If the signatures do not match, the bootloader enters device firmware upgrade mode and prevents the application from running.

**Secure Boot using ECDSA-P256-SHA256**

For an image to be signed for Secure Boot, the application needs to contain a copy of the **ApplicationProperties_t** struct. This struct contains information about which signature algorithm is used, and where to find the signature.

On every boot, the bootloader calculates the SHA-256 digest of the application image, from the beginning of the application to the start of the signature. The signature of the SHA-256 digest is then verified using ECDSA-P256.

If the signature is valid, the application is allowed to boot. Else, the bootloader is entered, and an application upgrade is attempted if one is available.

The public key used for signature verification is stored as a manufacturing token on Series 1 and EFR32xG22 devices. OnEFR32xG21 devices, the public key can either be stored as a manufacturing token or stored in the Secure Engine One-time Programmable memory (OTP). Simplicity Commander can be used to generate a key pair and write the public key to the device. See [Simplicity Commander Reference Guide](https://docs.silabs.com/simplicity-commander/latest/simplicity-commander-start/) for more information.

**Secure Boot with Application Rollback Protection**

On every boot, the application version in the **ApplicationData_t** struct is stored at the end of the bootloader area in flash, which is used to prevent applications from being downgraded. The application version can remain the same for upgrades. The bootloader will only allow applications to increment its version 6 times by default. The bootloader can be upgraded after that to reset the stored application versions and to allow application upgrades again. The allowed number of upgrades can be increased by modifying linkerfiles to reserve an extra 4 bytes for each version and by increasing SL_GBL_APPLICATION_VERSION_STORAGE_CAPACITY from _btl_bootload.c_. **For Series 2 devices, this option is not applicable on devices with Secure Engine configured to perform full page lock**. If the bootloader area in flash is locked by Secure Engine, the bootloader will not be able to store the application versions in flash, and it would continue without performing that function.

When the application upgrade version check option is enabled in the **Bootloader Core** component, the bootloader checks where App properties points into flash. If app properties struct is present, then it will check compatibility of the application properties struct and check if the upgrade version is greater than the installed app; otherwise, it will check if product ID is correct before applying the upgrade image.

If app properties struct is absent at the app properties location, it is assumed that the previous upgrade was interrupted, and allows the upgrade to continue.

If application rollback prevention component is installed in the bootloader project, then before applying the upgrade image, the bootloader will check the application version in the application properties structure that resides inside the signed/encrypted GBL file and will only apply the OTA image to application area if the application version in ApplicationData structure is equal or higher than the highest application version last seen.

The application rollback prevention feature can be enabled in the **Bootloader Core** component by selecting the **Enable application rollback protection** option. The **Minimum application version allowed** option can be used to configure the minimum application version that should be allowed to boot.

The application versions are stored in the bootloader area in flash. To properly protect the stored application versions, it is recommended to lock the bootloader flash by selecting the **Prevent bootloader write/erase** option in the **Bootloader Core** component.

**Secure Boot Using a Certificate**

On Series 2 devices, a certificate-based secure boot operation is supported. The Certificate contains:

- Struct version: The version of the certificate structure.
- Public key: ECDSA-P256 public key, X and Y coordinates concatenated, used to validate the image.
- Certificate version: The version of the running certificate.
- Signature: ECDSA-P256 signature, used for the authentication of the public key and the certificate version.

The definition of the certificate struct can be found in api/application_properties.h.

To utilize certificate-based secure boot, configure Secure Engine to authenticate the bootloader image by configuring the certificate-based secure boot option in the Secure Engine OTP. Configure the Gecko Bootloader to enable certificate-based secure boot in the **Bootloader Core** component by selecting the **Enable certificate support** option. The Gecko Bootloader certificate must be signed by the private key pair of the public key stored in the Secure Engine OTP. For more information on the key storage, see section [Key Storage](#key-storage).

The certificate-based secure boot procedure is illustrated in the following figure.

![Certificate-Based Secure Boot Procedure](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image21.jpg)

Once the certificate-based secure boot option on Secure Engine is turned on, Secure Engine verifies the Gecko Bootloader certificate. The public key stored in the certificate is used to validate the signature of the Gecko Bootloader. Secure Engine will not accept bootloader images without a certificate.

If only the secure boot option is enabled (not certificate-based) on Secure Engine, and Secure Engine identifies a certificate, the certificate will be used to validate the bootloader image. If the certificate version from the bootloader image is higher than 0 and it gets verified once, Secure Engine will never again accept direct signed bootloader images without a certificate.

The Gecko Bootloader will authenticate the direct signed application using the public key stored in the Gecko Bootloader certificate. If the application contains a certificate, Gecko Bootloader will authenticate it. The procedure is illustrated in the following figure.

![Advanced Certificate-Based Secure Boot Procedure](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image22.jpg)

After authentication of the application certificate, Gecko Bootloader verifies the signature of the application using the public key from the application certificate. In addition, Gecko Bootloader compares the Gecko Bootloader certificate version against the application certificate version. All application images with a certificate version lower than the certificate version of the Gecko Bootloader will be rejected. Gecko Bootloader can be configured to only allow applications with certificate to boot by configuring the **Bootloader Core** component by selecting the **Reject direct signed images** option.

The **ApplicationProperties_t** struct contains the certificate struct **ApplicationCertificate_t**. The certificate struct can be injected to images that contain an **ApplicationProperties_t** with **ApplicationCertificate_t**. To inject a certificate to an image, issue the following command from Simplicity Commander:

`commander convert <image file> --secureboot --keyfile <signing key> --certificate <certificate>--outfile <signed image file with certificate>`

###### Secure Firmware Upgrade (heading level 7)

The Gecko Bootloader supports a secure firmware upgrade process. This is achieved by using symmetric encryption to encrypt the upgrade image, and asymmetric cryptography to sign the upgrade image. Symmetric encryption provides confidentiality and asymmetric cryptography provides integrity and authenticity. Note that encryption alone is not enough to provide authenticity.

**Encryption Algorithms**

The Gecko Bootloader supports the AES-CTR-128 encryption algorithm. The GBL upgrade file is encrypted using 128-bit AES in Counter mode with a random nonce as the initial counter value. Through configuration, the GBL file’s decryption duration can be improved by increasing the counter's block size. The block size is configurable with options 1, 2, 4, or 8 blocks. The configuration is available as part of the AES CTR Stream Block Config component.

The AES-128 key used for decryption, more commonly called the OTA decryption key, is stored as a manufacturing token on Series 1 and EFR32xG22 devices. On EFR32xG21 devices, the OTA decryption key can either be stored as a manufacturing token or stored in the Secure Engine OTP. To make use of the OTA decryption key stored in the Secure Engine OTP, the **Use symmetric key stored in Secure Engine storage** option in the **Bootloader Core** component must be selected. Simplicity Commander can be used to generate an OTA decryption key and write the key to the device. See [Simplicity Commander Reference Guide](https://docs.silabs.com/simplicity-commander/latest/simplicity-commander-start/) for more information. For more information on storing the OTA decryption key on Series 2 devices, see [Production Programming of Series 2 and Series 3 Devices](https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/).

The Secure Engine OTP key support depends on the SE Manager component, which is enabled by default.

**Signature Algorithms**

The Gecko Bootloader supports the ECDSA-P256-SHA256 cryptographic signature algorithm. This is the ECDSA signature of the SHA-256 digest of the GBL upgrade file, using the NIST P-256 (secp256r1) curve.

**Summary of Operation**

Before starting a firmware upgrade process, the application can verify an image in storage by calling into the bootloader verification functions. For bootloaders with Communication Interface, the host device should verify the image before sending it to the NCP or RCP.

During firmware upgrade, the GBL file is parsed, and if encrypted, decrypted on-the-fly. A GBL Signature Tag in the GBL file indicates to the bootloader that the file is signed, and the signature is verified. If signature verification fails, the firmware upgrade process is aborted.

On Series 2 devices, Gecko Bootloader will authenticate the GBL signature tag using the public key stored in the bootloader certificate if the **Enable certificate support** option is selected in the **Bootloader Core** component. A GBL Certificate tag in the GBL file indicates to the bootloader that the GBL certificate tag needs to be authenticated using the public key stored in the bootloader certificate. The certificate version in the GBL certificate tag is compared with the bootloader certificate and only a version equal or higher than the bootloader certificate is accepted. Once the GBL certificate tag is authenticated, the GBL file's signature is verified using the authenticated public key from the GBL certificate tag. On EFRxG21 devices, the GBL certificate tag can also be signed by the private key pair of the public key stored in the Secure Engine OTP.

###### Using Application Image Security Features

This example assumes that a bootloader called **bootloader-uart-xmodem** has been built in Simplicity Studio. For a Series 1 device, three files of interest have been generated in the output directory:

- **bootloader-uart-xmodem.s37** – This file contains the main bootloader. Can be used for bootloader upgrade.
- **bootloader-uart-xmodem-crc.s37** - This file contains the main bootloader with CRC32 checksum.
- **bootloader-uart-xmodem-combined.s37** – This file contains the first stage and main bootloader with a CRC32 checksum in a single image. Can be used for manufacturing and initial deployment of the bootloader.

The relevant version can be flashed to the EFR32 using the Flash Programmer in Simplicity Studio or using Simplicity Commander. For Series 2 devices, only the main bootloader binary is generated.

This example provides two ways of signing the upgrade images. The first option uses Simplicity Commander to generate key material and sign data. This is suitable for development. The second option uses an external signer, such as a dedicated Hardware Security Module (HSM) to protect private key material and perform signing operations. Silicon Labs recommends using an HSM to safeguard private keys.

###### Generating Keys (heading level 7)

To use the security features of the Gecko Bootloader, encryption and signing keys need to be generated. These keys must then be written to the EFR32 device. The encryption key is used with the GBL file for secure firmware upgrade. The signing keys are used both with the GBL file for secure firmware upgrade and to sign the application image for Secure Boot.

**Generating a Signing Key Using Simplicity Commander**

`commander util genkey --type ecc-p256 --privkey signing-key --pubkey signing-key.pub --tokenfile signing-key-tokens.txt`

This creates an ECDSA-P256 key pair for signing; **signing-key** contains the private key in PEM format and **must be kept secret from third parties**. This key will later be used to sign images and GBL files. **signing-key.pub** contains the public key in PEM format and can be used to verify GBL files using commander gbl parse. **signing-key-tokens.txt** contains the public key in token format, suitable for writing to the EFR32 device.

**Generating a Signing Key Using a Hardware Security Module**

When using a Hardware Security Module, the private key is kept secret inside the HSM. According to the instructions from your HSM vendor, have it generate an ECDSA-P256 key pair and export the public key in PEM format to the file **signing-key.pub**. Then use Simplicity Commander to convert the key to token format, suitable for writing to the EFR32 device.

`commander gbl keyconvert --type ecc-p256 signing-key.pub --outfile signing-key-tokens.txt`

**Generating an Encryption Key**

`commander util genkey --type aes-ccm --outfile encryption-key`

This creates an AES-128 key for encryption in the file **encryption-key**. The file has token format, making it suitable to write to the EFR32 device using `commander flash --tokenfile`.

**Writing Keys to the Device**

To write the two token files containing the encryption key and public key as manufacturing tokens to the device, issue the following command:

`commander flash --tokengroup znet --tokenfile encryption-key --tokenfile signing-key-tokens.txt`

###### Signing an Application Image for Secure Boot (heading level 7)

If the bootloader enforces Secure Boot, the application needs to be signed to pass verification. On every boot, a SHA-256 digest of the application is calculated. The signature is verified using ECDSA-P256, with the same public key as for the GBL file signing. Signature verification failure prevents the application from booting.

Application images should contain an **ApplicationProperties_t** struct declaring the application version, capabilities, and other metadata. If **ApplicationProperties_t** is missing, the application image cannot be signed. For more details on adding **ApplicationProperties_t**, see section [Application Properties](12-application-interface#application-properties).

**Using Simplicity Commander**

Signing the application can be done with the command:

`commander convert myapp.s37 --secureboot --keyfile signing-key --outfile myapp-signed.s37`

**Using a Hardware Security Module**

The application can be prepared for signing by issuing the command:

`commander convert myapp.s37 --secureboot --extsign --outfile myapp-for-signing.s37`

###### Creating a Signed and Encrypted GBL Upgrade Image File from an Application (heading level 7)

To create a GBL file from an application, use `commander gbl create`.

Note that, as of this writing, secure application images can only be constructed through Simplicity Commander, not through the configuration options available through Simplicity Studio.

Application images should contain an **ApplicationProperties_t** struct declaring the application version, capabilities, and other metadata. If **ApplicationProperties_t** is missing, the application image cannot be signed. For more details on adding **ApplicationProperties_t**, see section [Application Properties](12-application-interface#application-properties).

**Using Simplicity Commander to Sign**

For an application called **myapp.s37**, use:

`commander gbl create myapp.gbl --app myapp.s37 --sign signing-key --encrypt  encryption-key`

This single command performs three actions:

- Creates a GBL file
- Encrypts the GBL file
- Signs the GBL file

If Secure Boot is also desired, the application must be signed using `commander convert --secureboot` prior to creating the GBL.

**Using a Hardware Security Module to Sign**

For an application called **myapp-signed.s37**, which has previously been signed for Secure Boot, use:

`commander gbl create myapp-for-signing.gbl --app myapp-signed.s37 --extsign --encrypt  encryption-key`

This command performs the following actions:

- Creates a partial GBL file
- Encrypts the partial GBL file
- Prepares the partial GBL file for signing by an external signer

Using an HSM, sign the output file **myapp-for-signing.gbl.extsign**, and supply the resulting DER-formatted signature file **signature.der** back to Simplicity Commander:

`commander gbl sign myapp-for-signing.gbl.extsign --signature signature.der --verify signing-key.pub --outfile myapp.gbl`

The GBL file is not valid until the signature has been applied using gbl sign.

###### System Security Considerations

The Gecko bootloader security features can be used to create a secure device, but do not create a secure system by themselves. This section goes over considerations that need to be taken when designing a secure system where the Gecko Bootloader is a component.

###### Key Storage (heading level 7)

On Series 1 devices, the decryption and public sign keys used by the Gecko bootloader are stored in the Lockbits page in flash. To prevent a rogue application from being able to change or wipe the keys, the Lockbits page should be write protected after the keys have been written in manufacturing.

On Series 2 devices, the decryption key and the sign key used by the Gecko Bootloader can either be stored in the topmost page of the main flash or in the Secure Engine OTP. The decryption key can be provisioned in the Secure Engine OTP using Simplicity Commander or using the Secure Engine Mailbox interface. The keys are not accessible from the user applications on EFR32xG22 devices, which means that the keys used by Gecko Bootloader need to be stored in the topmost page of the main flash. Once a key value has been programmed into the Secure Engine OTP, it cannot be changed. For more details, refer to [Series 2 and Series 3 Secure Boot with RTSL](https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/) and [Simplicity Commander Reference Guide](https://docs.silabs.com/simplicity-commander/latest/simplicity-commander-start/).

The default behavior of the Gecko Bootloader is to use the sign key stored in the Secure Engine OTP to perform the signature verification. If the sign key is not provisioned, the Gecko Bootloader will try to use the public sign key stored in the topmost page of the main flash. This fallback mechanism can be disabled by disabling the “Allow use of public key from manufacturing token storage” component option from the **Bootloader Core** component in Simplicity Studio.

###### Write-Protecting the Bootloader (heading level 7)

By default, the region in flash used by the Gecko bootloader is readable and writeable. The region needs to stay readable for the Application Interface to be able to interact with the bootloader. Immediately after reset, the region also needs to be writable to be able to perform bootloader upgrades.

###### Series 1 (heading level 8)

It is possible to write-protect the bootloader region on EFR32xG12 and newer. This is done by setting the write-once MSC_BOOTLOADERCTRL_BLWDIS bit on every bootup on Series 1 devices. The pages are write-protected until the next reboot. This is done by the Gecko bootloader main stage if the “Prevent bootloader write/erase” component option is enabled in the **Bootloader Core** component in Simplicity Studio.

On EFR32xG1, where the bootloader resides in main flash rather than the information block, the BLWDIS option does not exist. On EFR32xG1, the first flash page containing the first stage bootloader can be write-protected with a Page Lock word, using commander `device protect --write --range 0x0:+0x800`. If bootloader upgrades are to be supported, the pages containing the main bootloader need to stay writeable.

> **Note**: Setting MSC_BOOTLOADERCTRL_BLWDIS will also prevent debuggers from flashing a new bootloader. This means that debug tools that do not completely halt and reset the target device before re-flashing might fail to flash the new bootloader, as the flash is write-protected. If you are unsure whether your debug tools will handle this gracefully, Silicon Labs recommends keeping this setting disabled during development, and enabling it before going into production. If your debug tools halt and reset the target device before flashing, this is not an issue, and Silicon Labs recommends enabling this setting early in the development cycle.

###### Series 2 (heading level 8)

On Series 2 devices, the write-once MSC_PAGELOCKx register is used to write-protect the pages used by the bootloader if the “Prevent bootloader write/erase” component option is enabled in the **Bootloader Core** component in Simplicity Studio. The pages are write-protected until the next reboot.

###### Write-Protecting the Application (heading level 7)

On Series 2 devices, it is also possible to write-protect the application. When the component option “Prevent write/erase of verified application” is enabled, the bootloader will write-protect the pages used by the application after successfully verifying the application signature, before allowing the application to start. This option is only available when Secure Boot of the application is enabled. The MSC_PAGELOCKx register is used to protect the pages, and the write protection lasts until the next reboot.

###### Debug Access (heading level 7)

###### Series 1 (heading level 8)

To prevent debugger access to the Series 1 devices, the Debug Lock word needs to be written. Device recovery after enabling the Debug Lock is possible but will erase the main flash and the Lockbits page. This will erase the main application and the key material stored in the Lockbits page. The Userdata page and bootloader area will survive Debug Unlock, so secrets should not be stored in these locations. To debug lock the device, issue `commander device lock --debug enable`. The AAP Lock can be used to permanently lock the debug port. This also prevents Debug Unlock. To AAP lock the device, please see the reference manual for your device for the address location of the AAP lock word, and use `commander flash --patch` to write the appropriate value to this address.

###### Series 2 (heading level 8)

On Series 2 devices, debugger access can be prevented through Secure Engine. Debugger access can be re-enabled after enabling the Debug Lock by issuing device erase. To debug-unlock the device, issue commander device lock --debug disable from Simplicity Commander. This also erases the main flash, which results in the top page of main flash that stores the encryption and signing keys being erased. To permanently lock the debug port, the device erase disable command can first be issued through Secure Engine. Thereafter, the debug lock command can be issued to the Secure Engine. For more details, refer to [Series 2 and Series 3 Secure Debug](https://docs.silabs.com/iot-security/latest/series2-secure-debug/).

###### Disabling Debug Access from Software (heading level 8)

The software APIs `DBG_DisableDebugAccess(dbgLockModeAllowErase)` and `DBG_DisableDebugAccess (dbgLockModePermanent)` from the EMLIB DBG module can be used from the application to lock the debug interface.

##### Gecko Bootloader and TrustZone

With GSDK v4.2.2, Gecko Bootloader comes with TrustZone support to build TrustZone aware solutions. This section outlines the major points that should be considered when using a TrustZone aware bootloader.

###### Gecko Bootloader Operation

A TrustZone aware application can have the following configurations:

- Secure app only
- Non-secure app only
- Secure and Non-secure pair

With TrustZone enabled, the application bootloaders are entirely configured as Secure applications whereas the communication bootloaders are split into Secure and Non-secure application pairs. The communication interfaces are typically in the Non-secure part of the application and the GBL parser, and other functionalities are in the Secure part of the application. TrustZone aware applications are generated and built using pre-defined workspace applications. Refer to [Series 2 TrustZone](https://docs.silabs.com/mcu-bootloader/latest/series2-trustzone/) and [Software Project Generation and Configuration with SLC-CLI](https://docs.silabs.com/simplicity-studio-5-users-guide/latest/ss-5-users-guide-tools-slc-cli/) for more details regarding TrustZone workspaces and the SLC CLI tool.

For the communication bootloaders to function properly, a combined bootloader binary image (containing both the Secure and Non-secure binary images) needs to be programmed onto the device. A combined bootloader binary can be obtained by either using the post-build steps defined for the workspace or using the Commander tool. Refer to [Simplicity Commander Reference Guide](https://docs.silabs.com/simplicity-commander/latest/simplicity-commander-start/) for more details on how to use the Commander tool to combine multiple binaries into a single binary. Application bootloaders need to be built with the **Bootloader TrustZone Secure** component when building a TrustZone aware solution. The **Bootloader TrustZone Secure** component is required to set up the necessary configuration for the application to function correctly within a TrustZone aware solution.

To create a bootloader upgrade file, the combined bootloader binary (Secure and Non-secure) should be used. Refer to [Series 2 TrustZone](https://docs.silabs.com/mcu-bootloader/latest/series2-trustzone/) for more details on upgrading an existing application to TrustZone. Special care must be taken while building the TrustZone aware communication bootloaders for the xG21 family as the bootloader size increases from 16kB (non TrustZone aware solution) to 24kB (TrustZone aware solution). Similarly, the Bootloader – SoC Bluetooth Apploader OTA DFU application’s size increases from 72kB to 80kB with TrustZone enabled.

###### Gecko Bootloader Configuration

The following table provides three of the configuration options available as part of the Bootloader Interface with TrustZone enabled.

<table>
    <thead>
        <tr>
            <th>
                <p>Configuration Option</p>
            </th>
            <th>
                <p>Description</p>
            </th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>BOOTLOADER_DISABLE_OLD_BOOTLOADER_MITIGATION</p>
            </td>
            <td>
                <p>Disables multi-tiered fallback logic. The fault handling logic as well as the USART auto-detection logic will be disabled. Default value is set to 0.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>BOOTLOADER_DISABLE_NVM3_FAULT_HANDLING</p>
            </td>
            <td>
                <p>Disables peripheral access fault handling. The fault handling triggered by an erroneous access of peripherals will be disabled. This should be disabled if all the peripherals that are in use by the bootloader have been properly configured by the Manually override security state of peripherals option. Default value is set to 0.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>BOOTLOADER_MANUAL_OVERRIDE_SECURITY_STATE</p>
            </td>
            <td>
                <p>Manually override the security state of peripherals in use by the bootloader. This option can be used to manually set a peripheral's access state before calling into the bootloader. Default value is set to 0.</p>
            </td>
        </tr>
    </tbody>
</table>

##### Gecko Bootloader and Delta DFU Upgrades

With GSDK v2024.6.1, Gecko Bootloader support comes with support for delta DFU upgrades. This section provides an introduction to Delta DFU upgrades and describes the major points that should be considered while using the Delta DFU component.

###### Introduction to Delta DFU

The main function of the image delta DFU is to calculate the difference between the current firmware stored on the device and the new firmware to be updated. This difference is used to create a patch file that can be applied to the current firmware, resulting in the new firmware. The patch file is created using the Simplicity Commander tool, transferred to the device with any wireless protocol as a GBL, and applied on top of the current firmware by the Gecko Bootloader. The patch file creation and application are both implemented in a library that is shared between these tools.

The following diagram illustrates the complete process.

![Delta DFU process: patch creation, transfer, and firmware update](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image23.png)

###### Gecko Bootloader Operation

The Delta DFU upgrades can be enabled by including the Delta DFU component in the Gecko Bootloader. Simplicity Commander supports the creation of a Delta DFU GBL file.

With Delta DFU enabled, the application bootloaders are equipped to accept Delta DFU GBL files. These GBL files contain a patch file that is extracted into the slot. The Gecko Bootloader creates the new firmware using this patch file and the currently running firmware within the slot. The slot size should have enough number of pages to hold the GBL file, patch file, and the new firmware binary in the slot. Each file starts on a new page.

The Delta DFU upgrades are triggered in the same way as in an application bootloader. Once the bootloader finds a GBL that contains Delta Patch, it extracts the patch file and then re-constructs the new firmware file within the slot. If the new firmware is created correctly, then the bootloader copies this new firmware to the application area, completing the upgrade.

To create the GBL file, Commander needs two input files, one of the firmware that is current and one that is the upgrade. For Delta DFU upgrades to work, it is essential that the firmware on the device and the firmware used to create the GBL are the same. Also, the version dependency tag is included in Delta DFU and should be correctly set by the user.

Delta DFU operation is illustrated in the following figure:

![Delta DFU flow: patch extraction, firmware reconstruction, and upgrad](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image24.png)

###### Delta Patch Extraction (Flash vs. RAM)

In Delta DFU, the Silicon Labs Gecko Bootloader must extract the patch file inside the GBL before it can reconstruct the new firmware image. The bootloader supports two extraction modes:

- Flash-based extraction (default).
- RAM-based extraction (optional).

###### Flash-Based Extraction (Default) (heading level 7)

By default, the bootloader stores the extracted delta patch in the upgrade slot in flash. The storage slot must therefore be large enough to hold all three of the following, each written to flash and aligned to page boundaries:

- The GBL file.
- The extracted delta patch.
- The reconstructed firmware image.

This approach adds no RAM requirements, but it increases the total flash space the upgrade process needs.

###### RAM-Based Extraction (heading level 7)

On devices with sufficient RAM, you can configure the bootloader to extract the delta patch into RAM by enabling the `BTL_DELTA_DFU_EXTRACT_TO_RAM` configuration option. In this mode:

- The GBL file remains in the upgrade slot.
- The bootloader extracts and decompresses the delta patch into RAM.
- The bootloader reconstructs the firmware image and writes it to flash.

Because the extracted patch is no longer stored in flash, the storage slot only needs to accommodate the GBL file and the reconstructed image. This reduces flash (NVM) usage and allows larger firmware images to be updated in the same slot.

###### RAM Buffer Sizing (heading level 8)

When RAM-based extraction is enabled, the bootloader allocates a RAM buffer large enough to hold the entire extracted patch. The bootloader determines the buffer size as follows:

- **Uncompressed delta tags and LZMA-compressed patches:** The buffer matches the uncompressed patch size, which the bootloader reads directly.
- **LZ4-compressed patches:** The compressed data does not expose the uncompressed size, so the bootloader assumes a compression ratio of at least 20% and allocates approximately 1.25× the compressed size.

If RAM is insufficient, the bootloader rejects the update before reconstruction begins. Partial or streaming extraction is not supported — the entire patch must be fully present in RAM.

###### Behavior During Power Loss (heading level 8)

Because RAM is volatile, any reset or power interruption during extraction forces the bootloader to repeat the extraction step on resume. The GBL file itself stays intact in flash, so the bootloader can reprocess it when the upgrade continues.

Flash-based extraction avoids this rework: the extracted patch persists in non-volatile memory, so a reset doesn't cost the extraction work already done.

###### When to Use Each Mode (heading level 7)

|Choose this mode|When|
|---|---|
|RAM-based|The device has ample RAM, and you need to reduce flash usage or fit a larger upgrade.|
|Flash-based (default)|RAM is constrained, or your application can't afford to redo extraction after a reset.|

For more details on how to set up and configure Delta DFU in the Bootloader as well as creating the Patch File, see [OTA Delta DFU](https://docs.silabs.com/mcu-bootloader/latest/using-gecko-bootloader-with-bluetooth-apps/05-ota-delta-dfu).

##### Application Interface

The bootloader has an application interface exposed through a function table in the bootloader. The application interface provides APIs to use bootloader functions for storing and retrieving upgrade images and verifying their integrity. APIs to reboot into the bootloader are also provided. For details see the Gecko Bootloader API Reference, shipped with the SDK in the platform/bootloader/documentation folder.

If you are not using a protocol stack from Silicon Labs, the **api/btl_interface.h** header provides the bootloader application interface API. If you are using a protocol stack from Silicon Labs, the recommended bootloader interface API for the specific protocol stack should be used instead. The following files provide the implementation of the bootloader interface:

**api/btl_interface.c** (common interface)

**api/btl_interface_storage.c** (interface to storage functionality)

The application interface consists of functions that can be included into the customer application, and that communicate with the bootloader through the **MainBootloaderTable_t**. This table contains function pointers into the bootloader. The 10th word of the bootloader contains a pointer to this structure, allowing any application to easily locate it. Using the wrapper functions provided in the Bootloader Interface API is preferred over accessing the bootloader table directly. Modules include:

- **Application Parser Interface**: Application interface for interfacing with the bootloader image parser.
- **Application Storage Interface**: Application interface for interfacing with the bootloader storage. The Storage Interface is only available on bootloaders that support the storage interface.
- **Common Application Interface**: Generic application interface available on all versions of the bootloader, independently of which components are present.

###### Application Properties

Application images should contain an **ApplicationProperties_t** struct declaring the application version, capabilities, and other metadata. Simplicity Commander extracts the metadata contained in this structure from the application and places it in the GBL upgrade file **GBL Application Tag**. If the structure is not present in the application, Simplicity Commander will raise an error. The **ApplicationProperties_t** struct is added to the application on installing the **bootloader_interface** component to the application. The **bootloader_interface** component installs **bootloader_app_properties** component which adds an instance of **ApplicationProperties_t** named **sl_app_properties** to the project. The component adds a source file named **app_properties.c** and a configuration file named **app_properties_config.h**. This configuration file allows users to configure the application version via Simplicity Studio’s Component Editor. To open the Component Editor, locate the **App Properties** component under **Platform > Bootloader** as shown below.

![App Properties component location under Platform > Bootloader menu](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image25.png)

Click **Configure** to open the Component Editor.

![Component Editor interface](/bootloader-user-guide-gsdk-4/0.2/images/sld718-image26.png)

The application type is automatically populated based on the wireless stack used in building the project. The value of the application type is automatically set during the project generation step and can be found in **autogen/sl_application_type.h** file.

The contents of the **GBL Application Tag** can be extracted from a GBL file by a running application using the Application Storage interface. Note that the **GBL Application Tag** will only be added if the GBL file contains an application image, not if the GBL file only contains a bootloader upgrade or metadata.

The structure in the application is also used to declare whether the application image is signed, and what type of signature is used. This information is added by Simplicity Commander when signing the image using `commander convert (--secureboot, --extsign or -- signature)`. For the bootloader to locate the **ApplicationProperties_t** struct, if not already done by the linker, Simplicity Commander modifies word 13 of the application to insert a pointer to the **ApplicationProperties_t** struct when signing the application image for Secure Boot.

###### Error Codes

Most Gecko bootloader APIs return error codes. The following table lists the groups of error codes that may be returned. The full list of error codes within each group can be found in _api/btl_errorcode.h_ in the platform/bootloader directory of the SDK, as well as in the API Reference.

<table>
    <thead>
        <tr>
            <th>
                <p>ID</p>
            </th>
            <th>
                <p>Description</p>
            </th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>0x0</p>
            </td>
            <td>
                <p>OK</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>0x01xx</p>
            </td>
            <td>
                <p>Initialization error</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>0x02xx</p>
            </td>
            <td>
                <p>Image verification error</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>0x04xx</p>
            </td>
            <td>
                <p>Storage error</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>0x05xx</p>
            </td>
            <td>
                <p>Bootload error</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>0x06xx</p>
            </td>
            <td>
                <p>Security error</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>0x07xx</p>
            </td>
            <td>
                <p>Communication error</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>0x09xx</p>
            </td>
            <td>
                <p>XMODEM parser error</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>0x10xx</p>
            </td>
            <td>
                <p>GBL file parser error</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>0x11xx</p>
            </td>
            <td>
                <p>SPI slave driver error</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>0x12xx</p>
            </td>
            <td>
                <p>UART driver error</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>0x13xx</p>
            </td>
            <td>
                <p>Compression error</p>
            </td>
        </tr>
    </tbody>
</table>

###### SMP Switch Application Interface

When the bootloader is built with the **Bootloader SMP Switch** component (`bootloader_smp_switch`), it advertises the capability and exposes a storage callback so an application can locate the switch pages and update the switch record at runtime. The application links against the **Bootloader Application Interface Series 2** (`bootloader_interface_s2`), which provides the SMP Flash API header `core/smp_switch/btl_smp_switch_flash_api.h`.

> SMP support is currently available on **EFR32 Series 2** devices (EFR32ZG28).

Capability Bit

The bootloader sets the following bit in `BootloaderInformation_t.capabilities` (returned by `bootloader_getInfo()`):

|Capability bit|Meaning|
|---|---|
|`BOOTLOADER_CAPABILITY_SMP_SWITCH`|The bootloader supports the SMP two-page switch (built with `BTL_SMP_SUPPORT`). The storage vtable callback `getSmpSwitchPageBases` is non-NULL.|

Applications must check this bit before calling any SMP Flash API; the helpers in `btl_smp_switch_flash_api.h` perform this check internally.

Storage vtable Callback

The SMP component adds one function pointer to `BootloaderStorageFunctions_t`:

```c
void (*getSmpSwitchPageBases)(uint32_t *page1Base, uint32_t *page2Base);
```

#### Series 2 and Series 3 Secure Boot with RTSL

##### Series 2 and Series 3 Secure Boot with RTSL

> **Note: This section replaces _AN1218: Series 2 and Series 3 Secure Boot with RTSL_. Further updates to this application note will be provided here**.

This application note describes the design of Secure Boot with RTSL (Root of Trust and Secure Loader) on Series 2 and Series 3 devices. It also provides examples of how to implement the Secure Boot process.

For more information on using the Gecko Bootloader with Series 2 and Series 3 devices, see the following:

- [Bootloader Fundamentals](https://docs.silabs.com/mcu-bootloader/latest/bootloader-fundamentals/)
- [UG266: Silicon Labs Gecko Bootloader User’s Guide for GSDK 3.2 and Lower](https://www.silabs.com/documents/public/user-guides/ug266-gecko-bootloader-user-guide.pdf)
- [Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/)
- [Silicon Labs Gecko Bootloader User’s Guide for Series 3 and Higher](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-series3-and-higher/)

###### Key Points

- Compares the Secure Boot process in Series 1, Series 2, and Series 3 devices
- Describes the Series 2 and Series 3 Secure Boot with RTSL components and process
- Provides examples of configuring Series 2 and Series 3 devices for the Secure Boot process
- Recovers secure boot failure devices

##### Series 2 and Series 3 Device Security Features

Protecting IoT devices against security threats is central to a quality product. Silicon Labs offers several security options to help developers build secure devices, secure application software, and secure paths of communication to manage those devices. Silicon Labs’ security offerings were significantly enhanced by the introduction of the Series 2 and Series 3 products that included a Secure Engine. The Secure Engine is a tamper-resistant component used to securely store sensitive data and keys and to execute cryptographic functions and secure services.

On Series 1 devices, the security features are implemented by the TRNG (if available) and CRYPTO peripherals.

On Series 2 and Series 3 devices, the security features are implemented by the Secure Engine and CRYPTOACC (if available). The Secure Engine may be hardware-based, or virtual (software-based). Throughout this document, the following abbreviations are used:

- Series 2 devices: EFR32xx2xx devices
- Series 3 devices: SiG3xxx devices
- HSE: Hardware Secure Engine
- VSE: Virtual Secure Engine
- SE: Secure Engine (either HSE or VSE)

Additional security features are provided by Secure Vault. Three levels of Secure Vault feature support are available, depending on the part and SE implementation, as reflected in the following table:

|**Level (1)**|**SE Support**|
|---|---|
|Secure Vault High (SVH) and Series 3 Secure Vault|HSE only (HSE-SVH)|
|Secure Vault Mid (SVM)|HSE (HSE-SVM), VSE (VSE-SVM)|
|Secure Vault Base (SVB)|N/A|

**Notes**:

1. The features of different Secure Vault levels can be found in [Security](https://www.silabs.com/security).
2. Refer to [IoT Endpoint Security Fundamentals](https://docs.silabs.com/iot-security/latest/iot-endpoint-security-fundamentals/11-series-2-device-security-features) for details on supporting devices.

Secure Vault Mid consists of two core security functions:

- **Secure Boot**: Process where the initial boot phase is executed from an immutable memory (such as ROM) and where code is authenticated before being authorized for execution.
- **Secure Debug access control**: The ability to lock access to the debug ports for operational security, and to securely unlock them when access is required by an authorized entity.

Secure Vault High and Series 3 Secure Vault devices offer additional security options:

- **Secure Key Storage**: Protects cryptographic keys by “wrapping” or encrypting the keys using a root key known only to the HSE-SVH.
- **Anti-Tamper protection**: A configurable module to protect the device against tamper attacks.
- **Device authentication**: Functionality that uses a secure device identity certificate along with digital signatures to verify the source or target of device communications.

> **Note**: Series 3 Secure Vault devices do not include a device certificate for the host MCU.

A Secure Engine Manager and other tools allow users to configure and control their devices both in-house during testing and manufacturing, and after the device is in the field.

###### User Assistance

In support of these products, Silicon Labs offers whitepapers, webinars, and documentation. The following table summarizes the key security documents:

<table>
    <thead>
        <tr>
            <th>Document</th>
            <th>Summary</th>
            <th>Applicability</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/series2-secure-debug/">Series 2 and Series 3 Secure Debug</a></p>
            </td>
            <td>
                <p>How to lock and unlock Series 2 and Series 3 debug access, including background information about the SE</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Series 2 and Series 3 Secure Boot with RTSL (this application note)</p>
            </td>
            <td>
                <p>Describes the secure boot process on Series 2 and Series 3 devices using SE</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/efr32-secure-vault-tamper/">Anti-Tamper Protection Configuration and Use</a></p>
            </td>
            <td>
                <p>How to program, provision, and configure the anti-tamper module</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/authenticating-devices-using-device-certificates/">Authenticating Silicon Labs Devices using Device Certificates</a></p>
            </td>
            <td>
                <p>How to authenticate a device using secure device certificates and signatures, at any time during the life of the product</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/efr32-secure-key-storage/">Secure Key Storage</a></p>
            </td>
            <td>
                <p>How to securely "wrap" keys so they can be stored in non-volatile storage.</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/">Production Programming of Series 2 and Series 3 Devices</a></p>
            </td>
            <td>
                <p>How to program, provision, and configure security information using SE during device production</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>AN1504: Series 3 Security Overview</p>
            </td>
            <td>
                <p>High level overview of the security features included in Series 3 devices</p>
            </td>
            <td>
                <p>Series 3</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>AN1509: Series 3 AXiP</p>
            </td>
            <td>
                <p>How to encrypt and authenticate memory contents</p>
            </td>
            <td>
                <p>Series 3</p>
            </td>
        </tr>
    </tbody>
</table>

###### Key Reference

Public/Private keypairs along with other keys are used throughout Silicon Labs security implementations. Because terminology can sometimes be confusing, the following table lists the key names, their applicability, and the documentation where they are used.

|**Key Name**|**Customer Programmed**|**Purpose**|
|---|---|---|
|Public Sign key (Sign Key Public)|Yes|Secure Boot binary authentication and/or OTA upgrade payload authentication|
|Public Command key (Command Key Public)|Yes|Secure Debug Unlock or Disable Tamper command authentication|
|OTA Decryption key (GBL Decryption key) aka AES-128 Key|Yes|Decrypting GBL payloads used for firmware upgrades|
|Attestation key aka Private Device Key|No|Device authentication for secure identity|
|AXiP Key|No|Used on Series 3 devices for encryption/ decryption and authentication of firmware placed in flash|
|EXiP Key|No|Used on Series 3 devices for encryption/ decryption of firmware placed in flash|

###### SE Firmware

Silicon Labs strongly recommends installing the latest SE firmware on Series 2 and Series 3 devices to support the required security features. Refer to [Production Programming of Series 2 and Series 3 Devices](https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/) for the procedure to upgrade the SE firmware and [IoT Endpoint Security Fundamentals](https://docs.silabs.com/iot-security/latest/iot-endpoint-security-fundamentals/) for the latest SE Firmware shipped with Series 2 and Series 3 devices and modules.

##### Secure Boot Process

###### Introduction

Secure Boot is a foundational component of platform security, as the absence of it can allow malicious code to subvert protections like secure storage, secure data transport, secure identity, and data confidentiality.

Secure Boot works as a process by which each piece of firmware is validated for authenticity and integrity before it is allowed to run. Each authenticated module can also validate additional modules before executing them, forming a chain of trust. If any module fails its security check, it is not allowed to run, and program control will typically stall in the validating module. In most lightweight IoT systems, the behavior of a Secure Boot failure is to cause the device to stop working until an authentically signed image can be loaded onto it. Whereas this may seem extreme, it is a better outcome than a smart light bulb being repurposed to mine crypto-currency, or a smart speaker being repurposed as a surveillance device on the end user’s private conversations.

The first link in the chain of trust is the root of trust. This is often the weakest link in the Secure Boot chain because the root of trust itself is not checked for authenticity or integrity. The security strength of the root of trust lies in its immutability. The strongest roots of trust have their firmware origin in ROM and use a Public Sign Key that is also located in ROM.

Series 1, Series 2, and Series 3 devices use a two-stage boot design consisting of a non-upgradable first stage root of trust followed by an upgradable second stage. In Series 1 devices, the root of trust (also called the first-stage bootloader) is in flash rather than ROM, and the upgradable portion (the main bootloader) is checked for integrity using a CRC32 checksum but is not checked for authenticity using a Public Sign Key. In Series 2 and Series 3 devices, the root of trust is in ROM, and the upgradable portion is checked both for integrity and authenticity.

The Secure Boot with RTSL is implemented by Root code executed by the Hardware Secure Engine (HSE) or the Cortex-M33 operating in Root Mode (VSE). For more information about SE, see section _Secure Engine Subsystem_ in [Series 2 and Series 3 Secure Debug](https://docs.silabs.com/iot-security/latest/series2-secure-debug/).

In Series 3 devices, once firmware is programmed to the device, the code regions must be closed. Closing a code region locks it to prevent writes, as well as further configuration, and indicates to the SE that it can now be accessed by the SE. After flashing the firmware image, commander automatically closes the code region. To open a closed code region, an erase must be performed. If a code region is not closed, the SE will return a fault when attempting to access the open code region during secure boot verification of the host firmware. Each time a code region is closed, the SE's MTP (Multiple Times Programmable) memory section is updated, which causes an SE OTP bit to be used. More details on SE OTP bits is covered in the following section.

Silicon Labs provides [Custom Part Manufacturing Service (CPMS)](https://www.silabs.com/developers/custom-part-manufacturing-service) to customize the users' security features and settings. This application note uses the following abbreviations:

- FSB: First Stage Bootloader
- SSB: Second Stage Bootloader
- GBL: Gecko Bootloader
- RTSL: Root of Trust and Secure Loader
- HSM: Hardware Security Module
- OTP: One-Time Programmable
- WSTK: Wireless Starter Kit
- GSDK: Gecko Software Development Kit. For more information, refer to [GSDK](https://github.com/SiliconLabs/gecko_sdk)
- SiSDK: Simplicity Software Development Kit. For more information, refer to [SiSDK](https://github.com/SiliconLabsSoftware/sisdk-release)
- ECDSA-P256-SHA256: Elliptic Curve Digital Signature Algorithm aka ECDSA using a P-256 curve and a SHA256 hash
- PEM (.pem): Privacy Enhanced Mail
- DER (.der): Distinguished Encoding Rules

###### ECDSA-P256-SHA256 Secure Boot in Series 1 Devices

The Secure Boot process for Series 1 (SVB) devices originates in flash, typically with the execution of the first stage of GBL. The first stage of GBL checks to see if an upgrade is pending for the second stage of GBL. If so, it processes the upgrade of the second stage and then executes it. Otherwise, it just executes the second stage. If Secure Boot is enabled, the second stage of GBL checks the integrity and authenticity of the application image before executing it. If the integrity check fails, program control remains in the SSB. The following figure illustrates the Secure Boot process on Series 1 devices.

![Series 1 ECDSA-P256-SHA256 Secure Boot Process](/series2-secure-boot-with-rtsl/0.3/images/sld794-image15.png)

See [UG266](https://www.silabs.com/documents/public/user-guides/ug266-gecko-bootloader-user-guide.pdf)/[Silicon Labs Gecko Bootloader User’s Guide for Series 3 and Higher](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-series3-and-higher/) for more information to generate and download signed firmware images using Simplicity Commander.

###### ECDSA-P256-SHA256 Secure Boot in Series 2 and Series 3 Devices

For Series 2 and Series 3 devices, the Secure Engine (SE) implements the FSB to authenticate and upgrade the SSB. The GBL implements the SSB, also known as, Main Bootloader in _UG266: Silicon Labs Gecko Bootloader User’s Guide for GSDK 3.2 and Lower_, [Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/) or [Silicon Labs Gecko Bootloader User’s Guide for Series 3 and Higher](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-series3-and-higher/) to authenticate and upgrade the application firmware.

Refer to the _Gecko Bootloader Security Features_ section in [UG266](https://www.silabs.com/documents/public/user-guides/ug266-gecko-bootloader-user-guide.pdf), [Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/), or [Silicon Labs Gecko Bootloader User’s Guide for Series 3 and Higher](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-series3-and-higher/) and the [ECDSA-P256-SHA256 Secure Boot](03-examples#signing-for-ecdsa-p256-sha256-secure-boot) example for more information about the ECDSA-P256-SHA256 secure boot process in Series 2 devices and Series 3.

> **Note**: It is possible to have a 2-stage design to skip the SSB between FSB and application. However, the application cannot be upgraded if discarding the SSB, and this application note assumes the SSB is present.

###### HSE (heading level 7)

In HSE-SVM, HSE-SVH, and Series 3 Secure Vault devices, the Secure Boot process originates in ROM contained in the security co-processor (HSE). The following figures illustrate the Secure Boot process and flow on Series 2 HSE and Series 3 devices.

![Series 2 HSE and Series 3 ECDSA-P256-SHA256 Secure Boot Process](/series2-secure-boot-with-rtsl/0.3/images/sld794-image16.png)

![Series 2 HSE and Series 3 ECDSA-P256-SHA256 Secure Boot Flow](/series2-secure-boot-with-rtsl/0.3/images/sld794-image17.png)

###### VSE (heading level 7)

In VSE-SVM devices, the host MCU (Cortex-M33) assumes an elevated security state out of reset and securely boots itself from code that originates in ROM. The following figures illustrate the Secure Boot process and flow on Series 2 VSE devices.

![Series 2 VSE ECDSA-P256-SHA256 Secure Boot Process](/series2-secure-boot-with-rtsl/0.3/images/sld794-image18.png)

![Series 2 VSE ECDSA-P256-SHA256 Secure Boot Flow](/series2-secure-boot-with-rtsl/0.3/images/sld794-image19.png)

###### Certificate-based Secure Boot in Series 2 and Series 3 Devices

Refer to the _Gecko Bootloader Security Features_ section in [UG266: Silicon Labs Gecko Bootloader User’s Guide for GSDK 3.2 and Lower](https://www.silabs.com/documents/public/user-guides/ug266-gecko-bootloader-user-guide.pdf)/[Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/) and [Certificate-Based Secure Boot](03-examples#signing-for-certificate-based-secure-boot) example for details about the certificate-based Secure Boot process in Series 2 and Series 3 devices.

The certificate-based Secure Boot process uses key delegation to minimize the exposure of the Private Sign Key, thereby reducing the likelihood that the corresponding Public Sign Key would need to be revoked.

If the certificate’s private key is leaked, all devices that have been programmed with that certificate are at risk until they can be updated with an image containing a certificate with a [higher version](./03-examples.md#certificate-revocation(03-examples.md#certificate-revocation).

###### Secure Loader

In Series 2 and Series 3 devices, the Secure Loader is firmware pre-loaded into the chip. Silicon Labs maintains the Secure Loader and deploys through secure upgrade packages. It is the functional equivalent of the first-stage GBL on Series 1 devices (refer to [UG266: Silicon Labs Gecko Bootloader User’s Guide for GSDK 3.2 and Lower](https://www.silabs.com/documents/public/user-guides/ug266-gecko-bootloader-user-guide.pdf)/[Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/) for more information). The Secure Loader validates the authenticity and integrity of a staged image before performing an upgrade operation. The Secure Loader requires the staged image to reside on-chip and the staged image must not overlap with the target destination address range. Firmware images that originate from off-chip, either off-chip storage, external NCP/ RCP host interface, or through an OTA update procedure are expected to be staged either by the application or by SSB before calling the Secure Loader to perform the upgrade.

![Series 2 and Series 3 Secure Loader Example](/series2-secure-boot-with-rtsl/0.3/images/sld794-image20.png)

###### Secure Boot Time

Secure boot extends the recovery time from all sources of device reset. The duration of each authentication operation depends on the factors below:

- Computation of the SHA-256 digest (32 bytes) of the associated image, which is proportional to the size of the firmware image.
- Verification of the ECDSA-P256 signature of the SHA-256 digest above, which is independent of image size.
- The clock frequency of the Crypto Engine, which is the HSE or CRYPTOACC in VSE devices.

|**Authentication**|**Enable/Disable**|**Duration**|
|---|---|---|
|FSB code|Enable (cannot disable)|FSB code size dependent|
|SSB code|Disable (by default)|SSB code size and SE firmware version dependent|
|Application code|Disable (by default)|Application code size and SSB firmware (GBL) version dependent|

**Notes**:

- It will extend the boot time for certificates authentication if using [Certificate-Based Secure Boot](03-examples#signing-for-certificate-based-secure-boot).
- Refer to device-specific datasheets (like [EFR32MG21B](https://www.silabs.com/documents/public/data-sheets/efr32mg21b-datasheet.pdf)) for data about the boot timing of Series 2 devices.
- Refer to _Secure Boot Configuration_ below on how to enable the SSB and application code authentication.

###### Secure Boot Configuration

The following sections describe how to configure the Secure Boot of the SSB (GBL) and application firmware.

###### SSB (heading level 7)

In Series 2 and Series 3 devices, the immutable OTP memory stores the [Public Sign Key, Secure Boot Enable flag](03-examples#provision-public-sign-key-and-secure-boot-enabling), and Anti-rollback flag. The user cannot change its respective value once either is programmed. Once the Public Sign Key is provisioned, it remains provisioned to that key value for the life of the device. Once Secure Boot and Anti-rollback is enabled, it remains enabled for the life of the device. Both of these assignment operations are **IRREVOCABLE**.

The Public Sign Key used for Series 2 and Series 3 devices is the public portion of an ECDSA key pair over the NIST prime curve P-256. The Public Sign Key is a customer key and is typically provisioned during the initial product manufacturing and device programming phase. It is common for all products that share the same firmware image to be loaded with the same Public Sign Key. The key loaded into the device is a public key and has no confidentiality requirements. The private key associated with that public key, which will be used to sign firmware images or certificates, should be tightly held, ideally secured in the [HSM](03-examples#generate-key-and-signing) or equivalent key storage instrument.

You can use [Simplicity Commander](03-examples#simplicity-commander), [SE Manager](03-examples#se-manager-key-provisioning-platform-example), or [Simplicity Studio](03-examples#simplicity-studio) to program the Public Sign Key and configure the SSB Secure Boot with anti-rollback in SE OTP. The following figures show the Simplicity Studio and Simplicity Commander implementation, for detailed steps refer to the Example section:

![Configuration of SSB using Simplicity Studio](/series2-secure-boot-with-rtsl/0.3/images/sld794-image21.jpg)

![Configuration of SSB using Simplicity Commander](/series2-secure-boot-with-rtsl/0.3/images/sld794-image22.jpg)

###### Application Firmware (heading level 7)

You can use the AppBuilder or [Bootloader-core software component](03-examples#bootloader-core-software-component) in the [GBL](03-examples#generate-an-unsigned-gbl-image) project to configure the security options of the application firmware.

You can reconfigure the Secure Boot configuration of the application firmware by upgrading the GBL with the new custom settings.

![Using AppBuilder](/series2-secure-boot-with-rtsl/0.3/images/sld794-image23.png)

Using Bootloader core compoent

![Using Bootloader core component](/series2-secure-boot-with-rtsl/0.3/images/sld794-image24.jpg)

![Security Options of Application Firmware using AppBuilder and Bootloader Core Component](/series2-secure-boot-with-rtsl/0.3/images/sld794-image25.jpg)

> **Note**: Install "Bootloader Core (Series-3)" for series 3 devices.

##### Examples

###### Overview

The following table describes the examples for Series 2 and Series 3 Secure Boot.

> **Note**: Series 3 devices require SE firmware Version 3.1.0 or later to support Secure Boot. However, version 3.3.4 or newer is recommended for full feature support and latest security enhancements.

|**Example**|**Device (Radio Board)**|**Minimum SE Firmware**|**Tool**|
|---|---|---|---|
|Provision Public Sign Key and Secure Boot Enabling|EFR32MG21A010F1024IM32 (BRD4181A)|Version 1.2.9|Simplicity Commander|
|"|EFR32MG21A010F1024IM32 (BRD4181A)|Version 1.2.9|SE Manager|
|"|EFR32MG21A010F1024IM32 (BRD4181A)|Version 1.2.9|Simplicity Studio 5|
|Provision GBL Decryption Key|EFR32MG21A010F1024IM32 (BRD4181A)|Version 1.2.9|Simplicity Commander|
|"|EFR32MG21A010F1024IM32 (BRD4181A)|Version 1.2.9|SE Manager|
|Signing for ECDSA-P256-SHA256 Secure Boot|EFR32MG21A010F1024IM32 (BRD4181A)|Version 1.2.9|Simplicity Commander|
|Signing for Certificate-Based Secure Boot|EFR32MG21A010F1024IM32 (BRD4181A)|Version 1.2.9|Simplicity Commander|
|Generate a GBL Upgrade Image File|EFR32MG21A010F1024IM32 (BRD4181A)|Version 1.2.9|Simplicity Commander|
|Upgrade to Certificate-Based Secure Boot|EFR32MG21A010F1024IM32 (BRD4181A)|Version 1.2.9|Simplicity Commander|
|Certificate Revocation|EFR32MG21A010F1024IM32 (BRD4181A)|Version 1.2.9|Simplicity Commander|
|Upgrade to Secure Boot with RTSL|EFR32MG21A010F1024IM32 (BRD4181A)|Version 1.2.9|SE Manager & Simplicity Commander|
|Recover Devices when Secure Boot Fails|EFR32MG21A010F1024IM32 (BRD4181A)|Version 1.2.9|Simplicity Commander|

> **Note**: Unless specified in the example, these examples can be applied to other Series 2 and Series 3 devices.

###### Using Simplicity Commander (heading level 7)

1. This application note uses Simplicity Commander v1.19.2. The procedures and console output may be different for the other versions of Simplicity Commander. The latest version of Simplicity Commander can be downloaded from [MCU Programming options](https://www.silabs.com/developer-tools/mcu-programming-options).  
   > **Note**: It is recommended to upgrade to the latest version of Simplicity Commander.  
   ```sh  
   commander --version  
   ```  
   ```sh  
   Simplicity Commander 1v19p2b1907  
     
   JLink DLL version: 7.52d  
   Qt 5.12.10 Copyright (C) 2017 The Qt Company Ltd.  
   EMDLL Version: 0v17p19b0  
   mbed TLS version: 2.16.6  
     
   Emulator found with SN=440048205 USBAddr=0  
     
   DONE  
   ```
2. The Simplicity Commander's Command Line Interface (CLI) is invoked by `commander.exe` in the Simplicity Commander folder. The location for Simplicity Studio 5 in Windows is `C:\SiliconLabs\SimplicityStudio\v5\developer\adapter_packs\commander`. For ease of use, it is highly recommended to add the path of commander.exe to the system PATH in Windows.
3. If more than one WSTK is connected via USB, the target WSTK must be specified using the `--serialno <J-Link serial number>` option.
4. If the WSTK is in debug mode OUT, the target device must be specified using the `--device <device name>` option. For more information about Simplicity Commander, see [Simplicity Commander Reference Guide](https://docs.silabs.com/simplicity-commander/latest/simplicity-commander-start/).

###### Using an External Tool (heading level 7)

The [Secure Boot examples](#secure-boot) use the **OpenSSL** to sign the image files and certificates. The Windows version of OpenSSL can be downloaded from [https://slproweb.com/products/Win32OpenSSL.html](https://slproweb.com/products/Win32OpenSSL.html). This application note uses OpenSSL Version 3.5.0 (Win64).

```sh
openssl version
```

```sh
OpenSSL 3.5.0 8 Apr 2025 (Library: OpenSSL 3.5.0 8 Apr 2025)
```

The OpenSSL's Command Line Interface (CLI) is invoked by `openssl.exe` in the OpenSSL folder. The location in Windows (Win64) is `C:\Program Files\OpenSSL-Win64\bin`. For ease of use, it is highly recommended to add the path of `openssl.exe` to the system `PATH` in Windows.

###### Using a Platform Example (heading level 7)

Simplicity Studio 5 includes the [SE Manager platform example](https://docs.silabs.com/simplicity-studio-5-users-guide/latest/ss-5-users-guide-getting-started/start-a-project#examples) for key provisioning and Secure Boot enabling. This application note uses platform example of SiSDK v2025.6.1. The console output may be different on other versions.

Refer to the corresponding readme file for details about each SE Manager platform example. This file also includes the procedures to create the project and run the example.

###### Generate Key and Signing (heading level 7)

This section describes how to generate a key to sign an image file or certificate for Secure Boot using Simplicity Commander or using an HSM with Simplicity Commander (Openssl). For production environments, it is strongly recommended to generate keys with an HSM.

###### Using Simplicity Commander (heading level 8)

1. Run the `util genkey` command to generate the ECDSA-P256 Sign Key pair (`sign_key.pem` and `sign_pubkey.pem)` and Public Sign Key token file (`sign_pubkey.txt`). The Simplicity Commander can program the Public Sign Key in token file (`sign_pubkey.txt`) to the [top page of the main flash](#signing-for-ecdsa-p256-sha256-secure-boot).  
   ```sh  
   commander util genkey --type ecc-p256 --privkey sign_key.pem --pubkey sign_pubkey.pem  
   --tokenfile sign_pubkey.txt  
   ```  
   ```sh  
   Generating ECC P256 key pair...  
   Writing private key file in PEM format to sign_key.pem  
   Writing public key file in PEM format to sign_pubkey.pem  
   Writing EC tokens to sign_pubkey.txt...  
   DONE  
   ```  
   > **Note**: The same procedure can apply to generate the bootloader certificate and application certificate key pairs for [Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot).
2. Use the convert command with the Private Key (like sign_key.pem) from step 1 to sign an image file or certificate. Refer to [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) and [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot) for more information about the Simplicity Commander signing process.

###### Using an HSM and Simplicity Commander (heading level 8)

1. You can use HSM to generate the ECDSA-P256 Sign Key pair. The Private Sign Key is securely held in HSM and the Public Sign Key can be exported in a specific format (like `sign_pubkey.pem`).  
   > **Note**: The same procedure can apply to generate the bootloader certificate and application certificate key pairs for [Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot).
2. Use the `util keytotoken` command to convert the Public Sign Key from step 1 to token format (`sign_pubkey.txt`). The Simplicity Commander can program the Public Sign Key in token file (`sign_pubkey.txt`) to the [top page of the main flash](#signing-for-ecdsa-p256-sha256-secure-boot).  
   ```sh  
   commander util keytotoken sign_pubkey.pem --outfile sign_pubkey.txt  
   ```  
   ```sh  
   Writing EC tokens to sign_pubkey.txt...  
   DONE  
   ```
3. Use the `convert` command with `--extsign` option to prepare an unsigned image or certificate for HSM.
4. Use the Private Key from step 1 to generate a signature for the unsigned image or certificate from step 3.
5. Use the `convert` command with the signature from step 4 to generate a signed image or certificate. Refer to [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) and [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot) for more information about the HSM and Simplicity Commander signing process.

> **Note**: The Simplicity Commander v1.11.0 or above supports signature in DER format. The older version of Simplicity Commander can only handle signatures in Raw format.

###### Provision Public Sign Key and Secure Boot Enabling

The Public Sign Key in SE OTP is used to verify the host image signature or certificate during Secure Boot. You should provision this key before setting the Secure Boot enabled flag in SE OTP. On HSE-SVH devices, when anti-tamper functionality is needed, you need to provision the [anti-tamper protection configuration](https://docs.silabs.com/iot-security/latest/efr32-secure-vault-tamper/) with Secure Boot settings.

If you set the [SECURE_BOOT_ANTI_ROLLBACK](#simplicity-commander) option with Secure Boot, the SE will store the version counter (4 bytes) for anti-rollback of GBL (SSB) to SE flash and check the bootloader version during an upgrade and on every boot. The version counter will not roll to 0 if it reaches the maximum value (bootloader cannot be upgraded anymore). The anti-rollback does not prevent flashing an older signed GBL hex image to the device.

The following table describes the anti-rollback protection on signed GBL when `SECURE_BOOT_ANTI_ROLLBACK` is enabled or disabled.

- The GBL handles the anti-rollback protection when upgrading the GBL through the [GBL upgrade image file](#generate-a-gbl-upgrade-image-file) (`.gbl`).
- The SE handles the anti-rollback protection (if `SECURE_BOOT_ANTI_ROLLBACK` enabled) when booting the image.

|**Action**|**SECURE_BOOT_ANTI_ROLLBACK Disable**|**SECURE_BOOT_ANTI_ROLLBACK Enable**|
|---|---|---|
|Use a GBL upgrade image (`.gbl`) file (e.g., OTA)|Gecko Bootloader rejects upgrade if an equal or lower GBL version is detected.|Gecko Bootloader rejects upgrade if an equal or lower GBL version is detected.|
|Flash and boot a firmware image (`.s32`, `.hex`, `.bin`) directly (e.g., Simplicity Commander)|SE accepts the image to flash and boot regardless of the firmware image version.|The SE accepts and boots the firmware image if its version is equal to or higher than the stored version. A lower version will be blocked at boot.|

> **Note**: [Series 2 devices only] The device needs to execute a mass erase (`commander device masserase` or `commander security erasedevice` then reset) before flashing a GBL hex image (`.s37`) to the device if [SECURE_BOOT_PAGE_LOCK_NARROW](#simplicity-commander) or [SECURE_BOOT_PAGE_LOCK_FULL](#simplicity-commander) option in SE OTP is enabled.

For simplicity, the [Secure Boot examples](#secure-boot) in this application note do not enable the following options for Secure Boot.

- `SECURE_BOOT_PAGE_LOCK_NARROW`
- `SECURE_BOOT_PAGE_LOCK_FULL`

> **Note**: `SECURE_BOOT_PAGE_LOCK_NARROW` and `SECURE_BOOT_PAGE_LOCK_FULL` are only available on Series 2 devices.

###### Simplicity Commander (heading level 7)

The following procedures assume the required files are in the same folder.

1. Follow the procedures in [Generate Key and Signing](#generate-key-and-signing) to generate the ECDSA-P256 Sign Key pair (`sign_key.pem` and `sign_pubkey.pem`) and Public Sign Key token file (`sign_pubkey.txt`).
2. Run the `security writekey` command to provision the Public Sign Key (`sign_pubkey.pem`) to the SE OTP slot. The Public Sign Key cannot be changed once written.  
   ```sh  
   commander security writekey --sign sign_pubkey.pem --device EFR32MG21A010F1024 --serialno 440048205  
   ```  
   ```sh  
   Device has serial number 000000000000000014b457fffe045b21  
     
   ================================================================================  
   Please look through any warnings before proceeding.  
   THIS IS A ONE-TIME command, all code to be run on the device must be signed by this key.  
   Type 'continue' and hit enter to proceed or Ctrl-C to abort:  
   ================================================================================  
   continue  
   DONE  
   ```
3. Run the `security readkey` command to verify the Public Sign Key with the Public Sign Key in the token file (`sign_pubkey.txt`).  
   ```sh  
   commander security readkey --sign --device EFR32MG21A010F1024 --serialno 440048205  
   ```  
   ```sh  
   C4AF4AC69AAB9512DB50F7A26AE5B4801183D85417E729A56DA974F4E08A562C  
   DE6019DEA9411332DC1A743372D170B436238A34597C410EA177024DE20FC819  
   DONE  
   ```
4. For Series 2 VSE devices (like EFR32MG22C224F512IM40), run the `flash` command to program the Public Sign Key in the token file (`sign_pubkey.txt`) to the top page of the main flash for [ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot). It is optional on Series 2 HSE and Series 3 devices.  
   ```sh  
   commander flash --tokengroup znet --tokenfile sign_pubkey.txt --device EFR32MG22C224F512IM40 --serialno 440048205  
   ```  
   ```sh  
   Writing 8192 bytes starting at address 0x0007e000  
   Comparing range 0x0007E000 - 0x0007FFFF (8 KiB)  
   Programming range 0x0007E000 - 0x0007FFFF (8 KiB)  
   DONE  
   ```  
   > **Note**: The MCU Series 2 devices (like EFM32PG22C200F512IM40) require Simplicity Commander Version 1.12.2 or above to support the `flash --tokengroup znet` command.
5. Run the `security genconfig` command to generate the `user_configuration.json` file for secure boot.  
   ```sh  
   commander security genconfig --nostore --outfile user_configuration.json --device EFR32MG21A010F1024  
   --serialno 440048205  
   DONE  
   ```  
   |**Name**|**Description**|  
   |---|---|  
   |SECURE_BOOT_ENABLE|If set, verifies the host image on the Cortex-M33 before releasing the Cortex-M33 from reset.|  
   |SECURE_BOOT_VERIFY_CERTIFICATE|If set, requires certificate-based signing of the host image.|  
   |SECURE_BOOT_ANTI_ROLLBACK|If set, prevents secure upgrading to a host image with an equal or lower version than the image that is currently stored in flash.|  
   |SECURE_BOOT_PAGE_LOCK_NARROW|If set, locks flash pages that have been validated by the Secure Boot process to prevent re-flashing by other means than through the SE. Write/erase locks pages from 0 through the page where the Secure Boot host image signature is located, not including the last page if the signature is not on a page boundary.|  
   |SECURE_BOOT_PAGE_LOCK_FULL|If set, locks flash pages that have been validated by the Secure Boot process to prevent re-flashing by other means than through the SE. Write/erase locks pages from 0 through the page where the Secure Boot host image signature is located, including the last page if the signature is not on a page boundary.|  
   > **Notes**: The host image is the firmware in the device's flash starting address. It is usually the GBL.  
   >   
   > SECURE_BOOT_PAGE_LOCK_NARROW and SECURE_BOOT_PAGE_LOCK_FULL are not available in Series 3 devices.
6. Use a text editor to modify the default secure boot settings to the desired configurations used in this application note.  
   ![Secure boot settings](/series2-secure-boot-with-rtsl/0.3/images/sld794-image26.jpg)  
   > **Note**: For devices prior to EFR32xG23, if `SECURE_BOOT_ENABLE` is set to false, the SE ignores the other four options regardless of their values, whereas on EFR32xG23 and future Series 2 devices, this configuration cannot be programmed to the SE OTP.
7. Follow the procedure in [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) to flash a bootloader image for signature based secure boot.  
   OR  
   Follow the procedure in [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot) to flash a bootloader image for certificate based secure boot.
8. Run the `security writeconfig` command to program the secure boot configuration to the SE OTP. You can execute this command once per device.  
   ```sh  
   commander security writeconfig --configfile user_configuration.json --device EFR32MG21A010F1024  
   --serialno 440048205  
   ```  
   ```sh  
   ================================================================================  
     
   THIS IS A ONE-TIME configuration: Please inspect file before confirming: user_configuration.json  
   Type 'continue' and hit enter to proceed or Ctrl-C to abort:  
   ================================================================================  
   continue  
   DONE  
   ```
9. Run the `security readconfig` command to check the secure boot configuration of the device.  
   ```sh  
   commander security readconfig --serialno 440048205  
   ```  
   ![Secure Boot configuration](/series2-secure-boot-with-rtsl/0.3/images/sld794-image27.jpg)  
   > **Note**: Enabling Anti-rollback during development involves updating the firmware version every time you decide to flash a new image, this adds complexity. Therefore, it is recommended to enable Anti-rollback during production.

###### SE Manager Key Provisioning Platform Example (heading level 7)

1. Click the **View Project Documentation** link to open the readme file.  
   ![Key Provisioning Sample Application](/series2-secure-boot-with-rtsl/0.3/images/sld794-image28.jpg)
2. Modify the default Public Sign Key in `public_sign_key[PUB_KEY_SIZE]` array in `app_process.c` to the desired values.  
   ```sh  
   /// Public sign key  
   SL_ALIGN(4) static const uint8_t public_sign_key[PUB_KEY_SIZE] = {   
     0xc4, 0xaf, 0x4a, 0xc6, 0x9a, 0xab, 0x95, 0x12,  
   0xdb, 0x50, 0xf7, 0xa2, 0x6a, 0xe5, 0xb4, 0x80,   
   0x11, 0x83, 0xd8, 0x54, 0x17, 0xe7, 0x29, 0xa5,  
   0x6d, 0xa9, 0x74, 0xf4, 0xe0, 0x8a, 0x56, 0x2c,  
   0xde, 0x60, 0x19, 0xde, 0xa9, 0x41, 0x13, 0x32,  
   0xdc, 0x1a, 0x74, 0x33, 0x72, 0xd1, 0x70, 0xb4,  
   0x36, 0x23, 0x8a, 0x34, 0x59, 0x7c, 0x41, 0x0e,  
   0xa1, 0x77, 0x02, 0x4d, 0xe2, 0x0f, 0xc8, 0x19  
   };  
   ```
3. Modify the default secure boot settings in `init_se_otp_conf()` function in `app_se_manager_key_provisioning.c` to the desired configuration.  
   ```sh  
   // Overwrite secure boot options in SL_SE_OTP_INIT_DEFAULT if necessary.  
   otp_init.enable_secure_boot = true;  
   otp_init.verify_secure_boot_certificate = false;  
   otp_init.enable_anti_rollback = true;  
   otp_init.secure_boot_page_lock_narrow = false;  
   otp_init.secure_boot_page_lock_full = false;  
   ```  
   > **Note**: If `enable_secure_boot` is false, the SE will ignore the other four options regardless of whether they are true or false. The EFR32xG23 and higher Series 2 devices do not allow this setting to program to the SE OTP.
4. Follow the procedures in [Generate an Unsigned Application Image](#generate-an-unsigned-application-image) to generate the unsigned application image if the GBL is present in the device.
5. Build the project and run the application. Follow the procedures in [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) or [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot) if a signed application image is required.
6. Then press **SPACE** to skip the programming of the AES-128 key (HSE devices only). Optional to press **ENTER** to program the hard-coded [GBL Decryption Key](#se-manager-key-provisioning-platform-example-2) to HSE OTP.  
   ```sh  
   SE Manager Key Provisioning Example - Core running at 38000 kHz.  
   . SE manager initialization... SL_STATUS_OK (cycles: 9 time: 0 us)  
     
   . Get current SE firmware version... SL_STATUS_OK (cycles: 3578 time: 94 us)  
   + Current SE firmware version (MSB..LSB): 00010209  
     
   . Read SE OTP configuration... SL_STATUS_COMMAND_IS_INVALID (cycles: 3908 time: 102 us)  
     
   . Press ENTER to program 128-bit AES key in SE OTP or press SPACE to skip.  
     
   . Encrypt 16 bytes plaintext with 128-bit AES OTP key... SL_STATUS_FAIL (cycles: 4627 time: 121 us)  
     
   . Press ENTER to program public sign key in SE OTP or press SPACE to skip.  
   ```
7. Press **ENTER** to program the hard-coded Public Sign Key to SE OTP.  
   ```sh  
   + Warning: The public sign key in SE OTP cannot be changed once written!  
   + Press ENTER to confirm or press SPACE to skip if you are not sure.  
   ```
8. Press **ENTER** to confirm the operation. The SE returns `SL_STATUS_INVALID_PARAMETER` if the Public Sign Key is present in SE OTP.  
   ```sh  
   . Initialize public sign key... SL_STATUS_OK (cycles: 56052 time: 1475 us)  
     
   . Get public sign key... SL_STATUS_OK (cycles: 8450 time: 222 us)  
   + The public sign key (64 bytes):  
   C4 AF 4A C6 9A AB 95 12 DB 50 F7 A2 6A E5 B4 80  
   11 83 D8 54 17 E7 29 A5 6D A9 74 F4 E0 8A 56 2C  
   DE 60 19 DE A9 41 13 32 DC 1A 74 33 72 D1 70 B4  
   36 23 8A 34 59 7C 41 0E A1 77 02 4D E2 0F C8 19  
     
   . Press ENTER to program public command key in SE OTP or press SPACE to skip.  
   ```  
   ```sh  
   . Initialize public sign key... SL_STATUS_INVALID_PARAMETER (cycles: 4375 time: 115 us)  
     
   . Get public sign key... SL_STATUS_OK (cycles: 8435 time: 221 us)  
   + The public sign key (64 bytes):  
   C4 AF 4A C6 9A AB 95 12 DB 50 F7 A2 6A E5 B4 80  
   11 83 D8 54 17 E7 29 A5 6D A9 74 F4 E0 8A 56 2C  
   DE 60 19 DE A9 41 13 32 DC 1A 74 33 72 D1 70 B4  
   36 23 8A 34 59 7C 41 0E A1 77 02 4D E2 0F C8 19  
     
   . Press ENTER to program public command key in SE OTP or press SPACE to skip.  
   ```
9. Press **SPACE** to skip the programming of the Public Command Key. Optional to press **ENTER** to program the hard-coded Public Command Key to SE OTP.  
   ```sh  
   . Get public command key... SL_STATUS_FAIL (cycles: 4126 time: 108 us)  
     
   . Press ENTER to initialize SE OTP for secure boot configuration or press SPACE to skip.  
   ```
10. Press **ENTER** to program the secure boot configuration.  
    ```sh  
    + Warning: The SE OTP configuration cannot be changed once written!  
    + Press ENTER to confirm or press SPACE to skip if you are not sure.  
    ```
11. Press **ENTER** to confirm the operation. The SE returns `SL_STATUS_COMMAND_IS_INVALID` if an invalid setting from step 2 or the secure boot configuration has been programmed in SE OTP.  
    ```sh  
    . Initialize SE OTP... SL_STATUS_OK (cycles: 267256 time: 7033 us)  
    + Read SE OTP configuration... SL_STATUS_OK (cycles: 6865 time: 180 us)  
    + Secure boot	                 : Enabled  
    + Secure boot verify certificate : Disabled  
    + Secure boot anti-rollback	     : Enabled  
    + Secure boot page lock narrow	 : Disabled  
    + Secure boot page lock full	 : Disabled  
      
    . SE manager deinitialization... SL_STATUS_OK (cycles: 5 time: 0 us)  
    ```  
    ```sh  
    . Initialize SE OTP... SL_STATUS_COMMAND_IS_INVALID (cycles: 3989 time: 104 us)  
      
    . SE manager deinitialization... SL_STATUS_OK (cycles: 5 time: 0 us)  
    ```

###### Simplicity Studio (heading level 7)

The security operations are performed in the Security Settings of Simplicity Studio. This application note uses Simplicity Studio v5.2.3.1. The procedures and pictures may be different for the other versions of Simplicity Studio 5.

1. Right-click the selected debug adapter **RB (ID:J-Link serial number)** to display the context menu.  
   ![Debug Adapters Context Menu](/series2-secure-boot-with-rtsl/0.3/images/sld794-image29.jpg)
2. Click **Device configuration...** to open the **Configuration of device: J-Link Silicon Labs (serial number)** dialog box. Click the **Security Settings** tab to get the selected device configuration.  
   ![Configuration on Selected Device](/series2-secure-boot-with-rtsl/0.3/images/sld794-image30.png)
3. Click [**Start Provisioning Wizard…**] in the upper right corner to display the **Secure Initialization** dialog box. Checking the **Enable Version Rollback Prevention of Host Image** option is recommended. The **Verify intermediate certificate before secure boot** option is for [Certificate-based Secure Boot](#signing-for-certificate-based-secure-boot).  
   ![Secure Initialization Dialog Box](/series2-secure-boot-with-rtsl/0.3/images/sld794-image31.png)  
   > **Note**: The [SECURE_BOOT_PAGE_LOCK_NARROW and SECURE_BOOT_PAGE_LOCK_FULL](#simplicity-commander) options are not yet available in Simplicity Studio.
4. Click [**Next >**]. The **Security Keys** dialog box is displayed.  
   ![Security Keys Dialog Box](/series2-secure-boot-with-rtsl/0.3/images/sld794-image32.png)
5. Checking the **Enable Writing Sign Key** checkbox will automatically enable Secure Boot. The following **Secure Boot Warning** is displayed. Click [**Yes**] to confirm.  
   ![Secure Boot Warning](/series2-secure-boot-with-rtsl/0.3/images/sld794-image33.png)
6. Open the [Public Sign Key](#generate-key-and-signing) token file (`sign_pubkey.txt`).  
   ```sh  
   MFG_SIGNED_BOOTLOADER_KEY_X : 997011ED1708580BD4A6B7F8AD6EE19B0B8722611FB76A3A5702D5141180E101  
   MFG_SIGNED_BOOTLOADER_KEY_Y : 0AC8673C8ACC26EE2B534C004F4A4B7EBBC23D04506DD66E3EF0DDC81E3CA55E  
   ```
7. Copy Public Sign Key (X-point `9970...` first, then Y-point `0AC8...`) to **Key:** box under **Sign Key:**.  
   ![Enter Public Sign Key](/series2-secure-boot-with-rtsl/0.3/images/sld794-image34.jpg)
8. Click [**Next >**]. The **Secure Locks** dialog box is displayed. When Secure Boot is enabled, the **Debug Locks** are not set by default. Refer to [Series 2 and Series 3 Secure Debug](https://docs.silabs.com/iot-security/latest/series2-secure-debug/) for more information about these locks.  
   ![Security Locks Dialog Box](/series2-secure-boot-with-rtsl/0.3/images/sld794-image35.png)
9. Click [**Next >**] to display the **Summary** dialog box.  
   ![Summary Dialog Box](/series2-secure-boot-with-rtsl/0.3/images/sld794-image36.jpg)
10. If the information displayed is correct, click [**Provision**]. Click [**Yes**] to confirm. The Public Sign Key and Secure Boot configuration cannot be changed once written.

![One Time Device Provisioning Window](/series2-secure-boot-with-rtsl/0.3/images/sld794-image37.jpg)

1. The **Summary** dialog box displays the **Provisioning Status**.

![Provisioning Status](/series2-secure-boot-with-rtsl/0.3/images/sld794-image38.jpg)

1. Click [**Done**] to exit the provisioning process. The device configuration is updated.

###### Provision GBL Decryption Key

The GBL Decryption Key is used to decrypt the [GBL upgrade image file](#generate-a-gbl-upgrade-image-file) payloads during firmware upgrade. You should provision this key before enabling the **Require encrypted firmware upgrade files** option in AppBuilder plugin and [3.4.1.2 Bootloader-core Software Component](#bootloader-core-software-component).

The following figures and table show the available options, two for VSE and three for HSE, for selecting the GBL Decryption Key used to decrypt the GBL upgrade image (bootloader, SE, or application).

![App Builder Options](/series2-secure-boot-with-rtsl/0.3/images/sld794-image39.jpg)

![Bootloader Software Component Options](/series2-secure-boot-with-rtsl/0.3/images/sld794-image40.jpg)

|**Option for GBL Decryption Key Selection**|**GBL Decryption Key Storage**|
|---|---|
|1. Use symmetric key stored in Secure Element storage (HSE devices only and GSDK ≥ v3.0).|The 128-bit symmetric key stored in HSE OTP is used for GBL upgrade image file decryption.|
|2. Use symmetric key stored in Application Properties Struct (GSDK ≥ v4.1).|The 128-bit symmetric key stored in the GBL Application Properties Struct is used for GBL upgrade image file decryption. The key is stored in the Secure flash if TrustZone is implemented.|
|3. Default storage if none of the above options are selected.|The 128-bit symmetric key stored on the top page of the main flash is used for GBL upgrade image file decryption. The key is stored in the Non-secure flash if TrustZone is implemented.|

**Note**:

- Option 2 requires `ApplicationProperties_t` struct v1.2 or higher (defined in `application_properties.h` in the Windows folder `C:\Users\<PC USER NAME>\SimplicityStudio\SDKs\gecko_sdk\platform\bootloader\api`) in GSDK v4.1 or higher to store the GBL Decryption Key.

```c
/// Major version number of the ApplicationProperties_t struct #define APPLICATION_PROPERTIES_VERSION_MAJOR (1UL)
/// Minor version number of the ApplicationProperties_t struct #define APPLICATION_PROPERTIES_VERSION_MINOR (2UL)
```

```sh
/// Application Properties struct typedef struct {
/// @brief Magic value indicating this is an ApplicationProperties_t struct.
/// Must equal @ref APPLICATION_PROPERTIES_MAGIC uint8_t magic[16];
/// Version number of this struct uint32_t structVersion;
/// Type of signature this application is signed with uint32_t signatureType;
/// Location of the signature. Typically points to the end of the application uint32_t signatureLocation;
/// Information about the application ApplicationData_t app;
/// Pointer to information about the certificate ApplicationCertificate_t *cert;
/// Pointer to Long Token Data Section uint8_t *longTokenSectionAddress;
/// Parser Decryption Key const uint8_t decryptKey[16];
} ApplicationProperties_t;
```

- Option 2 must be implemented before signing the GBL image for [ECDSA-P256-SHA256](#signing-for-ecdsa-p256-sha256-secure-boot) or [certificate-based](#signing-for-certificate-based-secure-boot) Secure Boot.
- The options for the GBL Decryption Key are mutually exclusive. Either one of the two (VSE) or three (HSE) key storages will be selected for decryption.
- From a security point of view, it is highly recommended to use or upgrade to option 1 for HSE devices and option 2 for VSE devices.
- If the GBL Decryption Key in the selected option is compromised, the simple way is to upgrade the GBL to option 2 (if the existing option is 1 or 3) with the new GBL Decryption Key.

###### Simplicity Commander (heading level 7)

The following procedures describe how to program the GBL Decryption Key for the options below. All procedures assume the required files are in the same folder.

- Use symmetric key stored in Secure Element storage (HSE devices only and GSDK ≥ v3.0)
- Use symmetric key stored in Application Properties Struct (GSDK ≥ v4.1)
- Default Storage on the Top Page of the Main Flash

1. Generate a 128-bit Symmetric Key.  
   Run the `util genkey` command to generate the token file for the GBL Decryption Key.  
   ```sh  
   commander util genkey --type aes-ccm --outfile aes_key.txt  
   ```  
   ```sh  
   Using Windows' Cryptographic random number generator  
   DONE  
   ```
2. (**Use symmetric key stored in Secure Element storage**) Run the `security writekey` command to provision the GBL Decryption Key to the SE OTP slot. The GBL Decryption Key cannot be changed once written.  
   ```sh  
   commander security writekey --decrypt aes_key.txt --device EFR32MG21A010F1024 --serialno 440030580  
   ```  
   ```sh  
   Device has serial number 0000000000000000000d6ffffead3d94  
     
   ================================================================================  
   Please look through any warnings before proceeding.  
   THIS IS A ONE-TIME command, any encrypting of GBL files must be done with this key.  
   Type 'continue' and hit enter to proceed or Ctrl-C to abort:  
   ================================================================================  
   continue  
   DONE  
   ```  
   > **Note**: It cannot read back the GBL Decryption Key from the HSE OTP.
3. (**Use symmetric key stored in Application Properties Struct**) Run the `convert` command to program the GBL Decryption Key to the `Application Properties Struct` of the GBL.  
   ```sh  
   commander convert bootloader-uart-xmodem.s37 --aeskey aes_key.txt --outfile bootloader-uart-xmodem.s37  
   ```  
   ```sh  
   Parsing file bootloader-uart-xmodem.s37...  
   Writing to bootloader-uart-xmodem.s37...  
   Overwriting file: bootloader-uart-xmodem.s37...  
   DONE  
   ```  
   **Notes**:  
   - The `--aeskey` option in the `convert` command requires **Simplicity Commander v1.12.3 or above**.  
   - The GBL Decryption Key can only be added to the GBL with `Application Properties Struct` v1.2 or higher.
4. (**Default Storage on the Top Page of the Main Flash**) Run the `flash` command to program the GBL Decryption Key in the token file to the top page of the main flash.  
   ```sh  
   commander flash --tokengroup znet --tokenfile aes_key.txt --device EFR32MG21A010F1024 --serialno 440030580  
   ```  
   ```sh  
   Writing 8192 bytes starting at address 0x000fe000  
   Comparing range 0x000FE000 - 0x000FFFFF (8 KB)  
   Programming range 0x000FE000 - 0x000FFFFF (8 KB)  
   DONE  
   ```  
   > **Note**: The MCU Series 2 VSE devices (like EFM32PG22C200F512IM40) require Simplicity Commander Version 1.12.2 or above to support the flash --tokengroup znet command.

###### SE Manager Key Provisioning Platform Example (heading level 7)

This example only applies to [option 1](#provision-gbl-decryption-key) for Series 2 and Series 3 HSE devices. Click the **View Project Documentation** link to open the readme file.

![Key Provisioning Sample Application](/series2-secure-boot-with-rtsl/0.3/images/sld794-image28.jpg)

1. Modify the default GBL Decryption Key in the `aes_key[16]` array in `app_process.c` to the desired values.  
   ```sh  
   /// 128-bit AES key  
   SL_ALIGN(4) static const uint8_t aes_key[16] = {   
   0x81, 0xa5, 0xe2, 0x1f, 0xa1, 0x52, 0x86, 0xf1,   
   0xdf, 0x44, 0x5c, 0x2c, 0xc1, 0x20, 0xfa, 0x3f  
   };  
   ```
2. Modify the `ciphertext[16]` array in `app_process.c` to the expected ciphertext for AES ECB on 16 bytes zero plaintext to verify the GBL Decryption Key in step 1.  
   ```sh  
   /// Ciphertext to verify 128-bit AES key static const uint8_t ciphertext[16] = {  
   0x66, 0xd2, 0x0f, 0x99, 0x65, 0x3e, 0xa8, 0xd0, 0x83, 0x05, 0xa6, 0x39, 0xd4, 0x4e, 0x98, 0xa6  
   };  
   ```
3. Follow the procedures in [Generate an Unsigned Application Image](#generate-an-unsigned-application-image) to generate the unsigned application image if the GBL is present in the device.
4. Build the project and run the application. Follow the procedures in [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) or [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot) if a signed application image is required.
5. Then press ENTER to program the hard-coded GBL Decryption Key to HSE OTP.  
   ```sh  
   SE Manager Key Provisioning Example - Core running at 38000 kHz.  
   . SE manager initialization... SL_STATUS_OK (cycles: 9 time: 0 us)  
     
   . Get current SE firmware version... SL_STATUS_OK (cycles: 3578 time: 94 us)  
   + Current SE firmware version (MSB..LSB): 00010209  
     
   . Read SE OTP configuration... SL_STATUS_COMMAND_IS_INVALID (cycles: 3908 time: 102 us)  
     
   . Press ENTER to program 128-bit AES key in SE OTP or press SPACE to skip.  
   + Warning: The 128-bit AES key in SE OTP cannot be changed once written!  
   + Press ENTER to confirm or press SPACE to skip if you are not sure.  
   ```
6. Press **ENTER** to confirm the operation. The program either returns `SL_STATUS_OK` or `SL_STATUS_INVALID_PARAMETER` (already present) and performs AES ECB encryption to verify the GBL Decryption Key in HSE OTP.  
   ```sh  
   . Initialize 128-bit AES key... SL_STATUS_OK (cycles: 39059 time: 1027 us)  
     
   . Encrypt 16 bytes plaintext with 128-bit AES OTP key... SL_STATUS_OK (cycles: 11013 time: 289 us)  
   + Compare encrypted message with expected ciphertext... OK  
     
   . Press ENTER to program public sign key in SE OTP or press SPACE to skip.  
   ```  
   ```sh  
   . Initialize 128-bit AES key... SL_STATUS_INVALID_PARAMETER (cycles: 4474 time: 117 us)  
     
   . Encrypt 16 bytes plaintext with 128-bit AES OTP key... SL_STATUS_OK (cycles: 11001 time: 289 us)  
   + Compare encrypted message with expected ciphertext... OK  
     
   . Press ENTER to program public sign key in SE OTP or press SPACE to skip.  
   ```
7. Press **SPACE** to skip the programming of the Public Sign Key.  
   ```sh  
   . Get public sign key... SL_STATUS_FAIL (cycles: 4126 time: 108 us)  
     
   . Press ENTER to program public command key in SE OTP or press SPACE to skip.  
   ```
8. Press **SPACE** to skip the programming of the Public Command Key.  
   ```sh  
   . Get public command key... SL_STATUS_FAIL (cycles: 4126 time: 108 us)  
     
   . Press ENTER to initialize SE OTP for secure boot configuration or press SPACE to skip.  
   ```
9. Press **SPACE** to skip the programming of the secure boot configuration.  
   ```sh  
   . SE manager deinitialization... SL_STATUS_OK (cycles: 10 time: 0 us)  
   ```

###### Secure Boot

You should usually not enable Secure Boot during the development phase to avoid a clash on [debugging](04-debugging-on-secure-boot-enabled-device#debugging-on-secure-boot-enabled-device). The Secure Boot feature is enabled near firmware release and uses the following sections to validate the configuration and system functionality.

###### Generate an Unsigned GBL Image (heading level 7)

There are two ways to configure the application firmware through a GBL project.

1. Use [AppBuilder](#appbuilder) (`.isc file`) in **GSDK v3.2 and lower**.
2. Use [Bootloader-core software component](#bootloader-core-software-component) (`.slcp` file) in **GSDK v4.0 and higher**, and **SiSDK v2024.6.0 and higher**.

The following notes apply to the AppBuilder and Bootloader-core software component.

- Enabling the **Allow use of public key from manufacturing token storage** option is mandatory on VSE devices (cannot be disabled in AppBuilder and is discarded in Bootloader-core software component) for [ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot).
- The HSE device ignores this default enabled option if the Public Sign Key has been provisioned in OTP.
- When the **Enable application rollback protection** option (GSDK ≥ v3.0) is enabled, the GBL stores an application version counter at the end of the bootloader space in flash. The GBL checks this version during upgrades and at every boot, but it does not prevent flashing an older image onto the device.
- The **Minimum application version allowed** option (GSDK ≥ v3.0) sets the lowest application version that can boot. This option maintains the version counter, which resets to 0 after upgrading the GBL.
- The **Enable application rollback protection** option is not applicable if the [SECURE_BOOT_PAGE_LOCK_FULL](#simplicity-commander) in SE OTP is enabled. See section "_Secure Boot with Application Rollback Protection_" in [UG266](https://www.silabs.com/documents/public/user-guides/ug266-gecko-bootloader-user-guide.pdf) (for GSDK v3.2 and lower), [Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/), or [Silicon Labs Gecko Bootloader User’s Guide for Series 3 and Higher](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-series3-and-higher/) for details about the application rollback protection.
- The GBL size and starting address are device-dependent. For more information about the bootloader size and starting address on Series 2, see section "Memory Space For Bootloading" in [Bootloader Fundamentals](https://docs.silabs.com/mcu-bootloader/latest/bootloader-fundamentals/).

Refer to [Generate a GBL Upgrade Image File](#generate-a-gbl-upgrade-image-file) to create GBL upgrade image file for the **Require signed firmware upgrade files** and **Require encrypted firmware upgrade files** options. For simplicity, the Secure Boot examples in this application note do not enable these options. Refer to [UG266](https://www.silabs.com/documents/public/user-guides/ug266-gecko-bootloader-user-guide.pdf), [Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/), or [Silicon Labs Gecko Bootloader User’s Guide for Series 3 and Higher](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-series3-and-higher/) for information about these options.

The following sections describe how to build the unsigned GBL image from the **UART XMODEM Bootloader** (GSDK < v4.1) or **Bootloader - NCP UART XMODEM** (GSDK ≥ v4.1).

![Bootloader options](/series2-secure-boot-with-rtsl/0.3/images/sld794-image41.jpg)

###### AppBuilder (heading level 8)

This application note uses UART XMODEM Bootloader example v1.12.0 in GSDK v3.2.3. The procedures and pictures may be different for the other versions of this example.

1. Create a UART XMODEM Bootloader project.
2. The **Plugins** tab in AppBuilder (bootloader-uart-xmodem.isc) shows the default configurations for the UART XMODEM Bootloader example.
3. Use **Bootloader Core, provides API: core** in the **Plugins** tab to set up the application firmware configurations.  
   a. This application note uses the configuration below for [ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot).  
   ```c  
   ![ECDSA-P256-SHA256 Secure Boot configuration](./resources/sld794-image42.jpeg)  
   ```  
   b. This application note uses the configuration below for [Certificate-based Secure Boot](#signing-for-certificate-based-secure-boot).  
   ```c  
   ![Certificate-based Secure Boot configuration](./resources/sld794-image43.jpeg)  
   ```
4. Enter a higher version number (default is 0) to the macro **BOOTLOADER_VERSION_MAIN_CUSTOMER** → **Value** in the **Additional Macros** field on the **Other** tab for [anti-rollback protection](#provision-public-sign-key-and-secure-boot-enabling) of GBL.  
   ![Bootloader version macro](/series2-secure-boot-with-rtsl/0.3/images/sld794-image44.png)
5. The default setting of GBL will overwrite the existing application image when upgrading the GBL or SE. It forces to update the application image even without changes on the firmware. Use the AppBuilder settings below to keep the existing application image when upgrading the GBL or SE.  
   a. Enter the required application image size to the macro **BTL_APP_SPACE_SIZE** → **Value** in the **Additional Macros** field on the **Other** tab. Check the **-D?** checkbox to add this definition to the project.  
   ```c  
    This application note uses 507904 (496 kB) to replace the default value of ((FLASH_BASE + FLASH_SIZE) - BTL_APPLICATION_BASE).  
     
    ![Bootloader application size macro](./resources/sld794-image45.jpeg)  
   ```  
   b. The **Base address of bootloader upgrade image** ≥ (**BTL_APP_SPACE_SIZE** + size of the GBL). The example in this application note uses EFR32MG21A010F1024:  
   ```c  
    Base address of bootloader upgrade image = 507904 (496 kB) + 16384 (16 kB for GBL) = 524288 (512 kB)  
     
    ![Base address of bootlader upgrade image](./resources/sld794-image46.png)  
     
    > **Note**: The default value of Base address of bootloader upgrade image is 32768 (32 kB).  
   ```  
   c. The (**Base address of bootloader upgrade image** + size of the GBL or SE + upgrade file overhead) ≤ the available size of the device main flash for application use (see project linker file for details).  
   ```c  
    The example in this application note uses EFR32MG21A010F1024:  
     
    512 kB (Base address of bootloader upgrade image) + 16 kB (GBL) or 48 kB (SE) + overhead bytes \< 1024 kB (size of main flash)  
     
    For more information about the size of the GBL and SE, see section *Storage Space Size Configuration* in [UG266](https://www.silabs.com/documents/public/user-guides/ug266-gecko-bootloader-user-guide.pdf).  
     
    > **Note**: It requires GBL v1.11.0 or above to support this feature.  
   ```
6. Click [**Generate**] in the right upper corner.
7. In the **Generation Successful** dialog, click [**OK**].
8. Build the project to generate the unsigned GBL image file (`bootloader-uart-xmodem.s37`).
9. **(Optional)** Run the `util appinfo` command to check all available information (application properties) in an unsigned GBL image. The `App version` is the GBL version for the [SECURE_BOOT_ANTI_ROLLBACK](#simplicity-commander) option.

```sh
commander util appinfo bootloader-uart-xmodem.s37
```

```sh
Parsing file bootloader-uart-xmodem.s37...
Found application properties in image.
Application properties info:
Application properties location : 0x00002b1c
Signature location	            : 0x00002d08
Signature type	                : No signature
Long token section address	    : Not set (0x00000000)

Application data info:
For Series 2 devices: If rollback prevention is enabled in the OTP configuration, the device will not boot if the device has seen an application with a higher version number.
App type	                    : Bootloader (APPLICATION_TYPE_BOOTLOADER)
App version	                    : 0x010c0000
Product ID	                    : Not set (0x00000000000000000000000000000000)

No certificate found in image.
For Series 2 devices: If the configuration flag SECURE_BOOT_VERIFY_CERTIFICATE is set or a device has previously seen certificate based signing, it will not accept direct signing.
DONE
```

###### Bootloader-core Software Component (heading level 8)

This application note uses UART XMODEM Bootloader example v2.0.0 in GSDK v4.0. The procedures and pictures may be different for the other versions of this example.

1. Create a UART XMODEM Bootloader project.
2. Checking the **Installed Components** under the **SOFTWARE COMPONENTS** tab shows the list of installed components (`bootloader-uart-xmodem.slcp`) in the UART XMODEM Bootloader example.
3. Click [**Configure**] in the **Bootloader-core** component to open the **Bootloader Core Configuration**.  
   ![Bootloader-core component](/series2-secure-boot-with-rtsl/0.3/images/sld794-image47.jpg)  
   > **Note:** Install "Bootloader Core (Series-3)" for series 3 devices.
4. Use **Bootloader Core Configuration** in **Bootloader-core** to set up the application firmware configurations.
5. This application note uses the configuration below for [ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot).  
   ![Bootloader Core configuration for ECDSA-P256-SHA256 Secure Boot](/series2-secure-boot-with-rtsl/0.3/images/sld794-image48.png)
6. This application note uses the configuration below for [Certificate-based Secure Boot](#signing-for-certificate-based-secure-boot).  
   ![Bootloader Core configuration for Certificate-based Secure Boot](/series2-secure-boot-with-rtsl/0.3/images/sld794-image49.png)
7. [For series 2 devices only] Enter a higher version number (default is 0) to **BOOTLOADER VERSION MAIN CUSTOMER** for [anti-rollback protection](#provision-public-sign-key-and-secure-boot-enabling) of GBL.  
   ![screenshot](/series2-secure-boot-with-rtsl/0.3/images/sld794-image50.jpg)
8. [For series 2 devices only] The default setting of GBL will overwrite the existing application image when upgrading the GBL or SE. It forces to update the application image even without changes on the firmware. Use the Bootloader-core settings below to keep the existing application image when upgrading the GBL or SE.  
   a. Enter the required application image size to the **Enter Bootloader App Space Size** dialog box after enabling the **Use custom Bootloader Application Size** option.  
   ```c  
    This application note uses 507904 (496 kB) to replace the default value of 0.  
     
    ![screenshot](./resources/sld794-image51.jpeg)  
   ```  
   b. The **Base address of bootloader upgrade image** ≥ (**Enter Bootloader App Space Size** + size of the GBL). The example in this application note uses EFR32MG21A010F1024:  
   ```c  
    Base address of bootloader upgrade image = 507904 (496 kB) + 16384 (16 kB for GBL) = 524288 or 0x80000 (512 kB)  
     
    ![screenshot](./resources/sld794-image52.png)  
     
    > **Note**: The default value of Base address of bootloader upgrade image is 32768 or 0x8000 (32 kB).  
   ```  
   c. The (**Base address of bootloader upgrade image** + size of the GBL or SE + upgrade file overhead) ≤ the available size of the device main flash for application use (see project linker file for details).  
   ```c  
    The example in this application note uses EFR32MG21A010F1024:  
     
    512 kB (Base address of bootloader upgrade image) + 16 kB (GBL) or 48 kB (SE) + overhead bytes \< 1024 kB (size of main flash)  
     
    For more information about the size of the GBL and SE, see section *Storage Space Size Configuration* in [Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/).  
   ```
9. Click [**X**] in the right upper corner to exit the **Bootloader Core Configuration**.
10. Build the project to generate the unsigned GBL image file (bootloader-uart-xmodem.s37).
11. **(Optional)** Run the `convert` command to program the GBL Decryption Key to the Application Properties Struct if this [GBL Decryption Key option](#provision-gbl-decryption-key) in GBL (GSDK ≥ v4.1) is selected.

```sh
commander convert bootloader-uart-xmodem.s37 --aeskey aes_key.txt --outfile bootloader-uart-xmodem.s37
```

```sh
Parsing file bootloader-uart-xmodem.s37...
Writing to bootloader-uart-xmodem.s37...
Overwriting file: bootloader-uart-xmodem.s37...
DONE
```

1. **(Optional)** Run the `util appinfo` command to check all available information (application properties) in an unsigned GBL image. The App version is the GBL version for the [SECURE_BOOT_ANTI_ROLLBACK](#simplicity-commander) option.

```sh
commander util appinfo bootloader-uart-xmodem.s37
```

```sh
Parsing file bootloader-uart-xmodem.s37...
Found application properties in image.
Application properties info:
Application properties location : 0x00002b30
Signature location	            : 0x00002c44
Signature type	                : No signature
Long token section address	    : Not set (0x00000000)

Application data info:
For Series 2 devices: If rollback prevention is enabled in the OTP configuration, the device will not boot if the device has seen an application with a higher version number.
App type	                    : Bootloader (APPLICATION_TYPE_BOOTLOADER)
App version	                    : 0x02000000
Product ID	                    : Not set (0x00000000000000000000000000000000)

No certificate found in image.
For Series 2 devices: If the configuration flag SECURE_BOOT_VERIFY_CERTIFICATE is set or a device has previously seen certificate based signing, it will not accept direct signing.
DONE
```

> **Note**: For TrustZone-aware bootloaders, the unsigned GBL image is the combined image of Secure and Non-secure bootloaders. The **Bootloader-core** component is installed in the Secure bootloader.

###### Generate an Unsigned Application Image (heading level 7)

This section describes how to generate an unsigned application image for the GBL.

1. For Series 2 devices, the application image should be placed on the main flash page after the GBL. For more information about the application starting address, see section _Memory Space For Bootloading_ in [Bootloader Fundamentals](https://docs.silabs.com/mcu-bootloader/latest/bootloader-fundamentals/).
2. For Series 3 devices, the application image should be placed in the code region 1 next to the bootloader which is in region 0, see section _External Flash Architecture in Series 3 Devices_ in [Platform Memory Model](https://docs.silabs.com/gecko-platform/latest/platform-memory-model/).
3. **(Simplicity Studio 5)** You can use the **Bootloader Application Interface** component to set up the start address of the application image. This application note uses **Platform - Blink Bare-metal** example in GSDK v3.2.3. The procedures and pictures may be different on other versions of the GSDK. The following steps can apply to other platform examples in GSDK and SiSDK.  
   a. Create a **Platform - Blink Bare-metal** project.  
   b. The **Software Components** tab shows the list of available components (`blink_baremetal.slcp`) that you can install in the project.  
   c. Select **Platform > Bootloader > Bootloader Application Interface**.  
   d. Click [**Install**].  
   ![Bootloader Application Interface](/series2-secure-boot-with-rtsl/0.3/images/sld794-image53.jpg)  
   > **Note**: For the wireless protocol stack example, the Bootloader Application Interface component is already present in the project.
4. The application image should contain an `ApplicationProperties_t` struct (defined in `application_properties.h` in the Windows folder below) declaring the application version, capabilities, and other metadata.  
   For GSDK v3.2 and lower: `C:\SiliconLabs\SimplicityStudio\v5\developer\sdks\gecko_sdk_suite\<GSDK VERSION>\platform\bootloader\api`  
   For SiSDK v2024.6.0 and higher, or GSDK v4.0 and higher: `C:\Users\<PC USER NAME>\SimplicityStudio\SDKs\gecko_sdk\platform\bootloader\api`  
   Below is an example source file `app_properties.c` with `ApplicationProperties_t` struct for Secure Boot on GSDK v3.2 and lower.  
   ```sh  
   #include <stddef.h>  
   #include "application_properties.h"  
     
   const ApplicationProperties_t sl_app_properties = {  
   .magic = APPLICATION_PROPERTIES_MAGIC,  
   .structVersion = APPLICATION_PROPERTIES_VERSION,  
   .signatureType = APPLICATION_SIGNATURE_NONE,  
   .signatureLocation = 0,  
   .app = {  
       .type = APPLICATION_TYPE_MCU,  
       .version = 1UL,  
       .capabilities = 0UL,  
       .productId = {0U},  
   },  
   };  
   ```  
   The `signatureType` and `signatureLocation` are filled by Simplicity Commander when signing the application image using the `convert` command.
5. The following table describes how to add the `app_properties.c` file in step 4 to **Platform - Blink Bare-metal** project. For the wireless protocol stack example, the `app_properties.c` file with `ApplicationProperties_t` struct is already present in the project.  
   **App properties for older Simplicity Studio and GSDK versions**  
   |**Simplicity Studio 4 & Simplicity Studio 5 with GSDK v3.2 and lower**|**Simplicity Studio 5 with GSDK v4.0 and higher**|  
   |---|---|  
   |Manually added|Automatically added after installing the Bootloader Application Interface component in step 3 to the project.|  
   > **Note**: Refer to the [Knowledge Article](https://community.silabs.com/s/article/what-are-the-steps-to-add-application-properties-to-a-application-project-x?language=en_US) in Silicon Labs Community to add `app_properties.c` to the project in Simplicity Studio 4.
6. **(Simplicity Studio 4 & Simplicity Studio 5 with GSDK v3.2 and lower)** Enter a higher version number to `.version` in `app_properties.c` for [anti-rollback protection](#generate-an-unsigned-gbl-image) (if enabled) of the application.
7. **(Simplicity Studio 5 with GSDK v4.0 and higher)** Click [**Configure**] in the **App Properties** component under **Platform** > **Bootloader** to open the **App Properties** configuration. The example below uses GSDK v4.0. The procedures and pictures may be different on other versions of the GSDK.  
   ![App Properties component](/series2-secure-boot-with-rtsl/0.3/images/sld794-image54.jpg)
8. Enter a higher version number to **Version number for this application** dialog box in **App Properties settings** for [anti-rollback protection](#generate-an-unsigned-gbl-image) (if enabled) of the application.  
   ![App properties version number dialog box](/series2-secure-boot-with-rtsl/0.3/images/sld794-image55.png)  
   > **Note**: The `app_properties.c` is in the Windows folder below.  
   >   
   > `C:\Users\<PC USER NAME>\SimplicityStudio\SDKs\gecko_sdk\platform\bootloader\app_properties`
9. Build the project to generate the unsigned application image file (`blink_baremetal.s37`).
10. **(Optional)** Run the `util appinfo` command to check all available information about `ApplicationProperties_t` struct in an un-signed application image. The App version is for the **Enable application rollback protection** option in the [AppBuilder](#appbuilder or [Bootloader-core software component](#bootloader-core-software-component).

```sh
commander util appinfo blink_baremetal.s37
```

```sh
Parsing file blink_baremetal.s37...
Found application properties in image. Application properties info:
Application properties location : 0x00006198
Signature location	            : Not set (0x00000000)
Signature type	                : No signature
Long token section address	    : Not set (0x00000000)

Application data info:
For Series 2 devices: If rollback prevention is enabled in the OTP configuration, the device will not boot if the device has seen an application with a higher version number.
App type	                    : MCU application (APPLICATION_TYPE_MCU)
App version	                    : 0x00000001
Product ID	                    : Not set (0x00000000000000000000000000000000)

No certificate found in image.
For Series 2 devices: If the configuration flag SECURE_BOOT_VERIFY_CERTIFICATE is set or a device has previously seen certificate based signing, it will not accept direct signing.
DONE
```

**Note:** For the TrustZone-aware applications, the unsigned application image is the combined image of Secure and Non-secure applications. The `ApplicationProperties_t` struct is located in the Secure application.

###### Signing for ECDSA-P256-SHA256 Secure Boot (heading level 7)

The following figure describes the signing and verification for ECDSA-P256-SHA256 Secure Boot.

![ECDSA-P256-SHA256 Sign and Verify](/series2-secure-boot-with-rtsl/0.3/images/sld794-ecdsa-p256-sha256-sign-verify.png)

> **Note**: The bootloader cannot access the Public Sign Key in VSE OTP to verify the application image. Therefore VSE devices need to store a [Public Sign Key copy](#simplicity-commander) on the top page of the main flash (see section _Key Storage_ in [UG266](https://www.silabs.com/documents/public/user-guides/ug266-gecko-bootloader-user-guide.pdf)/[Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/)).

**Public Sign Key usage for VSE and HSE Devices**

|**Device**|**FSB to Verify the Bootloader Image**|**SSB to Verify the Application Image**|
|---|---|---|
|HSE|Use the Public Sign Key in HSE OTP|Use the Public Sign Key in HSE OTP|
|VSE|Use the Public Sign Key in VSE OTP|Use the Public Sign Key on the top page of the main flash|

The HSE device ignores the default enabled [Allow use of public key from manufacturing token storage option](02-secure-boot-process#application-firmware) once the Public Sign Key has been provisioned.

To have better protection on the Public Sign Key, the [certificate-based](#signing-for-certificate-based-secure-boot) Secure Boot is strongly recommended on VSE devices since the SSB does not require accessing the Public Sign Key to verify the application signature.

The following sections provide two methods to sign the bootloader image and application image files. All procedures assume the required files are in the same folder.

1. Using Simplicity Commander
2. Using an HSM and Simplicity Commander

###### Bootloader Image File (heading level 8)

1. If Secure Boot flag is not enabled in SE OTP, follow the procedures in [Simplicity Commander](#simplicity-commander) to set up the ECDSA-P256-SHA256 Secure Boot configuration for the bootloader.
2. Follow the procedures in [AppBuilder](#appbuilder) or [Bootloader-core Software Component](#bootloader-core-software-component) to set up the ECDSA-P256-SHA256 Secure Boot configuration for the user application to generate an unsigned bootloader image.
3. The following steps show two methods to generate a signed bootloader image—either directly with Simplicity Commander (3a) or using an HSM with Commander (3b).  
   a. **(Using Simplicity Commander)** Run the `convert` command with **Private Sign Key** to overwrite the unsigned bootloader image file with the signed bootloader image file (`bootloader-uart-xmodem.s37`).  
   ```c  
    ```sh  
    commander	convert	bootloader-uart-xmodem.s37	--secureboot	--keyfile	sign_key.pem	--verify sign_pubkey.pem  
    --outfile bootloader-uart-xmodem.s37  
    ```  
     
    ```sh  
    Parsing file bootloader-uart-xmodem.s37... Found Application Properties at 0x00002b1c  
    Writing Application Properties signature pointer to point to 0x00002d08  
    Setting signature type in Application Properties: 0x00000001  
    Image SHA256: c53bb8a3fd88a5071bfb71444324bb136b276160318488ff89011bbd269e114e  
    R = AB62F3A52B13D137FBCC6A2176D4D1852E06B6E4E6B2673DC251FC491450CBDA  
    S = 9C7C7AF2624165FD90FB3B114E3FA6FE4F4C5625B15C9F3D50DCB04DD06A7B19  
     
    Verifying signed image...  
    Writing to bootloader-uart-xmodem.s37...  
    Overwriting file: bootloader-uart-xmodem.s37...  
    DONE  
    ```  
   ```  
   b. **(Using an HSM and Simplicity Commander)** Run the `convert` command with `--extsign` option to generate an external signing bootloader image file (`bootloader-uart-xmodem.extsign`).  
   ```c  
    ```sh  
    commander convert bootloader-uart-xmodem.s37 --secureboot --extsign --outfile bootloader-uart-xmodem  
    ```  
     
    ```sh  
    Parsing file bootloader-uart-xmodem.s37... Found Application Properties at 0x00002b1c  
    Writing Application Properties signature pointer to point to 0x00002d08  
    Setting signature type in Application Properties: 0x00000001  
    Writing to bootloader-uart-xmodem.extsign...  
    DONE  
    ```  
     
    Use an HSM containing the Private Sign Key to generate the signature for the external signing bootloader image. This example uses the [OpenSSL](#using-an-external-tool) with the **Private Sign Key** to simulate this process. The signature is in the `bl_signature.der`.  
     
    ```sh  
    openssl dgst -sha256 -binary -sign sign_key.pem -out bl_signature.der bootloader-uart-xmodem.extsign  
    ```  
     
    Run the `convert` command with the **bootloader image signature** to overwrite the unsigned bootloader image file with the signed bootloader image file (`bootloader-uart-xmodem.s37`).  
     
    ```sh  
    commander convert bootloader-uart-xmodem.s37 --secureboot --signature bl_signature.der  
    --verify sign_pubkey.pem --outfile bootloader-uart-xmodem.s37  
    ```  
     
    ```sh  
    Parsing file bootloader-uart-xmodem.s37... Parsing signature file bl_signature.der...  
    R = 0E9FC64F41B55367894908D3ADAC40E8D145E33224C4BAA8151EC3EFD107A154  
    S = F56230AA6484E55270F22A4D164377CA918F66A367656AB6E10CB3F58641CE84  
    Found Application Properties at 0x00002b1c  
    Writing Application Properties signature pointer to point to 0x00002d08  
    Setting signature type in Application Properties: 0x00000001  
     
    Verifying signed image...  
    Writing to bootloader-uart-xmodem.s37...  
    Overwriting file: bootloader-uart-xmodem.s37...  
    DONE  
    ```  
   ```
4. **(Optional)** Run the `util appinfo` command to check all available information about `ApplicationProperties_t` struct in a signed GBL image.  
   ```sh  
   commander util appinfo bootloader-uart-xmodem.s37  
   ```  
   ```sh  
   Parsing file bootloader-uart-xmodem.s37...  
   Found application properties in image.  
   Application properties info:  
   Application properties location : 0x00002b30  
   Signature location	            : 0x00002c44  
   Signature type	                : ECDSA-P256  
   Long token section address	    : Not set (0x00000000)  
     
   Application data info:  
   For Series 2 devices: If rollback prevention is enabled in the OTP configuration, the device will not boot if the device has seen an application with a higher version number.  
   App type	                    : Bootloader (APPLICATION_TYPE_BOOTLOADER)  
   App version	                    : 0x02000000  
   Product ID	                    : Not set (0x00000000000000000000000000000000)  
     
   No certificate found in image.  
   For Series 2 devices: If the configuration flag SECURE_BOOT_VERIFY_CERTIFICATE is set or a device has previously seen certificate based signing, it will not accept direct signing.  
   DONE  
   ```
5. The signed bootloader image file (`.s37`) can be used for [production programming](https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/) or for generating a [GBL upgrade image file](#generate-a-gbl-upgrade-image-file) for bootloader upgrade.
6. Run the `flash` command to program the signed bootloader image (`bootloader-uart-xmodem.s37`) to the device if the device does not have a bootloader.  
   ```sh  
   commander flash bootloader-uart-xmodem.s37 --device EFR32MG21A010F1024 --serialno 440048205  
   ```  
   ```sh  
   Parsing file bootloader-uart-xmodem.s37...  
   Writing 16384 bytes starting at address 0x00000000  
   Comparing range 0x00000000 - 0x00003FFF (16 KiB)  
   Programming range 0x00000000 - 0x00001FFF (8 KiB)  
   Programming range 0x00002000 - 0x00003FFF (8 KiB)  
   DONE  
   ```  
   Simplicity Commander output for Series 3 devices will look as follows:  
   ```sh  
   WARNING: Failed secure boot detected. Issuing a mass erase before flashing to recover the device...  
   Parsing file bootloader-uart-xmodem.s37...  
   Writing 14280 bytes starting at address 0x01000000  
   Erasing range 0x01000000 - 0x01007FFF (1 sector, 32 KB)  
   Programming range 0x01000000 - 0x01000FFF (4 KB)  
   Programming range 0x01001000 - 0x01001FFF (4 KB)  
   Programming range 0x01002000 - 0x01002FFF (4 KB)  
   Programming range 0x01003000 - 0x01003FFF (4 KB)  
   Programming range 0x01004000 - 0x01004FFF (4 KB)  
   Programming range 0x01005000 - 0x01005FFF (4 KB)  
   Programming range 0x01006000 - 0x01006FFF (4 KB)  
   Programming range 0x01007000 - 0x01007FFF (4 KB)  
   JLinkError: Failed to halt CPU.  
   Closing region 0 (this consumes one OTP bit, consider --noclose on development/testing devices)  
   Flashing completed successfully!  
   DONE  
   ```

###### Application Image File (heading level 8)

1. Follow the procedures in [Generate an Unsigned Application Image](#generate-an-unsigned-application-image) to generate an unsigned application image for the bootloader.
2. The following steps show two methods to generate a signed application image, either directly with Simplicity Commander (2a) or using an HSM with Commander and OpenSSL (2b).  
   a. **(Using Simplicity Commander)** Run the `convert` command with **Private Sign Key** to overwrite the unsigned application image file with the signed application image file (`blink_baremetal.s37`).  
   ```c  
    ```sh  
    commander convert blink_baremetal.s37 --secureboot --keyfile sign_key.pem --verify sign_pubkey.pem  
    --outfile blink_baremetal.s37  
    ```  
     
    ```sh  
    Parsing file blink_baremetal.s37...  
    Found Application Properties at 0x000061bc  
    Writing Application Properties signature pointer to point to 0x000064d8  
    Setting signature type in Application Properties: 0x00000001  
    Image SHA256: 8b58ec567126aa1f6baa88afc916581477745aca6f47697ec093512fc30dcc6f  
    R = 056E3AA36BD882B5467D44A56DB7CC1AEE44D45BC9B98FAB05BE2C032573A1F7  
    S = BE1D27CE7877D0BC761C0F02690CC74251EBE3A458474C573C21B3A738A03577  
     
    Verifying signed image...  
    Writing to blink_baremetal.s37... Overwriting file: blink_baremetal.s37...  
    DONE  
    ```  
   ```  
   b. **(Using an HSM and Simplicity Commander)** Run the `convert` command with `--extsign` option to generate an external signing application image file (`blink_baremetal.extsign`).  
   ```c  
    ```sh  
    commander convert blink_baremetal.s37 --secureboot --extsign --outfile blink_baremetal  
    ```  
     
    ```sh  
    Parsing file blink_baremetal.s37...  
    Found Application Properties at 0x00006198  
    Writing Application Properties signature pointer to point to 0x0000643c  
    Setting signature type in Application Properties: 0x00000001  
    Writing to blink_baremetal.extsign...  
    DONE  
    ```  
     
    Use an HSM containing the Private Sign Key to generate the signature for the external signing application image. This example uses the [OpenSSL](#using-an-external-tool) with the **Private Sign Key** to simulate this process. The signature is in the `app_signature.der`.  
     
    ```sh  
    openssl dgst -sha256 -binary -sign sign_key.pem -out app_signature.der blink_baremetal.extsign  
    ```  
     
    Run the `convert` command with the **application image signature** to overwrite the unsigned application image file with the signed application image file. (`blink_baremetal.s37`).  
     
    ```sh  
    commander	convert	blink_baremetal.s37	--secureboot	--signature	app_signature.der	--verify sign_pubkey.pem  
    --outfile blink_baremetal.s37  
    ```  
     
    ```sh  
    Parsing file blink_baremetal.s37...  
    Parsing signature file app_signature.der...  
    R = BD5BDC866CE67DA104B1E7B686C45B7BF96F2643154D37ACC63DACDF69C27E89  
    S = 2DD3BFFAC857A5B0BD8C9B4DDB23D21944D062F8E431D36541B84EF411C1CC92  
    Found Application Properties at 0x000061bc  
    Writing Application Properties signature pointer to point to 0x000064d8  
    Setting signature type in Application Properties: 0x00000001  
     
    Verifying signed image...  
    Writing to blink_baremetal.s37... Overwriting file: blink_baremetal.s37...  
    DONE  
    ```  
   ```
3. **(Optional)** Run the `util appinfo` command to check all available information about `ApplicationProperties_t` struct in a signed application image.  
   ```sh  
   commander util appinfo blink_baremetal.s37  
   ```  
   ```sh  
   Parsing file blink_baremetal.s37...  
   Found application properties in image. Application properties info:  
   Application properties location : 0x000061bc  
   Signature location	            : 0x000064d8  
   Signature type	                : ECDSA-P256  
   Long token section address	    : Not set (0x00000000)  
     
   Application data info:  
   For Series 2 devices: If rollback prevention is enabled in the OTP configuration, the device will not boot if the device has seen an application with a higher version number.  
   App type	                    : MCU application (APPLICATION_TYPE_MCU)  
   App version	                    : 0x00000001  
   Product ID	                    : Not set (0x00000000000000000000000000000000)  
     
   No certificate found in image.  
   For Series 2 devices: If the configuration flag SECURE_BOOT_VERIFY_CERTIFICATE is set or a device has previously seen certificate based signing, it will not accept direct signing.  
   DONE  
   ```
4. The signed application image file (`.s37`) can be used for [production programming](https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/) or for generating a [GBL upgrade image file](#generate-a-gbl-upgrade-image-file) for application upgrade.

###### Signing for Certificate-Based Secure Boot (heading level 7)

The following figure describes the signing and verification for certificate-based Secure Boot. You can freely switch between standard and advanced certificate-based Secure Boot by upgrading the application firmware without and with the application certificate.

![Certificate-Based Sign and Verify](/series2-secure-boot-with-rtsl/0.3/images/certificate-based-sign-verify-standard.png)

![Certificate-Based Sign and Verify](/series2-secure-boot-with-rtsl/0.3/images/certificate-based-sign-verify-advanced.png)

###### Certificate (heading level 8)

The following table describes the elements of a certificate.

|**Element**|**Description**|
|---|---|
|Certificate structure version|The version of the certificate structure.|
|Reserved flags|Reserved in the current certificate structure version.|
|Certificate public key|ECDSA-P256 public key, X and Y coordinates concatenated, used to validate the image.|
|Certificate version|The version of the running certificate.|
|Certificate signature|ECDSA-P256 signature, used for the authentication of the public key and the certificate version.|

**Notes**:

- The `application_properties.h` file in the Windows folder below defines the parameters of the certificate structure (`ApplicationCertificate_t`).  
  For GSDK v3.2 and lower: `C:\SiliconLabs\SimplicityStudio\v5\developer\sdks\gecko_sdk_suite\<GSDK VERSION>\platform\bootloader\api`  
  For GSDK v4.0 and higher: `C:\Users\<PC USER NAME>\SimplicityStudio\SDKs\gecko_sdk\platform\bootloader\api`
- The certificate is not in X.509 format.

###### Private/Public Key Pair (heading level 8)

The following table describes two Private/Public Key pairs used in certificates for certificate-based Secure Boot. You can use [Simplicity Commander or HSM](#generate-key-and-signing) to generate these key pairs.

|**Certificate**|**Private Key**|**Public Key**|**Description**|
|---|---|---|---|
|Bootloader (bl_cert.bin) (1)|bl_cert_key.pem (Private Bootloader Key)|bl_cert_pubkey.pem (Public Bootloader Key)|The bootloader certificate is signed by the Private Sign Key corresponding to the Public Sign Key in SE OTP.|
|Application (app_cert.bin) (2)|app_cert_key.pem (Private Application Key)|app_cert_pubkey.pem (Public Application Key)|The application certificate is signed by the Private Bootloader Key corresponding to the Public Bootloader Key in the bootloader certificate.|

**Notes**:

1. a. Certificate version in the bootloader certificate < certificate version in SE flash - the certificate is rejected.  
   b. Certificate version in the bootloader certificate = certificate version in SE flash - the certificate is accepted.  
   c. Certificate version in the bootloader certificate > certificate version in SE flash - the certificate is accepted. The certificate version in SE flash is updated to match ([revocation mechanism](#certificate-revocation)).
2. The certificate version in the application certificate is compared with the certificate version in the bootloader certificate. The application certificate is accepted if its version is equal to or higher than the certificate version in the bootloader certificate.

The following sections provide two methods to sign the bootloader image and application image files. All procedures assume the required files are in the same folder.

1. Using Simplicity Commander
2. Using an HSM and Simplicity Commander

###### Bootloader Image File (heading level 8)

1. If Certificated based Secure Boot flag is not enabled in SE OTP, follow the procedures in [Simplicity Commander](#simplicity-commander) to set up the certificate-based Secure Boot configuration for the bootloader.
2. Follow the procedures in [AppBuilder](#appbuilder) or [Bootloader-core Software Component](#bootloader-core-software-component) to set up the certificate-based Secure Boot configuration for the user application to generate an unsigned bootloader image.
3. The following steps show two methods to generate a bootloader certificate, either directly with Simplicity Commander (3a) or using an HSM with Commander (3b).  
   a. **(Using Simplicity Commander)** Run the `util gencert` command with **Public Bootloader Key** and **Private Sign Key** to generate the bootloader certificate (`bl_cert.bin`). Refer to the table abovefor details about the `--cert-version` for bootloader certificate.  
   ```c  
    ```sh  
    commander util gencert --cert-type secureboot --cert-version 1 --cert-pubkey bl_cert_pubkey.pem  
    --sign sign_key.pem --outfile bl_cert.bin  
    ```  
     
    ```sh  
    Successfully signed certificate  
    DONE  
    ```  
     
    Run the `convert` command with **Bootloader Certificate** and **Private Bootloader Key** to overwrite the unsigned bootloader image file with the signed bootloader image file (`bootloader-uart-xmodem.s37`).  
     
    ```sh  
    commander convert bootloader-uart-xmodem.s37 --secureboot --certificate bl_cert.bin  
    --keyfile bl_cert_key.pem --outfile bootloader-uart-xmodem.s37  
    ```  
     
    ```sh  
    Parsing file bootloader-uart-xmodem.s37...  
    Writing certificate to location 0x00002cf0  
    Private key matches public key in certificate.  
    Found Application Properties at 0x00002d78  
    Writing Application Properties signature pointer to point to 0x00002f64  
    Setting signature type in Application Properties: 0x00000001  
    Image SHA256: 3cf574b688853a801e8dc98687414db27f886c60c55dbf7fea2d47633df94e8d  
    R = C866592B4CB7BAD9EFC35985F1B9D52C65C26453D4808597EEEFFB16DC4AA962  
    S = 94CAA21ED5D7772F96BBF4D24A0711A94DCCB6D4D38DFA45182876B9BE2A8DE3  
     
    Verifying signed image...  
    Writing to bootloader-uart-xmodem.s37... Overwriting file: bootloader-uart-xmodem.s37...  
    DONE  
    ```  
   ```  
   b. **(Using an HSM and Simplicity Commander)** Run the `util gencert` command with **Public Bootloader Key** and `--extsign` option to generate an external signing bootloader certificate (`bl_cert.extsign`). Refer to the table above for details about the `--cert-version` for bootloader certificate.  
   ```c  
    ```sh  
    commander util gencert --cert-type secureboot --cert-version 1 --cert-pubkey bl_cert_pubkey.pem  
    --extsign --outfile bl_cert  
    ```  
     
    ```sh  
    DONE  
    ```  
     
    Use an HSM containing the Private Sign Key to generate the signature for the external signing bootloader certificate. This example uses the [OpenSSL](#using-an-external-tool) with the **Private Sign Key** to simulate this process. The signature is in the `bl_cert_signature.der`.  
     
    ```sh  
    openssl dgst -sha256 -binary -sign sign_key.pem -out bl_cert_signature.der bl_cert.extsign  
    ```  
     
    Run the `util signcert` command with the **bootloader certificate signature** to generate the bootloader certificate (`bl_cert.bin`).  
     
    ```sh  
    commander util signcert bl_cert.extsign --cert-type secureboot --signature bl_cert_signature.der  
    --verify sign_pubkey.pem --outfile bl_cert.bin  
    ```  
     
    ```sh  
    R = 065A58EA6CE6BBA44F3C59C6D255A901DBBC55FA97F261658B2026ABC8CD9680  
    S = 8A0011AA6393BC284B13C8313EE6772030DE07E213E74CA0FEA740F3D33E6518  
    Successfully verified signature Successfully signed certificate  
    DONE  
    ```  
     
    Run the `convert` command with **Bootloader Certificate** and `--extsign` option to generate an external signing bootloader image file (`bootloader-uart-xmodem.extsign`).  
     
    ```sh  
    commander convert bootloader-uart-xmodem.s37 --secureboot --certificate bl_cert.bin --extsign  
    --outfile bootloader-uart-xmodem  
    ```  
   ```
4. **(Optional)** Run the `util verifysign` command with **Public Sign Key** to verify that the **Bootloader Certificate and image** were correctly signed.  
   ```sh  
   commander util verifysign bootloader-uart-xmodem.s37 --verify sign_pubkey.pem  
   ```  
   ```sh  
   Parsing file bootloader-uart-xmodem.s37... Found application properties at 0x00002d78 Found certificate at 0x00002cf0  
   Successfully verified certificate signature with verification key.  
   Using certificate key to verify application signature.  
   Successfully verified application signature.  
   DONE  
   ```
5. **(Optional)** Run the `util appinfo` command to check all available information about `ApplicationProperties_t` struct in a signed GBL image.  
   ```sh  
   commander util appinfo bootloader-uart-xmodem.s37  
   ```  
   ```sh  
   Parsing file bootloader-uart-xmodem.s37...  
   Found application properties in image.  
   Application properties info:  
   Application properties location : 0x00002d00  
   Signature location	            : 0x00002e14  
   Signature type	                : ECDSA-P256  
   Long token section address	    : Not set (0x00000000)  
     
   Application data info:  
   For Series 2 devices: If rollback prevention is enabled in the OTP configuration, the device will not boot if the device has seen an application with a higher version number.  
   App type	                    : Bootloader (APPLICATION_TYPE_BOOTLOADER)  
   App version	                    : 0x02000000  
   Product ID	                    : Not set (0x00000000000000000000000000000000)  
     
   Found certificate at 0x00002c78  
   Application certificate info:  
   Certificate located at	        : 0x00002c78  
   Certificate version	            : 0x00000001  
   Certificate key	                : 0xb1bc6f6fa56640ed522b2ee0f5b3cf7e5d48f60be8148f0dc08440f0a4e1dca4  
                                   7c04119ed6a1be31b7707e5f9d001a659a051003e95e1b936f05c37ea793ad63  
   Certificate signature	        : 0xef3b53368d4cd7821eb30a96140bbde8840378cfea30687a8c10642e1c7728fd  
                                   309f976adf46e4eac62a2233f0c1f08f4e58344bdec61775b5282ceb351bb3d0  
   DONE  
   ```
6. The signed bootloader image file (`.s37`) can be used for [production programming](https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/) or for generating a [GBL upgrade image file](#generate-a-gbl-upgrade-image-file) for bootloader upgrade.
7. Run the `flash` command to program the signed bootloader image (`bootloader-uart-xmodem.s37`) to the device if the device does not have a bootloader.

```sh
commander flash bootloader-uart-xmodem.s37 --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
Parsing file bootloader-uart-xmodem.s37...
Writing 16384 bytes starting at address 0x00000000
Comparing range 0x00000000 - 0x00003FFF (16 KiB)
Programming range 0x00000000 - 0x00001FFF (8 KiB)
Programming range 0x00002000 - 0x00003FFF (8 KiB)
DONE
```

Simplicity Commander output for Series 3 devices will look as follows:

```sh
WARNING: Failed secure boot detected. Issuing a mass erase before flashing to recover the device...
Parsing file bootloader-uart-xmodem.s37...
Writing 14280 bytes starting at address 0x01000000
Erasing range 0x01000000 - 0x01007FFF (1 sector, 32 KB)
Programming range 0x01000000 - 0x01000FFF (4 KB)
Programming range 0x01001000 - 0x01001FFF (4 KB)
Programming range 0x01002000 - 0x01002FFF (4 KB)
Programming range 0x01003000 - 0x01003FFF (4 KB)
Programming range 0x01004000 - 0x01004FFF (4 KB)
Programming range 0x01005000 - 0x01005FFF (4 KB)
Programming range 0x01006000 - 0x01006FFF (4 KB)
Programming range 0x01007000 - 0x01007FFF (4 KB)
JLinkError: Failed to halt CPU.
Closing region 0 (this consumes one OTP bit, consider --noclose on development/testing devices)
Flashing completed successfully!
DONE
```

###### Application Image File (Standard Certificate-Based) (heading level 8)

1. Follow the procedures in [Generate an Unsigned Application Image](#generate-an-unsigned-application-image) to generate an unsigned application image for the bootloader.
2. a. **(Using Simplicity Commander)** Run the convert command with **Private Bootloader Key** to overwrite the unsigned application image file with the signed application image file (`blink_baremetal.s37`).  
   ```sh  
   commander	convert	blink_baremetal.s37	--secureboot	--keyfile	bl_cert_key.pem	--verify bl_cert_pubkey.pem  
   --outfile blink_baremetal.s37  
   ```  
   ```sh  
   Parsing file blink_baremetal.s37...  
   Found Application Properties at 0x000061bc  
   Writing Application Properties signature pointer to point to 0x000064d8  
   Setting signature type in Application Properties: 0x00000001  
   Image SHA256: 8b58ec567126aa1f6baa88afc916581477745aca6f47697ec093512fc30dcc6f  
   R = 994739A26AB520A88A5550F1643AE263D88A952F185F96EE7021FA43DEA6138C  
   S = 65B7112715E2F999A6B216C32D3331AB63B2D31A0A1311DF36EEE62269F8D6AA  
     
   Verifying signed image...  
   Writing to blink_baremetal.s37...  
   Overwriting file: blink_baremetal.s37...  
   DONE  
   ```  
   b. **(Using an HSM and Simplicity Commander)** Run the `convert` command with `--extsign` option to generate an external signing application image file (`blink_baremetal.extsign`).  
   ```c  
    ```sh  
    commander convert blink_baremetal.s37 --secureboot --extsign --outfile blink_baremetal  
    ```  
     
    ```sh  
    Parsing file blink_baremetal.s37...  
    Found Application Properties at 0x000061bc  
    Writing Application Properties signature pointer to point to 0x000064d8  
    Setting signature type in Application Properties: 0x00000001  
    Writing to blink_baremetal.extsign...  
    DONE  
    ```  
     
    Use an HSM containing the Private Bootloader Key to generate the signature for the external signing application image. This example uses the [OpenSSL](#using-an-external-tool) with the **Private Bootloader Key** to simulate this process. The signature is in the `app_signature.der`.  
     
    ```sh  
    openssl dgst -sha256 -binary -sign bl_cert_key.pem -out app_signature.der blink_baremetal.extsign  
    ```  
     
    Run the `convert` command with the **application image signature** to overwrite the unsigned application image file with the signed application image file. (`blink_baremetal.s37`).  
     
    ```sh  
    commander convert blink_baremetal.s37 --secureboot --signature app_signature.der  
    --verify bl_cert_pubkey.pem --outfile blink_baremetal.s37  
    ```  
     
    ```sh  
    Parsing file blink_baremetal.s37...  
    Parsing signature file app_signature.der...  
    R = 8DA79B020E954D24C23423D80627E046E44052736F6546902F016D64464E82DE  
    S = 9D5A1CC424E97A5AD0352A4EEA6BBF565FED5FC61FF99E63AA73DFFEAD9EE399  
    Found Application Properties at 0x000061bc  
    Writing Application Properties signature pointer to point to 0x000064d8  
    Setting signature type in Application Properties: 0x00000001  
     
    Verifying signed image...  
    Writing to blink_baremetal.s37...  
    Overwriting file: blink_baremetal.s37...  
    DONE  
    ```  
   ```
3. **(Optional)** Run the `util verifysign` command with **Public Bootloader Key** to verify that the application image file was correctly signed.  
   ```sh  
   commander util verifysign blink_baremetal.s37 --verify bl_cert_pubkey.pem  
   ```  
   ```sh  
   Parsing file blink_baremetal.s37...  
   Found application properties at 0x000061bc  
   Did not find application certificate in file  
   If the configuration flag SECURE_BOOT_VERIFY_CERTIFICATE is set or a device has previously seen certificate based signing, it will not accept direct signing.  
   Successfully verified application signature.  
   DONE  
   ```
4. **(Optional)** Run the `util appinfo` command to check all available information about `ApplicationProperties_t` struct in a signed application image.  
   ```sh  
   commander util appinfo blink_baremetal.s37  
   ```  
   ```sh  
   Parsing file blink_baremetal.s37...  
   Found application properties in image.  
   Application properties info:  
   Application properties location : 0x000061bc  
   Signature location	            : 0x000064d8  
   Signature type	                : ECDSA-P256  
   Long token section address	    : Not set (0x00000000)  
     
   Application data info:  
   For Series 2 devices: If rollback prevention is enabled in the OTP configuration, the device will not boot if the device has seen an application with a higher version number.  
   App type	                    : MCU application (APPLICATION_TYPE_MCU)  
   App version	                    : 0x00000001  
   Product ID	                    : Not set (0x00000000000000000000000000000000)  
     
   No certificate found in image.  
   For Series 2 devices: If the configuration flag SECURE_BOOT_VERIFY_CERTIFICATE is set or a device has previously seen certificate based signing, it will not accept direct signing.  
   DONE  
   ```
5. The signed application image file (`.s37`) can be used for [production programming](https://docs.silabs.com/iot-security/latest/series2-secure-debug/) or for generating a [GBL upgrade image file](#generate-a-gbl-upgrade-image-file) for application upgrade.

###### Application Image File (Advanced Certificate-Based) (heading level 8)

1. Follow the procedures in [Generate an Unsigned Application Image](#generate-an-unsigned-application-image) to generate an unsigned application image for the bootloader.
2. a. **(Using Simplicity Commander)** Run the `util gencert` command with **Public Application Key** and **Private Bootloader Key** to generate the application certificate (`app_cert.bin`). Refer to [Private/Public Key Pair](#privatepublic-key-pair) for details about the `--cert-version` for application certificate.  
   ```sh  
   commander util gencert --cert-type secureboot --cert-version 1 --cert-pubkey app_cert_pubkey.pem  
   --sign bl_cert_key.pem --outfile app_cert.bin  
   ```  
   ```sh  
   Successfully signed certificate  
   DONE  
   ```  
   Run the `convert` command with **Application Certificate** and **Private Application Key** to overwrite the unsigned application image file with the signed application image file (`blink_baremetal.s37`). This command will inject the application certificate into the application image before signing.  
   ```sh  
   commander	convert	blink_baremetal.s37	--secureboot	--certificate	app_cert.bin	--keyfile  
   app_cert_key.pem  
   --outfile blink_baremetal.s37  
   ```  
   ```sh  
   Parsing file blink_baremetal.s37...  
   Writing certificate to location 0x000064d8  
   Private key matches public key in certificate.  
   Found Application Properties at 0x000061bc  
   Writing Application Properties signature pointer to point to 0x00006560  
   Setting signature type in Application Properties: 0x00000001  
   Image SHA256: 38fd11214c36abf3bb4c4eeda8cfdd2ca2ac2ff1e07072d555a06c74700a23f5  
   R = 6B4E3BB454513CAA4569415AE8F79453973AAC7FD1FC4914284B65010F3790A6  
   S = 1657CAAABED579880187261038358C83B1780A67CC41475370D94ED4445A5557  
     
   Verifying signed image...  
   Writing to blink_baremetal.s37...  
   Overwriting file: blink_baremetal.s37...  
   DONE  
   ```  
   b. **(Using an HSM and Simplicity Commander)** Run the `util gencert` command with **Public Application Key** and `--extsign` option to generate an external signing application certificate (`app_cert.extsign`). Refer to [Private/Public Key Pair](#privatepublic-key-pair) for details about the `--cert-version` for application certificate.  
   ```c  
    ```sh  
    commander util gencert --cert-type secureboot --cert-version 1 --cert-pubkey app_cert_pubkey.pem  
    --extsign --outfile app_cert  
    DONE  
    ```  
     
    Use an HSM containing the Private Bootloader Key to generate the signature for the external signing application certificate. This example uses the [OpenSSL](#using-an-external-tool) with the **Private Bootloader Key** to simulate this process. The signature is in the `app_cert_signature.der`.  
     
    ```sh  
    openssl dgst -sha256 -binary -sign bl_cert_key.pem -out app_cert_signature.der app_cert.extsign  
    ```  
     
    Run the `util signcert` command with the **application certificate signature** to generate the application certificate (`app_cert.bin`).  
     
    ```sh  
    commander util signcert app_cert.extsign --cert-type secureboot --signature app_cert_signature.der  
    --verify bl_cert_pubkey.pem --outfile app_cert.bin  
    ```  
     
    ```sh  
    R = 279D4FA1B801D108F82E30B0CF1164BF597549287290BD3883C5847B91095CCE  
    S = 567F0E219D2089EF4D79C3D94E43D2FADFE1899B71492ED358E6A1B46AE8162F  
    Successfully verified signature Successfully signed certificate  
    DONE  
    ```  
     
    Run the `convert` command with the **Application Certificate** and `--extsign` option to generate an external signing application image file (`blink_baremetal.extsign`).  
     
    ```sh  
    commander convert blink_baremetal.s37 --secureboot --certificate app_cert.bin --extsign --outfile blink_baremetal  
    ```  
     
    ```sh  
    Parsing file blink_baremetal.s37...  
    Writing certificate to location 0x000064d8  
    ```  
   ```
3. **(Optional)** Run the `util verifysign` command with **Public Bootloader Key** to verify that the **Application Certificate and image** were correctly signed.  
   ```sh  
   commander util verifysign blink_baremetal.s37 --verify bl_cert_pubkey.pem  
   ```  
   ```sh  
   Parsing file blink_baremetal.s37...  
   Found application properties at 0x000061bc Found certificate at 0x000064d8  
   Successfully verified certificate signature with verification key.  
   Using certificate key to verify application signature.  
   Successfully verified application signature.  
   DONE  
   ```
4. **(Optional)** Run the `util appinfo` command to check all available information about `ApplicationProperties_t` struct in a signed application image.  
   ```sh  
   commander util appinfo blink_baremetal.s37  
   ```  
   ```sh  
   Parsing file blink_baremetal.s37...  
   Found application properties in image.  
   Application properties info:  
   Application properties location : 0x000061bc  
   Signature location	            : 0x00006560  
   Signature type	                : ECDSA-P256  
   Long token section address	    : Not set (0x00000000)  
     
   Application data info:  
   For Series 2 devices: If rollback prevention is enabled in the OTP configuration, the device will not boot if the device has seen an application with a higher version number.  
   App type	                    : MCU application (APPLICATION_TYPE_MCU)  
   App version	                    : 0x00000001  
   Product ID	                    : Not set (0x00000000000000000000000000000000)  
     
   Found certificate at 0x000064d8  
   Application certificate info:  
   Certificate located at	        : 0x000064d8  
   Certificate version	            : 0x00000001  
   Certificate key	                : 0xe562003cd86e225decfd35712e431a19ecd5031a079b06c1d473620a6be9f57a  
                                   879820100fee074f28b5885fd6759f480b62aaa0717f96e245aab6635cfb1e11  
   Certificate signature	        : 0x039aaba62b5258e68d16e167c3a611c719c542bb3483f5d4b522472b06adf30f  
                                   8cfcc484bf8551a208256e3d2d8c9194a7d2ac551e2cac659a99822308a40aa6  
   DONE  
   ```
5. The signed application image file (`.s37`) can be used for [production programming](https://docs.silabs.com/iot-security/latest/series2-secure-debug/) or for generating a [GBL upgrade image file](#generate-a-gbl-upgrade-image-file) for application upgrade.

###### Generate a GBL Upgrade Image File (heading level 7)

This section provides steps to create GBL files used to upgrade bootloader, application and/or SE firmware on Series 2 and Series 3 devices.

> **Note**: Series 3 devices support only GBLv4 files for firmware upgrade process.

- Follow the procedures in [AppBuilder](#appbuilder) or [Bootloader-core Software Component](#bootloader-core-software-component) to avoid overwriting the existing application image (if necessary) when upgrading the bootloader or SE.
- For a standalone bootloader with communication interface, you can only generate three separate GBL upgrade image files containing bootloader, SE, and application images.
- (Series 2 devices) For an application bootloader with storage, you can generate a single GBL upgrade image file (see example below) with a combination of bootloader, SE, and application images.

```sh
commander gbl create all.gbl --app app.s37 --bootloader bl.s37 --seupgrade se.seu
```

- A signed GBL upgrade image file is required if you enable the **Require signed firmware upgrade files** option in [AppBuilder](#appbuilder) or [Bootloader-core Software Component](#bootloader-core-software-component). The following table shows which private key(s) can be used to sign the GBL upgrade image file (bootloader, SE, or application) on VSE, HSE and Series 3 devices. The VSE devices store a [Public Sign Key](#signing-for-ecdsa-p256-sha256-secure-boot) copy on the top page of the main flash to verify the GBL upgrade image file for ECDSA-P256-SHA256 Secure Boot.

**Private Key(s) usage to sign GBL upgrade image file**

|**Secure Boot**|**HSE and Series 3**|**VSE**|
|---|---|---|
|ECDSA-P256-SHA256|Private Sign Key|Private Sign Key (Public Sign Key in main flash)|
|Certificate-Based|Private Sign Key or Private Bootloader Key|Private Bootloader Key|

- An encrypted GBL upgrade image file is required if you enable the **Require encrypted firmware upgrade files** option in [AppBuilder](#appbuilder) or [Bootloader-core Software Component](#bootloader-core-software-component). Refer to [Provision GBL Decryption Key](#provision-gbl-decryption-key) on how to provision the GBL Decryption Key for this option.
- [Only applicable for Series 2 devices] For an application bootloader with storage, you can enable the **Upgrade SE without using the staging area** option in **GSDK v4.1.1 or higher** to directly fetch the SE image from the GBL upgrade image file in storage instead of copying the image to the pre-configured upgrade location.

![Upgrade SE without using the staging area option](/series2-secure-boot-with-rtsl/0.3/images/sld794-image56.jpg)

To use the above option, the SE image cannot be in the encrypted part of the GBL upgrade image file if the **Require encrypted firmware upgrade files** option is enabled. Use the `--seunencrypted` option in **Simplicity Commander v1.13.0 or higher** (see example below) to generate an encrypted GBL upgrade image file with a SE image outside the encrypted part of the file.

```sh
commander gbl create se-upgrade.gbl --seupgrade secure-element.seu --seunencrypted --app myapp.s37
--encrypt aes_key.txt
```

- In Series 3 devices, the storage bootloader supports direct SE upgrades from the GBL file without the need for staging.

The following sections provide two methods to sign the bootloader, Secure Engine, and application upgrade image files if the **Require signed firmware upgrade files** option is enabled.

1. Using Simplicity Commander
2. Using an HSM and Simplicity Commander

The sections also include encryption examples with an AES-128 key (like `aes_key.txt`) for the **Require encrypted firmware upgrade files** option. All procedures assume the required files are in the same folder.

Series 3 devices require GBLv4. GBLv4 is a new format that supports more command line options than the upgrade file for series 2 devices which do not support GBLv4.

- To compress an upgrade file, append `--compress <compression algorithm>` to the command, supported algorithms are `lz4` and `lzma`.
- To add the product id, append `--productid <16-byte long identifier>` to the command.
- To add the bundle version and minimum version, append `--bundleversion <version number> --minversion <version number>` to the command.

Read more about GBLv4 files and the configurations supported in **Gecko Bootloader File Format v4** section of [Gecko Bootloader User's Guide for Series 3 and Higher](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-series3-and-higher/02-gecko-bootloader-file-format-v4).

- All `gbl create` and `gbl4 create` commands support both encrypted and unencrypted images. To create an unencrypted image, omit the `--encrypt` option. To create an encrypted image, append `--encrypt aes_key.txt` to the command.

###### Bootloader Upgrade (heading level 8)

1. Create an unsigned GBL upgrade image:  
   a. Series 2 devices: Run the `gbl create` command with `--bootloader` option to generate the bootloader GBL upgrade image file (`bootloader-uart-xmodem.gbl`) with the signed bootloader image file (`bootloader-uart-xmodem.s37`) from [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) or [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot).  
   ```c  
    ```sh  
    commander gbl create bootloader-uart-xmodem.gbl --bootloader bootloader-uart-xmodem.s37  
    ```  
     
    ```sh  
    Initializing GBL file...  
    Adding bootloader to GBL...  
    Writing GBL file bootloader-uart-xmodem.gbl...  
    DONE  
    ```  
   ```  
   b. Series 3 devices: Run the `gbl4 create` command with `--data` option to generate the bootloader GBL upgrade image file (`bootloader.gbl`) with the signed bootloader image file (`bootloader-uart-xmodem.s37`) from [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) or [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot).  
   ```c  
    ```sh  
    commander gbl4 create bootloader.gbl --data bootloader-uart-xmodem.s37 --device SIMG301LIL  
    ```  
     
    ```sh  
    Initializing GBLV4 file...  
    Parsing file bootloader.s37...  
    Writing GBL file bootloader.gbl...  
    DONE  
    ```  
   ```
2. Create a signed GBL upgrade image using Simplicity Commander:  
   a. Series 2 devices: Run the `gbl create` command with `--bootloader` option to generate the signed bootloader GBL upgrade image file (`bootloader-uart-xmodem.gbl`) with **Private Sign Key** or **Private Bootloader Key** and the signed bootloader image file (`bootloader-uart-xmodem.s37`) from [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) or [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot).  
   ```c  
    ```sh  
    commander gbl create bootloader-uart-xmodem.gbl --bootloader bootloader-uart-xmodem.s37 --sign  
    sign_key.pem  
    ```  
     
    ```sh  
    commander gbl create bootloader-uart-xmodem.gbl --bootloader bootloader-uart-xmodem.s37  
    --sign bl_cert_key.pem  
    ```  
     
    ```sh  
    Initializing GBL file...  
    Adding bootloader to GBL...  
    Signing GBL...  
    Image SHA256: 3eb09993ffca5f9b34df3f38b65ab9d2f6619b828b014a186516016d4bbd80f7  
    R = C21E0C19254AC4F62374BBCA65DEBB42C7349384F5527330CD030A51DC2170F7  
    S = E1680C3670DE68D731086845E2726EF3BF07B96EB54AA2DB2F390F60BDB6DAB2  
    Writing GBL file bootloader-uart-xmodem.gbl...  
    DONE  
    ```  
   ```  
   b. Series 3 devices: Run the `gbl4 create` command with `--data` option to generate the signed bootloader GBL upgrade image file (`bootloader.gbl`) with **Private Sign Key** or **Private Bootloader Key** and the signed bootloader image file (`bootloader-u art-xmodem.s37`) from [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) or [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot).  
   ```c  
    ```sh  
    commander gbl4 create bootloader.gbl --data bootloader-uart-xmodem.s37 --sign sign_key.pem  
    --device SIMG301LIL  
    ```  
     
    ```sh  
    commander gbl4 create bootloader.gbl --data bootloader-uart-xmodem.s37 --sign bl_cert_key.pem  
    --device SIMG301LIL  
    ```  
     
    ```sh  
    Initializing GBLV4 file...  
    Parsing file bootloader.s37...  
    Writing GBL file bootloader.gbl...  
    Image SHA256: 5ba67b656103a3539f0f3802a4398f0babb3ff182fe9890962e678052571d42b  
    R = 6845219257E660918B7BAE26FFF3DC7BBE8150DA3281CE8881B7088E9E9736EC  
    S = B8C5B58987FA3C7E44444B7C43096A41F48D245ED148C4E0863F0F910133811E  
    DONE  
    ```  
   ```
3. Creating a signed GBL upgrade image using an HSM and Simplicity Commander:  
   a. Series 2 devices: Run the `gbl create` command with `--bootloader` and `--extsign` options to generate an external signing bootloader GBL upgrade image file (`bootloader-uart-xmodem.extsign`) with the signed bootloader image file (`bootloader-uart-xmodem.s37`) from [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) or [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot).  
   ```c  
    ```sh  
    commander gbl create bootloader-uart-xmodem --bootloader bootloader-uart-xmodem.s37 --extsign  
    ```  
     
    ```sh  
    Initializing GBL file...  
    Adding bootloader to GBL...  
    Preparing GBL for external signing...  
    Writing GBL file bootloader-uart-xmodem.extsign...  
    DONE  
    ```  
     
    Use an HSM containing the Private Sign Key or Private Bootloader Key to generate the signature for the external signing bootloader GBL upgrade image file. This example uses the [OpenSSL](#using-an-external-tool) with the **Private Sign Key** or **Private Bootloader Key** to simulate this process. The signature is in the gbl_signature.der.  
     
    ```sh  
    openssl dgst -sha256 -binary -sign sign_key.pem -out gbl_signature.der bootloader-uart-xmodem.extsign  
    ```  
     
    ```sh  
    openssl	dgst	-sha256	-binary	-sign	bl_cert_key.pem	-out	gbl_signature.der	bootloader-uart-xmodem.extsign  
    ```  
     
    Run the `gbl sign` command with the **signature** above to generate a signed bootloader GBL upgrade image file (`bootloader-uart-xmodem.gbl`).  
     
    ```sh  
    commander	gbl	sign	bootloader-uart-xmodem.extsign	--signature	gbl_signature.der	--verify  
    sign_pubkey.pem  
    --outfile bootloader-uart-xmodem.gbl  
    ```  
     
    ```sh  
    commander gbl sign bootloader-uart-xmodem.extsign --signature gbl_signature.der  
    --verify bl_cert_pubkey.pem --outfile bootloader-uart-xmodem.gbl  
    ```  
     
    ```sh  
    Reading GBL data from bootloader-uart-xmodem.extsign...  
    Parsing signature file gbl_signature.der...  
    R = 90F0A3C0D5D9ED2DC10EB3F55595FF21AB31307DC6283E3F3B7494A30FB741D4  
    S = 2765041F515A960F048CA250BFAB92031D4D1E569FB3F917C9329E7362C17B51  
    Writing signature to GBL... Verifying GBL...  
    Successfully verified GBL signature  
    Writing GBL file bootloader-uart-xmodem.gbl...  
    DONE  
    ```  
   ```  
   b. Series 3 devices: Run the `gbl4 create` command with `--data` and `--extsign` options to generate an unsigned and manifest files (`bootloader.unsigned` and `bootloader.manifest`) with the signed bootloader image file (`bootloader-uart-xmodem.s37`) from [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) or [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot).  
   ```c  
    ```sh  
    commander gbl4 create bootloader --data bootloader-uart-xmodem.s37 --extsign --device SIMG301LIL  
    ```  
     
    ```sh  
    Initializing GBLV4 file...  
    Parsing file bootloader-uart-xmodem.s37...  
    Writing manifest data (for external signing) to bootloader.manifest  
    Writing unsigned GBL file (for later use in 'gbl4 sign') to bootloader.unsigned...  
    After computing the signature of bootloader.manifest, assemble the GBL4 using:  
    commander gbl4 sign bootloader.unsigned --outfile bootloader --signature <signature.der>  
    DONE  
    ```  
     
    Use an HSM containing the Private Sign Key or Private Bootloader Key to generate the signature for the manifest file. This example uses the [OpenSSL](#using-an-external-tool) with the **Private Sign Key** or **Private Bootloader Key** to simulate this process. The signature is in the `gbl_signature.der`.  
     
    ```sh  
    openssl dgst -sha256 -binary -sign sign_key.pem -out gbl_signature.der bootloader.manifest  
    ```  
     
    ```sh  
    openssl dgst -sha256 -binary -sign bl_cert_key.pem -out gbl_signature.der bootloader.manifest  
    ```  
   ```
4. Follow the procedures in [Upload a GBL Upgrade Image File](#upload-a-gbl-upgrade-image-file) to upgrade the bootloader with the bootloader GBL upgrade image file.

###### Secure Engine Upgrade

1. Creating an unsigned GBL upgrade image:  
   a. Series 2 devices: Run the `gbl create` command with `--seupgrade` option to generate the SE GBL upgrade image file (`s2c1_se_fw_upgrade_1v2p9.gbl`) with the [SE image file](01-series-2-and-series-3-device-security-features#se-firmware) (`s2c1_se_fw_upgrade_1v2p9.seu`).  
   ```c  
    ```sh  
    commander gbl create s2c1_se_fw_upgrade_1v2p9.gbl --seupgrade s2c1_se_fw_upgrade_1v2p9.seu  
    ```  
     
    ```sh  
    Initializing GBL file...  
    Adding Secure Element upgrade image to GBL...  
    Writing GBL file s2c1_se_fw_upgrade_1v2p9.gbl...  
    DONE  
    ```  
   ```  
   b. Series 3 devices: Run the `gbl4 create` command with `--seupgrade` option to generate the SE GBL upgrade image file (`se-upgrade.gbl`) with the [SE image file](01-series-2-and-series-3-device-security-features#se-firmware) (`x301_se_fw_upgrade_3v3p2.seuv2`).  
   ```c  
    ```sh  
    commander gbl4 create se-upgrade.gbl --seupgrade x301_se_fw_upgrade_3v3p2.seuv2 --device SIMG301LIL  
    ```  
     
    ```sh  
    Initializing GBLV4 file...  
    Reading SE upgrade file from x301_se_fw_upgrade_3v3p2.seuv2  
    Writing GBL file se-upgrade...  
    DONE  
    ```  
   ```
2. Creating a signed GBL upgrade image using Simplicity Commander:  
   a. Series 2 devices: Run the `gbl create` command with `--seupgrade` option to generate the signed SE GBL upgrade image file (`s2c1_se_fw_upgrade_1v2p9.gbl`) with **Private Sign Key** or **Private Bootloader Key** and the [SE image file](01-series-2-and-series-3-device-security-features#se-firmware) (`s2c1_se_fw_upgrade_1v2p9.seu`).  
   ```c  
    ```sh  
    commander gbl create s2c1_se_fw_upgrade_1v2p9.gbl --seupgrade s2c1_se_fw_upgrade_1v2p9.seu  
    --sign sign_key.pem  
    ```  
     
    ```sh  
    commander gbl create s2c1_se_fw_upgrade_1v2p9.gbl --seupgrade s2c1_se_fw_upgrade_1v2p9.seu  
    --sign bl_cert_key.pem  
    ```  
     
    ```sh  
    Initializing GBL file...  
    Adding Secure Element upgrade image to GBL...  
    Signing GBL...  
    Image SHA256: 599d7fc35996b4715441b642709ed262525d09d811d4726e423c0d605ec0f0bf  
    R = EF8EC2DDEDDF44DF88FEAD4ED0A9FDC6351B4D745D5A05BFB87204791871A525  
    S = FCB26EF005D97E8C5341153A210AE9927E1CF646A3E473FFB90DA8C857E6421F  
    Writing GBL file s2c1_se_fw_upgrade_1v2p9.gbl...  
    DONE  
    ```  
   ```  
   b. Series 3 devices: Run the `gbl4 create` command with `--seupgrade` option to generate the signed SE GBL upgrade image file (`se-upgrade.gbl`) with **Private Sign Key** or **Private Bootloader Key** and the [SE image file](01-series-2-and-series-3-device-security-features#se-firmware) (`x301_se_fw_upgrade_3v3p2.seuv2`).  
   ```c  
    ```sh  
    commander gbl4 create se-upgrade.gbl --seupgrade x301_se_fw_upgrade_3v3p2.seuv2  
    --sign sign_key.pem --device SIMG301LIL  
    ```  
     
    ```sh  
    commander gbl4 create se-upgrade.gbl --seupgrade x301_se_fw_upgrade_3v3p2.seuv2  
    --sign bl_cert_key.pem --device SIMG301LIL  
    ```  
     
    ```sh  
    Initializing GBLV4 file...  
    Reading SE upgrade file from x301_se_fw_upgrade_3v3p2.seuv2  
    Writing GBL file se-upgrade.gbl...  
    Image SHA256: e6462d8810f9afee36974b82b3790af55c94f559c532ee0e264617d705a1fd6b  
    R = 095A9D64BD490FCA444D49DADC53C666B717006D9E7C91C196B259F6E27E5C9C  
    S = 0BFC9B86BA5B3C2A4C98AE5EC8264D905659AE8528433832FAD225D51E9899FD  
    DONE  
    ```  
   ```
3. Creating a signed GBL upgrade image using an HSM and Simplicity Commander:  
   a. Series 2 devices: Run the `gbl create` command with `--seupgrade` and `--extsign` options to generate an external signing SE GBL upgrade image file (`s2c1_se_fw_upgrade_1v2p9.extsign`) with the [SE image file](01-series-2-and-series-3-device-security-features#se-firmware) (`s2c1_se_fw_upgrade_1v2p9.seu`).  
   ```c  
    ```sh  
    commander gbl create s2c1_se_fw_upgrade_1v2p9 --seupgrade s2c1_se_fw_upgrade_1v2p9.seu --extsign  
    ```  
     
    ```sh  
    Initializing GBL file...  
    Adding Secure Element upgrade image to GBL...  
    Preparing GBL for external signing...  
    Writing GBL file s2c1_se_fw_upgrade_1v2p9.extsign...  
    DONE  
    ```  
     
    Use an HSM containing the Private Sign Key or Private Bootloader Key to generate the signature for the external signing SE GBL upgrade image file. This example uses the [OpenSSL](#using-an-external-tool) with the **Private Sign Key** or **Private Bootloader Key** to simulate this process. The signature is in the `gbl_signature.der`.  
     
    ```sh  
    openssl dgst -sha256 -binary -sign sign_key.pem -out gbl_signature.der s2c1_se_fw_upgrade_1v2p9.extsign  
    ```  
     
    ```sh  
    openssl dgst -sha256 -binary -sign bl_cert_key.pem -out gbl_signature.der s2c1_se_fw_upgrade_1v2p9.extsign  
    ```  
     
    Run the `gbl sign` command with the **signature** above to generate a signed SE GBL upgrade image file (`s2c1_se_fw_upgrade_1v2p9.gbl`).  
     
    ```sh  
    commander	gbl	sign	s2c1_se_fw_upgrade_1v2p9.extsign	--signature	gbl_signature.der	--verify  
    sign_pubkey.pem  
    --outfile s2c1_se_fw_upgrade_1v2p9.gbl  
    ```  
     
    ```sh  
    commander gbl sign s2c1_se_fw_upgrade_1v2p9.extsign --signature gbl_signature.der  
    --verify bl_cert_pubkey.pem --outfile s2c1_se_fw_upgrade_1v2p9.gbl  
    ```  
     
    ```sh  
    Reading GBL data from s2c1_se_fw_upgrade_1v2p9.extsign...  
    Parsing signature file gbl_signature.der...  
    R = 2798B98194EE02717C738B5866ABD8D234D0F0E096E90495D371D2507D8E1C67  
    S = 19F2586E2C6177D6B4EEC708E006F67334C989D0398D4233C686C98ECB6992FB  
    Writing signature to GBL... Verifying GBL...  
    Successfully verified GBL signature  
    Writing GBL file s2c1_se_fw_upgrade_1v2p9.gbl...  
    DONE  
    ```  
   ```  
   b. Series 3 devices: Run the `gbl4 create` command with `--seupgrade` and `--extsign` options to generate unsigned and manifest files (`se-upgrade.unsigned` and `se-upgrade.manifest`) with the [SE image file](01-series-2-and-series-3-device-security-features#se-firmware) (`x301_se_fw_upgrade_3v3p2.seuv2`).  
   ```c  
    ```sh  
    commander gbl4 create se-upgrade --seupgrade x301_se_fw_upgrade_3v3p2.seuv2 --extsign  
    --device SIMG301LIL  
    ```  
     
    ```sh  
    Initializing GBLV4 file...  
    Reading SE upgrade file from x301_se_fw_upgrade_3v3p2.seuv2  
    Writing manifest data (for external signing) to se-upgrade.manifest  
    Writing unsigned GBL file (for later use in 'gbl4 sign') to se-upgrade.unsigned...  
    After computing the signature of se-upgrade.manifest, assemble the GBL4 using:  
    commander gbl4 sign se-upgrade.unsigned --outfile se-upgrade --signature <signature.der>  
    DONE  
    ```  
     
    Use an HSM containing the Private Sign Key or Private Bootloader Key to generate the signature for the manifest file. This example uses the [OpenSSL](#using-an-external-tool) with the **Private Sign Key** or **Private Bootloader Key** to simulate this process. The signature is in the `gbl_signature.der`.  
     
    ```sh  
    openssl dgst -sha256 -binary -sign sign_key.pem -out gbl_signature.der se-upgrade.manifest  
    ```  
     
    ```sh  
    openssl dgst -sha256 -binary -sign bl_cert_key.pem -out gbl_signature.der se-upgrade.manifest  
    ```  
     
    Run the `gbl4 sign` command with the **signature** above to generate a signed SE GBL upgrade image file (`se-upgrade.gbl`).  
   ```
4. Follow the procedures in [Upload a GBL Upgrade Image File](#upload-a-gbl-upgrade-image-file) to upgrade the SE with the SE GBL upgrade image file.

**Notes**:

- The `sign_key.pem`/`sign_pubkey.pem` key pair is for [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot), and the `bl_cert_key.pem`/`bl_cert_pubkey.pem` key pair is for [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot).
- Trying to apply a lower version of the SE image file (`.seu`) to the device will be ignored.

###### Application Upgrade

1. Creating an unsigned GBL upgrade image:  
   a. Series 2 devices: Run the `gbl create` command with `--app` option to generate the application GBL upgrade image file (`blink_baremetal.gbl`) with the signed application image file (`blink_baremetal.s37`) from [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) or [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot).  
   ```c  
    ```sh  
    commander gbl create blink_baremetal.gbl --app blink_baremetal.s37  
    ```  
     
    ```sh  
    Parsing file blink_baremetal.s37...  
    Initializing GBL file...  
    Adding application to GBL...  
    Writing GBL file blink_baremetal.gbl...  
    DONE  
    ```  
   ```  
   b. Series 3 devices: Run the `gbl4 create` command with `--data` option to generate the bootloader GBL upgrade image file (`app-upgrade.gbl`) with the signed bootloader image file (`bt_soc_blinky_freertos.s37`) from [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) or [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot).  
   ```c  
    ```sh  
    commander gbl4 create app-upgrade.gbl --data bt_soc_blinky_freertos.s37 --device SIMG301LIL  
    ```  
     
    ```sh  
    Initializing GBLV4 file...  
    Parsing file bt_soc_blinky_freertos.s37...  
    Writing GBL file app-upgrade.gbl...  
    DONE  
    ```  
   ```
2. Creating a signed GBL upgrade image using Simplicity Commander:  
   a. Series 2 devices: Run the `gbl create` command with `--app` option to generate the signed application GBL upgrade image file (`blink_baremetal.gbl`) with **Private Sign Key** or **Private Bootloader Key** and the signed application image file (`blink_baremetal.s37`) from [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) or [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot).  
   ```c  
    ```sh  
    commander gbl create blink_baremetal.gbl --app blink_baremetal.s37 --sign sign_key.pem  
    ```  
     
    ```sh  
    commander gbl create blink_baremetal.gbl --app blink_baremetal.s37 --sign bl_cert_key.pem  
    ```  
     
    ```sh  
    Parsing file blink_baremetal.s37...  
    Initializing GBL file...  
    Adding application to GBL...  
    Signing GBL...  
    Image SHA256: 116c1be47d799ab75afc7b3f4c9a8023e5cd031103b1d28c578eebfaf1ad73d2  
    R = CE4D85C058301A2437440E00385D97E496F1D8B5CAFFB8C184F8A88B5266E3E9  
    S = 90BBF754EBC0AB343CC32AA06ADED85F9D12D1A67CA6608F9085137142000A40  
    Writing GBL file blink_baremetal.gbl...  
    DONE  
    ```  
   ```  
   b. Series 3 devices: Run the `gbl4 create` command with `--data` option to generate the signed bootloader GBL upgrade image file (`app-upgrade-signed.gbl`) with **Private Sign Key** or **Private Bootloader Key** and the signed bootloader image file (`bt_soc_blinky_freertos.s37`) from [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) or [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot).  
   ```c  
    ```sh  
    commander gbl4 create app-upgrade-signed.gbl --data bt_soc_blinky_freertos.s37  
    --sign sign_key.pem --device SIMG301LIL  
    ```  
     
    ```sh  
    commander gbl4 create app-upgrade-signed.gbl --data bt_soc_blinky_freertos.s37  
    --sign bl_cert_key.pem --device SIMG301LIL  
    ```  
     
    ```sh  
    Initializing GBLV4 file...  
    Parsing file bt_soc_blinky_freertos.s37...  
    Writing GBL file app-upgrade-signed.gbl...  
    Image SHA256: 665ee8c703bc57ee59078d48183d39087e6a52e564ee3bd73971f33acbd0b009  
    R = 5C72EDFA23B947BAB1827A566C14BDF4542C24CE62F30DE928216AC10FE8AA5B  
    S = 22A4912CA192274681FB000F882D0CE500FC3D29C54CB354430553EC4A61B11A  
    DONE  
    ```  
   ```
3. Creating a signed GBL upgrade image using an HSM and Simplicity Commander:  
   a. Series 2 devices: Run the `gbl create` command with `--app` and `--extsign` options to generate an external signing application GBL upgrade image file (`blink_baremetal.extsign`) with the signed application image file (`blink_baremetal.s37`) from [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) or [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot).  
   ```c  
    ```sh  
    commander gbl create blink_baremetal --app blink_baremetal.s37 --extsign  
    ```  
     
    ```sh  
    Parsing file blink_baremetal.s37...  
    Initializing GBL file...  
    Adding application to GBL...  
    Preparing GBL for external signing...  
    Writing GBL file blink_baremetal.extsign...  
    DONE  
    ```  
     
    Use an HSM containing the Private Sign Key or Private Bootloader Key to generate the signature for the external signing application GBL upgrade image file. This example uses the [OpenSSL](#using-an-external-tool) with the **Private Sign Key** or **Private Bootloader Key** to simulate this process. The signature is in the `gbl_signature.der`.  
     
    ```sh  
    openssl dgst -sha256 -binary -sign sign_key.pem -out gbl_signature.der blink_baremetal.extsign  
    ```  
     
    ```sh  
    openssl dgst -sha256 -binary -sign bl_cert_key.pem -out gbl_signature.der blink_baremetal.extsign  
    ```  
     
    Run the `gbl sign` command with the **signature** above to generate a signed application GBL upgrade image file (`blink_baremetal.gbl`).  
     
    ```sh  
    commander gbl sign blink_baremetal.extsign --signature gbl_signature.der --verify sign_pubkey.pem  
    --outfile blink_baremetal.gbl  
    ```  
     
    ```sh  
    commander gbl sign blink_baremetal.extsign --signature gbl_signature.der --verify bl_cert_pubkey.pem  
    --outfile blink_baremetal.gbl  
    ```  
     
    ```sh  
    Reading GBL data from blink_baremetal.extsign...  
    Parsing signature file gbl_signature.der...  
    R = 533499660E24F1620EF25D862FB607F46E9E4ECC41CBDECBE77C64EF1970D96A  
    S = FA8901878218F5F1DB0FAF8B074CE98A27C63FFDE63730CD49EE47E847B9811D  
    Writing signature to GBL...  
    Verifying GBL...  
    Successfully verified GBL signature  
    Writing GBL file blink_baremetal.gbl...  
    DONE  
    ```  
   ```  
   b. Series 3 devices: Run the `gbl4 create` command with `--data` and `--extsign` options to generate an unsigned and manifest files (`app-upgrade.unsigned` and `app-upgrade.manifest`) with the signed bootloader image file (`bootloader-uart-xmodem.s37`) from [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) or [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot).  
   ```c  
    ```sh  
    commander gbl4 create app-upgrade --data bt_soc_blinky_freertos.s37 --extsign --device SIMG301LIL  
    ```  
     
    ```sh  
    Initializing GBLV4 file...  
    Parsing file bt_soc_blinky_freertos.s37...  
    Writing manifest data (for external signing) to app-upgrade.manifest  
    Writing unsigned GBL file (for later use in 'gbl4 sign') to app-upgrade.unsigned...  
    After computing the signature of app-upgrade.manifest, assemble the GBL4 using:  
    commander gbl4 sign app-upgrade.unsigned --outfile app-upgrade --signature <signature.der>  
    DONE  
    ```  
     
    Use an HSM containing the Private Sign Key or Private Bootloader Key to generate the signature for the manifest file. This example uses the [OpenSSL](#using-an-external-tool) with the **Private Sign Key** or **Private Bootloader Key** to simulate this process. The signature is in the `gbl_signature.der`.  
     
    ```sh  
    openssl dgst -sha256 -binary -sign sign_key.pem -out gbl_signature.der app-upgrade.manifest  
    ```  
     
    ```sh  
    openssl dgst -sha256 -binary -sign bl_cert_key.pem -out gbl_signature.der app-upgrade.manifest  
    ```  
     
    Run the `gbl4 sign` command with the **signature** above to generate a signed bootloader GBL upgrade image file (`bootloader.gbl`).  
   ```
4. Follow the procedures in [Upload a GBL Upgrade Image File](#upload-a-gbl-upgrade-image-file) to upgrade the application with the application GBL upgrade image file.

**Notes**:

- [Series 2 devices] The Simplicity Commander v1.11.0 or above supports GBL upgrade image file in `util verifysign` command.  
  ```sh  
  commander util verifysign blink_baremetal.gbl --verify sign_pubkey.pem  
  ```  
  ```sh  
  Successfully verified GBL signature  
  DONE  
  ```
- [Series 2 devices] The Simplicity Commander v1.12.0 or above fixes a bug introduced in v1.11.0 when using the `--extsign` option on the GBL upgrade image file.

###### Upload a GBL Upgrade Image File (heading level 7)

This section describes how to use UART XMODEM Bootloader v2.0.0 in GSDK v4.0 and higher to upload a GBL upgrade image file (`.gbl`) to the device. The procedures and pictures may be different for the other versions of this example.

The GBL upgrade image file uses a proprietary format to store the upgrade image for a firmware upgrade. Use the gbl create command to generate the GBL upgrade image file for bootloader, application, and Secure Engine. Refer to [UG266](https://www.silabs.com/documents/public/user-guides/ug266-gecko-bootloader-user-guide.pdf)/[Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/) and [Generate a GBL Upgrade Image File](#generate-a-gbl-upgrade-image-file) for more information about GBL upgrade image file creation.

You can use any terminal software that supports the XMODEM-CRC protocol for file transfer. This application note uses [Tera Term](https://ttssh2.osdn.jp/index.html.en) as terminal software. The default serial port setting is 115200 bps 8-N-1.

1. Assume the [UART XMODEM Bootloader](#generate-an-unsigned-gbl-image) and application firmware had already flashed to the radio board on WSTK.
2. Press the **RESET** and **PB0** push buttons on the WSTK.
3. Release the **RESET** push button to run the UART XMODEM Bootloader.  
   ![Options in UART XMODEM Bootloader](/series2-secure-boot-with-rtsl/0.3/images/sld794-image57.png)
4. Release the **PB0** push button. Press 1 (`upload gbl`) in Tera Term to upload a GBL upgrade image file.  
   ![Upload GBL option](/series2-secure-boot-with-rtsl/0.3/images/sld794-image58.png)
5. Transfer a file through XMODEM-CRC in Tera Term, navigate to **File** > **Transfer** > **XMODEM** > **Send...**.  
   ![XMODEM Transfer in Tera Term](/series2-secure-boot-with-rtsl/0.3/images/sld794-image59.png)
6. Select the target GBL upgrade image file. Click [**Open**] to upload.  
   ![Target GBL file location](/series2-secure-boot-with-rtsl/0.3/images/sld794-image60.png)
7. If no error occurs, press 2 (`run`) to start a firmware upgrade.  
   ![Run option](/series2-secure-boot-with-rtsl/0.3/images/sld794-image61.png)

###### Upgrade to Certificate-Based Secure Boot

You can upgrade the Series 2 and Series 3 devices deployed in the field from [ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) to [certificate-based Secure Boot](#signing-for-certificate-based-secure-boot) even if the [SECURE_BOOT_VERIFY_CERTIFICATE](#simplicity-commander) option in SE OTP is disabled.

```sh
commander security readconfig --serialno 440048205
```

```sh
MCU Flags
Secure Boot                    : Enabled
Secure Boot Verify Certificate : Disabled
Secure Boot Anti Rollback      : Enabled
Secure Boot Page Lock Narrow   : Disabled
Secure Boot Page Lock Full     : Disabled
```

The following procedures for the upgrade to certificate-based Secure Boot is an **IRREVERSIBLE** process.

1. Follow the procedures in [Generate Key and Signing](#generate-key-and-signing) to generate an ECDSA-P256 bootloader certificate key pair.
2. Follow the procedures in [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot) to generate the signed GBL image file with the bootloader certificate key pair in step 1. The bootloader certificate version `(--cert-version` in the `util gencert` command) in this signed GBL image file must be **equal to or higher than one (≥ 1)**.
3. Follow the procedures in [Generate a GBL Upgrade Image File](#generate-a-gbl-upgrade-image-file) to upgrade the bootloader to certificate-based Secure Boot. Use the [Private Sign Key](#generate-a-gbl-upgrade-image-file) for ECDSA-P256-SHA256 Secure Boot to sign the bootloader GBL upgrade image file if required.  
   SE will use the Public Bootloader Key to [validate](#signing-for-certificate-based-secure-boot) the bootloader image once SE identifies a bootloader certificate in the bootloader image. If the bootloader certificate version from step 2 is **higher than zero (> 0) and gets verified once**, SE will never again accept the [ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) signed bootloader image. Refer to the "_Secure Boot Procedure_" section in [UG266](https://www.silabs.com/documents/public/user-guides/ug266-gecko-bootloader-user-guide.pdf)/[Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/) for more information.  
   ```sh  
   commander security status --device EFR32MG21A010F1024 --serialno 440048205  
   ```  
   ```sh  
   SE Firmware version : 1.2.9  
   Serial number	    : 000000000000000014b457fffe045a2d  
   Debug lock	        : Disabled  
   Device erase	    : Enabled  
   Secure debug unlock : Disabled  
   Tamper status	    : Not OK  
   Secure boot	        : Enabled  
   Boot status	        : 0x18 - Failed: Secure Boot requires cert, but none found  
   DONE  
   ```
4. **(Standard Certificate-Based)** Follow the procedures in [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot) to generate the signed application image file with the Private Bootloader Key in step 1.
5. **(Advanced Certificate-Based)** Follow the procedures in [Generate Key and Signing](#generate-key-and-signing) to generate an ECDSA-P256 application certificate key pair.  
   Follow the procedures in [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot) to generate the signed application image file with the application certificate key pair in this step and the Private Bootloader Key in step 1. The application certificate version (`--cert-version` in the `util gencert` command) in this signed application image file must be **equal to or higher** than the bootloader certificate version in step 2 ([Private/Public Key Pair](#privatepublic-key-pair)).
6. Follow the procedures in [Generate a GBL Upgrade Image File](#generate-a-gbl-upgrade-image-file) to upgrade application with the signed image from step 4 or 5 for certificate-based Secure Boot. Use the [Private Sign Key](#generate-a-gbl-upgrade-image-file) or [Private Bootloader key](#generate-a-gbl-upgrade-image-file) in **step 1** for certificate-based Secure Boot to sign the application GBL upgrade image file if required.

###### Certificate Revocation

The certificate revocation is the act of invalidating a certificate when its private key shows signs of being compromised. The following procedures describe how to revoke the Series 2 and Series 3 devices' bootloader certificates deployed in the field.

1. Follow the procedures in [Generate Key and Signing](#generate-key-and-signing) to generate a new ECDSA-P256 bootloader certificate key pair.
2. Follow the procedures in [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot) to generate the signed GBL image file with the bootloader certificate key pair in step 1. The bootloader certificate version (`--cert-version` in the `util gencert` command) in this signed GBL image file must be **higher than** the certificate version in SE flash (see [Private/Public Key Pair](#privatepublic-key-pair)).
3. Follow the procedures in [Generate a GBL Upgrade Image File](#generate-a-gbl-upgrade-image-file) to upgrade the bootloader with the signed image from step 2. Use the [Private Sign Key](#generate-a-gbl-upgrade-image-file) or **existing** [Private Bootloader Key](#generate-a-gbl-upgrade-image-file) for certificate-based Secure Boot to sign the bootloader GBL upgrade image file if required.
4. **(Standard Certificate-Based)** Follow the procedures in [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot) to generate the signed application image file with the Private Bootloader Key in step 1.
5. **(Advanced Certificate-Based)** Follow the procedures in [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot) to generate the signed application image file with the Private Bootloader Key in step 1. The application certificate version (--cert-version in the util gencert command) in this signed application image file must be **equal to or higher** than the bootloader certificate version in step 2 (see [Private/Public Key Pair](#privatepublic-key-pair)).  
   You should generate a new [ECDSA-P256 application certificate key pair](#generate-key-and-signing) if the Private Application Key for the application certificate is compromised.
6. Follow the procedures in [Generate a GBL Upgrade Image File](#generate-a-gbl-upgrade-image-file) to upgrade the application with the signed image from step 4 or 5. Use the [Private Sign Key](#generate-a-gbl-upgrade-image-file) or [Private Bootloader key](#generate-a-gbl-upgrade-image-file) in **step 1** for certificate-based Secure Boot to sign the application GBL upgrade image file if required.

###### Upgrade to Secure Boot with RTSL

The following procedures describe upgrading Series 2 and Series 3 devices deployed in the field without Secure Boot to Secure Boot with RTSL.

1. **(Recommended)** Upgrade SE firmware to the latest version if available. See the _Gecko Bootloader Operation - Secure Engine Upgrade_ section in [UG266](https://www.silabs.com/documents/public/user-guides/ug266-gecko-bootloader-user-guide.pdf)/[Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/).
2. Follow the procedures in [AppBuilder](#) or [3.4.1.2 Bootloader-core Software Component](#bootloader-core-software-component) to prepare an unsigned GBL image with the required [Secure Boot configuration](02-secure-boot-process#application-firmware) for the application firmware.
3. Follow the procedures in [Generate Key and Signing](#generate-key-and-signing) to generate the ECDSA-P256 Sign Key pair for Secure Boot. The key pairs for the bootloader certificate and application certificate (advanced) are required if using [Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot).
4. Follow steps 1 to 2 in [SE Manager Key Provisioning Platform Example](#se-manager-key-provisioning-platform-example-2) if this HSE [GBL Decryption Key option](#provision-gbl-decryption-key) is selected. Use the Public Sign Key in step 3 and follow steps 1 to 3 in [SE Manager Key Provisioning Platform Example](#se-manager-key-provisioning-platform-example) to generate an **unsigned** image. Use this image to create an [application GBL upgrade image file](#generate-a-gbl-upgrade-image-file).
5. The original GBL (application Secure Boot is disabled) boots into the **unsigned** SE Manager Key Provisioning Platform Example after upgrading the application with the image file in step 4.
6. Follow steps 5 to 8 in [SE Manager Key Provisioning Platform Example](#se-manager-key-provisioning-platform-example) to install the Public Sign Key to SE OTP and GBL Decryption Key (optional) to HSE OTP. Press **SPACE** instead of **ENTER** in step 9 to **BYPASS** the programming of the [Secure Boot configuration](02-secure-boot-process#ssb) in SE OTP.  
   ```sh  
   . Press ENTER to initialize SE OTP for secure boot configuration or press SPACE to skip.  
     
   . SE manager deinitialization... SL_STATUS_OK (cycles: 5 time: 0 us)  
   ```  
   **Notes**:  
   - Programming the [Public Sign Key](#signing-for-ecdsa-p256-sha256-secure-boot) to the top page of the main flash (not included in this example) is required for the VSE device ECDSA-P256-SHA256 Secure Boot.  
   - Programming the GBL Decryption Key to the top page of the main flash (not included in this example) is required if the [default storage option](#provision-gbl-decryption-key) for GBL Decryption Key is selected and the **Require encrypted firmware upgrade files** option is enabled in step 2.
7. Follow the signing procedures in [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) or [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot) (Bootloader Image File section, skip the Secure Boot configuration for the bootloader) with the required key(s) generated in step 3 to sign the unsigned GBL image generated from step 2. Use this signed image to create a [bootloader GBL upgrade image file](#generate-a-gbl-upgrade-image-file).
8. Follow the signing procedures in [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) or [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot) (Application Image File section) with the required key(s) generated in step 3 to sign the unsigned application image generated from step 4. Use this signed image to create an [application GBL upgrade image file](#generate-a-gbl-upgrade-image-file).  
   > **Note**: For the application bootloader with storage, you can generate a [single GBL upgrade image file](#generate-a-gbl-upgrade-image-file) for signed images from steps 7 and 8.
9. The Secure Boot in SE OTP is not yet enabled, so FSB does not verify the signature when upgrading to the signed GBL in step 7. The updated GBL (application Secure Boot enabled) verifies the signature when upgrading or booting to the signed SE Manager Key Provisioning Platform Example in step 8.
10. Follow steps 9 to 10 (use **SPACE** to skip previous steps for OTP key programming) in [SE Manager Key Provisioning Platform Example](#se-manager-key-provisioning-platform-example) to program the required [Secure Boot configuration](02-secure-boot-process#ssb) in SE OTP for signed GBL.
11. Update a **signed** custom application firmware to replace the signed SE Manager Key Provisioning Platform Example used for Secure Boot with RTSL upgrade.

**Notes**:

- Refer to the "_Enabling Secure Boot RTSL on Series 2 and Series 3 Devices_" section (either Standalone Bootloaders or Application Bootloaders with Storage) in _UG266: Silicon Labs Gecko Bootloader User’s Guide for GSDK 3.2 and Lower_, [Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/) or [Silicon Labs Gecko Bootloader User’s Guide for Series 3 and Higher](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-series3-and-higher/) for details.
- The SE Manager Key Provisioning Platform Example used here is just for reference. You can modify or write a new application to automate the processes for the Secure Boot with RTSL upgrade.
- If the [Require signed firmware upgrade files](#generate-a-gbl-upgrade-image-file) option is enabled in step 2, the GBL upgrade image files from steps 8 and 11 must be signed.
- If the [Require encrypted firmware upgrade files](#generate-a-gbl-upgrade-image-file) option is enabled in step 2, the GBL upgrade image files from steps 8 and 11 must be encrypted. And the GBL Decryption Key for the corresponding [option](#provision-gbl-decryption-key) in GBL must be in place.

###### Recover Devices when Secure Boot Fails

If a Secure Boot process fails (meaning firmware image at device starting address validation fails), the only way to recover is to flash a correctly signed image.

There are two scenarios to recover the device from a Secure Boot failure:

1. Debug Lock
2. BUSLOCK

###### DEBUG LOCK (heading level 7)

The following table describes the different debug lock scenarios on recovering the Secure Boot failure device.

|**Secure Debug**|**Device Erase**|**Debug Lock**|**State**|**Recover from Secure Boot Failure**|
|---|---|---|---|---|
|Disabled|Enabled|Disabled|Unlock|Flash a correctly signed image.|
|Disabled|Enabled|Enabled|Standard debug lock|Flash a correctly signed image after standard debug unlocking the device.|
|Disabled|Disabled|Enabled|Permanent debug lock|There is no way to recover the device. Make sure the programmed image is correctly signed before locking the device.|
|Enabled|Disabled|Enabled|Secure debug lock|Flash a correctly signed image after secure debug unlocking the device.|

> **Note**: The error code in the **Boot status** of examples below depends on boot failure caused by the host image (GBL).

The following procedures describe how to recover the Secure Boot failure device from the lock states below.

- Unlocked
- Standard debug locked
- Secure debug locked

1. Follow the procedure in [Signing for ECDSA-P256-SHA256 Secure Boot](#signing-for-ecdsa-p256-sha256-secure-boot) or [Signing for Certificate-Based Secure Boot](#signing-for-certificate-based-secure-boot) to generate a correctly signed GBL.
2. **(Unlocked)** Run the `security status` command to get the boot status.  
   ```sh  
   commander security status --device EFR32MG21A010F1024 --serialno 440048205  
   ```  
   ```sh  
   SE Firmware version : 1.2.9  
   Serial number	    : 000000000000000014b457fffe045afd  
   Debug lock	        : Disabled  
   Device erase	    : Enabled  
   Secure debug unlock : Disabled  
   Tamper status	    : Not OK  
   Secure boot	        : Enabled  
   Boot status	        : 0x12 - Failed: Error while checking signature of host firmware  
   DONE  
   ```  
   Run the `flash` command to flash the correctly signed image (like `bootloader-uart-xmodem.s37`). If a failed Secure Boot is detected, the device will be erased before flashing the new image.  
   ```sh  
   commander flash bootloader-uart-xmodem.s37 --device EFR32MG21A010F1024 --serialno 440048205  
   ```  
   ```sh  
   WARNING: Failed secure boot detected. Issuing a mass erase before flashing to recover the device...  
   Parsing file bootloader-uart-xmodem.s37...  
   Writing 16384 bytes starting at address 0x00000000  
   Comparing range 0x00000000 - 0x00003FFF (16 KiB)  
   Programming range 0x00000000 - 0x00001FFF (8 KiB)  
   Programming range 0x00002000 - 0x00003FFF (8 KiB)  
   DONE  
   ```
3. **(Standard debug locked)** Run the `security status` command to get the boot status.  
   ```sh  
   commander security status --device EFR32MG21A010F1024 --serialno 440048205  
   ```  
   ```sh  
   SE Firmware version : 1.2.9  
   Serial number	    : 000000000000000014b457fffe045afd  
   Debug lock	        : Enabled  
   Device erase	    : Enabled  
   Secure debug unlock : Disabled  
   Tamper status	    : Not OK  
   Secure boot	        : Enabled  
   Boot status	        : 0x12 - Failed: Error while checking signature of host firmware  
   DONE  
   ```  
   Run the `security erasedevice` command to unlock the device.  
   ```sh  
   commander security erasedevice --device EFR32MG21A010F1024 --serialno 440048205  
   ```  
   ```sh  
   Successfully erased device  
   DONE  
   ```  
   > **Note**: Issue a power-on or pin reset to complete the unlock process.  
   Run the `flash` command to flash the correctly signed image (like `bootloader-uart-xmodem.s37`). If a failed Secure Boot is detected, the device will be erased before flashing the new image.  
   ```sh  
   commander flash bootloader-uart-xmodem.s37 --device EFR32MG21A010F1024 --serialno 440048205  
   ```  
   ```sh  
   WARNING: Failed secure boot detected. Issuing a mass erase before flashing to recover the device...  
   Parsing file bootloader-uart-xmodem.s37...  
   Writing 16384 bytes starting at address 0x00000000  
   Comparing range 0x00000000 - 0x00003FFF (16 KiB)  
   Programming range 0x00000000 - 0x00001FFF (8 KiB)  
   Programming range 0x00002000 - 0x00003FFF (8 KiB)  
   DONE  
   ```
4. **(Secure debug locked)** Run the `security status` command to get the boot status.  
   ```sh  
   commander security status --device EFR32MG21A010F1024 --serialno 440048205  
   ```  
   ```sh  
   SE Firmware version : 1.2.9  
   Serial number	    : 0000000000000000000d6ffffe0a3a5f  
   Debug lock	        : Enabled  
   Device erase	    : Disabled  
   Secure debug unlock : Enabled  
   Tamper status	    : Not OK  
   Secure boot	        : Enabled  
   Boot status	        : 0x12 - Failed: Error while checking signature of host firmware  
   DONE  
   ```  
   Run the `security unlock` command to unlock the device with the debug unlock token.  
   ```sh  
   commander security unlock --device EFR32MG21A010F1024 --serialno 440048205  
   ```  
   ```sh  
   Unlocking with unlock payload:  
   C:/Users/<username>/AppData/Local/SiliconLabs/commander/SecurityStore/device_0000000000000000000d6ffffe0a3a5f/challenge_020fc3cc9e492088d06d75d71b7aabfe/unlock_payload_0000000000111110.bin  
   Secure debug successfully unlocked  
   DONE  
   ```  
   Run the `flash` command with the `--noreset` option to flash the correctly signed image (like `bootloader-uart-xmodem.s37`).  
   ```sh  
   commander flash --noreset bootloader-uart-xmodem.s37 --device EFR32MG21A010F1024 --serialno 440048205  
   ```  
   ```sh  
   Parsing file bootloader-uart-xmodem.s37...  
   Writing 16384 bytes starting at address 0x00000000  
   Comparing range 0x00000000 - 0x00003FFF (16 KiB)  
   Erasing range 0x00000000 - 0x00003FFF (2 sectors, 16 KiB)  
   Programming range 0x00000000 - 0x00001FFF (8 KiB)  
   Programming range 0x00002000 - 0x00003FFF (8 KiB)  
   DONE  
   ```  
   > **Note**: The `--noreset` option prevents the device from returning to the secure debug lock state before flashing.
5. Run the `security status` command to check the boot status. The example below is an unlocked device.  
   ```sh  
   commander security status --device EFR32MG21A010F1024 --serialno 440048205  
   ```  
   ```sh  
   SE Firmware version : 1.2.9  
   Serial number       : 000000000000000014b457fffe045afd  
   Debug lock          : Disabled  
   Device erase        : Enabled  
   Secure debug unlock : Disabled  
   Tamper status       : OK  
   Secure boot         : Enabled  
   Boot status         : 0x20 - OK  
   DONE  
   ```

###### BUSLOCK (heading level 7)

When secure boot is enabled, the SE enforces the secure boot process through a hardware mechanism called BUSLOCK. This BUS-LOCK will halt the host-side (M33) bus so that it will not start the executing code. The BUSLOCK is enabled out of reset when secure boot is enabled.

- **BUSLOCK status for secure boot success**: BUSLOCK is enabled out of reset and is released only when the Secure boot process is successful and returns 0x20 - OK status code.

```sh
commander security status --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
SE Firmware version : 1.2.9
Serial number       : 000000000000000014b457fffe045afd
Debug lock          : Disabled
Device erase        : Enabled
Secure debug unlock : Disabled
Tamper status       : OK
Secure boot         : Enabled
Boot status         : 0x20 - OK
DONE
```

- **BUSLOCK status for secure boot failure**: BUSLOCK remains applied when the Secure Boot process fails and returns anything but 0x20 - OK status code. This keeps the BUSLOCK intact, and the host CPU is frozen, resulting in halting firmware execution on the M33 core. The device can be recovered from the BUSLOCK state by following these steps:

1. Issue a `security erase` command to the SE:  
   ```sh  
   commander security erasedevice  
   ```  
   ```sh  
   Successfully erased device  
   DONE  
   ```
2. After issuing the `security erase` command to the SE, flash the correctly signed image to recover from a secure boot failure.

##### Debugging on Secure Boot Enabled Device

Assume a correctly signed GBL image has been programmed to the device. Follow the procedures in [Generate an Unsigned Ap- plication Image](03-examples#generate-an-unsigned-application-image) to generate an unsigned application image for the GBL.

The Windows environment variable PATH should include the folder (`C:\SiliconLabs\SimplicityStudio\v5\developer\adapter_packs\commander`) that locates the `commander.exe` of Simplicity Commander.

The following sections describe how to debug an application firmware with Simplicity IDE, or IAR on a Secure Boot enabled device.

###### Simplicity IDE

This application note uses Simplicity Studio v5.2.3.1. The procedures and pictures may be different for the other versions of Simplicity Studio 5.

1. The Simplicity IDE creates a folder below (`<NAME>` is the Windows User Name on PC) in Windows when building the unsigned application image.  
   `C:\Users\<NAME>\SimplicityStudio\v5_workspace\blink_baremetal\GNU ARM v10.2.1 - Default`
2. Follow the procedures in [Signing for ECDSA-P256-SHA256 Secure Boot](03-examples#signing-for-ecdsa-p256-sha256-secure-boot) or [Signing for Certificate-Based Secure Boot](03-examples#signing-for-certificate-based-secure-boot) to create a batch file (Windows) to sign the unsigned application image and then flash it to the device. This application note uses ECDSA-P256-SHA256 Secure Boot (Using Simplicity Commander) as an example to create a `secure_boot_debug.bat` file below.  
   ```sh  
   commander convert blink_baremetal.s37 --secureboot --keyfile sign_key.pem --verify sign_pubkey.pem  
   --outfile blink_baremetal.s37  
   commander flash blink_baremetal.s37  
   ```
3. Copy the batch file in step 2 and files (`sign_key.pem` and `sign_pubkey.pem` in this example) specified in `secure_boot_debug.bat` to the folder in step 1.
4. Right-click the project in the **Project Explorer** window, and then click **Properties** to open the properties dialog.  
   ![Project Explorer window](/series2-secure-boot-with-rtsl/0.3/images/sld794-image62.png)
5. Select **C/C++ Build > Settings > Build Steps**. Enter the phrase below to the **Command:** box under the **Post-build steps** (enter text to **Description:** box is optional) to run the batch file as a post-build action. Click [**Apply and Close**] to exit.  
   ```sh  
   cmd //c 'secure_boot_debug.bat'  
   ```  
   ![Properties dialog box](/series2-secure-boot-with-rtsl/0.3/images/sld794-image63.jpg)
6. After building the project, the batch file in the **Post-build steps** overwrites the unsigned application image with the signed application image.  
   ![Overwriting in Post-build steps](/series2-secure-boot-with-rtsl/0.3/images/sld794-image64.jpg)  
   > **Note**: If the project is already up-to-date, it will not invoke the **Post-build steps** in step 5 to run the batch file. Use a dummy edit (add space or newline) on one of the source files in the project to trigger the build action.  
   ![Console window](/series2-secure-boot-with-rtsl/0.3/images/sld794-image65.jpg)  
   The application starts to run if no error in step 6.
7. Select the project in the **Project Explorer** window, click **Run→Attach to→1 Silicon Labs ARM Program** to attach to the running target for debugging on the signed application image.  
   ![Run window](/series2-secure-boot-with-rtsl/0.3/images/sld794-image66.jpg)

###### IAR

This section uses Simplicity Studio v5.4.2.0 and IAR v9.20.4. The procedures and pictures may be different for the other versions of Simplicity Studio 5 and IAR.

1. The **Overview** tab shows the **Target and Tool Settings** card on the left side. Scroll down if necessary and click [**Change Target/SDK/Generators**].  
   ![Overview tab in SCLP file](/series2-secure-boot-with-rtsl/0.3/images/sld794-image67.jpg)
2. Drop down the **CHANGE PROJECT GENERATORS** list and select **IAR Embedded Workbench Project**. Click [**Save**] to generate an IAR project.  
   ![Target and tool settings dialog box](/series2-secure-boot-with-rtsl/0.3/images/sld794-image68.png)
3. Double click the IAR workspace file (`blink_baremetal.eww`) in the **Project Explorer** window to open the IAR project. The IAR creates a folder below (`<NAME>` is the Windows User Name on PC) in Windows to store the compiled image.  
   `C:\Users\<NAME>\SimplicityStudio\v5_workspace\blink_baremetal\ewarm-iar\exe`  
   ![Project Explorer window](/series2-secure-boot-with-rtsl/0.3/images/sld794-image69.png)
4. Follow the procedures in [Signing for ECDSA-P256-SHA256 Secure Boot](03-examples#signing-for-ecdsa-p256-sha256-secure-boot) or [Signing for Certificate-Based Secure Boot](03-examples#signing-for-certificate-based-secure-boot) to create a batch file (Windows) to sign the unsigned application image. This application note uses ECDSA-P256-SHA256 Secure Boot (Using Simplicity Commander) as an example to create a `secure_boot_debug.bat` file below.  
   ```sh  
   cd C:\Users\<NAME>\SimplicityStudio\v5_workspace\blink_baremetal\ewarm-iar\exe  
   commander convert blink_baremetal.s37 --secureboot --keyfile sign_key.pem --verify sign_pubkey.pem  
   --outfile blink_baremetal.s37  
   ```
5. Copy the batch file in step 4 and files (`sign_key.pem` and `sign_pubkey.pem` in this example) specified in `secure_boot_debug.bat` to the folder in step 3.
6. Right-click the project in the workspace, and then click **Options...**.  
   ![Workspace window](/series2-secure-boot-with-rtsl/0.3/images/sld794-image70.jpg)  
   > **Note**: For GSDK v3.2 and lower, the `app_properties.c` is manually added to the IAR project.
7. Click **Build Actions** to open the **Build Actions Configuration** dialog box. Enter the phrase below to the **Post-build command line:** box to run the batch file as a post-build action. Click [**OK**] to exit.  
   ```sh  
   cmd /c "$PROJ_DIR$\ewarm-iar\exe\secure_boot_debug.bat > $PROJ_DIR$\log.txt 2>&1"  
   ```
8. After building the project, the batch file in the **Post-build command** overwrites the unsigned application image with the signed application image.  
   ![Build messages](/series2-secure-boot-with-rtsl/0.3/images/sld794-image72.png)  
   > **Note**: If the project is already up-to-date, it will not invoke the **Post-build command** in step 7 to run the batch file. Use a dummy edit (add space or newline) on one of the source files in the project to trigger the build action.  
   ![Build messages](/series2-secure-boot-with-rtsl/0.3/images/sld794-image73.png)
9. The `> $PROJ_DIR$\log.txt 2>&1` redirects the batch file output to the `log.txt` file in the IAR project folder.  
   ![Log.txt file](/series2-secure-boot-with-rtsl/0.3/images/sld794-image74.jpg)
10. If no error in step 8, click the ![start](/series2-secure-boot-with-rtsl/0.3/images/sld794-image75.jpg) icon to start debugging on the signed application image.

##### Failure Analysis

The following table describes the different scenarios when returning a Series 2 or Series 3 device to Silicon Labs for failure analysis.

|**State**|**Secure Boot Disabled**|**Secure Boot Enabled (2)**|
|---|---|---|
|Standard debug unlock|Device erase is not necessary for failure analysis.|Device erase is not necessary, but a correctly signed image is required to perform failure analysis.|
|Standard debug lock|Device erase is required to perform failure analysis.|Require device erase and correctly signed image to perform failure analysis.|
|Permanent debug lock|Cannot perform failure analysis.|Cannot perform failure analysis.|
|Secure debug lock (1)|Require debug unlock token to perform failure analysis.|Require debug unlock token and correctly signed image to perform failure analysis.|

**Notes**:

1. Follow the procedures in [Series 2 and Series 3 Secure Debug](https://docs.silabs.com/iot-security/latest/series2-secure-debug/) section _Secure Debug Unlock and Roll Challenge - Simplicity Commander_ to generate a valid debug unlock token for each device returned to Silicon Labs for failure analysis.
2. Secure boot enabled devices, especially with secure boot failure, may limit Silicon Labs' ability to determine the root cause of failure.

##### Secure Boot Status Codes

Boot status codes can be used to know the status of the boot mechanism. The `security status` command can be used to get the boot status.

```sh
commander security status --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
SE Firmware version : 1.2.9
Serial number : 000000000000000014b457fffe045afd
Debug lock : Disabled
Device erase : Enabled
Secure debug unlock : Disabled
Tamper status : OK
Secure boot : Enabled
Boot status : 0x20 - OK
DONE
```

The following table shows Status codes in Secure Boot mechanism and their description:

|**STATUS CODE**|**DESCRIPTION**|
|---|---|
|0x00|Start PUF|
|0x01|Fetch OTP for bootloader|
|0x02|Tamper test|
|0x03|Self-tests|
|0x04|TRNG Initialization failed|
|0x05|NVM Initialization failed|
|0x0B|Jump to main loop|
|0x0C|Fetch OTP for host boot|
|0x0D|Fetch pointers host firmware|
|0x0E|Fetch header host firmware|
|0x0F|Fetch host firmware|
|0x10|Check version host firmware|
|0x11|Check signature on certificate for host firmware|
|0x12|Check signature host firmware|
|0x13|Failed to get data from internal NVM|
|0x14|Finding host application properties pointer|
|0x15|Validating host application properties structure|
|0x16|Validating host application signature pointer|
|0x17|Getting SecureBoot key|
|0x18|SecureBoot requires cert, but none found|
|0x19|Updating required certificate version failed|
|0x1A|Certificate is of an older version as the last cert we validated|
|0x1B|Certificate structure version is not supported by this firmware|
|0x1C|Certificate pointer is out of range|
|0x1D|Region 0 is not closed|
|0x20|Main loop entered|
|0x80|PUF AC was somehow cleared|
|0x81|PUF failed to reconstruct after the longest delay|
|0x90|ESEC aborted booting due to catching too many successive tamper resets|
|0xFF|Finished verifying host app|

#### Transitioning to the Updated Gecko Bootloader in GSDK 4.0 and Higher

##### Transitioning to the Updated Gecko Bootloader in GSDK 4.0 and Higher

> **Note: This section replaces _AN1326: Transitioning to the Updated Gecko Bootloader in GSDK 4.0 and Higher_. Further updates to this application note will be provided here**.

Gecko Bootloader v2.x, introduced in GSDK 4.0, contains a number of changes compared to Gecko Bootloader v1.x. Many of these changes are due to an underlying framework redesign that results in an improved developer experience in Simplicity Studio 5 as well as enhanced compatibility within the GSDK. This document describes the differences between the versions, including how to configure the new Gecko Bootloader within Simplicity Studio 5.

##### Introduction

Gecko Software Development Kit suite (GSDK) version 3 (GSDK v3.0) introduced a new underlying Gecko Platform architecture based on components. Beginning with GSDK 4.0, the Gecko Bootloader now uses this underlying architecture. With Simplicity Studio 5 (SSv5) and GSDK 4.0, developers working with Gecko Bootloader will benefit from the following component-based project configuration features:

- Search and filter to find and discover software components that work with the target device
- Automatically pull in all component dependencies and initialization code
- Configurable software components including peripheral inits, drivers, middleware, and stacks
- All configuration settings are in C header files for usage outside of Simplicity Studio
- Configuration validation to alert developers to errors or issues
- Easily manage all project source via git or other SCM tools
- Managed migration to future component and SDK versions
- Simplified transitions from Silicon Labs development kits to custom hardware

Other features of the SSv5/GSDK 4.x development environment include:

- Project source management options (link to SDK sources or copy all contents to user folder)
- Graphical pin configuration through the Pin Tool
- Redesigned Radio Configurator with a fresh UI that’s more intuitive for single- and multi-PHY customization
- Iterative development (configure components, edit sources, compile, debug) using SSv5 configuration tools and third-party IDEs
- GNU makefiles as a build option

This document summarizes the differences between the Gecko Bootloader v2.x in GSDK 4.0 and earlier AppBuilder-based versions. These differences include:

- Differences between AppBuilder and the new component-based Project Configurator
- Comparison of the new Project Configurator components with AppBuilder plugins
- Features of the Pin Tool, now used instead of Hardware Configurator
- Other differences including:  
  - Linker file  
  - Additional Macros  
  - Postbuild steps  
  - Main bootloader in main flash  
  - Callbacks  
  - Storage Slots  
  - App Properties

##### About Projects

###### Creating a Project

If you have used earlier versions of SSv5, initial project creation is unchanged. If you are unfamiliar with project creation in SSv5, see the [Simplicity Studio 5 User's Guide](https://docs.silabs.com/simplicity-studio-5-users-guide/latest/ss-5-users-guide-overview/). This section highlights the differences with AppBuilder project creation and configuration.

Once you have selected an example project, an initial configuration dialog opens. Note that the Project Location, which in AppBuilder is shown on the General tab, is defined here.

![image1](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image1.png)

In SSv5, the compiler to use is specified when you create a project and, after that, is difficult to change (see [Simplicity Studio 5 User’s Guide Tips & Tricks](https://docs.silabs.com/simplicity-studio-5-users-guide/latest/ss-5-users-guide-tips-and-tricks/) for a procedure to convert an existing project from GCC to IAR). If you do not want to use the default compiler (GCC unless otherwise specified), click **BACK**, and change it on the first Project Configuration dialog.

![image2](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image2.png)

Click **FINISH** and the project will be generated.

![image3](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image3.png)

###### Project File Structure

On project generation, the required project files and folders are created.

![image4](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image4.png)

Projects always include the following parts:

- **autogen** folder: Only the autogen folder includes generated code. It includes configuration (mbedtls_config_autogen.h), init code, the linker script, and other generated code used by components, like the command descriptors for the CLI interface.
- **config** folder: Component configuration headers are located in this folder. These can be edited with the Simplicity IDE Component Editor, but directly editing the header file is also possible. The Component Editor is available through the Project Configurator’s **Configure** control, available only for configurable components.
- **gecko_sdk** folder (with version number): Contains source and binary files added by components.
- **files in the root folder**: Only the application specific files should be in the root folder, including source files, the project configurator (_.slcp_) file and the Pin Tool _(.pintool_) file. For more details, see [Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/).

![image5](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image5.png)

The first stage bootloader binary file is copied to the **autogen** folder at the time of project generation. The correct first stage binary file is located in accordance with the target hardware and placed in the **autogen,** which is later used by the postbuild script to generate a combined bootloader binary. The **autogen** folder is shown below with the first stage bootloader binary.

![image6](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image6.png)

###### Configuring a Project

Most project configuration can be done through tools in Simplicity Studio. These tools provide the functionality previously provided by AppBuilder.

- **Project Configurator**: the top-level project configuration tool through which you install and uninstall components and change other project parameters. It also provides access to other configuration tools.
- **Component Editor**: provides access to the configurable parameters of an individual component.
- **Pin Tool**: configures peripherals.

Once you create the project, the Project Configurator tabbed interface is presented, including:

- Overview
- Software Components
- Configuration Tools

You can always re-open the Project Configurator by double-clicking the <projectname>.slcp file. For details on using the Project Configurator and Component Editor, see the [Simplicity Studio 5 User's Guide](https://docs.silabs.com/simplicity-studio-5-users-guide/latest/ss-5-users-guide-overview/).

###### OVERVIEW Tab (heading level 7)

The OVERVIEW tab, like AppBuilder’s General tab, shows the target part information, and a description of the project.

![image7](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image7.png)

Note that there is no **Generate** control. Changes made through Project Configurator are auto-saved and the project files are auto-generated. The Force Generation control on the Project Details card is provided for use only in the event of a system problem, where auto-generation fails.

The Project Generators interface controls generation of additional files for import into other IDEs. It does not change the compiler used to build the application image.

###### SOFTWARE COMPONENTS Tab (heading level 7)

The SOFTWARE COMPONENTS tab shows the available components and those that are already installed in the example code.

![image8](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image8.png)

Component groupings are subject to change in SDK releases, as new components are added and organization is updated. The most reliable way to find a specific component is to search for it. You can also filter the displayed components using the checkboxes at the top.

Installed components have a circled checkmark to the left. Configurable components have a gear icon to the right.

Select a component to see details about it. Click **Install** to install the component or **Uninstall** to remove it. If it is configurable, a **Configure** control is shown in the upper right.

![image9](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image9.png)

Click that control or the gear icon next to an installed component name to open the Component Editor in a new tab.

![image10](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image10.png)

Save any changes to trigger file generation.

###### CONFIGURATION TOOLS Tab (heading level 7)

The CONFIGURATION TOOLS tab is a quick way of opening useful tools for project development.

![image11](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image11.png)

##### Components

This section maps the various Gecko Bootloader components to the previously available plugins (in the AppBuilder workflow). Although the basic subgroups might remain the same, specific component locations are subject to change across various GSDK versions. Therefore, it is good practice to search for individual components by their names rather than the specific subgroups that they belong to.

|AppBuilder Plugin|Simplicity Studio v5 Component|Additional Comments|
|---|---|---|
|Communication| | |
|BGAPI UART DFU|BGAPI UART DFU| |
|EZSP-SPI|EZSP-SPI| |
|UART XMODEM|UART XMODEM| |
|XMODEM Parser|XMODEM Parser| |
|Core| | |
|Application Upgrade Version Check|Application Upgrade Version Check| |
|Bootloader Core|Bootloader Core| |
|GBL Compression (LZ4)|GBL Compression (LZ4)| |
|GBL Compression (LZMA)|GBL Compression (LZMA)| |
|Image Parser|Image Parser| |
|Image Parser with legacy EBL support|Image Parser with legacy EBL support| |
|Image Parser without encryption support|Image Parser without encryption support| |
|“|Bootloader Include Parser|New component that includes common Image Parser header files|
|“|Bootloader in Main Flash|New component that enables the user to place bootloader in main flash for xG13 and xG14 devices|
|Drivers| | |
|Delay|Bootloader Delay Driver| |
|SPI Master|Bootloader SPI Controller USART Driver| |
|SPI Slave|Bootloader SPI Peripheral USART Driver| |
|UART|Bootloader UART Driver| |
|EUART|Bootloader EUART Driver| |
|“|Bootloader SPI Controller EUSART Driver|New component supporting SPI EUSART driver. This component can be used with devices supporting EUSART interface.|
|“|Bootloader SPI Peripheral EUSART Driver|New component supporting SPI EUSART driver. This component can be used with devices supporting EUSART interface.|
|Storage| | |
|Common Storage|Common Storage| |
|Common Storage (single storage slot only)|Common Storage (single storage slot only)| |
|Internal Storage|Internal Storage| |
|SPI Flash Storage|SPI Flash Storage| |
|“|Bootloader Storage Slot Setup|New component that facilitates configuring storage slots for Internal and SPI Flash based bootloaders|
|Utils| | |
|Crypto|Crypto| |
|Cyclic Redundancy Check|Cyclic Redundancy Check| |
|Debug|Debug| |
|EMLIB|EMLIB Peripheral HAL|This component is no longer part of the Bootloader module. EMLIB component(s) can be found under Platform > Peripheral|
|EZSP GPIO Activation|EZSP GPIO Activation| |
|GPIO Activation|GPIO Activation| |
|SE Manager|SE Manager|This component is now available under Platform > Security. This component is only applicable for Series 2 devices.|
|Token Management|Token Management| |
|mbed TLS|Mbed TLS …|These components are available under Platform > Security|
|Bootloader Interface| | |
| |App Properties|A new component to configure application properties (version), which is used during creation of a GBL file using Simplicity Commander.|
|Bootloader-interface|Bootloader Application Interface| |

##### Pin Tool

In contrast to the AppBuilder workflow, Simplicity Studio v5 offers the **Pin Tool** configuration tool, which can be used to configure the various pins of the MCU used in the project. Pin Tool can be used in standalone mode through the tile on the **Configuration Tools** tab.

![image12](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image12.png)

Pin Tool-related configuration is also available through the Component Editor of all applicable configurable components, making it a one-stop solution for configuring pin details and setting user configuration details. Following is the list of configurable components that also include Pin Tool configuration (wherever applicable) in their configuration wizard.

|Configurable Component|Configuration Options|
|---|---|
|UART XMODEM|Menu Idle Timeout|
|Bootloader Core|All options that were available for the bootloader plugin are now available in the Bootloader Core component. Options include: Enable Secure Boot, Enable certificate support, Prevent bootloader write/erase, etc|
|Bootloader UART/EUART Driver|Configuration options include: USART settings, USART peripheral, Serial VCOM Enable/Disable|
|Bootloader SPI Controller USART/EUSART Driver|Configuration options include: Frequency, Peripheral and SPI Port Settings|
|Bootloader SPI Peripheral USART/EUSART Driver|Configuration options include: Tx Buffer Size, Rx Buffer Size, Peripheral and Port Settings|
|Bootloader Storage Slot Setup|Supports configuration of storage slot (start addresses and slot lengths) up to a maximum of 3 slots|
|Common Storage/Common Storage (single storage slot only)|Configuration options include: Start address of bootload info|
|Internal Storage|Configuration options include: Enable DMA based MSC Write, DMA channel to reserve|
|SPI Flash Storage|Enables configuration of the list of SPI flash devices that must be enabled in the bootloader image|
|Debug|Enable/disable Debug prints/asserts|
|EZSP GPIO Activation|Configuration options include: Properties of SPI NCP, WAKE INT Pin, HOST INT Pin|
|GPIO Activation|Configuration options include: Properties of Bootloader entry, Button Pin configuration|

The configuration options of any installed configurable component can be accessed by clicking **Configure**. Refer to section [2 Project Creation](_Ref88045121) for more details on the Component Editor.

As an example, in the following procedure the Bootloader SPI Controller USART Driver is configured using the Component Editor and the pin configuration is verified using the Pin Tool.

1. Pin Tool configuration before installing the SPI Controller component. The device used for this example is the EFM32GG11 Giant Gecko Starter Kit board (BRD2204A Rev B00).  
   ![image13](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image13.png)
2. Install the Bootloader SPI Controller USART driver component.  
   ![image14](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image14.png)
3. Click **Configure** to open the Component Editor and configure the USART peripheral as shown.  
   ![image15](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image15.png)
4. Open the Pin Tool.  
   ![image16](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image16.png)
5. The pin configurations are shown. Notice that pins PA12, PA13, PB6, PB7, and PB8 are reserved for USART0.  
   ![image17](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image17.png)

##### Linker File

The linker files for GCC and IAR are now generated during the project generation step. The linker files for the bootloader are generated from template files using the Jinja template engine. The template files are included with theGecko SDK Suite (GSDK), which is installed through Simplicity Studio. The jinja template files are located at **<path-to-simplicity-studio-installation>|v5|developer|sdks|gecko_sdk_suite|<gecko_sdk_version>|platform|common|toolchain|**.This path contains folders for both **gcc** and **iar**.

##### Additional Macros

In the AppBuilder workflow, the user could define additional compiler macros used during compilation of the bootloader application. In Gecko Bootloader v2.x, these additional macros are now part of the various components. These components have these macros defined internally, and those macros which require user intervention are available to the user as configuration options through the Component Editor.

##### Postbuild Steps

For Series 1 devices, the main stage bootloader needs to be combined with the first stage bootloader, which results in a combined bootloader binary. To accomplish this, a postbuild script is made available with the GSDK, which combines the appropriate first stage bootloader with the main stage bootloader to produce a combined bootloader binary. A postbuild step must be explicitly added through the Project’s settings so that the postbuild script can be run after the bootloader build is complete. For more details, see [Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/).

> **Note**: Adding the postbuild steps is mandatory when building bootloader binaries for Series 1 devices. If the postbuild step is not configured correctly, Simplicity Studio cannot build a combined bootloader binary image.

##### Main Bootloader in Main Flash

For xG13 and xG14 devices, the entire main stage bootloader might not fit into the bootloader flash if the user installs some extra components. In such scenarios, the main stage bootloader can be placed in the main flash by installing the **Bootloader in Main Flash** core component in the bootloader project.

![image18](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image18.png)

For more information on this component, see [Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/).

##### Callbacks

In contrast to the AppBuilder workflow, callbacks are now part of **btl_callbacks_stub.c**. This file is added to the project when the **SPI Flash Storage** component is installed. This file contains dummy implementation of callbacks that the bootloader relies on. This file can be found in **gecko_sdk_<version> > platform > bootloader > storage > btl_callbacks_stub.c** as shown.

![image19](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image19.png)

##### Storage Slots

Gecko Bootloader supports the Application Bootloader mode. This mode supports single/multiple storage slots that can be configured at compile time. In Gecko Bootloader v2.x, the storage slots can now be configured in the **Bootloader Storage Slot Setup** component using the Component Editor. A maximum of 3 storage slots can be configured.

![image20](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image20.png)

##### App Properties

Previously, in the AppBuilder workflow, a variable of type **AppProperties_t** had to be configured to add application properties to the application. This was then later used by Simplicity Commander during gbl file creation. Beginning with Gecko Bootloader version 2.x, a new configurable component named **App Properties** can be used to configure the application properties. This component can be installed using the Component editor which is available as part of Simplicity Studio. It is also installed automatically as a dependency on installing the **Bootloader Application Interface** component in the project. The component allows the user to configure the application version using Simplicity Studio’s Component Editor as shown below.

![image21](/bootloader-transitioning-guide-gsdk-v40-and-higher/0.1/images/sld795-image21.png)

For more details on how to configure this component, see [Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/).

### Non-Volatile Memory Use

#### Non-Volatile Data Storage

This section offers an introduction to non-volatile data storage and describes how to use NVM3 data storage.

- [**Non-Volatile Data Storage Fundamentals**](/openthread/3.1.0/non-volatile-data-storage-fundamentals): Introduces non-volatile data storage using flash and the three different storage implementations offered for Silicon Labs microcontrollers and SoCs: Simulated EEPROM, PS Store, and NVM3.
- [**Using NVM3 Data Storage**](/openthread/3.1.0/using-third-generation-nonvolatile-memory): Explains how NVM3 can be used as non-volatile data storage in various protocol implementations.

#### Non-Volatile Data Storage Fundamentals

##### Non-Volatile Data Storage Fundamentals

> **Note: This section replaces _UG103.07: Non-Volatile Data Storage Fundamentals_. Further updates to this user guide will be provided here**.

This guide provides a general introduction to non-volatile data storage using flash, with a focus on the three different dynamic data storage implementations offered for Silicon Labs microcontrollers and radio SoCs (Systems on Chip). It offers a comparison of the three implementations and provides recommendations on when to use each. Additional detail on using the various data storage implementations may be found in the following documents:

- [Using Tokens for Non-Volatile Data Storage](https://docs.silabs.com/gecko-platform/latest/using-tokens-for-non-volatile-storage/index.html)
- _AN703: Using Simulated EEPROM Version 1 and Version 2 for the EFR32 SoC Series 1 Platform_
- [Using Third Generation Non-Volatile Memory (NVM3) Data Storage](https://docs.silabs.com/gecko-platform/latest/using-third-generation-nonvolatile-memory/)

Silicon Labs’ _Fundamentals_ series covers topics that project managers, application designers, and developers should understand before beginning to work on an embedded networking solution using Silicon Labs chips, networking stacks such as EmberZNet PRO or Silicon Labs Bluetooth, and associated development tools. These guides can be used as a starting place for anyone needing an introduction to developing wireless networking applications, or who is new to the Silicon Labs development environment.

##### Introduction

Non-volatile memory (NVM) is memory that persists even when the device is power-cycled. On Silicon Labs microcontrollers and radio SoCs, the NVM is implemented as flash memory. In many applications the flash is not only used to store the application code but also to store data objects that are written and read frequently by the application. As flash memory can only be erased a limited number of times, several methods exist to efficiently read and write non-volatile data without wearing out the flash.

Some data is considered manufacturing data that is written only once at manufacturing time. This document is concerned with dynamic data that changes frequently over the life of the product.

This document provides an introduction to the main design options for dynamic data storage in microcontrollers and radio SoCs, along with guidelines on what factors affect flash lifetime. In addition it introduces the main flash data storage implementations offered by Silicon Labs:

- NVM3
- Simulated EEPROM version 1 (SimEEv1) and version 2 (SimEEv2)
- Persistent Store (PS Store)

##### Implementations of Non-Volatile Data Storage

This chapter introduces some of the challenges and design options when implementing non-volatile data storage in flash memory. It describes at a high level how non-volatile data storage is implemented in flash memory in PS Store, SimEEv1/v2, and NVM3.

###### Basic Implementations

One of the characteristics of flash memory is that it is writable in smaller pieces, usually 32-bit words, while it can only be erased in larger chunks, usually pages of several kilobytes. When using flash as data storage, the most straightforward implementation option would be to store each data object in its own flash page, as shown in the following figure. This way each object can be erased and re-written without influencing the other data objects. Usually the data objects are much smaller than the page size, and this solution is not an effective way of using the available flash space.

![One Object Per Page](/non-volatile-data-storage-fundamentals/0.1/images/one-object-per-page.png)

To avoid wasting flash space we can store several data objects in one flash page, as shown in the following figure. This solution then introduces a challenge when we want to write a new value to one of the data objects. In that case the page must be erased before all objects are written back to the page again, including the object we changed. As flash memory can only endure a limited amount of flash erases before the flash cells are worn out, this solution results in a very limited device lifetime.

![Multiple Objects in One Flash Page](/non-volatile-data-storage-fundamentals/0.1/images/multiple-objects-in-one-flash-page.png)

To avoid erasing the flash page for every object write, we can instead write new versions of each object to new empty locations in the flash page. This is a simple form of wear-levelling that reduces the number of page erases. However, this requires that we store some identification information along with the object data that tells us what object the data belongs to, so we know how to find the latest version of the data object. This is illustrated in the following figure, where a key is added to each version of the object data to identify what object the data belongs to. When accessing an object we then need to search through the flash page for the most recent version of the object. In this case the newest version is the one with the highest address, as we start writing from the lowest address of the page.

![Object Versions With Keys](/non-volatile-data-storage-fundamentals/0.1/images/object-versions-with-keys.png)

###### Handling Resets and Power Failures

As we fill up the flash page with new versions of the data objects, eventually no room is left to write new object data. At this point we need to erase the page and start over by writing only the latest versions of each object to the flash page. In many applications, however, power failures or resets can happen at any time, and we should not risk losing data if this occurs. If a reset occurs after the flash page is being erased, but before the data objects are written back, then we will lose this data. To handle this case we introduce a second page, to which we copy the latest version of the data objects, before erasing the original page, as shown in the following figure Then we can start filling the second page with data. When the second page is full we move the latest data back to the first page and so on. This mechanism, where the storage is alternated between two flash pages, is how PS Store operates.

![Latest Data Copied to New Page Before Erase](/non-volatile-data-storage-fundamentals/0.1/images/latest-data-copied-to-new-page-before-erase.png)

###### Introducing Virtual Pages

In some applications we write to data objects frequently, and the flash pages therefore also need to be erased frequently. As the data objects in the implementation so far are only spread across two flash pages, each page will frequently get erased and the flash lifetime will be limited. To increase the lifetime we can use more flash pages to store the data objects. In this example, instead of two physical pages, we operate with two virtual pages (A and B) that each consist of several physical flash pages. The virtual pages are erased and written to as if they were one larger flash page. The difference is simply that each virtual page is bigger and we can write more data before we need to erase the virtual page, hence the lifetime is extended. In addition to increasing flash lifetime, using several flash pages per virtual page allows you to store more or larger objects. SimEEv1 uses this design, with each virtual page consisting of two flash pages, A and B, as shown in the following figure.

![Virtual Pages](/non-volatile-data-storage-fundamentals/0.1/images/virtual-pages.png)

In some applications the time it takes to write a non-volatile data object must be minimized so as to not interfere with the timing of other critical operations. If an object write is triggered when a virtual page is full, the latest version of all objects must first be copied to the new virtual page before writing the new object version in question to the new page. All objects must be copied over immediately to allow the first page to be quickly erased so we can move data there in case of a failure. Copying all objects at once increases the worst-case object write time.

To reduce the write times, a third virtual page can be introduced that is always kept erased. Instead of copying over the latest version of every object when the first page is full, we can instead copy over only some of the objects. The rest of the objects are copied over as a part of subsequent write operations. This way we spread the copy process to the new page over more write operations, hence each write operation takes less time to complete. With this approach we have live object data spread out across two virtual pages and the third page is always kept erased, so we have somewhere to move the data to in case of a failure. SimEEv2 uses this implementation with 3 virtual pages where each virtual page consists of 6 flash pages.

###### Basic Storage

Basic storage is defined as the size of all the latest version of all objects, including any overhead stored with them. Except for NVM3, every time a flash page or virtual page is erased we must first move the basic storage over to a new page. The basic storage size is important, as it determines how much flash space is left over in a page to store any new versions of the object data. If the basic storage takes up almost the entire page, we can only write a few new object versions before we need to move to a new page and erase the old one. This leads to frequent page erases and a short flash lifetime.

NVM3 only copies unique objects that are stored in the page that will be erased. If newer versions of the object exist in other pages, the object will not be copied.

###### FIFO Model

The flash data storage implementations can be modelled as a First-In First-Out (FIFO) buffer (see the following figure), where we write new versions to the input of the FIFO. As the FIFO fills up, we need to free up space by erasing one or more pages at the end of the FIFO. Before we erase a page, we need to copy any object versions for which no newer version of the same object exists in the rest of the flash pages. Other object data can be discarded, because newer versions exist. To maximize flash lifetime we want to copy as few object versions as possible, so that most of the writes to the flash memory are new versions of the objects. If the FIFO is implemented over a large flash space, it is more likely that the new version of the object has been written and that object versions at the end of the FIFO can be discarded. In this case the flash page can be erased with few or no object versions copied.

![FIFO Buffer](/non-volatile-data-storage-fundamentals/0.1/images/fifo-buffer.png)

A drawback of using a few virtual pages is that the available memory for the FIFO is limited to only the virtual pages that can hold live data. For implementations using two virtual pages, like SimEEv1, this means only half the storage space is used for the FIFO, while for SimEEv2 two thirds of the storage space is used for the FIFO.

To allow a higher portion of the storage space to be used for the FIFO, we can instead implement the FIFO as a circular buffer over the entire flash storage space allocated, as shown in the following figure. In this implementation we always need to keep enough erased pages in front of the leading edge of the buffer to write the largest-sized object in case of a failure. When the FIFO fills up to reach the critical number of erased flash pages, we copy over any object versions that have not been superseded and erase the page at the back of the FIFO. This means that, instead of keeping a full virtual page erased, we only need to keep enough space erased to fit the largest-sized object. We can use the rest of the space for the FIFO. NVM3 is implemented as a circular buffer implemented over the entire storage space, thus increasing flash lifetime compared to implementations using smaller virtual pages.

![Circular Buffer](/non-volatile-data-storage-fundamentals/0.1/images/circular-buffer.png)

###### Counter Objects

For some types of data, the storage format can be optimized for the flash medium. EFR32 Series 0 and 1 can write each word two times, allowing for half word updates.  Series 2 devices allow just one write per word.

For example, counter values are usually incremented by 1 or some other low value every time they are written. Normally this means that we would have to write the entire counter value in addition to identification bytes every time the counter is incremented. We can optimize this by only storing a start value in addition to the identification value the first time a counter is written. Then we reserve a number of the words following the initial value for writing increments. The flash words in Silicon Labs devices EFR32 Series 0 and 1 can write words twice between each erase by keeping a 1 in the bits that are not to be changed.

As an example, assume we are writing two 16 bit values, 0xAAAA and 0x5555. To safely write them in the same flash word this method can be used:

- Write 0xFFFFAAAA (word in flash becomes 0xFFFFAAAA)
- Write 0x5555FFFF (word in flash becomes 0x5555AAAA)

Note that there is a maximum of two writes to the same word between each erase due to a physical limitation of the flash.

To find the current counter value, we start with the initial value and add the increment values in the halfwords following the initial value. This means that we only need to write one halfword for each increment, instead of the whole counter value and identification value. NVM3 and SimEEv1/v2 support counter objects.

###### Indexed Objects

When storing data such as arrays in flash, we often only update one index at a time. If we store the whole array as one regular object, we must write the whole array to flash even if just one index is updated. Instead of storing the entire array in one object, we can divide each data array over multiple objects and only update the objects holding the changed array indexes. While it is possible to split arrays into multiple objects manually for all the Silicon Labs storage implementations, SimEEv1/v2 allow all of the indexes to share one object key. The index entry into the array to be looked up is then provided as a separate parameter.

NVM3 does not support reading and writing parts of an object. If the application wants to access indexes individually, each index must be accessed using a unique key.

##### Dynamic Data Storage Implementations

This chapter introduces some of the dynamic data storage implementations offered by Silicon Labs, including SimEEv1 and SimEEv2, PS Store, and NVM3.

###### SimEEv1/v2

SimEEv1/v2 can be used with EmberZNet PRO and Silicon Labs Connect (installed with the Flex SDK). SimEEv1 uses two virtual pages with a fixed total size of 8 kB, while SimEEv2 uses three virtual pages with a fixed total size of 36 kB.

One characteristic of SimEEv1/v2 is that all objects are defined with size and type at compile time, hence a new object cannot be created or deleted at runtime.

Silicon Labs provides a plugin for upgrading SimEEv1 data to SimEEv2.

Information about the SimEEv1/v2 implementations is found in _AN703: Using Simulated EEPROM Version 1 and Version 2 for the EFR32 Series 1 SoC Platform_.

###### PS Store

PS Store can be used with Bluetooth devices on all platforms except for EFR32 Series 2. PS Store API commands are used to manage user data in PS keys in the flash memory of the Bluetooth device. User data stored within the flash memory is persistent across reset and power cycling of the device. The persistent store size is 2048 bytes and uses two flash pages for storage. As Bluetooth bondings are also stored in this area, the space available for user data also depends on the number of bondings the device has at the time. The size of a Bluetooth bonding is around 150 bytes. With its simple implementation and few storage flash pages, PS Store is the smallest of Silicon Labs' non-volatile storage options. PS Store allows objects to be created and deleted at runtime.

Information about the PS Store APIs may be found in the _Bluetooth API Software Reference Manual_.

###### NVM3

The third generation Non-Volatile Memory (NVM3) data storage driver is an alternative to SimEEv1/v2 and PS Store. The NVM3 driver provides a means to write and read data objects (key/value pairs) stored in flash. Wear-leveling is applied to reduce erase and write cycles and maximize flash lifetime. The driver is resilient to power loss and reset events, ensuring that objects retrieved from the driver are always in a valid state. Because NVM3 can be used with several protocols, such as Bluetooth and EmberZNet PRO, it allows a single data storage instance to be shared in Dynamic Multiprotocol (DMP) applications.

Some of the main features of NVM3 are as follows:

- Key/value pair data storage in flash
- Runtime creation and deletion of objects
- Persistence across power loss and reset events
- Wear-leveling to maximize flash lifetime
- Object sizes from 0 to 4096 bytes
- Configurable flash storage size (minimum 3 flash pages)
- Cache with configurable size for fast object access
- Data and counter object types
- Compatibility layers with token and PS Store APIs provided
- Single shared storage instance in multiprotocol applications
- Repack API to allow application to run clean-up page erases during periods with low CPU load

Detailed information on NVM3 is documented in the EMDRV->NVM3 section of the [Gecko HAL & Driver API Reference Guide](http://devtools.silabs.com/dl/documentation/doxygen/). Users who are accessing NVM3 through its native API should refer to this API reference guide for information. Users who are developing dynamic multiprotocol applications should refer to [Using Third Generation Non-Volatile Memory (NVM3) Data Storage](https://docs.silabs.com/gecko-platform/latest/using-third-generation-nonvolatile-memory/).

##### Comparing Non-Volatile Data Storage Implementations

The following table presents an overview of the main features of the various Silicon Labs Non-Volatile Data Storage implementations.

|Feature|NVM3|SimEEv1|SimEEv2|PS Store|
|---|---|---|---|---|
|Compatible devices|EFM32, EFR32|EFR32 Series 1|EFR32 Series 1|EFR32 Series 1|
|Compatible radio protocols|EmberZNet, Connect, Z-Wave, OpenThread, Bluetooth|EmberZNet, Connect|EmberZNet, Connect|Bluetooth|
|Flash used for storage|3 or more flash pages|8 KB|36 KB|4 KB|
|Virtual pages|NA|2|3|NA|
|Max basic storage (Sum of all object data with overhead)|Variable (see [Using Third Generation Non-Volatile Memory (NVM3) Data Storage](https://docs.silabs.com/gecko-platform/latest/using-third-generation-nonvolatile-memory/) for details)|2 KB|8 KB|2 KB|
|Max number of objects|Limited by basic storage|254|254|Limited by basic storage|
|Max object size (bytes)|Configurable 208-4096|254|254|56|
|Object creation/deletion|Runtime|Compile time|Compile time|Runtime|
|Compiled flash size excluding data storage|9.1 KB|3.5 KB|5.4 KB|1.6 KB|
|Counter object support|Yes|Yes|Yes|No|
|Indexed object support|Partial (note 1)|Yes|Yes|No|
|Overhead per object (bytes)|4 (size <= 128 bytes); 8 (size > 128 bytes)|2|6|10|
|Counter object size including overhead (bytes)|212|60|56|NA|
|Counter increments before rewrite|100 (EFR32 Series 1); 50 (EFR32 Series 2)|25|25|NA|

Note 1: When using NVM3, indexed objects are implemented by storing each index in a separate NVM3 object.

###### Flash Lifetime

All Silicon Labs Flash Data Storage implementations use some form of wear-levelling to prolong flash lifetime. The effectiveness of the wear-levelling depends on the implementation, the type of data stored, and how often it is updated. The main factors that affect wear-levelling and thereby flash lifetime are:

- Size of flash used for data storage: More flash area gives longer flash lifetime. For NVM3, the number of flash pages used for data storage can be configured, while the rest of the implementations use fixed storage sizes.
- Stored overhead per object: When writing data to the object storage, some overhead bytes are added to identify the data. Implementation with less overhead means the data objects take up less space in flash, and gives longer flash lifetime.
- Alignment to minimum object size: Objects are stored in multiples of the smallest object size. If the data size does not align with this size, padding bytes are added, which adds to the stored data and reduces flash lifetime. For instance, when storing 16-bit objects, NVM3 and PS Store add two extra bytes of padding in addition to the overhead bytes. SimEEv1/v2 are able to store 16-bit data objects without padding.
- Remaining storage after basic storage: For implementations using virtual pages, when switching to a new virtual page one instance of each object is written to the page. The rest of the virtual page can then be used to store new writes of the objects. If a lot of space is used to store one instance of each object, little space is left in the virtual page to use for wear-levelling the subsequent object writes. The flash lifetime will therefore be reduced when the total amount of object data is large relative to the virtual page size. Even for NVM3, where virtual pages are not used, the flash lifetime is limited by the available space of the total NVM3 storage.

To help monitor the actual flash wear, NVM3 and SimEEv1/v2 include function calls for reporting the number of page erases of the data storage flash pages. These erase counters can be read during accelerated lifetime testing of a product to verify if the flash wears at an acceptable rate.

#### Using Third Generation Non-Volatile Memory (NVM3) Data Storage

##### Using Third Generation Non-Volatile Memory (NVM3) Data Storage

> **Note: This section replaces _AN1135: Using Third Generation Non-Volatile Memory (NVM3) Data Storage_. Further updates to this application note will be provided here**.

The NVM3 driver provides a means to write and read data objects (key/value pairs) stored in flash. Wear-leveling is applied to reduce erase and write cycles and maximize flash lifetime. The driver is resilient to power loss and reset events, ensuring that objects retrieved from the driver are always in a valid state. A single NVM3 instance can be shared among several wireless stacks and application code, making it well-suited for multiprotocol applications. This application note explains how NVM3 can be used as non-volatile data storage in Zigbee (EmberZNet), Open Thread, Z-Wave, Bluetooth, Connect, and WiseConnect applications.

Version 3.x of the Gecko SDK Suite, used with Simplicity Studio 5, introduced a new component-based project architecture that replaced AppBuilder. Eventually all wireless protocols will move to the component-based architecture. This document addresses both this new approach to NVM3 configuration as well as the AppBuilder configuration still in use.

###### Key Points

- Key/value pair data object storage in flash
- Wear-leveling to maximize flash lifetime
- Resilient to power and reset events
- Shared by Zigbee, Connect, OpenThread, Z-Wave, and Bluetooth stacks
- Compatible with PS Store and Token APIs through wrappers
- Data upgradable from Simulated EEPROM version 2 to NVM3

##### Overview

The third generation Non-Volatile Memory (NVM3) data storage driver is for storing persistent data in flash.

NVM3 is designed to work on all Silicon Labs wireless stacks running on EFR32, Series-3, and Si91x devices, as well as MCU applications running on EFM32.

Some of the main features of NVM3 are:

- Key/value pair data storage in flash
- Runtime object creation and deletion
- Persistence across power loss and reset events
- Wear-leveling to maximize flash lifetime
- Object sizes configurable up to 4096 bytes
- Configurable flash storage size (minimum 3 flash pages)
- Cache with configurable size for fast object access
- Data and counter object types
- Compatibility layers included for several Silicon Labs persistent storage APIs
- Single shared storage instance in multiprotocol applications
- Repack API to allow application to run clean-up page erases during periods with low CPU load

NVM3 is described in detail in the NVM3 Documentation on [https://docs.silabs.com/](https://docs.silabs.com/). The rest of this document assumes you are familiar with that content.

While the NVM3 API can be used directly, NVM3 is also used as the underlying storage mechanism for several other persistent storage APIs provided by Silicon Labs:

- Token API used in Zigbee EmberZNet and Connect applications
- Persistent Storage API used in Silicon Labs Bluetooth applications

##### NVM3 Default Instance

Several NVM3 instances can be created on a device and live independently of each other, but to save memory it is usually desirable to use only one NVM3 instance as each instance adds some overhead. For applications based on Silicon Labs wireless stacks, a common default instance is used. This allows Dynamic Multiprotocol (DMP) applications that combine several wireless stacks to share the same NVM3 instance.

The number of flash pages used for the NVM3 default instance is configurable, but this setting must match if an application includes several stacks that all use the default instance.

> **IMPORTANT**: When creating an application that includes an NVM3 instance for a device that already contains an NVM3 instance in flash, the number of flash pages configured for the NVM3 instance must match the number of flash pages for the NVM3 instance already found on the device. Therefore it is not possible to change the size of an NVM3 instance once it has been installed on a device, without first erasing the flash pages holding the NVM3 instance and the NVM3 objects stored there.

NVM3 has a cache to speed up access to NVM3 objects. The cache size must be set to a value greater than or equal to the number of objects found in NVM3. This includes the number of NVM3 objects created through the native NVM3 API and any objects created through higher level APIs such as the Token API. The cache must also be large enough to hold any deleted NVM3 objects. The `nvm3_countObjects()` and `nvm3_countDeletedObjects()` functions can be used to find the number of live and deleted objects in NVM3 at any given point. Silicon Labs recommends checking these functions after initialization of all NVM3 objects, both through the native NVM3 API and higher level APIs such as the token API, to figure out the correct size of the NVM3 default cache size.

###### NVM3 Default Instance Key Space

NVM3 uses a 20-bit key to identify each object. To avoid using the same key for more than one object, the NVM3 key space for the default NVM3 instance has been divided into several domains as outlined in the following table. For example, NVM3 objects defined in the Zigbee EmberZNet stack should use NVM3 keys in the range 0x10000 to 0x1FFFF, while user application tokens should use keys below 0x10000. Note that any user defined NVM3 objects should be placed below 0x10000.

|**Domain**|**NVM3 Key**|
|---|---|
|User|0x00000 - 0x0FFFF|
|Zigbee EmberZNet stack|0x10000 - 0x1FFFF|
|OpenThread stack|0x20000 - 0x2FFFF|
|Connect stack|0x30000 - 0x3FFFF|
|Bluetooth stack|0x40000 - 0x4FFFF|
|Z-Wave stack|0x50000 - 0x5FFFF|
|Bluetooth mesh stack|0x60000 - 0x6FFFF|
|Reserved|0x70000 - 0x7FFFF|
|Apple HomeKit|0x80000 - 0x80FFF|
|Zigbee Cluster Library (ZCL)|0x81000 - 0x81FFF|
|dotdot|0x82000 - 0x82FFF|
|Platform CLI SM|0x83000 - 0x830FF|
|Platform Crypto|0x83100 - 0x870FF|
|Bootloader|0x87100 - 0x871FF|
|Matter|0x87200 - 0x87FFF|
|AWS Late Provision|0x88000 - 0x88FFF|
|KNX IoT|0x89000 - 0x897FF|
|Antenna Calibration (Platform)|0x89800 - 0x8987F|
|Antenna Calibration (RAIL)|0x89880 - 0x898FF|
|Trackers|0x89900 - 0x899FF|
|Aliro|0x89A00 - 0x89FFF|
|Reserved|0x8A000 - 0x8DBFF|
|Platform|0x8DC00 - 0x8FFFF|
|Wi-SUN|0x90000 - 0x9FFFF|
|Sidewalk|0xA0000 - 0xAFFFF|
|Wi-Fi|0xB0000 - 0xBFFFF|
|Reserved|0xC0000 - 0xFFFFF|

##### NVM3 in the Simplicity Studio 5 Project Configurator

The Simplicity Studio 5 Project Configurator, used with Silicon Labs Bluetooth v3.x, Connect v3.x, EmberZNet Zigbee v7.x, and OpenThread SDK applications, includes an NVM3 Core component. A separate component is also provided for the NVM3 default instance, which will initialize this instance.

![screenshot](/using-third-generation-nonvolatile-memory/0.2/images/sld770-image1.png)

For Si91x devices, the NVM3 default instance can be configured under NVM3 for Si91x.

![screenshot](/using-third-generation-nonvolatile-memory/0.2/images/sld770-image4.png)

The **NVM3 Default Instance** component provides the following configurations:

- **Cache Size**: Number of objects to cache. To reduce access times, this number should be equal to or higher than the number of live and deleted objects stored in NVM3 at any time.
- **Max Object Size**: Size of largest allowed NVM3 object in bytes. Must be between 208 and 4096 bytes.
- **User Repack Headroom**: Headroom determining how many bytes below the forced repack limit the user repack limit is placed. The default value is 0, which means that the forced and the user repack limits are the same.
- **Default Instance Size**: Size of the NVM3 storage region in flash. This must be set to match an integer number of flash pages, 3 pages at minimum.

If the **NVM3 Default Instance** component is included in a project, the default instance will be initialized automatically during `sl\_system\_init()` if the **System Init** component is included in the project.

##### Using NVM3 with Silicon Labs Connect

This chapter applies to Connect in the Proprietary Flex SDK v3.x used with the Project Configurator in Simplicity Studio 5. Connect in Proprietary Flex SDK v2.x uses AppBuilder in Simplicity Studio 4. AppBuilder use is described in [Using NVM3 in AppBuilder-Based Applications](08-using-nvm3-in-appbuilder-based-applications).

For Silicon Labs Connect v3.x applications, a **Token Manager** component is available under Platform > Driver in the Simplicity Studio Project Configurator Software Component view. While the token manager provides the [Token API](10-nvm3-api-options), an additional component must be selected for the token storage backend, either **Token Manager using Sim EEPROM 1**, **Token Manager using Sim EEPROM 2**, or **Token Manager using NVM3.** If NVM3 is chosen as the storage backend, **NVM3** and the **NVM3 Default Instance** components are included in the project.

##### Using NVM3 with Silicon Labs OpenThread Applications

This section applies to the Silicon Labs OpenThread SDK v1.x used with the Simplicity Project Configurator in Simplicity Studio 5. All OpenThread sample applications in the GSDK by default are configured to use NVM3 to store data in non-volatile memory. When doing so, these applications:

- Use the common default NVM3 instance.
- Include the NVM3 Core and the NVM3 Default Instance components in the project.
- Use the native NVM3 API to access the NVM3 object

The NVM3 key space used by the OpenThread stack is 0x20000 to 0x2FFFF.

##### Using NVM3 with Zigbee EmberZNet v7.x and Higher

This chapter applies to the Silicon Labs Zigbee EmberZNet SDK v7.x and higher used with the Simplicity Project Configurator in Simplicity Studio 5. Zigbee EmberZNet 6.x and lower use AppBuilder. AppBuilder use is described in [Using NVM3 in AppBuilder-Based Applications](08-using-nvm3-in-appbuilder-based-applications).

All Zigbee sample applications in the GSDK by default are configured to use NVM3 to store data in non-volatile memory. When doing so, these applications:

- Use the common default NVM3 instance
- Include the NVM3 Core and the NVM3 Default Instance components in the project
- Use the native NVM3 API to access the NVM3 object

If you have non-volatile data stored in the older SimEE format and need to preserve the data when migrating your Zigbee application to EmberZNet v7.x or higher, then the Token Manager component is available to help manage the data storage. For more information, see the Token Manager component's description.

The NVM3 key space used by the Zigbee stack is 0x10000 to 0x1FFFF.

##### Using NVM3 with Silicon Labs Bluetooth Applications

Traditionally the Bluetooth stack uses its own proprietary solution to store data in non-volatile memory, called **Persistent Store (PS Store)**. PS Store stores both data handled by the stack (such as temporary Bluetooth address, bonding keys, and so on) and user data (such as the device state) that has to be preserved on resetting the device. To learn more about PS Store, read the related section of the [Bluetooth API Reference Guide](https://docs.silabs.com/bluetooth/latest/bluetooth-stack-api/).

Some of the sample applications in the Bluetooth SDK are still configured to use PS Store, while others are already configured to use NVM3. This means that:

- On Series 1 devices sample apps are configured to use PS Store, except Bluetooth Mesh NCP sample projects, where NVM3 is used by default.
- On Series 2 devices all sample apps are configured to use NVM3.

While Series 2 devices support NVM3 only, on Series 1 devices both PS Store and NVM3 can be used. This section describes how to configure NVM3 in the Bluetooth SDK, and how to switch between PS Store and NVM3 if needed.

###### Configuring NVM3 in the Bluetooth SDK

**With Simplicity Studio 5 and Bluetooth SDK v3.x**

NVM3 can be configured with the Project Configurator as described in [NVM3 in the Simplicity Studio 5 Project Configurator](03-nvm3-in-the-simplicity-studio-5-project-configurator).

**With Simplicity Studio 4 and Bluetooth SDK v2.x**

The Project Configurator is not available to configure NVM3 parameters. Therefore, the parameters have to be defined manually. To overwrite the default parameters:

1. Open the project settings.
2. Find the defined symbols:  
   - If you use GCC as your compiler, go to C/C++ Build>Settings>GNU ARM C Compiler>Symbols>Defined Symbols  
   - If you use IAR as your compiler, go to C/C++ Build>Settings>IAR C/C++ compiler for ARM>Preprocessor>Defined Symbols
3. Add any of the following defines to overwrite the default parameters:  
   - NVM3_DEFAULT_NVM_SIZE  
   - NVM3_DEFAULT_CACHE_SIZE  
   - NVM3_DEFAULT_MAX_OBJECT_SIZE  
   - NVM3_DEFAULT_REPACK_HEADROOM

You can find the description of each parameter in [NVM3 in the Simplicity Studio 5 Project Configurator](03-nvm3-in-the-simplicity-studio-5-project-configurator). Be careful when providing the size of NVM3, as it must be a multiple of the flash page size. Note: On Series 1 devices the flash page size is typically 2 kB, while on Series 2 devices the flash page size is typically 8 kB. Check the Wireless Gecko Reference Manual for your device. The NVM size must be at least 3 flash pages.

###### Switching from PS Store to NVM3

Beginning with Bluetooth SDK v2.13.0, both PS Store and NVM3 are supported as non-volatile memory solutions on Series 1 devices. Most sample applications are configured to use PS Store by default, but for some applications (where larger non-volatile memory is needed) NVM3 may be a better solution.

> **Note**: PS Store and NVM3 are not compatible with each other, therefore upgrading an already existing application from PS Store to NVM3 will result in losing all data stored on the device. If you have an application running in the field, it may be wiser to stay with PS Store. If you still want to upgrade, see [Using the Gecko Bootloader with the Silicon Labs Bluetooth Application](https://docs.silabs.com/bluetooth/latest/using-gecko-bootloader-with-bluetooth-apps/) for details.

> **Note**: PS Store uses only 2 flash pages (=4 kB on an EFR32BG1/12/13 device). Therefore, changing to NVM3 will affect the available space in flash. You must be particularly careful when you upgrade the firmware not to overwrite the NVM3 area with the application.

**With Simplicity Studio 5 and Bluetooth SDK v3.x**

To change your project configuration from PS Store to NVM3, simply install the **NVM3 Default Instance** component in the Project Configurator as discussed in [NVM3 in the Simplicity Studio 5 Project Configurator](03-nvm3-in-the-simplicity-studio-5-project-configurator). This will automatically uninstall the (otherwise hidden) PS Store component.

**With Simplicity Studio 4 and Bluetooth SDK v2.x**

To change your project configuration from PS Store to NVM3, use the following procedure.

1. Copy the following folder with all of its content:  
   `C:\SiliconLabs\SimplicityStudio\v4\developer\sdks\gecko_sdk_suite\<version>\platform\emdrv\nvm3` under the `/platform/emdrv` folder of your project.
2. Remove the following files from the project:  
   - `/platform/emdrv/nvm3/src/nvm3_hal_extflash.c`  
   - `/platform/emdrv/nvm3/src/nvm3_default_extflash.c` (NVM3 use with external flash is deprecated)
3. If you use Apploader in your project, also copy the NVM3 version of Apploader from:  
   `C:\SiliconLabs\SimplicityStudio\v4\developer\sdks\gecko_sdk_suite\<version>\protocol\bluetooth\lib\<device>\<compiler>\binapploader_nvm3.o`  
   into the `/protocol/bluetooth/lib/<device>/<compiler>` folder of your project.
4. **If you use GCC as a compiler:**  
   1. Go to `Project > Properties > C/C++ Build > Settings > GNU ARM C Compiler > Includes`.  
   2. Add `${workspace_loc:/${ProjName}/platform/emdrv/nvm3/inc}` to the include directory.  
   3. Go to `Project > Properties > C/C++ Build > Settings > GNU ARM C Linker > Miscellaneous`.  
   4. Remove `${workspace_loc:/${ProjName}/protocol/bluetooth/lib/<device>/<compiler>/libpsstore.a}`.  
   5. Add `${workspace_loc:/${ProjName}/platform/emdrv/nvm3/lib/libnvm3_CM4_gcc.a}`.  
   **If you use IAR as a compiler:**  
   1. Go to `Project > Properties > C/C++ Build > Settings > IAR C/C++ Compiler for ARM > Preprocessor`.  
   2. Add `${workspace_loc:/${ProjName}/platform/emdrv/nvm3/inc}` to the include directory.  
   3. Go to `Project > Properties > C/C++ Build > Settings > IAR Linker for ARM > Library`.  
   4. Remove `${workspace_loc:/${ProjName}/protocol/bluetooth/lib/\<device\>/\<compiler\>/libpsstore.a}`.  
   5. Add `${workspace_loc:/${ProjName}/platform/emdrv/nvm3/lib/libnvm3_CM4_iar.a}`.
5. If you use Apploader, also modify:  
   `${workspace_loc:/${ProjName}/protocol/bluetooth/lib/\<device\>/\<compiler\>/binapploader.o}`  
   to  
   `${workspace_loc:/${ProjName}/protocol/bluetooth/lib/\<device\>/\<compiler\>/binapploader_nvm3.o}`
6. Configure NVM3 as described in [Configuring NVM3 in the Bluetooth SDK](#configuring-nvm3-in-the-bluetooth-sdk).

###### Switching from NVM3 to PS Store

It may happen that, for a reason such as backward compatibility, you have to change the configuration from NVM3 to PS Store.

**With Simplicity Studio 5 and Bluetooth SDK v3.x**

To change your project configuration from NVM3 to PS Store, simply uninstall the NVM3 Default Instance component. This will automatically install the (otherwise hidden) PS Store component. Note, that this can only be done on series 1 devices, as series 2 devices do not support PS Store. **Also, it is not possible with Bluetooth Mesh Stack in use**.

**With Simplicity Studio 4 and Bluetooth SDK v2.x**

To change your project configuration from NVM3 to PS Store, use the following procedure:

1. Remove the /platform/emdrv/nvm3 folder from your project.
2. Copy the PS Store library from:  
   `C:\SiliconLabs\SimplicityStudio\v4\developer\sdks\gecko_sdk_suite\<version>\protocol\bluetooth\lib\<device>\<compiler>\libpsstore.a`  
   into the `/protocol/bluetooth/lib/<device>/<compiler>` folder of your project.
3. If you use Apploader in your project, also copy the PS Store version of Apploader from:  
   `C:\SiliconLabs\SimplicityStudio\v4\developer\sdks\gecko_sdk_suite\<version>\protocol\bluetooth\lib\<device>\<compiler>\binapploader.o`  
   into the `/protocol/bluetooth/lib/<device>/<compiler>` folder of your project.  
   > **Note**: PS Store is not supported on Series 2 devices (EFR32xG2x), therefore there is only an NVM3 version of the Apploader for these devices.
4. **If you use GCC as your compiler**  
   1. Go to `Project > Properties > C/C++ Build > Settings > GNU ARM C Compiler > Includes`.  
   2. Remove `${workspace_loc:/${ProjName}/platform/emdrv/nvm3/inc}` from the include directory.  
   3. Go to `Project > Properties > C/C++ Build > Settings > GNU ARM C Linker > Miscellaneous`.  
   4. Add `${workspace_loc:/${ProjName}/protocol/bluetooth/lib/<device>/<compiler>/libpsstore.a}`.  
   5. Remove `${workspace_loc:/${ProjName}/platform/emdrv/nvm3/lib/libnvm3_CM4_gcc.a}`.  
   **If you use IAR as your compiler**  
   1. Go to `Project > Properties > C/C++ Build > Settings > IAR C/C++ Compiler for ARM > Preprocessor`.  
   2. Remove `${workspace_loc:/${ProjName}/platform/emdrv/nvm3/inc}` from the include directories.  
   3. Go to `Project > Properties > C/C++ Build > Settings > IAR Linker for ARM > Library`.  
   4. Add `${workspace_loc:/${ProjName}/protocol/bluetooth/lib/<device>/<compiler>/libpsstore.a}`.  
   5. Remove `${workspace_loc:/${ProjName}/platform/emdrv/nvm3/lib/libnvm3_CM4_iar.a}`.
5. If you use Apploader, also change:  
   `${workspace_loc:/${ProjName}/protocol/bluetooth/lib/<device>/<compiler>/binapploader_nvm3.o}`  
   to  
   `${workspace_loc:/${ProjName}/protocol/bluetooth/lib/<device>/<compiler>/binapploader.o}`.

##### Using NVM3 in AppBuilder-Based Applications

This section explains how NVM3 can be used for non-volatile storage in AppBuilder-based applications like EmberZNet 6.10.x and lower, EmberZNet-based DMP applications in GSDK 3.2 and lower, and Connect applications in Gecko SDK Suite 2.7 and lower.

###### NVM Library Plugin

To use NVM3 with an AppBuilder-based example application, the NVM3 Library plugin should be included in the project. All PS Store and SimEE plugins should be deselected.

![NVM3 Library Plugin in AppBuilder](/using-third-generation-nonvolatile-memory/0.2/images/sld770-image2.png)

The **NVM3 Library** plugin offers four plugin options:

- **Flash Pages**: Number of flash pages to use for NVM3 data storage. Must be 3 or higher. Default is 18 (36KB) for EFR32 Series-1 devices and 4 (32KB) for EFR32 Series-2 devices
- **Cache Size**: Number of objects to cache. To reduce access times, this number should be equal to or higher than the number of objects, including tokens and deleted objects, stored in NVM3 at any time.
- **Max Object Size**: Size of largest allowed NVM3 object in bytes. Must be between 208 and 4096 bytes. Note that the token API can only be used to access objects of 254 bytes or smaller. When accessing larger objects, the native NVM3 API must be used.
- **User Repack Headroom**: Headroom determining how many bytes below the forced repack limit the user repack limit is placed. The default value is 0, which means that the forced and the user repack limits are the same.

When the NVM3 Library plugin is used the **Simulated EEPROM version 2 to NVM3 Upgrade Library** or **Simulated EEPROM version 2 to NVM3 Upgrade Stub Library** must be included, as described in [SimEEv2 to NVM3 Upgrade Plugin](#simeev2-to-nvm3-upgrade-plugin).

###### SimEEv2 to NVM3 Upgrade Plugin

An AppBuilder plugin (**Simulated EEPROM version 2 to NVM3 Upgrade Library**) is provided for EmberZNet applications that upgrade tokens stored in SimEEv2 to NVM3. For tokens to be successfully upgraded to NVM3, CREATOR_* and NVM3KEY_* defines must be added for all tokens as described in [Token API](10-nvm3-api-options). The upgrade plugin will replace the SimEEv2 storage in-place with an NVM3 storage instance. The plugin does this by compacting the SimEEv2 storage down to 12 kB, and then creates an NVM3 instance in the remaining 24 kB of the original 36 kB SimEEv2 storage space. After the token data has been copied over from SimEEv2 to NVM3, the SimEEv2 storage is erased and the NVM3 instance is resized to use the entire 36 kB storage space. Apart from the code space needed for the upgrade library code, the upgrade does not require any additional flash space to the 36 kB storage area. The upgrade plugin requires that the existing SimEEv2 storage space and new NVM3 storage space are located at the same address and have the same size.

The **Simulated EEPROM version 2 to NVM3 Upgrade Library** plugin should be included to enable the upgrade as shown in the figure below. If no SimEEv2 token data is found, the upgrade plugin will look for NVM3 data, and if neither is found it will create a new NVM3 instance with tokens set to their default values. For applications that do not need to upgrade any SimEEv2 tokens, the **Simulated EEPROM version 2 to NVM3 Upgrade Stub** plugin should be included instead.

![SimEEv2 to NVM3 Upgrade Library and Stub Plugins in AppBuilder](/using-third-generation-nonvolatile-memory/0.2/images/sld770-image3.png)

##### Using NVM3 with Z-Wave Applications

For details on using NVM3 with Z-Wave applications, see [https://www.silabs.com/documents/public/user-guides/INS14259.pdf](https://www.silabs.com/documents/public/user-guides/INS14259.pdf), section 7.4.

##### NVM3 API Options

This chapter describes the three different APIs available to access NVM3 objects.

- Token API
- Persistent Store API
- Native NVM3 API

###### Token API

The token API is used to access data stored in SimEEv1 and SimEEv2 with the EmberZNet and Connect stacks, as well as multiprotocol applications. Information on how to define and access tokens can be found in [Using Tokens for Non-Volatile Data Storage](https://docs.silabs.com/gecko-platform/latest/using-tokens-for-non-volatile-storage/), and users should read this document before using the token API. When selecting the NVM3 Library plugin instead of one the SimEE plugins, the NVM3 default instance is used to store the token data instead of SimEE. The same token API can still be used to access tokens stored in NVM3, but the token definition needs some modifications to work with NVM3, as described below.

When defining a token to be used with SimEE, a creator code must be defined as an identifier for the token. Similarly, when defining a token to be used with NVM3, an NVM3 key must be defined for the token. A token definition that is compatible with both NVM3 and SimEE would include both an NVM3 key and a creator code and look like this:

```sh
#define CREATOR_name 16bit_value
#define NVM3KEY_name 20bit_value
#ifdef DEFINETYPES
  typedef data_type type #endif
#ifdef DEFINETOKENS
DEFINE_*_TOKEN(name, type, ... ,defaults) #endif
```

Select a 20-bit NVM3 key for the token, according to the domains in [NVM Default Instance Key Space](02-nvm3-default-instance#nvm3-default-instance-key-space). Each token must have a unique NVM3 key, except for indexed tokens, where more NVM3 keys must be reserved as outlined in [Special Considerations for Indexed Tokens](#special-considerations-for-indexed-tokens).

###### Deleting Tokens (heading level 7)

As tokens are created at compile time, they cannot be created or deleted at run time. NVM3 objects are, however, created and deleted at run time, and the token initialization function creates NVM3 objects for each defined token if they do not already exist. The token initialization generally does not delete NVM3 objects found that do not have a corresponding token associated with them. Therefore, if a token is no longer included in an application, the application should manually delete the associated NVM3 object by using the NVM3 Native API described in [Native NVM3 API](#native-nvm3-api). For indexed tokens, however, the token initialization checks if indexed tokens have more or less indexes than the number of NVM3 objects found in the indexed token's NVK3KEY range. If there are fewer indexes, the token initialization deletes the extra NVM3 objects. If the number of indexes has been increased, new NVM3 objects will be created to hold these indexes.

When NVM3 objects are deleted, the actual object data remains in NVM3 but is marked as deleted. The deleted object data remains in NVM3 and consumes cache space until NVM3 repacks have erased the page(s) holding all versions of these objects.

###### Special Considerations for Indexed Tokens (heading level 7)

NVM3 does not have native support for indexed tokens. Therefore an extra requirement is imposed on the NVM3 key selection for indexed tokens. With NVM3, indexed tokens are implemented by storing each index in a separate object, starting with index 0 stored at the defined NVM3KEY_name key value and the last index (127) stored with key NVM3KEY_name + 127. Because of this implementation, 128 NVM3 keys must be reserved for each indexed token. The user still only defines one NVM3KEY_name key value, but no other tokens should be defined with key values in the 127 values following this defined key. Even if the token is defined with fewer than 128 indices, all 128 indices should be reserved as the token might be expanded with more indices later on.

The example below shows two indexed tokens defined in the user key domain:

```sh
// This key is used for an indexed token and the subsequent 0x7F keys are also reserved #define
NVM3KEY_MY_INDEXED_TOKEN_A 0x00000
// This key is used for an indexed token and the subsequent 0x7F keys are also reserved #define
NVM3KEY_MY_INDEXED_TOKEN_B 0x00080
```

The table below provides indexed token NVM3 key selections in the example.

|**NVM3KEY**|**NVM3 Objects Contents**|
|---|---|
|0x00000|Reserved for TOKEN_MY_INDEXED_TOKEN_A index 0|
|0x00001|Reserved for TOKEN_MY_INDEXED_TOKEN_A index 1|
|0x00002|Reserved for TOKEN_MY_INDEXED_TOKEN_A index 2|
|…| |
|0x0007F|Reserved for TOKEN_MY_INDEXED_TOKEN_A index 127|
|0x00080|Reserved for TOKEN_MY_INDEXED_TOKEN_B index 0|
|0x00081|Reserved for TOKEN_MY_INDEXED_TOKEN_B index 1|
|…| |
|0x000FF|Reserved for TOKEN_MY_INDEXED_TOKEN_B index 127|

###### Bluetooth NVM API

The Bluetooth NVM API that was originally designed for PS Store can be used in the same way when using NVM3 as when using PS Store. The Bluetooth stack will automatically translate its NVM API calls to PS Store API calls or to NVM3 API calls in the background, depending on what components the project uses. The same applies for Zigbee + Bluetooth DMP projects, where NVM3 is applied as the storage mechanism, but the Bluetooth NVM API can still be used. The Bluetooth API is documented in the Bluetooth API Reference Manual. In Bluetooth SDK v2.x it can be found under the _Flash_ class (commands starting with `gecko_cmd_flash_`), while in Bluetooth SDK v3.x it can be found under the _NVM_ class (commands starting with `sl_bt_nvm`).

16-bit keys are used with the Bluetooth NVM API, which are then mapped to a 20-bit NVM3 key when NVM3 is used as storage mechanism. The four most significant bits are set to 0x4 to place these objects in the Bluetooth domain of the NVM3 default instance key space. As the Bluetooth NVM API is fixed to use only the Bluetooth domain, any objects to be placed in other domains, for example the User domain, must be created and accessed using the native NVM3 API.

If you want to use the Bluetooth NVM API and the native NVM3 API in the same app, then:

1. Call `gecko_init(pconfig)` to initialize the Bluetooth stack and open its own NVM3 instance. This is done in all Bluetooth sample apps.
2. Open NVM3 by calling the `nvm3_open()` function with the default (!) parameters to open your NVM3 instance:  
   `nvm3_open(nvm3_defaultHandle, nvm3_defaultInit);`

User data can now be saved to:

- PS key range 0x4000 - 0x407F. All other PS keys (0x0000-0xFFFF) are reserved for the stack (for example for storing bonding data).
- NVM3 key range 0x00000-0x0FFFF (NVM3 user data area), and 0x44000-0x4407F (PS Store user data area).

For example, the following API calls will have the same effect (writing to the same area):

- `nvm3_writeData(nvm3_defaultHandle,0x44000,(void\*)data,len);`
- `gecko_cmd_flash_ps_save(0x4000,len,data); //in Bluetooth SDK v2.x`
- `sl_bt_nvm_save(0x4000,len,data); //in Bluetooth SDK v3.x`

Similarly, you can read the same data with the following API calls:

- `nvm3_readData(nvm3_defaultHandle,0x44000,(void\*)read_buffer,maxlen);`
- `gecko_cmd_flash_ps_load(0x4000); //in Bluetooth SDK v2.x`
- `sl_bt_nvm_load(0x4000,maxlen,&read_len,(uint8_t\*)read_buffer); //in Bluetooth SDK v3.x`

###### Native NVM3 API

For code accessing NVM3 objects that does not need to be compatible with the token or PS Store APIs, using the native NVM3 API to access NVM3 data is recommended to reduce code size and allow using the full feature set of NVM3. Any PS Store object or token can also be accessed through the native NVM3 API using the same NVM3 key. Complete documentation of this API is found online in the Drivers section of the [Gecko Platform Developer Documentation](https://docs.silabs.com/gecko-platform/latest/driver/api/group-nvm3). If you are using an earlier version of the GSDK, search that page's version history for the corresponding release.

##### Simplicity Commander and NVM3

Simplicity Commander is a single, all-purpose tool to be used in a production environment. It is invoked using a simple CLI (Command Line Interface) that is also scriptable. Simplicity Commander supports reading out the NVM3 data area from a device and parsing the NVM3 data to extract stored values. This can be useful in a debugging scenario where you may need to find out the stored state of an application that has been running for some time.

More information on how to use the Simplicity Commander with NVM3 can be found in [Simplicity Commander Reference Guide](https://docs.silabs.com/simplicity-commander/latest/simplicity-commander-start/).

##### Securing NVM3 Data on External Flash Devices

Series-3 devices do not have on-die NVM. Since off-chip memory such as external flash is vulnerable to persistent attacks, data objects are securely stored and retrieved from the external flash using AES-GCM authenticated encryption. On Series-3 devices, the NVM3 AES-128 symmetric key is used to encrypt the data, ensuring it cannot be read in plain text from the external flash. The NVM3 AES-128 symmetric key is a device-generated, non-exportable key stored in the Secure Engine MTP. The NVM3 symmetric key is invalidated or erased during a device erase. Any attempt to load previously recorded NVM3 data (encrypted with the older key) into flash will result in invalid reads and must be avoided. In the case of a device erase, it is essential to reinitialize the NVM3 instance and add the objects again. For details about DFA and DPA countermeasures and the AES engines being used, refer to [Secure Key Storage](https://docs.silabs.com/iot-security/latest/efr32-secure-key-storage/).

###### Impact of External Flash and Security on Repack Timings

For Series-3 devices using external flash, repack operations may take longer due to the increased latency of accessing external flash over the bus. Additionally, with security features such as authenticated encryption (AES-GCM) enabled, the repack timings may be further impacted due to the cryptographic overhead. It is recommended to schedule repack operations during periods of low CPU and bus activity to minimize the impact on application performance.

##### Best Practices and Optimizations for NVM3

###### Right Sizing the NVM3 Storage Region in Flash

To minimize repacks and reduce flash wear, it is important to configure the NVM3 region size appropriately. The size of the NVM3 region should be based on:

- The number of objects stored in NVM3.
- The size of each object, including metadata overhead.
- The frequency of object updates and deletions.
- The flash page size of the device.

With the addition of security on Series 3 devices, storing a data object incurs a size overhead of 8 bytes. Ensure that the NVM3 region has sufficient space to accommodate active objects, deleted objects, and spare pages for repack operations. A larger NVM3 region reduces the frequency of repacks, thereby extending the flash lifetime. The "Maximum allowed basic storage" section at [Storage Capacity](https://docs.silabs.com/gecko-platform/latest/platform-driver/nvm3#storage-capacity) can help with configuring the appropriate number of flash pages and right-sizing the NVM3 storage in flash.

###### Right Sizing the Cache

The NVM3 cache size should be configured to match the number of live and deleted objects in the NVM3 region. This ensures faster access to objects and reduces the overhead of searching for objects in flash. Users can query the cache size and memory information using the `nvm3_getMemInfo()` API. This API provides details about the current memory state, including available memory, cache status, and additional cache requirements. Properly sizing the cache improves performance and reduces object lookup latency.

###### Low Memory Notification Callback

Users can use the `nvm3_registerCallback()` API to register a callback function, which is triggered when the NVM3 instance detects low memory conditions or a cache overflow. The callback allows the application to handle conditions such as freeing up memory by triggering repack. It is recommended that the callback be registered before calling any NVM3 APIs. Additionally, users can deregister the callback using the `nvm3_deregisterCallback()` API.

###### NVM3 Optimizations

Enabling NVM3 optimization improves the NVM3 initialization and object lookup time. NVM3 optimization can be enabled or disabled from Simplicity Studio UC for Series-2 and Series-3 devices. The optimization support has implications on code size. With optimization support enabled, the code size increases by approximately 1248 bytes. NVM3 optimization is enabled by default for Series-3 devices and disabled for Series-2 devices.

By following these best practices and leveraging the available APIs and optimizations, users can maximize the efficiency and reliability of the NVM3 storage system while maintaining high performance for their applications.

##### Repack Management in NVM3

###### What is a Repack Operation?

A **repack** operation in NVM3 reclaims memory in non-volatile memory (NVM) by copying valid objects to new pages and erasing old pages with obsolete or deleted data. This operation is essential for maintaining available storage and ensuring the NVM3 instance can continue to store new or updated objects.

###### Why is Repack Needed? (heading level 7)

As the NVM3 fills up with new and updated objects, old versions and deleted objects accumulate, consuming memory. When there is insufficient free memory to store additional objects, a repack operation is required to release this memory by removing outdated data.

###### When is Repack Triggered?

Repack can be triggered in two ways:

- **Forced repack**  
  NVM3 automatically initiates a repack during a write operation if free memory falls below a critical threshold (forced threshold). This threshold represents the minimum limit and cannot be modified by the user. Forced repacks are performed internally using the `repackUntilGood` API. When a forced repack occurs, write operations may take longer, potentially impacting application timing.
- **User repack**  
  Applications can manually trigger a repack by calling `nvm3_repack()`. The function will execute the repack only if the available free memory drops below the user threshold. This threshold is configurable through the `repackHeadroom` parameter and is evaluated by `nvm3_repackNeeded()`. The function `nvm3_repackNeeded()` can be used to check whether a repack is required, and `nvm3_repack()` will proceed only if this function returns `true`. For time-sensitive applications, it is recommended to trigger a user repack during periods of low activity.

###### The Role of Repack Headroom

The **repack headroom** parameter (`repackHeadroom` in `nvm3_Init_t`) specifies the gap between the user-defined repack threshold and the forced repack threshold. This allows applications to reserve additional memory before a forced repack occurs, giving more control over when repacks happen.

- **Default Value**  
  By default, `repackHeadroom` is configured to 0, making the user threshold equal to the forced threshold plus the maximum configured object size.
- **Increasing Headroom**  
  Configuring a higher `repackHeadroom` value means user or manually triggered repacks will occur earlier, reserving more memory and reducing the risk of forced repacks during critical operations. However, this can increase the frequency of user repacks and thus flash wear.

###### Best Practices for Repack Configuration

- **Call `nvm3_repack()` proactively**  
  Before writing large objects or performing a batch of writes, check `nvm3_repackNeeded()` and call `nvm3_repack()` if needed. This avoids forced repacks during time-critical operations.
- **Tune `repackHeadroom`**  
  Configure `repackHeadroom` according to the application's tolerance for repack latency (the amount of delay it can accept when a repack occurs during a write) and the available NVM3 memory. A smaller value allows more objects to be stored before a repack is needed but increases the risk of a forced repack during a write, adding latency. A larger value causes repacks to occur earlier, which lowers the risk of a forced repack but results in more frequent user repacks.  
  Repack headroom can be configured using `NVM3_DEFAULT_REPACK_HEADROOM` macro or through the Studio UI to reserve a specific amount of memory in advance, helping to avoid forced repacks during time-critical operations.  
  For example, if the application writes 512 bytes of data (including object overhead) during bootup, which is a time-sensitive operation, configure the repack headroom to 512 bytes to ensure that a forced repack does not occur during boot. Proactively check for repack before the next bootup.  
  ```c  
  // Example: Configure repack headroom to 512 bytes to ensure sufficient memory for a critical write  
  #define NVM3_DEFAULT_REPACK_HEADROOM  512  
    
  // Proactively check and perform a repack if needed  
  if (nvm3_repackNeeded(nvm3_defaultHandle)) {  
    nvm3_repack(nvm3_defaultHandle);  
  }  
  ```
- **Monitor Available Memory:**  
  Use `nvm3_getMemInfo()` to monitor available memory and repack proactively if memory is running low.  
  ```c  
  // Example: Monitor available memory and trigger a repack  
  nvm3_MemInfo_t memInfo;  
  nvm3_getMemInfo(nvm3_defaultHandle, &memInfo);  
    
  if (memInfo.nvm3Available < 512) {  // Threshold: 512 bytes  
    nvm3_repack(nvm3_defaultHandle);  
  }  
  ```  
  For more information, see the [NVM3 Memory Info](https://docs.silabs.com/gecko-platform/latest/platform-driver/nvm3-mem-info-t).

###### Avoid Large Max Object Size Unless Needed

The `maxObjectSize` parameter has a significant impact on both the minimum required NVM3 size and the amount of usable storage before a repack is triggered. Basic storage is defined as the total size of all objects, including any overhead stored with the data. The maximum amount of data that can be stored in NVM3 depends on the number of flash pages allocated for storage and the configured maximum object size.

The maximum allowed basic storage for an NVM3 instance can be estimated using the following formula:

```c
allowed_basic_storage =
  total_nvm_size
  - ((total_nvm_size / page_size) * page_header_size)             // per-page bookkeeping
  - 2 * (page_size - page_header_size)                            // repack window
  - (max_object_size + obj_header_size_large)
  - ((page_size == 4096 && max_object_size > 4064) ? 212 : 0);    // extra buffer for 4KB pages
```

where:

- `total_nvm_size` is the total NVM3 region size in bytes
- `page_size` is the flash page size in bytes
- `page_header_size` is the page header size in bytes (20 bytes)
- `max_object_size` is the configured maximum object size in bytes
- `object_header_size_large` is the large object header size in bytes (8 bytes)

This is a theoretical limit. If the basic storage is at this limit, no memory is left for wear-levelling, and page erases will be forced for every object written. Therefore, the NVM3 instance should be configured with enough flash pages so that the maximum allowed basic storage is well above the actual basic storage required by the application.

For detailed tables showing the relationship between flash page count, page size, and max object size, see the [Storage Capacity](https://docs.silabs.com/gecko-platform/latest/platform-driver/nvm3#storage-capacity) section in the NVM3 API documentation.

> **Best Practice**: Configure `maxObjectSize` to the smallest value that still accommodates the largest object. Avoid configuring it unnecessarily high, as this reduces usable memory and increases the frequency of repacks, which can accelerate flash wear.
> 
> **Tip**: If an application only occasionally needs to store a few large objects (e.g., 4096 bytes), these objects can be broken into smaller chunks (such as four 1024-byte objects). Each chunk is written separately and later read back and reassembled into the original object when needed. This approach keeps `maxObjectSize` small, maximizing usable memory and minimizing repack frequency and flash wear for the majority of the data.

###### Impact of Max Object Size on Repack and Usable Storage

The maximum object size (`maxObjectSize`) directly affects the repack thresholds and the amount of usable storage before a repack is needed. A larger `maxObjectSize`:

- Increases the minimum NVM3 size required for operation.
- Reduces the amount of storage available for user data before a repack is triggered.
- May result in more frequent repacks, especially if the stored objects are much smaller than the configured maximum.

### Security

#### Security

Silicon Labs offers a range of security features depending on the part you are using and your application and production needs. As well as the security features available, this section describes security issues specific to OpenThread.

- [**IoT Security Fundamentals**](/openthread/3.1.0/iot-endpoint-security-fundamentals): Introduces the security concepts that must be considered when implementing an Internet of Things (IoT) system. Using the ioXt Alliance's eight security principles as a structure, it clearly delineates the solutions Silicon Labs provides to support endpoint security and what you must do outside of the Silicon Labs framework.
- [**Using Silicon Labs Secure Vault Features with OpenThread**](/openthread/3.1.0/using-secure-vault-openthread):  Describes how Secure Vault features are leveraged in OpenThread applications. It focuses on specific PSA features and emphasizes how these are integrated into the OpenThread stack.
- [**Series 2 and Series 3 Secure Debug**](/openthread/3.1.0/series2-secure-debug): Explains the different debug lock and unlock features available in Series 2 and Series 3 devices and their capabilities.
- [**Production Programming of Series 2 and Series 3 Devices**](/openthread/3.1.0/prod-programming-series2-and-series3): Demonstrates how to properly program, provision, and configure Series 2 and Series 3 devices in a production environment.
- [**Anti-Tamper Protection Configuration and Use**](/openthread/3.1.0/efr32-secure-vault-tamper): Describes how to program, provision, and configure the Secure Engine anti-tamper module. The anti-tamper module is only available on Secure Vault High and Series 3 Secure Vault devices.
- [**Authenticating Silicon Labs Devices using Device Certificates**](/openthread/3.1.0/authenticating-devices-using-device-certificates): Shows how to authenticate an EFR32 Series 2 device with Secure Vault, using secure device certificates and signatures.
- [**Secure Key Storage**](/openthread/3.1.0/efr32-secure-key-storage): Explains how to securely "wrap" keys in EFR32 Series 2 devices with Secure Vault, so they can be stored in non-volatile storage.
- [**Programming Series 2 Devices Using the DCI and SWD**](/openthread/3.1.0/efr32-dci-swd-programming): Describes how to provision and configure Series 2 devices through the DCI and SWD.
- [**Integrating Crypto Functionality with PSA Crypto vs. Mbed TLS**](/openthread/3.1.0/mbedtls-psa-crypto-porting-guide): Describes how to integrate crypto functionality into applications using PSA Crypto compared to Mbed TLS.
- [**Series 2 TrustZone**](/openthread/3.1.0/series2-trustzone): Provides background and information on implementing TrustZone on series 2 devices.

#### IoT Endpoint Security Fundamentals

##### IoT Endpoint Security Fundamentals

> **Note: This section replaces _UG103.05: IoT Endpoint Security Fundamentals_. Further updates to this user guide will be provided here**.

This guide introduces the security concepts that must be considered when implementing an Internet of Things (IoT) system. The guide clearly delineates the solutions Silicon Labs provides to support endpoint security and what you must do outside of the Silicon Labs framework. Where appropriate, Silicon Labs’ approach to our own security is offered as an example. This document is designed for product developers and managers.

Silicon Labs’ _Fundamentals_ series covers topics that project managers, application designers, and developers should understand before beginning to work on an embedded networking solution using Silicon Labs chips, networking stacks such as EmberZNet PRO or Silicon Labs _Bluetooth®_, and associated development tools. The guides can be used as a starting place for anyone needing an introduction to developing wireless networking applications, or who is new to the Silicon Labs development environment.

###### Key Points

- Describes what manufacturers must do to have a secure product
- Reviews the tools and solutions Silicon Labs provides to support product security
- Provides references to other general and product-specific information

##### Overview

Securing the IoT is challenging. It is also mission-critical. Threats are continuously evolving, and the demand on product developers to keep up can be burdensome – particularly in low-cost, resource-constrained IoT products. Protecting your product in a connected world is a necessity, as customer data and modern online business models are increasingly targets for costly hacks that jeopardize end-user privacy and corporate brand damage. Silicon Labs is committed to working with the security community, customers, and other experts to bring state-of-the-art technology to help protect your connected portfolio.

##### No Universal Passwords

_The product shall not have a universal password; unique security credentials will be required for operation. Universal passwords allow an attacker to easily gain access to any device. Therefore, products shall either have a unique password or require the user to enter a new password immediately upon first use._

It is your responsibility to ensure that your product enforces the creation of a unique password before activation.

Silicon Labs’ products are designed to be configured by the manufacturer before being delivered to customers, and therefore passwords are outside of our scope. However, Silicon Labs tools are designed to support the various levels of security provided by the protocol in question. Most protocols offer different security levels, with tradeoffs between security level and other features such as ease of network formation. You need to review and decide on the level required by your application. For example:

- The EmberZNet Pro SDK supports a highly secure centralized trust-center-controlled method that replaces a device’s factory-programmed link key with a key that is unique to each device on the network.
- Z-Wave 700 products come with a factory-programmed unique S2 keypair on first power-up, and support SmartStart commissioning through a package QR code containing the public key.
- Bluetooth options range from an unsecured “Just Works” approach to a LE Secure Connections Pairing model. Application designers can implement additional device authentication methods, such as through the companion smartphone app, to help ensure secure pairing even for devices without a user interface.

##### Secured Interfaces

_All product interfaces shall be appropriately secured by the manufacturer._

The interfaces to be secured will vary by product configuration. For example, in an NCP topology the NCP interface must be secured. Debug interfaces should always be locked. Wireless interfaces should be secured by using strong pairing and commissioning methods and by enabling encrypted and authenticated transmissions.

While securing the interfaces is in the end your responsibility, Silicon Labs provides the tools to enable that security.

Series 1, Series 2 and Series 3 devices are designed to support securing debug access. For Series 1 devices, that functionality is provided through writing a Debug Lock word to the device. Unlocking the device erases the main application and the key material stored in the Lockbits page. For Series 2 and 3 devices, securing debug access is done through the device’s Secure Engine. Both allow the developer to lock the debug port itself. See [Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/) or _UG266: Silicon Labs Gecko Bootloader User’s Guide for GSDK 3.2 and Lower_ for an overview of securing debug access, and [Series 2 and 3 Secure Debug](https://docs.silabs.com/iot-security/latest/series2-secure-debug/) for details on the Series 2 and 3 implementation. [Testing and Debugging Applications for the Silicon Labs EFR32MG Platforms](https://docs.silabs.com/zigbee/latest/test-debug-apps-efr32mg/) provides an overview of the various application testing stages and the debug access (hardware and software) required in each.

For more information on Wireless interface security in the different protocols, see the following:

- [Zigbee Security](https://docs.silabs.com/zigbee/latest/zigbee-security/)
- [Bluetooth LE Fundamentals](https://docs.silabs.com/bluetooth/latest/bluetooth-le-fundamentals/)
- [UG235.03: Architecture of the Silicon Labs Connect Stack v2.x](https://www.silabs.com/documents/public/user-guides/ug235-03-architecture-of-connect.pdf)
- [Architecture of the Silicon Labs Connect Stack v3.x](https://docs.silabs.com/connect-stack/latest/network-co-processor-applications-connect-v3x/)

##### Proven Cryptography

_Product security shall use strong, proven, updatable cryptography using open, peer-reviewed methods and algorithms._

An important aspect of any IoT device is how secure the device is when it communicates with other devices, gateways, or the cloud. This standard mandates using proven cryptographic methods rather than attempting to implement your own.

Developers commonly secure communications such as TCP/IP connections, Bluetooth, Zigbee, or Z-Wave using the standardized and proven cryptographic methods native to the protocol. However, if a microcontroller sends sensitive information over a simple interface such as a UART to another microcontroller, it is important to realize that data should also be secured to prevent someone from snooping the UART line.

Silicon Labs offers a hardware accelerator module that provides an efficient acceleration of common cryptographic operations and allows these to be used efficiently with low CPU overhead. The cryptographic accelerator module includes hardware accelerators for the Advanced Encryption Standard (AES), optionally ChaCha20/Poly1305, Secure Hash Algorithm SHA-1 and SHA-2 (SHA-224 and SHA-256), and modular multiplication used in ECC (Elliptic Curve Cryptography) and GCM (Galois Counter Mode). The accelerator module can autonomously execute and iterate a sequence of instructions to aid software and speed up complex cryptographic functions like ECC, GCM, and CCM (Counter with CBC-MAC).

In addition to the cryptographic accelerator module, Silicon Labs includes the PSA Crypto and Mbed TLS libraries as part of the Gecko Platform SDK. Mbed TLS is open source software licensed by ARM Limited. It provides an SSL library that makes it easy to use cryptography and SSL/TLS in applications. Mbed TLS supports software implementations of all crypto algorithms that are supported by TLS 1.3 as well as a build API that allows hardware drivers to replace the software implementations when cipher accelerators are supported by the platform. Its modular framework allows for subcomponents like the crypto libraries to be incorporated into a design independently of the SSL/TLS components, saving valuable code space and runtime RAM. Mbed TLS supports SSLv3 up to TLSv1.3 communication by providing the following:

- TCP/IP communication functions: listen, connect, accept, read/write.
- SSL/TLS communication functions: init, handshake, read/write.
- X.509 functions: CRT, CRL and key handling
- Random number generation
- Hashing
- Encryption/decryption

These functions are split up into logical interfaces. They can be used separately to provide any of the above functions or to mix-and-match into an SSL server/client solution that utilizes a X.509 public key infrastructure (PKI). Examples of such implementations are provided with the source code. Components or plugins and APIs provide configuration interfaces accessible through the various SDK installations.

For more information, see the latest MCU and Peripheral Software Documentation for the target part at [https://docs.silabs.com.](https://docs.silabs.com/).

##### Security by Default

_Product security shall be appropriately enabled by default by the manufacturer._

The state in which a product is shipped is up to the manufacturer. This standard mandates that any security features provided with the product be enabled before shipping. Customers should not have to turn security on; rather they should actively have to disable it. For example, Silicon Labs Z-Wave end-nodes and gateway SDKs ship with S2 cryptography and SmartStart network formation enabled by default.

Silicon Labs believes that product security should be considered during product design, and not as an afterthought. Within development environments, all Silicon Labs application security features may be enabled or disabled as appropriate during application development. Security must also be considered during device design and testing. [Bringing Up Custom Devices for the EFR32MG and EFR32FG Families](https://docs.silabs.com/connect-stack/latest/custom-nodes-efr32/) describes the security tokens (keys, certificates, and so on) that can be programmed into a custom device to support various types of security, including that provided by the Gecko Bootloader. See [Signed Software Updates](06-signed-software-updates).

##### Signed Software Updates

_The product shall only support signed software updates. While it is critical that all products be updatable, it is just as critical that these update images be secured. A manufacturer must cryptographically sign update images to prevent tampering during deployment. The product must not use unsigned updates, as they could be fraudulent._

Silicon Labs development tools support building signed upgrade images and securely updating devices in the field, through the Silicon Labs Gecko Bootloader. The Gecko Bootloader can be configured to perform a variety of functions, from device initialization to firmware upgrades. Key features of the bootloader are:

- Usable across Silicon Labs Gecko microcontroller and wireless microcontroller families
- In-field upgradeable
- Configurable
- Enhanced security features, including:
- Secure Boot: When Secure Boot is enabled, the bootloader enforces cryptographic signature verification of the application image on every boot, using asymmetric cryptography. This ensures that the application was created and signed by a trusted party.
- Signed upgrade image file: The Gecko Bootloader supports enforcing cryptographic signature verification of the upgrade image file. This allows the bootloader and application to verify that the application or bootloader upgrade comes from a trusted source before starting the upgrade process, ensuring that the image file was created and signed by a trusted party.
- Encrypted upgrade image file: The image file can also be encrypted to prevent eavesdroppers from acquiring the plaintext firmware image.

On Series 1 devices, the Gecko Bootloader has a two-stage design, first stage and main stage, where a minimal first stage bootloader is used to upgrade the main bootloader. The first stage bootloader only contains functionality to read from and write to fixed addresses in internal flash. To perform a main bootloader upgrade, the running main bootloader verifies the integrity and authenticity of the bootloader upgrade image file. The running main bootloader then writes the upgrade image to a fixed location in internal flash and issues a reboot into the first stage bootloader. The first stage bootloader verifies the integrity of the main bootloader firmware upgrade image, by computing a CRC32 checksum before copying the upgrade image to the main bootloader location.

On Series 2 and Series 3 devices, the Gecko Bootloader consists only of the first stage bootloader. The main bootloader is upgradable through the hardware peripheral Secure Engine. The Secure Engine provides functionality to install an image to the base address of flash, by copying from a configurable location in flash. To perform a main bootloader upgrade, the running main bootloader verifies the integrity and authenticity of the bootloader upgrade image file. The running main bootloader then writes the upgrade image to the upgrade location in flash and requests that the Secure Engine install it. The Secure Engine is also capable of verifying the authenticity of the main bootloader update image against a root of trust. The Secure Engine itself is upgradable using the same mechanism.

In summary, Series 2 and Series 3 devices support a hardware root of trust and a Secure Boot process that verifies the authenticity and integrity of Gecko Bootloader, whereas in Series 1 devices, the authenticity and integrity of Gecko Bootloader are assumed trusted and are not explicitly checked.

The Gecko Bootloader can enforce application image security on two levels:

- Secure Boot refers to the verification of the authenticity of the application image in main flash on every boot of the device. When Secure Boot is enabled, the cryptographic signature of the application image in flash is verified on every boot, before the application is allowed to run. Secure Boot is not enabled by default in the example configurations provided by Silicon Labs, but enabling it is highly recommended to ensure the validity and integrity of firmware images.
- Secure Firmware Upgrade refers to the verification of the authenticity of an upgrade image before applying the upgrade and optionally enforcing that upgrade images are encrypted. The Secure Firmware Upgrade process uses symmetric encryption to encrypt the upgrade image, and asymmetric cryptography to sign the upgrade image in order to ensure its integrity and authenticity.

For more information on Silicon Labs’ support for software update security, refer to the following:

- Bootloaders in general: [Bootloader Fundamentals](https://docs.silabs.com/mcu-bootloader/latest/bootloader-fundamentals/).
- The Gecko Bootloader in general: [Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/) or _UG266: Silicon Labs Gecko Bootloader User’s Guide for GSDK 3.2 and Lower_

Using the Gecko Bootloader with specific protocols:

[Using the Gecko Bootloader with EmberZNet](https://docs.silabs.com/zigbee/latest/using-gecko-bootloader-with-zigbee/)

[Using the Gecko Bootloader with Silicon Labs Connect](https://docs.silabs.com/connect-stack/latest/using-gecko-bootloader-with-connect/)

[Using the Gecko Bootloader with Silicon Labs Bluetooth Applications](https://docs.silabs.com/bluetooth/latest/using-gecko-bootloader-with-bluetooth-apps/)

[Series 2 and Series 3 Secure Boot with RTSL](https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/)

##### Automatically Applied Updates

_The manufacturer will act quickly to apply timely security updates. Whenever a security vulnerability is detected, the manufacturer will automatically apply a patch to the product. No user intervention will be required._

It is the manufacturer’s responsibility to develop and implement automatic security updates. The design and methodology of such systems, for example through a Cloud-connected infrastructure or by direct intervention by a service representative, is up to you.

Silicon Labs will notify you of any security-related updates, as described in [Vulnerability Reporting Program](08-vulnerability-reporting-program) Your responsibility is to evaluate the level of risk that vulnerability poses for your particular product and to integrate the update into your platform as appropriate so that your end users are protected. Updated components might include the protocol libraries, Secure Engine firmware inside the Series 2 and Series 3 families, or an SDK module such as the Gecko Bootloader that enforces secure OTA updates and secure boot functionality.

Silicon Labs recommends the following:

- Subscribe to security updates through our [Salesforce portal](https://community.silabs.com/s/support-home). To review or change your subscriptions, log in to the portal, click **HOME** to go to the portal home page, and then click the **Manage Notifications** tile. Make sure that **Software/Security Advisory Notices & Product Change Notices (PCNs)** is checked, and that you are subscribed at minimum for your platform and protocol. Click **Save** to save any changes.  
  ![screenshot](/iot-endpoint-security-fundamentals/0.3/images/sld875-image15.png)
- Do not turn off Simplicity Studio’s update notification. Within Simplicity Studio you can download updates and easily access product release notes.

##### Vulnerability Reporting Program

_The manufacturer shall implement a vulnerability reporting program, which will be addressed in a timely manner. All companies that offer Internet-connected devices and services shall provide a public point of contact as part of a vulnerability disclosure policy in order that security researchers and others are able to report issues. Disclosed vulnerabilities should be acted on in a timely manner._

Manufacturers are responsible for implementing their own program. For any individual vulnerability, you will need to weigh the value of transparency with your customers against the risk of malicious use of the information to exploit a vulnerability before it can be addressed. Silicon Labs makes similar decisions about how broadly to report security vulnerabilities discovered in our products.

Silicon Labs customers and security researchers can report security vulnerabilities in Silicon Labs hardware and software products on the Silicon Labs website: [https://www.silabs.com/security/product-security](https://www.silabs.com/security/product-security).

Silicon Labs’ Security Vulnerability Disclosure Policy may be found here: [https://www.silabs.com/security/security-vulnerability-disclosure-policy](https://www.silabs.com/security/security-vulnerability-disclosure-policy).

Silicon Labs has a Product Security Incident Response Team (PSIRT) that is dedicated to the case management of reported security vulnerabilities. The PSIRT works with other Silicon Labs groups including Applications, Developers, Sales, and Marketing to assess reported vulnerabilities, perform technical analysis and determine an appropriate response. The key processes for addressing vulnerabilities include:

- **Triage**: Determines what is needed to reproduce the vulnerability.
- **Technical Analysis and Disposition**: Confirms the validity of the security vulnerability, its scope, and its impact, and provides a resolution or disposition decision. Silicon Labs scores incidents according to CVSS 4.0 (Common Vulnerability Scoring System): low, medium, high, critical.
- **Output**: Communicates with our customers. The level and method of disclosure beyond the reporting entity depends on the severity and scope of the vulnerability.

Silicon Labs’ provides broad vulnerability reporting to customers subscribed through our Salesforce portal (see [Automatically Applied Updates](07-automatically-applied-updates) for information on how to subscribe). A subscribed customer will see Security Advisory notifications something like the following:

![screenshot](/iot-endpoint-security-fundamentals/0.3/images/sld875-image16.jpg)

##### Security Expiration Date

_The manufacturer shall be transparent about the period of time that security updates will be provided. Like a manufacturer’s product warranty, there shall be transparency around the support period of security updates._

Manufacturers should provide details about product support at various stages and publish security expiration dates. Z-Wave’s Protocol Lifecycle provides an example.

![diagram](/iot-endpoint-security-fundamentals/0.3/images/sld875-image17.jpg)

The Lifecycle details in what phases updates will be applied, and to what product branch. For details on the various phases and how the lifecycle is implemented for specific Z-Wave products, see:

[https://www.silabs.com/products/development-tools/software/z-wave/embedded-sdk/life-cycle](https://www.silabs.com/products/development-tools/software/z-wave/embedded-sdk/life-cycle)

##### Next Steps

The Silicon Labs Security web page ([https://www.silabs.com/security)](https://www.silabs.com/security)) contains links to a variety of general security-related resources. You may wish to bookmark the page, as it will be continually updated with new content, new tools, and new flows.

If you are already in development, we strongly recommend that you implement the standards described here as you develop, test, and release your product to customers.

If you are in the early stages of your product design and have not already selected a device or development environment, we recommend that you include security considerations in your decision. Silicon Labs provides information about the security features of our devices and development environments. [Series 2 and Series 3 Device Security Features](11-series-2-and-series-3-device-security-features) highlights the features and their documentation references. In addition, protocol-specific security information is available in the following documents.

- [Zigbee Security](https://docs.silabs.com/zigbee/latest/zigbee-security/) (contents were previously in this document under the title Application Security Fundamentals)
- [Bluetooth® Low Energy Application Security Design Considerations in SDK v3.x and Higher](https://docs.silabs.com/bluetooth/latest/bluetooth-application-security-design-considerations/)
- [Using Silicon Labs Secure Vault Features with OpenThread](https://docs.silabs.com/openthread/latest/using-secure-vault-openthread/)
- [Bluetooth® LE Fundamentals](https://docs.silabs.com/bluetooth/latest/bluetooth-le-fundamentals/) and relevant Knowledge Base Articles (KBAs)
- [Architecture of the Silicon Labs Connect Stack v3.x](https://docs.silabs.com/connect-stack/latest/architecture-of-connect-v3x/)

##### Series 2 and Series 3 Device Security Features

Protecting IoT devices against security threats is central to a quality product. Silicon Labs offers several security options to help developers build secure devices, secure application software, and secure paths of communication to manage those devices. Silicon Labs’ security offerings were significantly enhanced by the introduction of the Series 2 products that included a Secure Engine. The Secure Engine is a tamper-resistant component used to securely store sensitive data and keys, and to execute cryptographic functions and secure services.

On Series 1 devices, the security features are implemented by the TRNG (if available) and CRYPTO peripherals.

On Series 2 devices, the security features are implemented by the Secure Engine and CRYPTOACC (if available). The Secure Engine may be hardware-based, or virtual (software-based). Throughout this document, the following abbreviations are used:

- HSE - Hardware Secure Engine
- VSE - Virtual Secure Engine
- SE - Secure Engine (either HSE or VSE)

On Series 3 devices, the security features are implemented by the Secure Engine and HOSTCRYPTO (if available). The Secure Engine may is hardware-based.

Additional security features are provided by Secure Vault. Three levels of Secure Vault feature support are available, depending on the part and SE implementation, as reflected in the following table:

|**Security Level (1)**|**SE Support**|**MCU**|**Wireless SoC (2)**|
|---|---|---|---|
|Secure Vault Base (SVB)|N/A|EFM32JG1, EFM32PG1, EFM32JG12, EFM32PG12, EFM32GG11, EFM32GG12, EFM32TG11|EFR32xG1, EFR32xG12, EFR32xG13, EFR32xG14|
|Secure Vault Mid (SVM)|VSE (VSE-SVM)|EFM32PG22C|EFR32xG22C, EFR32xG27C (3)|
|Secure Vault Mid (SVM)|HSE (HSE-SVM)|-|EFR32xG21A, EFR32xG21A (Rev C), EFR32MR21A (Rev C), EFR32xG23A, EFR32xG24A, EFR32xG25A, EFR32xG28A|
|Secure Vault High (SVH)|HSE only (HSE-SVH)|EFM32PG23B, EFMPG26B, EFM32PG28B|EFR32xG21B, EFR32Xg21B (Rev C) EFR32xG23B, EFR32xG24B, EFR32xG25B, EFR32xG26B, EFR32xG28B, EFR32xG29B, SixG301|

> Note:
> 
> 1. The features of different Secure Vault levels can be found in [https://www.silabs.com/security.](https://www.silabs.com/security).
> 2. The x is a letter B, F, M, or Z.
> 3. Unlike other VSE-SVM parts, the EFR32xG27C device has a built-in PUF.

Secure Vault Mid consists of two core security functions:

- **Secure Boot**: Process where the initial boot phase is executed from an immutable memory (such as ROM) and where code is authenticated before being authorized for execution.
- **Secure Debug access control**: The ability to lock access to the debug ports for operational security, and to securely unlock them when access is required by an authorized entity.

Secure Vault High offers additional security options:

- **Secure Key Storage**: Protects cryptographic keys by “wrapping” or encrypting the keys using a root key known only to the HSE-SVH.
- **Anti-Tamper protection**: A configurable module to protect the device against tamper attacks.
- **Device authentication**: Functionality that uses a secure device identity certificate along with digital signatures to verify the source or target of device communications.

A Secure Engine Manager and other tools allow users to configure and control their devices both in-house during testing and manufacturing, and after the device is in the field.

Silicon Labs strongly recommends installing the latest SE FW image on Series 2 and Series 3 devices and updating to the latest GSDK or SiSDK to mitigate security vulnerabilities. The latest SE FW image can be found in this Windows folder for GSDK v4.x:

`C:\Users\<PC USER NAME>\SimplicityStudio\SDKs\gecko_sdk\util\se_release\public`

Or

`C:\Users\<PC USER NAME>\SimplicityStudio\SDKs\simplicity_sdk\util\se_release\public`

Refer to [Production Programming of Series 2 and Series 3 Devices](https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/) for guidance on the SE firmware upgrade procedure. The latest SE firmware shipped with Series 2 and Series 3 devices and modules (if available) at the time of this writing are listed in the following table:

|**Series 2 MCU and Wireless SoC VSE - SVM**|**Shipped SE Firmware Version**|
|---|---|
|EFM32PG22C|1.2.12|
|EFR32xG22C|1.2.12|
|EFR32xG22C (Rev D)|1.2.14|
|EFR32xG27C|2.2.1|

|**Series 2 Wireless SoC HSE - SVM**|**Shipped SE Firmware Version**|
|---|---|
|EFR32xG21A|1.2.13|
|EFR32MR21A (Rev C)|1.2.16|
|EFR32xG21A (Rev C)|1.2.16|
|EFR32xG23A|2.1.7|
|EFR32xG24A|2.1.7|
|EFR32xG25A|2.2.1|
|EFR32xG28A|2.2.2|

|**Series 2 MCU and Wireless SoC HSE - SVH**|**Shipped SE Firmware Version**|
|---|---|
|EFR32xG21B|1.2.13|
|EFR32xG21B (Rev C)|1.2.16|
|EFM32PG23B|2.1.7|
|EFR32xG23B|2.1.7|
|EFR32xG24B|2.1.7|
|EFR32xG25B|2.2.1|
|EFM32PG28B|2.2.2|
|EFR32xG28B|2.2.2|

|**Series 3 MCU and Wireless SoC HSE - SVH**|**Shipped SE Firmware Version**|
|---|---|
|SixG301|3.3.2|

In support of these products Silicon Labs offers whitepapers, webinars, and documentation. The following table summarizes the key security documents:

<table>
    <thead>
        <tr>
            <th>Document</th>
            <th>Summary</th>
            <th>Applicability</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/series2-secure-debug/">Series 2 and Series 3 Secure Debug</a></p>
            </td>
            <td>
                <p>How to lock and unlock Series 2 and Series 3 debug access, including background information about the SE</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/">Series 2 and Series 3 Secure Boot with RTSL</a></p>
            </td>
            <td>
                <p>Describes the secure boot process on Series 2 and Series 3 devices using SE</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/efr32-secure-vault-tamper/">Anti-Tamper Protection Configuration and Use</a></p>
            </td>
            <td>
                <p>How to program, provision, and configure the anti-tamper module</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/series2-trustzone/">Series 2 TrustZone</a></p>
            </td>
            <td>
                <p>Describes the basics of TrustZone, secure and privileged programming model, and shows how to upgrade existing application to TrustZone.</p>
            </td>
            <td>
                <p>Series 2 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/authenticating-devices-using-device-certificates/">Authenticating Silicon Labs Devices using Device Certificates</a></p>
            </td>
            <td>
                <p>How to authenticate a device using secure device certificates and signatures, at any time during the life of the product</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/efr32-secure-key-storage/">Secure Key Storage</a></p>
            </td>
            <td>
                <p>How to securely "wrap" keys so they can be stored in non-volatile storage.</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/">Production Programming of Series 2 and Series 3 Devices</a></p>
            </td>
            <td>
                <p>How to program, provision, and configure security information using SE during device production</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>AN1504: Series 3 Security Overview</p>
            </td>
            <td>
                <p>High level overview of the security features included in Series 3 devices</p>
            </td>
            <td>
                <p>Series 3</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>AN1509: Series 3 AXiP</p>
            </td>
            <td>
                <p>How to encrypt and authenticate memory contents</p>
            </td>
            <td>
                <p>Series 3</p>
            </td>
        </tr>
    </tbody>
</table>

|**Document**|**Summary**|**Applicability**|
|---|---|---|
|[Programming Series 2 Devices Using the Debug Challenge Interface (DCI) and Serial Wire Debug (SWD)](https://docs.silabs.com/iot-security/latest/efr32-dci-swd-programming/)|How to provision and configure Series 2 devices through the DCI and how to program their internal flash memory through the SWD|Series 2|
|[Integrating Crypto Functionality Using PSA Crypto Compared to Mbed TLS](https://docs.silabs.com/iot-security/latest/mbedtls-psa-crypto-porting-guide/)|How to integrate crypto functionality into applications using Silicon Labs implementation of PSA Crypto compared to Mbed TLS|Series 1, Series 2 and Series 3|

#### Using Silicon Labs Secure Vault Features with OpenThread

##### Using Silicon Labs Secure Vault Features with OpenThread

**NOTE: This section replaces _AN1329: Using Silicon Labs Secure Vault Features with OpenThread_. Further updates to this application note will be provided here**.

This application note describes how the secure vault features are leveraged in OpenThread applications. It focuses on specific PSA features and emphasizes how these are integrated into the OpenThread stack.

This document focuses on the updates to secure key storage and crypto modules of OpenThread to leverage Vault features.

###### Key Points

- Features of Secure Vault devices
- Key Management in OpenThread
- Crypto Modules in OpenThread
- Integration of PSA in OpenThread

##### Introduction

Google’s OpenThread is an open-source implementation of Thread. Google has released OpenThread to make the networking technology used in Google Nest products more broadly available to developers in order to accelerate the development of products for the connected home and commercial buildings.

With a narrow platform abstraction layer and a small memory footprint, OpenThread is highly portable. It supports both system-on-chip (SoC) and network co-processor (NCP) designs. OpenThread implements all features defined in the Thread 1.1.1 Specification. This specification defines an IPv6-based reliable, secure, and low-power wireless device-to-device communication protocol for home and commercial building applications.

Silicon Labs has enhanced OpenThread to work with Silicon Labs hardware. This source code is available on GitHub and also as a software development kit (SDK) installed with Simplicity Studio. The SDK includes a fully tested snapshot of the GitHub source code. It supports a broader range of hardware than does the GitHub version and includes documentation and example applications not available on GitHub.

Some EFR32 Series 2 products offer additional security options through Secure Vault. Secure Vault is a dedicated security CPU that isolates cryptographic functions and data from the host processor core. Devices with Secure Vault (High) offer the following security features:

- Secure Key Storage: Protects cryptographic keys by “wrapping” or encrypting the keys using a root key known only to the Secure Vault.
- Anti-Tamper protection: A configurable module to protect the device against tamper attacks.
- Device authentication: Functionality that uses a secure device identity certificate along with digital signatures to verify the source or target of device communications.

This guide describes how OpenThread applications leverage Secure Vault features using PSA Crypto APIs.

##### Secure Vault

On Series 1 devices, the security features are implemented by the TRNG (if available) and CRYPTO peripherals.

All the security features on Silicon Labs Series 2 devices are implemented using Secure Engine and CRYPTOACC (if available). Types of secure engine implementations in Series 2 devices fall in one of the following categories:

- HSE - Hardware Secure Engine
- VSE - Virtual Secure Engine
- SE - Secure Engine (either HSE or VSE, in general)

The Secure Engine implements and extends all cryptography-related hardware accelerations. Three levels of Secure Vault feature support are available, depending on the part and SE implementation, as reflected in the following table:

**Table**: Cryptographic Hardware Acceleration Features on Silicon Labs Devices

|Level (1)|SE Support|Part (2)|
|---|---|---|
|Secure Vault High (SVH)|HSE only (HSE-SVH)|EFR32xG2yB (3)|
|Secure Vault Mid (SVM)|HSE (HSE-SVM)|EFR32xG2yA (3)|
|“|VSE|EFM32PG2y, EFR32xG2y (4)|
|Secure Vault Base (SVB)|N/A|MCU Series 1 and Wireless SoC Series 1|

**Notes**:

1. The features of different Secure Vault levels can be found in [https://www.silabs.com/security](https://www.silabs.com/security).
2. The x can be a letter B, F, M, or Z.
3. At the time of this writing, the y is a digit 1 for the HSE device.
4. At the time of this writing, the y is a digit 2 for the VSE device.

The Secure Vault Mid consists of two core security functions:

- Secure Boot: Process where the initial boot phase is executed from an immutable memory (such as ROM) and where code is authenticated before being authorized to be executed.
- Secure Debug access control: The ability to lock access to the debug ports for operational security, and to securely unlock them when access is required by an authorized entity.

The Secure Vault High offer additional security options as follows:

- Secure Key Storage: Protects cryptographic keys by “wrapping” or encrypting the keys using a root key known only to the HSE-SVH.
- Anti-Tamper protection: A configurable module to protect the device against tamper attacks.
- Device authentication: Functionality that uses a secure device identity certificate along with digital signatures to verify the source or target of device communications.

###### Secure Key Storage

One of the main features of Secure Vault is secure key storage (see [Secure Key Storage](/openthread/{build-docspace-version}/efr32-secure-key-storage) for more information). The cryptographic keys are encrypted by the device’s root key before they are stored in memory for later use. These keys are then available only through the Cryptographic APIs and are referenced by the application with their unique key reference. This allows potentially unlimited secure key storage in any storage location. The keys themselves can be either non-volatile or temporary and the user can choose the key storage location using the attributes.

OpenThread uses the following mechanism to store and use keys available in the stack:

- The user generates the key and specifies the crypto operation that is allowed by the key and the access settings (whether the key can be exported).
- The application passes this key to Secure Vault. Secure Vault then wraps the key and stores it in the specified memory (volatile or non-volatile, based on user settings).
- The application then provides the user with a reference that can be used for Crypto operations.

###### PSA Crypto (heading level 7)

Platform Security Architecture (PSA) is designed by ARM to address the security requirements for IoT devices. It is made up of four stages:

- Threat modelling
- Predefined architectural choices
- Standardized implementation
- Certification

PSA Crypto is one of the standardized implementations of the features recommended by PSA. It implements low-level APIs for cryptographic operations, optimized for MCUs and Wireless SoCs. The PSA Crypto APIs provide easy-to-use interfaces for crypto primitives. These APIs are based on the idea of secure key storage. Access to keys is restricted to the Cryptographic APIs.

![PSA Functional APIs](/using-secure-vault-openthread/0.1/images/sld803-image1.png)

##### Security Handling in OpenThread

Several cryptographic keys are described and used in the Thread protocol. These keys are stored in different modules in the OpenThread stack. All the keys, apart from the Thread Master Key and PSKc, are volatile and are not stored in non-volatile memory (NVM). OpenThread implements a Key Manager module to handle most of the keys required by the Thread protocol.

The following table lists the cryptographic keys used by the OpenThread stack and the module that handles these keys.

**Cryptographic keys in OpenThread stack**

|Key|Usage|Module|Storage location|
|---|---|---|---|
|Thread Master Key|Used to derive the MAC and MLE keys|Key Manager|NVM|
|Pre-shared Key for the Commissioner (PSKc)|Used to establish a commissioner session|Key Manager|NVM|
|MLE Key|Used for Mesh Link Establishment Used by MLE for transmit and receive operations|Key Manager|RAM|
|Temporary MLE key|Used to process received UDP packet, if the incoming packet has a different keySequence number than that used in the device.|Key Manager|RAM|
|Key Encryption Key|Used by MAC to process incoming and outgoing packets with keyIdMode0 as 802.15.4 security material|Key Manager|RAM|
|Pre-shared key for the device (PSKd)|Used in the DTLS exchange to verify the identity of the Joiner|Key Manager|RAM|
|MAC Previous Key|MAC key for **key sequence = current seq - 1**|MAC|RAM|
|MAC Current Key|MAC key for **key sequence = current seq**|MAC|RAM|
|MAC Next Key|MAC key for **key sequence = current seq + 1**|MAC|RAM|

OpenThread has a modular approach to security. Each layer of the stack has security implementations and uses common modules for the actual security processing. The current implementation of OpenThread in GitHub uses mbed TLS for all the security operations and, as such, uses all the keys in plaintext for these operations.

![Crypto Module Operation in OpenThread](/using-secure-vault-openthread/0.1/images/sld803-image2.png)

##### Updates to OpenThread to Include PSA Crypto APIs

The OpenThread stack supports crypto operations using either literal keys or key references. This can be configured using the macro **OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE**. Also, Openthread stack now supports PSA or mbedTLS APIs for Crypto operations. The stack defaults to using mbedTLS APIs, but it can be configured to use PSA using **OPENTHREAD_CONFIG_CRYPTO_LIB**.

> **Note**: The OpenThread sample apps provided in the SDK have the PSA Crypto with key references enabled by default, except for radio co-processor (RCP) and network co-processor (NCP) applications.

In general, the following points highlight the general changes in OpenThread stack to integrate PSA Crypto API usage.

- All the keys stored in the `key_manager` and `sub_mac` modules are replaced with key references.
- When `key_manager` and `sub_mac` receive security keys, they pass the keys to the PSA abstraction for storage, and only retain the references to these keys.
- The packets in any Rx or Tx events are updated with the relevant key references so that PSA Crypto modules can use them to perform security processing.

![Crypto Module Operation in OpenThread with PSA](/using-secure-vault-openthread/0.1/images/sld803-image3.png)

All keys are wrapped before storing and all the keys are stored in volatile memory (RAM), apart from **Thread Master Key** and **PSKc.** These are stored in NVM, to be retained and re-used in the future. The following table lists the security keys stored in Secure Vault along with their corresponding PSA attributes.

**Cryptographic Keys in the OpenThread Stack**

|Key|Type|Usage|Persistence|
|---|---|---|---|
|Thread Master Key|PSA_KEY_TYPE_HMAC|PSA_KEY_USAGE_SIGN_HASH PSA_KEY_USAGE_EXPORT|PSA_KEY_LIFETIME_PERSISTENT|
|PSKc|PSA_KEY_TYPE_RAW_DATA|PSA_KEY_USAGE_EXPORT|PSA_KEY_LIFETIME_PERSISTENT|
|MLE Key|PSA_KEY_TYPE_AES|PSA_KEY_USAGE_ENCRYPT PSA_KEY_USAGE_DECRYPT|PSA_KEY_LIFETIME_ VOLATILE|
|Temp MLE key|PSA_KEY_TYPE_AES|PSA_KEY_USAGE_ENCRYPT PSA_KEY_USAGE_DECRYPT|PSA_KEY_LIFETIME_ VOLATILE|
|Key Encryption Key|PSA_KEY_TYPE_AES|PSA_KEY_USAGE_ENCRYPT PSA_KEY_USAGE_DECRYPT PSA_KEY_USAGE_EXPORT|PSA_KEY_LIFETIME_ VOLATILE|
|MAC Previous Key|PSA_KEY_TYPE_AES|PSA_KEY_USAGE_ENCRYPT PSA_KEY_USAGE_DECRYPT|PSA_KEY_LIFETIME_ VOLATILE|
|MAC Current Key|PSA_KEY_TYPE_AES|PSA_KEY_USAGE_ENCRYPT PSA_KEY_USAGE_DECRYPT|PSA_KEY_LIFETIME_ VOLATILE|
|MAC Next Key|PSA_KEY_TYPE_AES|PSA_KEY_USAGE_ENCRYPT PSA_KEY_USAGE_DECRYPT|PSA_KEY_LIFETIME_ VOLATILE|

> **Note**: The keys part of the dataset are still stored in the NVM and are only copied into Secure Vault.

Crypto modules in OpenThread are updated to support the PSA APIs. These APIs use the key references provided by the stack to perform security processing on the incoming/outgoing messages.

###### Security Manager

A new platform abstraction has also been introduced to abstract some of the security operations from the core stack. This abstraction acts as an interface to the PSA module and extends PSA interfaces to the stack.

> **Note**: As the PSA does not yet support the PBKDF2, the stack uses mbed TLS APIs for these operations.

#### Series 2 and Series 3 Secure Debug

##### Series 2 and Series 3 Secure Debug

> **Note: This section replaces _AN1190: Series 2 and Series 3 Secure Debug_. Further updates to this application note will be provided here**.

To protect intellectual property and proprietary algorithms from malicious actors, it is important to lock the debug port. This prevents attackers from reading flash contents through the debug  interface  and  reverse  engineering  the application. However, there are situations where it becomes necessary to debug a device already deployed in the field. Silicon Labs Series 2 and 3 devices offer a feature called Secure Debug. This feature allows only authenticated users to unlock the debug port and securely debug the application.

This application note explains the different debug lock and unlock features available in Series 2 and Series 3 devices and their capabilities.

###### Key Points

- Basic overview of the Secure Engine
- Debug port access by Debug Challenge Interface (DCI) or Mailbox Interface
- Locking and unlocking features for Series 2 and Series 3 devices
- Examples for Public Command Key provisioning and Secure Debug Unlock

> **Note**: This application note covers both Series 2 and Series 3 devices. Unless explicitly stated otherwise, all items are applicable to both. If an item is not applicable to one of the series, it will be mentioned separately.

##### Series 2 and Series 3 Device Security Features

Protecting IoT devices against security threats is central to a quality product. Silicon Labs offers several security options to help developers build secure devices, secure application software, and secure paths of communication to manage those devices. 'Silicon Labs’ security offerings were significantly enhanced by the introduction of the Series 2 products that included a Secure Engine. Series 3 devices further enhanced these capabilities, offering more advanced security features than their Series 2 counterparts. The Secure Engine is a tamper-resistant component used to securely store sensitive data and keys and to execute cryptographic functions and secure services.

On Series 2 devices, the security features are implemented by the Secure Engine and CRYPTOACC (if available). The Secure Engine may be hardware-based or virtual (software-based). Throughout this application note, the following abbreviations are used:

- HSE - Hardware Secure Engine
- VSE - Virtual Secure Engine
- SE - Secure Engine (either HSE or VSE)

Additional security features are provided by Secure Vault. Three levels of Secure Vault feature support are available, depending on the part and SE implementation, as reflected in the following table:

|**Level**|**SE Support**|
|---|---|
|Secure Vault High (SVH)|HSE only (HSE-SVH)|
|Secure Vault Mid (SVM)|HSE (HSE-SVM), VSE (VSE-SVM)|
|Secure Vault Base (SVB)|N/A|

**Notes**:

1. Series 3 devices have no secure vault Base and Mid devices. All devices are Secure Vault High.
2. Refer to [IoT Endpoint Security Fundamentals](https://docs.silabs.com/iot-security/latest/iot-endpoint-security-fundamentals/11-series-2-device-security-features) for details on supporting devices.

Secure Vault Mid consists of two core security functions:

- **Secure Boot**: Process where the initial boot phase is executed from an immutable memory (such as ROM) and where code is authenticated before being authorized for execution.
- **Secure Debug Access Control**: The ability to lock access to the debug ports for operational security, and to securely unlock them when access is required by an authorized entity.

Secure Vault High (SVH) offers additional security options:

- **Secure Key Storage**: Protects cryptographic keys by “wrapping” or encrypting the keys using a root key known only to the HSE-SVH.
- **Anti-Tamper Protection**: A configurable module to protect the device against tamper attacks.
- **Device Authentication**: Functionality that uses a secure device identity certificate along with digital signatures to verify the source or target of device communications.

A Secure Engine Manager and other tools allow users to configure and control their devices both in-house during testing and manufacturing, and after the device is in the field.

###### User Assistance

In support of these products, Silicon Labs offers whitepapers, webinars, and documentation. The following table summarizes the key security documents:

<table>
    <thead>
        <tr>
            <th>Document</th>
            <th>Summary</th>
            <th>Applicability</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Series 2 and Series 3 Secure Debug (this application note)</p>
            </td>
            <td>
                <p>How to lock and unlock Series 2 and Series 3 debug access, including background information about the SE</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/">Series 2 and Series 3 Secure Boot with RTSL</a></p>
            </td>
            <td>
                <p>Describes the secure boot process on Series 2 and Series 3 devices using SE</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/efr32-secure-vault-tamper/">Anti-Tamper Protection Configuration and Use</a></p>
            </td>
            <td>
                <p>How to program, provision, and configure the anti-tamper module</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/authenticating-devices-using-device-certificates/">Authenticating Silicon Labs Devices using Device Certificates</a></p>
            </td>
            <td>
                <p>How to authenticate a device using secure device certificates and signatures, at any time during the life of the product</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/efr32-secure-key-storage/">Secure Key Storage</a></p>
            </td>
            <td>
                <p>How to securely "wrap" keys so they can be stored in non-volatile storage.</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/">Production Programming of Series 2 and Series 3 Devices</a></p>
            </td>
            <td>
                <p>How to program, provision, and configure security information using SE during device production</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>AN1504: Series 3 Security Overview</p>
            </td>
            <td>
                <p>High level overview of the security features included in Series 3 devices</p>
            </td>
            <td>
                <p>Series 3</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>AN1509: Series 3 AXiP</p>
            </td>
            <td>
                <p>How to encrypt and authenticate memory contents</p>
            </td>
            <td>
                <p>Series 3</p>
            </td>
        </tr>
    </tbody>
</table>

###### Key Reference

Public/Private keypairs along with other keys are used throughout Silicon Labs security implementations. Because terminology can sometimes be confusing, the following table lists the key names, their applicability, and the documentation where they are used.

|**Key Name**|**Customer Programmed**|**Purpose**|
|---|---|---|
|Public Sign key (Sign Key Public)|Yes|Secure Boot binary authentication and/or OTA upgrade payload authentication|
|Public Command key (Command Key Public)|Yes|Secure Debug Unlock or Disable Tamper command authentication|
|OTA Decryption key (GBL Decryption key) aka AES-128 Key|Yes|Decrypting GBL payloads used for firmware upgrades|
|Attestation key aka Private Device Key|No|Device authentication for secure identity|
|Certificate Key (Used for generating Debug lock token)|Yes|Device authentication token which enabling debug lock port|

###### SE Firmware

Silicon Labs strongly recommends installing the latest SE firmware on Series 2 and Series 3 devices to support the required security features. Refer to [Production Programming of Series 2 and Series 3 Devices](https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/) for the procedure to upgrade the SE firmware and [IoT Endpoint Security Fundamentals](https://docs.silabs.com/iot-security/latest/iot-endpoint-security-fundamentals/) for the latest SE Firmware shipped with Series 2 and Series 3 devices and modules.

##### Introduction to Secure Debug

###### Debug Lock

All Silicon Labs Series 2 and Series 3 devices have the capability to lock debug access to the device. This prevents attackers from using the debug interface to perform the following illegal operations:

- Reprogramming the device
- Interrogating the device
- Interfering with the operation of the device

A fairly standard practice during the board-level test in production is to program, test, and lock the parts. Three different locks can be enabled on debug interface:

- Standard debug lock
- Secure debug lock
- Permanent debug lock

Silicon Labs provides Custom Part Manufacturing Service (CPMS) to securely configure the debug port of the chip to one of the three possible locks before the devices leave the factory.

###### Debug Unlock

Users need to unlock parts under a number of circumstances:

- Code development
- Field failure diagnosis
- Product field service
- Existing inventory reprogramming

Two different unlocks can run on debug interface:

- Standard debug unlock
- Secure debug unlock

##### Secure Engine Subsystem

###### Overview

The HSE refers to a separate security co-processor that provides hardware isolation between security functions and the host processor.

The VSE refers to a collection of security functions available to the host processor in Root mode if a separate security co-processor is not provided.

The SE is used to perform a series of cryptographic operations and other secure system operations as described in the following table.

|**Operation**|**VSE-SVM**|**HSE-SVM**|**HSE-SVH**|**Description**|
|---|---|---|---|---|
|Unique ID|Y|Y|Y|Software can identify every device.|
|Secure Boot with RTSL|Y|Y|Y|Only boot authenticated firmware.|
|Secure Debug|Y|Y|Y|Securely lock/unlock debug ports using authorized tokens|
|Crypto Engine (1)|Y|Y|Y|Up to 256-bit ciphers and elliptic curves.|
|TRNG (1)|Y|Y|Y|Generate keys for cryptography.|
|DPA Countermeasures|—|Y|Y|Resist side channel attacks.|
|Secure Key Storage|—|—|Y|Protected by PUF technology.|
|Secure Key Management|—|—|Y|Isolate encrypted keys from application code.|
|Secure Attestation|—|—|Y|Ensure integrity and authenticity.|
|Anti-Tamper|—|—|Y|Detect tamper and protect keys/data.|
|Advanced Crypto|—|—|Y|Up to 512-bit ciphers and P-521-bit elliptic curves.|

**Note**:

1. On VSE-SVM devices, the crypto engine and TRNG (True Random Number Generator) are implemented by the CRYPTOACC (Cryptographic Accelerator) peripheral.

To start using the secure debug unlock functionality, the device needs to be provisioned. These steps include writing one time programmable (OTP) settings to the SE to determine which functionality is enabled, and uploading the Public Command Key to validate a secure debug attempt.

This application note describes how different device debug locks and unlocks are implemented through the SE on Series 2 and Series 3 devices.

The secure debug feature is implemented by Root code executed by the HSE Core. For VSE devices secure debug feature is implemented by Cortex-M33 operating in Root mode.

Silicon Labs strongly recommends installing the latest SE firmware to support the required security features. The latest SE firmware image (`.seu` and `.hex`) and release notes can be found in the Windows folder mentioned below.

`C:\Users\\<userName\>\SimplicityStudio\SDKs\Simplicity_sdk\util\se_release\public`

###### Command Interface

Interaction with the SE is performed over a command interface. The command interface is available through a dedicated Debug Challenge Interface (DCI) as well as through a mailbox interface from the Cortex-M33.

Some commands may not be available at all times and may not be accessible over both interfaces. The DCI typically only contains operations for setting up a new device and for locking it down (meant for production processes), while the mailbox interface also contains commands to support cryptographic operations in HSE.

###### Mailbox (heading level 7)

The SE Manager Mailbox Interface is a mechanism used on Silicon Labs Series 2 and Series 3 devices to communicate with the Secure Engine (SE) for cryptographic and security operations. The mailbox interface allows the host processor (such as Cortex-M33) to send commands to the SE, typically for secure key storage, cryptographic operations, device provisioning, and secure identity management.

Secure Engine Manager (SE Manager) provides an abstraction of the Secure Engine's command set. The SE Manager also provides APIs for cryptographic operations and thread synchronization.

![SE Manager Mailbox interface](/series2-secure-debug/0.3/images/sld714-image17.png)

###### Debug Challenge Interface (DCI) (heading level 7)

The Debug Challenge Interface (DCI) is made available through commands in Simplicity Studio and Simplicity Commander. This is the easiest way to access and set up the different security options.

For more information about DCI, see [Programming Series 2 Devices using the Debug Challenge Interface (DCI) and Serial Wire Debug (SWD)](https://docs.silabs.com/iot-security/latest/efr32-dci-swd-programming/).

##### Debug Lock

###### Overview

The debug access port connected to the Series 2 and Series 3 device's Cortex-M33 processor can be closed by issuing commands to the SE, either from a debugger over DCI or through the mailbox interface. Three properties govern the behavior of the debug lock.

|**Property**|**Description If Set**|**Default Value**|
|---|---|---|
|Debug Lock|The debug port is kept locked on boot.|Disabled|
|Device Erase|The Erase Device command is available.|Enabled|
|Secure Debug|Secure debug unlock is available.|Disabled|

The following sections describe how to interact with these properties and how to enable debug locks using the SE command interface over DCI. The status of the debug lock can be inspected using the [Read Lock Status](#debug-lock-command-reference) command.

###### Standard Debug Unlock

The device is in standard debug unlock state if the debug lock properties are set to the default values.

|**Debug Lock**|**Device Erase**|**Secure Debug**|**Description**|
|---|---|---|---|
|Disabled|Enabled|Disabled|All debug operations are allowed.|

###### Standard Debug Lock

With the default properties in the table above, the device can be locked using the [Apply Lock](#debug-lock-command-reference) command. The typical flow for this configuration is simply to issue the Apply Lock command after the device has been programmed, either using a DCI command from the programming debugger or through the mailbox interface.

|**Debug Lock**|**Device Erase**|**Secure Debug**|**Description**|
|---|---|---|---|
|Enabled|Enabled|Disabled|The Erase Device command will wipe the main flash and RAM, and then a reset will yield an unlocked device.|

The standard debug lock disables debug access port, but issuing a device erase wipes the device and enables the debug port again.

###### Secure Debug Lock

For secure debug lock, the debug interface can temporarily be unlocked by answering a challenge, if the Secure debug property is enabled before locking.

|**Debug Lock**|**Device Erase**|**Secure Debug**|**Description**|
|---|---|---|---|
|Enabled|Disabled (2)|Enabled (1)|Secure debug lock is enabled, which makes it possible to securely open the debug lock temporarily to reprogram or debug a locked device.|

**Notes**:

1. Secure debug lock is enabled in two steps before the debug lock is enabled:  
   1. Install the Public Command Key using Simplicity Studio, Simplicity Commander, or directly through the SE Manager API.  
   2. Enable secure debug using Simplicity Studio, Simplicity Commander, or directly through the SE Manager APIs.
2. Disable the device erase using Simplicity Studio or Simplicity Commander or directly through the SE Manager API. This is an **Irreversible** action and should be disabled **After** the secure debug is enabled.
3. The **Device Erase** option should **Disabled** only during production. If this setting is applied during development, it prevents the device from being re-flashed with new firmware using a debugger and significantly hinders debugging efforts.

###### Secure Debug Unlock

To enable debugging or to reprogram the device, the debug port is temporarily opened. It automatically closes upon a power-on reset or device reset, and the device returns to the Secure Debug Lock state.

|**Debug Lock**|**Device Erase**|**Secure Debug**|**Description**|
|---|---|---|---|
|Enabled|Disabled|Enabled|Device debug port remains unlocked, till power on rest or device rest is performed.|

###### Permanent Debug Lock

The device can enter into **Permanent Debug Lock** state when both the **Device Erase** and **Secure Debug** properties are disabled.

|**Debug Lock**|**Device Erase**|**Secure Debug**|**Description**|
|---|---|---|---|
|Enabled|Disabled|Disabled|The part cannot be unlocked. Devices with Permanent Debug Lock engaged cannot be returned for failure analysis.|

###### Debug Lock State Transition

The following figure describes the transitions between different debug lock states.

![Debug Lock State Transition](/series2-secure-debug/0.3/images/sld714-debug-lock-state-transition.png)

1. Standard debug unlock can transit to **Standard Debug Lock** state, by enabling '**Debug Lock**' flag.
2. Standard debug lock can revert to **Standard debug unlock** via an erasedevice/device unlock command (erases the main flash and RAM). After the device is reset, the debug port remains unlocked until it is explicitly locked again.
3. Device can transit to **Secure Debug lock** state by enabling Secure Debug property.
4. Secure debug lock can use Debug Unlock Token to temporary transit to secure debug unlock, which does not erase the main flash and RAM but enables debug operations. The device reverts to the secure debug lock through a power on or pin reset.

5,6,7. From any state, device can transit to Permanent Debug lock by Disabling **Device erase** and **Secure debug**. This is terminal state and can not transit to any other state. This should be done only after development is completed.

> **Note**: Device can be brought back to "**Standard Debug unlock**" state from "**Secure Debug Lock**" state by erasedevice command, if **Device Erase** is Enabled. So, customerrs are strongly advised to disable "**Device erase**" during production. Failing to do so can expose the device to security risks, as adversaries could reprogram it with unauthorized or potentially malicious firmware.

###### Debug Lock Command Reference

The commands for debug lock are described in the following table.

|**DCI Command (1)**|**Mailbox (SE Manager) API (2)**|**Description**|**Availability**|
|---|---|---|---|
|Apply Lock|sl_se_apply_debug_lock|Enables the debug lock for the part.|While debug is unlocked.|
|Read Lock Status|sl_se_get_debug_lock_status|Returns the current debug lock status and con-figuration.|Always.|
|Disable Device Erase|sl_se_disable_device_erase|Disables the Erase Device command. This command does not lock the debug interface to the part, but it is an IRREVERSIBLE action for the part.|Always.|
|Disable Secure Debug|sl_se_disable_secure_debug|Disables the secure debug functionality that can be used to open a locked debug port.|While secure debug is enabled.|
|Enable Secure Debug|sl_se_enable_secure_debug|Enables the secure debug functionality that can be used to open a locked debug port.|While debug is unlocked and Public Command Key is provisioned.|
|Set debug options|sl_se_set_debug_options|Configures the TrustZone access permissions of the debug interface. (3)|While debug is unlocked.|
|Init Pub Key|sl_se_init_otp_key|Used to provision a single public key during device initialization. The public key cannot be changed once written, and the command will be unavailable for that key.|Available once for each key.|
|Read Pub Key|sl_se_read_pubkey|Reads the stored public key.|Always.|
|Get Challenge|sl_se_roll_challenge|Used to roll the current challenge value (16 bytes) to revoke secure debug access. (4)|While Public Command Key is uploaded.|

**Notes**:

1. Performing these commands over DCI is implemented in Simplicity Studio and Simplicity Commander.
2. The `sl_se_apply_debug_lock`, `sl_se_get_debug_lock_status`, `sl_se_init_otp_key`, and `sl_se_read_pubkey` are available on all Series 2 and Series 3 devices. Other APIs are only available on HSE and Series 3 devices. The SE Manager API document can be found at [https://docs.silabs.com/gecko-platform/latest/platform-security-api/sl-se-manager](https://docs.silabs.com/gecko-platform/latest/platform-security-api/sl-se-manager).
3. For more information about debug options, see [TrustZone Debug Authentication](05-debug-unlock#trustzone-debug-authentication).
4. A new challenge will only be generated if the current one has been successfully used at least once. On Series 2 devices, there is no limitation on rolling a challenge. However, on Series 3 devices, challenge rolls are restricted to a maximum of 128. Once a user has rolled the challenge 128 times on a Series 3 device, no further rolls are allowed—but the user can still unlock the device using the last generated challenge.

##### Debug Unlock

###### Overview

The debug access port connected to the device's Cortex-M33 processor can be opened by issuing commands to the SE, either from a debugger over DCI or through the mailbox interface.

When secure debug unlock functionality is enabled, it is possible to request a challenge from the device and, by answering the challenge, debug lock can be disabled until the next power-on or reset.

The status of the debug lock can be inspected using the [Read Lock Status](04-debug-lock#debug-lock-command-reference) command.

###### Standard Debug Unlock

With the properties of the [standard debug lock](04-debug-lock#standard-debug-lock) with Device Erase enabled, the device can be returned to the standard debug unlock state using the Erase Device command. This command will wipe the main flash and RAM and verify they are empty before opening the debug lock. It will not wipe user data (USERDATA section of Flash) and provisioned SE settings.

![Standard Debug Unlock](/series2-secure-debug/0.3/images/sld714-standard-debug-unlock.png)

###### Secure Debug Unlock

In a secure debug unlock setup, the customer owns a private key and installs a matching public key into the device. This public key checks a certificate that shows what actions the customer allows either for themselves or someone they trust. For example, they might allow only the debug port on a Cortex-M33 to be unlocked, or only certain tamper signals to be restored on HSE-SVH devices.

This method is very helpful during failure analysis because it lets the device be unlocked without erasing the data in flash or RAM.

> **Note**: Enabling secure debug lock, without writing command public key into the device key, will cause the device to be permanently locked and cannot be debugged and even cannot be reprogrammed.

###### Secure Debug Lock/Unlock Flow (heading level 7)

The secure debug was designed with three organizations in mind:

- Direct Customer to whom Silicon Labs sells the chip. This chip has the Public Command Key installed in the SE OTP.
- That Direct Customer may be creating a white-labeled product for another company or a sub-component that goes into another company’s product. The Product Company is the customer of the direct customer.
- The Debug 3rd Party could be anyone, internal or external, that the Product Company decides is qualified to debug the device.

Because the Public Command Key is installed into the SE OTP of a large number of devices and cannot be changed, the corresponding Private Command Key should be stored securely in HSM. If this Private Command Key is ever leaked, all the devices programmed with the corresponding Public Command Key will be compromised.

A secure debug unlock use case is described in the following figure. The secure debug unlock flow moves across the time axis from left to right.

![image](/series2-secure-debug/0.3/images/sld714-image32.png)

![image](/series2-secure-debug/0.3/images/sld714-debug-unlock-token-contents.png)

1. The Product Company creates a Private/Public Certificate Key pair for each device. Because the key pair is assigned only to a single device the company may not need to protect the Private Certificate key as securely as the Private Command key by the direct customer.
2. The Public Certificate Key (cert_pubkey.pem) for each device is passed to the Silicon Labs Direct Customer. The part number and serial number are also required if Direct Customer cannot access the device.
3. The Direct Customer then places that Public Certificate Key in the access certificate. The access certificate is per-device because it contains the unique device serial number. This certificate is generated once upon creation of the device, and thereafter, is generally only modified when the Private/Public Certificate Key pair is changed by the Product Company.
4. The Access certificate should be signed by Private Command key.
5. The access certificate is passed to the Product Company. The purpose of the access certificate is to grant overall debug access capabilities to the Product Company and authorize them to allow third parties to debug the device. The Product Company can now use the access certificate to generate the Debug Unlock Token. The same access certificate can be used to generate as many Debug Unlock Tokens as necessary without having to ever go back to the Direct Customer.
6. To create the Debug Unlock Token, a debug session must be started with the device and the challenge value (which is a random number) should be read out to generate the challenge response.
7. The challenge response is then cryptographically hashed (SHA-256) to create a digest. The digest is then signed by the Private Certificate Key to generate the debug access command signature.
8. Generate Debug Unlock Token, using access certificate and debug access command signature generated in above steps.
9. **(Alternative)** The key protection is not required if the Private Certificate Key is ephemeral. In such case steps 6 to 8 can be skipped and Debug Unlock Token can be directly generated with the access certificate from direct customer's Private Certificate key.
10. The Debug Unlock Token and the device are now delivered to the Debug 3rd Party.
11. The device compares the Debug Unlock Token contents with its internal serial number, challenge value, and Public Command Key to determine the token’s authenticity. If authentic, it will execute the debug access command to unlock the device; otherwise, it will ignore the command.
12. The Debug 3rd Party can now use this same Debug Unlock Token to unlock the device (step 11), over and over again after each power on or reset, until they have finished debugging the device.
13. Once the Debug 3rd Party has finished debugging, they will send the device back to the Product Company. After receiving the device product company has to invalidate the debug key, so that it should not be misused. To do so product company has to start debug session and should roll the challenge and put the device back into secure debug lock state. Rolling the challenge will effectively invalidate any Debug Unlock Token that has been previously given to any third party.

> **Note**: Direct customer can use the Private Command Key on the connected chip to generate the Debug Unlock Token within the Security Store. However, this method carries a significant risk, as it bypasses the use of an HSM(Hardware Security Module), potentially exposing the Private Command Key to third parties.

###### Debug Unlock Token (heading level 7)

The elements of the Debug Unlock Token are described in the following figure and tables.

![image](/series2-secure-debug/0.3/images/sld714-debug-unlock-token.png)

|**Element**|**Value**|**Description**|
|---|---|---|
|Debug access command|0xfd010001|The command word of the Debug Unlock Token.|
|Debug mode request|Device dependent|The command parameter of the debug access command.|
|Access certificate (1)|Device dependent|See section Access Certificate.|
|Debug access command signature (1)|Device dependent|See section Challenge Response.|

**Note**:

1. The debug access command payload consists of an access certificate and a debug access command signature.

![Debug Mode Request](/series2-secure-debug/0.3/images/sld714-image35.jpg)

**Notes**:

- Enable debug port - Debug port enabled if set.
- [DBGLOCK](#trustzone-debug-authentication) (Non-secure, Invasive debug lock): The Invasive debug features for the Non-secure state are unlocked if set.
- [NIDLOCK](#trustzone-debug-authentication) (Non-secure, Non-invasive debug lock): The Non-invasive debug features for the Non-secure state are unlocked if set.
- [SPIDLOCK](#figure-54-debug-mode-request) (Secure, Invasive debug lock): The Invasive debug features for the Secure state are unlocked if set.
- [SPNIDLOCK](#figure-54-debug-mode-request) (Secure, Non-Invasive debug lock): The Non-invasive debug features for the Secure state are unlocked if set.
- All reserved bits should be 0, and bit 1 must be 1 to access the debug port.
- For the TrustZone-unaware debugging, bits 2 to 5 are irrelevant, so bits 1 to 5 are usually set (`0x0000003e`) to match with the [Authorizations](#access-certificate) in the access certificate.
- For the TrustZone-aware debugging, bits 2 to 5 are relevant. Refer to [TrustZone Debug Authentication](#trustzone-debug-authentication) for details about these debug options.

###### Access Certificate (heading level 7)

The elements of the access certificate are described in the following figures and table.

![Access Certificate](/series2-secure-debug/0.3/images/sld714-access-certificate.png)

|**Element**|**Value**|**Description**|
|---|---|---|
|Magic word|0xe5ecce01|A constant value used to identify the access certificate.|
|Authorizations|0x0000003e (1)|A value used to authorize which bit in the debug mode request can be enabled for secure debug.|
|Tamper Authorizations|0x00000000 or 0xffffffb6 (2)|A value used to authorize which bit in the tamper disable mask can be set to disable the tamper response.|
|Serial number|Device dependent|A number used to compare against the on-chip serial number for secure debug or tamper disable.|
|Public Certificate Key (3)|Device dependent|The public key corresponding to the Private Certificate Key (3) used to generate the signature (ECDSA-P256-SHA256) in a challenge response.|
|Access certificate signature|Device dependent|All the content above is signed (ECDSA-P256-SHA256) by the Private Command Key corresponding to the Public Command Key in the SE OTP.|

**Notes**:

1. This value allows all debug options to be reset for secure debug.
2. Value that sets available bits in the tamper disable mask for tamper disable (HSE-SVH device only).
3. The Private/Public Certificate Key is a randomly generated key pair. It can be ephemeral or retainable.

The Private Certificate Key can be used repeatedly to generate the signature in a challenge response on one device until the Private/ Public Certificate Key pair is discarded. This can reduce the frequency of access to the Private Command Key, allowing more restrictive access control on that key.

For more information about tamper disable, see [Anti-Tamper Protection Configuration and Use](https://docs.silabs.com/iot-security/latest/efr32-secure-vault-tamper/).

![image](/series2-secure-debug/0.3/images/sld714-image36.jpg)

**Notes**:

- Set the bit to enable the corresponding bit in the debug mode request.
- The Debug Unlock Token will reset the corresponding debug option if the same bit is set in Debug mode request and Authorizations.

###### Challenge Response (heading level 7)

The elements of the challenge response are described in the following figure and table.

![Challenge Response](/series2-secure-debug/0.3/images/sld714-challenge-response.png)

|**Element**|**Value**|**Description**|
|---|---|---|
|Debug access command|0xfd010001|The command word of the Debug Unlock Token.|
|Debug mode request|Device dependent|The command parameter of the debug access command.|
|Challenge|Device dependent (1)|A random value generated by the SE.|
|Debug access command signature|Device dependent (2)|All the content above is signed (ECDSA-P256-SHA256) by the Private Certificate Key corresponding to the Public Certificate Key in the access certificate.|

**Notes**:

1. The challenge remains unchanged until it is updated to a new random value by rolling the challenge. The Private Certificate Key can be reused for signing when the device challenge is refreshed.
2. This signature is the final argument of the Debug Unlock Token.

###### Debug Access Flow (heading level 7)

The debug access flow is described in the following figure.

![Debug Access Flow](/series2-secure-debug/0.3/images/sld714-debug-access-flow.png)

1. Get the serial number and challenge from the SE.
2. Generate the access certificate with the device serial number.
3. Generate the challenge response with device challenge.
4. Generate the debug access command payload with access certificate and debug access command signature.
5. Send the Debug Unlock Token to the SE.
6. Verify the debug access command signature using the Public Certificate Key in the access certificate.
7. Verify the serial number and the access certificate signature using the on-chip serial number and Public Command Key in the SE OTP.
8. Authorize the debug mode request to reset the debug options until the next power-on or pin reset.
9. Roll the challenge to invalidate the current Debug Unlock Token.

###### TrustZone Debug Authentication (heading level 7)

The debug and trace support in the Cortex-M33 devices are based on the [CoreSight](https://developer.arm.com/documentation/ihi0029/f?lang=en) architecture, which can be classified into Invasive and Non-invasive debugging features as described in the following table.

|**Classification**|**Debug and Trace Features**|**Description**|
|---|---|---|
|Invasive|Core debug (e.g., single stepping), Breakpoints, Data watchpoints, Halt mode debugging|These features halt the Cortex-M33 core and change the program execution flow.|
|Non-invasive|Embedded Trace Macrocell (ETM), Micro Trace Buffer (MTB), Data trace, Instrumentation Trace Macrocell (ITM), Profiling|These features have a minor or no impact on the program execution flow.|

The separation of Invasive and Non-invasive debug and trace operations in CoreSight architecture can apply to TrustZone debug authentication, which defines the permission levels of the debug and trace features on Secure and Non-secure worlds.

The table below describes four debug options in SE to support TrustZone debug authentication. It is possible to restrict the TrustZone access permissions of the debug interface by setting one or more of the following options.

|**Debug Option**|**Description**|
|---|---|
|DBGLOCK|Non-secure, Invasive debug lock. If this bit is set, the Invasive debug features for the Non-secure state are locked.|
|NIDLOCK|Non-secure, Non-invasive debug lock. If this bit is set, the Non-invasive debug features for the Non-secure state are locked.|
|SPIDLOCK|Secure, Invasive debug lock. If this bit is set, the Invasive debug features for the Secure state are locked.|
|SPNIDLOCK|Secure, Non-invasive debug lock. If this bit is set, the Non-invasive debug features for the Secure state are locked.|

**Notes**:

- Use [Simplicity Commander](07-examples#examples-secure-debug-lock-using-commander-tool) or the SE Manager API to set the debug options.
- The state of the debug options is stored permanently in SE and can only be reset to the default value (0000) through the [Erase Device](#debug-unlock-command-reference) command (if enabled).
- A secure debug lock device [Device Erase](04-debug-lock#secure-debug-unlock) was disabled can only use the Debug Unlock Token to temporarily unlock (reset) the debug options to debug the Secure and Non-secure applications.

The following conditions are recommended (1, 2, and 3) or mandatory (4) when setting up the debug options for secure debug unlock.

1. If SPIDLOCK is unlocked, then DBGLOCK should also be unlocked.
2. If SPNIDLOCK is unlocked, then NIDLOCK should also be unlocked.
3. If DBGLOCK is unlocked, the NIDLOCK should also be unlocked.
4. If SPIDLOCK is unlocked, then SPNIDLOCK is automatically unlocked. The following table lists the recommended combinations of debug options.

|**SPNIDLOCK**|**SPIDLOCK**|**NIDLOCK**|**DBGLOCK**|**Description**|
|---|---|---|---|---|
|0|0|0|0|Allows all debug and trace features for both the Secure and the Non-secure world (default setting).|
|0|1|0|0|Only allows a Non-invasive debug in the Secure world. Allows both Invasive and Non-invasive debugs in the Non-secure world.|
|0|1|0|1|Only allows a Non-invasive debug in the Secure and the Non-secure world.|
|1|1|0|0|Only allows debug and trace features in the Non-secure world.|
|1|1|0|1|Only allows a Non-invasive debug in the Non-secure world.|
|1|1|1|1|All debug and trace features are disabled.|

**Notes**:

- [Trace Point Interface Unit (TPIU) registers' access fault](https://developer.arm.com/documentation/ka005320/latest) will occur and lock the processor in a security assertion if both NIDLOCK and DBGLOCK in debug option are set (xx11). The device will be unrecoverable if it is in the permanent debug lock state.
- The workaround is to avoid using the xx11 debug option or avoid accessing the TPIU registers and upgrade to SE firmware **≥ v1.2.14** (xG21 and xG22) or **≥ v2.2.1** (other Series 2 and all Series 3 devices) so that the debug options cannot be modified after the device is locked.

The highly recommended setting of debug options is to allow debugging in the Non-secure world while, at the same time, disabling debugging for the Secure world (1100).

- Secure memories (flash and RAM) are not accessible by the debugger.
- All debug access is blocked from accessing Secure addresses.
- The debugger will ignore the vector-catch events generated by the Secure exceptions.
- Trace sources (e.g., ETM) will stop generating instruction/data trace packets when the Cortex-M33 is in a Secure state.
- The debugger can neither halt a Secure application (e.g., breakpoint) nor single step into it.
- The Cortex-M33 will not stop when stepping into the Secure application until it returns to the Non-secure state.

The figure below describes the debug scenario of debug options with `1100` configuration.

![image](/series2-secure-debug/0.3/images/sld714-image67.jpg)

The following examples describe the relationship between **debug options** and **debug mode request** when performing a secure debug unlock on Series 2 devices.

**Example 1: All debug and trace features for both the Secure and the Non-secure world are allowed (0000)**

|**Debug Options**|**Authorizations**|**Debug Mode Request**|**Debug options after Secure Debug Unlock**|**Description**|
|---|---|---|---|---|
|0000|00|1111|10|00|xxxx|10|0000|No action|

**Example 2: Only debug and trace features in the Non-secure world are allowed (1100)**

|**Debug Options**|**Authorizations**|**Debug Mode Request**|**Debug options after Secure Debug Unlock**|**Description**|
|---|---|---|---|---|
|1100|00|1111|10|00|00xx|10|1100|No action|
|1100|00|1111|10|00|10xx|10|0100|Unlock SPNIDLOCK|
|1100|00|1111|10|00|01xx|10 or 00|11xx|10|0000 (reset SPIDLOCK will automatically unlock SPNIDLOCK)|Unlock SPNIDLOCK and SPIDLOCK|

**Notes**:

- The bit order of debug options are SPNIDLOCK (MSB), SPIDLOCK, NIDLOCK, and DBGLOCK (LSB).
- Debug options : 0 = Unlocked, 1 = Locked
- Authorizations in the access certificate: 0 = Disable, 1 = Enable
- The authorizations in the access certificate are usually set to 00|1111|10 (0x3e), so the corresponding debug options (bits 2 to 5) can be reset (unlocked) by debug mode request during secure debug unlock.
- Debug mode request (bits 2 to 5) in the Debug Unlock Token:  
  - 0 = No action on the corresponding debug option if it was locked (i.e., 1)  
  - 1 = Reset (unlock) the corresponding debug option from 1 to 0 if it was locked (i.e., 1)  
  - x = No action (either 0 or 1) on the corresponding debug option if it was unlocked (i.e., 0)
- Debug options return to the original state after power-on or pin reset.

###### Debug Unlock Command Reference

The commands for debug unlock are described in the following table.

|**DCI Command (1)**|**Mailbox (SE Manager) API (2)**|**Description**|**Availability**|
|---|---|---|---|
|Erase Device|sl_se_erase_device|Performs a device mass erase and resets the debug configuration to its initial unlocked state.|While Device Erase is enabled.|
|Read Serial Number|sl_se_get_serialnumber|Reads out the serial number (16 bytes) of the Series 2 device.|Always.|
|Get Challenge|sl_se_get_challenge|Reads out the current challenge value (16 bytes) for Secure debug unlock.|While Public Command key is uploaded.|
|Debug Access|sl_se_open_debug|Opens the secure debug access of the Cortex-M33.|Only when Secure Debug is enabled.|

**Notes**:

1. Performing these commands over DCI is implemented in Simplicity Studio and Simplicity Commander.
2. These APIs are only available on HSE devices. The SE Manager API document can be found at [https://docs.silabs.com/gecko-platform/latest/platform-security-api/sl-se-manager](https://docs.silabs.com/gecko-platform/latest/platform-security-api/sl-se-manager).

##### Precautions

###### Device Erase for Secure Debug

Disabling the Device Erase is mandatory for secure debug as described in the following table.

<table>
    <thead>
        <tr>
            <th>Device Erase</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Enabled</td>
            <td>By using <code>erasedevice</code> command, entire Flash contents can be erased, except OTP and Userdata contents. If<code>security erasedevice </code> is enabled on production devices, it may expose the device to significant security risks, as malicious actors could potentially reprogram it with unauthorized or malicious firmware.</td>
        </tr>
        <tr>
            <td>Disabled</td>
            <td>
                <ul>
                    <li>Disable device erase command disables the use of the commander security erasedevice command. This will prevent the clearing of the debug lock and secure debug configurations.</li>
                    <li>If <strong>secure Debug</strong> is enabled and <strong> Device Erase </strong> is disabled, device allows only authorized parties to debug the device.</li>
                    <li>If secure boot is also enabled boot loader will accept only authenticated (signed) firmware image, thereby ensures authenticity and integrity of the firmware.</li>
                </ul>
            </td>
        </tr>
    </tbody>
</table>

**Notes**:

1. Advised not to disable **Device Erase** during development phase as it is one time operation.
2. Without enabling **Secure Debug** disabling **Device Erase** will make the device permanently not usable.

Run the `security disabledeviceerase` command to disable **Device Erase**.

```sh
commander security disabledeviceerase --device sixg301 --serialno 440326972
```

```sh
================================================================================
THIS IS A ONE-TIME command which Permanently disables device erase.
If secure debug lock has not been set, there is no way to regain debug access to this device. Type 'continue' and hit enter to proceed or Ctrl-C to abort:
================================================================================
continue
Disabled device erase successfully
DONE
```

###### Secure Boot and Debug Lock

The following table describes the different debug lock scenarios on the secure boot-enabled device.

|**Secure Debug**|**Device Erase**|**Debug Lock**|**State**|**Recover from Secure Boot Failure**|
|---|---|---|---|---|
|Disabled|Enabled|Disabled|Standard debug unlock|Flash a correctly signed image.|
|Disabled|Enabled|Enabled|Standard debug lock|Flash a correctly signed image after standard debug unlocking the device.|
|Disabled|Disabled|Enabled|Permanent debug lock|There is no way to recover the device. Make sure the programmed image is correctly signed before locking the device.|
|Enabled|Disabled|Enabled|Secure debug lock|Flash a correctly signed image after secure debug unlocking the device.|

**Note**: See _Recover Devices when Secure Boot Fails_ in [Series 2 and Series 3 Secure Boot with RTSL](https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/) to flash a correctly signed image on different debug lock scenarios.

###### Limitation on Roll Challenge

On Series 2 devices, challenge can be rolled any number of times, whereas on Series 3, challenge can be rolled a maximum of 128 times.
Once a user has reached the 128 number of maximum rolls for debug challenges, new challenges will no longer be generated. The user will be able to continue to unlock the device using the last generated challenge..

##### Examples

###### Using Commander tool

###### Standard Debug Lock/Unlock using Simplicity Commander (heading level 7)

This application note uses Simplicity Commander v1.19.2. The procedures and console output may be different for the other versions of Simplicity Commander. The latest version of Simplicity Commander can be downloaded from silabs.com.

```sh
commander --version
```

```sh
Simplicity Commander 1v19p2b1907

JLink DLL version: 8.44
Qt 5.15.2 Copyright (C) 2017 The Qt Company Ltd.
EMDLL Version: 0v19p19b793 mbed TLS version: 2.16.6

Emulator found with SN=440328778 USBAddr=0

DONE
```

For more information about Simplicity Commander, see [Simplicity Commander Reference Guide](https://docs.silabs.com/simplicity-commander/latest/simplicity-commander-start/).

1. Run the security status command to get the selected device configuration.  
   ```sh  
   commander security status --device sixg301 --serialno 440328778  
   ```  
   ```sh  
   SE ROM version          : 5.3  
   SE Firmware version     : 3.3.2  
   Serial number           : 0000000000000000781c9dfffe58959e  
   Debug lock              : Disabled  
   Device erase            : Enabled  
   Secure debug unlock     : Disabled  
   Tamper status           : OK  
   Secure boot             : Disabled  
   Boot status             : 0x20 - OK  
   Command key installed   : False  
   Sign key installed      : False  
   Security state          : Production  
   DONE  
   ```
2. Run the security lock command to lock the selected device.  
   ```sh  
   commander security lock --device sixg301 --serialno 440328778  
   ```  
   ```sh  
   WARNING: Secure debug unlock is disabled. Only way to regain debug access is to run a device erase.  
   Device is now locked.  
   DONE  
   ```
3. Run the security lock command to lock the selected device.  
   ```sh  
   commander security status --device sixg301 --serialno 440328778  
   ```  
   ```sh  
   SE ROM version        : 5.3  
   SE Firmware version   : 3.3.2  
   Serial number         : 0000000000000000781c9dfffe58959e  
   Debug lock            : Enabled  
   Device erase          : Enabled  
   Secure debug unlock   : Disabled  
   Tamper status         : OK  
   Secure boot           : Disabled  
   Boot status           : 0x20 - OK  
   Command key installed : False  
   Sign key installed    : False  
   Security state        : Production  
   DONE  
   ```
4. Run the `security erasedevice` command to unlock the selected device.  
   ```sh  
   commander security erasedevice --device sixg301 --serialno 440328778  
   ```  
   ```sh  
   Successfully erased device  
   DONE  
   ```  
   > **Note**: Issue a power-on or pin reset to complete the unlock process.
5. Run the `security status` command again to check the device configuration.

```sh
commander security status --device sixg301 --serialno 440328778
```

```sh
SE ROM version          : 5.3
SE Firmware version     : 3.3.2
Serial number           : 0000000000000000781c9dfffe58959e
Debug lock              : Disabled
Device erase            : Enabled
Secure debug unlock     : Disabled
Tamper status           : OK
Secure boot             : Disabled
Boot status             : 0x20 - OK
Command key installed   : False
Sign key installed      : False
Security state          : Production
DONE
```

###### Secure Debug Lock Using Commander Tool (heading level 7)

1. Run the `security status` command to get the selected device configuration.  
   ```sh  
   commander security status --device sixg301 --serialno 440326972  
   ```  
   ```sh  
   commander security status --device sixg301 --serialno 440326972  
   ---------------------------------------------------------------  
   SE ROM version          : 5.3  
   SE Firmware version     : 3.3.2  
   Serial number           : 0000000000000000781c9dfffe589591  
   Debug lock              : Disabled  
   Device erase            : Enabled  
   Secure debug unlock     : Disabled  
   Tamper status           : OK  
   Secure boot             : Disabled  
   Boot status             : 0x20 - OK  
   Command key installed   : False  
   Sign key installed      : False  
   Security state          : Production  
   DONE  
   ```
2. Run `util genkey` to generate commander private/public key pair.  
   ```sh  
   commander util genkey --type ecc-p256 --privkey command_key.pem --pubkey command_pubkey.pem  
   ```  
   ```sh  
   Generating ECC P256 key pair...  
   Writing private key file in PEM format to command_key.pem  
   Writing public key file in PEM format to command_pubkey.pem  
   DONE  
   ```
3. Run the `security writekey` command to provision the Public Command Key (e.g., `command_pubkey.pem`).  
   ```sh  
   commander security writekey --command command_pubkey.pem --device sixg301 --serialno 440326972  
   ```  
   ```sh  
   Device has serial number 0000000000000000781c9dfffe589591  
     
   ================================================================================  
   Please look through any warnings before proceeding.  
   THIS IS A ONE-TIME command which permanently ties debug and tamper access to certificates signed by this key.  
   Type 'continue' and hit enter to proceed or Ctrl-C to abort:  
   ================================================================================  
   continue  
   Command public key stored in: C:/Users/<userName>/AppData/Local/SiliconLabs/commander/SecurityStore/ device_0000000000000000781c9dfffe589591/command_pubkey.pem  
   DONE  
   ```  
   > **Note**: The Public Command Key cannot be changed once written.
4. This step is optional. To verify the public command key written into the device's SE OTP, run the `security readkey` command.  
   ```sh  
   commander security readkey --command --device sixg301 --serialno 440326972  
   ```  
   ```sh  
   50DF50A09242A49F53251xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxE6E81C6  
   B3B891B1B9DBFC3D5F2D0yyyyy435DA6E8AFAF60037DA21AD7B2E1  
   DONE  
   ```
5. Run the `security lockconfig` command to enable the secure debug.  
   ```sh  
   commander security lockconfig --secure-debug-unlock enable --device sixg301 --serialno 440326972  
   ```  
   ```sh  
   Secure debug unlock was enabled  
   DONE  
   ```
6. a. For the **TrustZone-unaware** application, run the `security lock` command to lock the selected device.  
   ```sh  
   commander security lock --device sixg301 --serialno 440326972  
   ```  
   ```sh  
   Device is now locked.  
   DONE  
   ```  
   b. For the **TrustZone-aware** application, run the `security lock --trustzone ####` command to set the [debug options](05-debug-unlock#trustzone-debug-authentication) (e.g., `1100`) and lock the selected device. The bit order of #### is SPNIDLOCK (MSB), SPIDLOCK, NIDLOCK, and DBGLOCK (LSB).  
   ```sh  
   commander security lock --trustzone 1100 --device sixg301 --serialno 440048205  
   ```  
   ```sh  
   Writing debug restriction bits:  
   DBGLOCK:	0  
   NIDLOCK:	0  
   SPIDLOCK:   1  
   SPNIDLOCK:  1  
   Device is now locked.  
   DONE  
   ```  
   **Notes**:  
   - The `--trustzone` option for the `security lock` command requires Simplicity Commander **≥ v1.13.3**.  
   - It is strongly recommended to upgrade to SE firmware **≥ v1.2.14** (xG21 and xG22) or **≥ v2.2.1** (other Series 2 devices). For Series 3 devices, it is strongly recommended to upgrade SE firmware to ≥ v3.3.2 so that the debug options cannot be modified after the device is locked.  
   - Use `commander security lock` without the `--trustzone ####` option if the default setting of debug options (`0000`) is good enough for a TrustZone-aware application.
7. Run the `security disabledeviceerase` command to disable device erase. This is an **IRREVERSIBLE** action, and should be the last step in production. (**It is recommended not to set this option while evaluating secure debug functionality.**)  
   ```sh  
   commander security disabledeviceerase --device sixg301 --serialno 440326972  
   ```  
   ```sh  
   ================================================================================  
   THIS IS A ONE-TIME command which Permanently disables device erase.  
   If secure debug lock has not been set, there is no way to regain debug access to this device. Type 'continue' and hit enter to proceed or Ctrl-C to abort:  
   ================================================================================  
   continue  
   Disabled device erase successfully  
   DONE  
   ```  
   > **Note**: The debug options cannot be reset to the default value 0000 (unlock) if the device erase option is disabled.
8. Read back device status after it is securely locked.  
   a. For the TrustZone-unaware application, run the security status command to check the debug lock status of the device  
   ```sh  
   commander security status --device sixg301 --serialno 440326972  
   ```  
   ```sh  
   SE ROM version        : 5.3  
   SE Firmware version   : 3.3.2  
   Serial number         : 0000000000000000781c9dfffe589591  
   Debug lock            : Enabled  
   Device erase          : Enabled  
   Secure debug unlock   : Enabled  
   Tamper status         : OK  
   Secure boot           : Disabled  
   Boot status           : 0x20 - OK  
   Command key installed : True  
   Sign key installed    : False  
   Security state        : Production  
   DONE  
   ```  
   b. For the TrustZone-aware application, run the `security status --trustzone` command to check the full debug lock status of the device.  
   ```sh  
   commander security status --trustzone --device sixg301 --serialno 440326972  
   ```  
   ```sh  
   SE ROM version          : 5.3  
   SE Firmware version     : 3.3.2  
   Serial number           : 0000000000000000781c9dfffe589591  
   Debug lock              : Enabled  
   Device erase            : Enabled  
   Secure debug unlock     : Enabled  
     
   Debug lock state: Locked  
     
   TrustZone Config:  
   Non-secure, invasive debug lock     (DBGLOCK) : Unlocked  
   Non-secure, non-invasive debug lock (NIDLOCK) : Unlocked  
   Secure, invasive debug lock         (SPIDLOCK) : Unlocked  
   Secure, non-invasive debug lock     (SPNIDLOCK) : Unlocked  
     
   TrustZone State:  
   Non-secure, invasive debug lock state       (DBGLOCK) : Unlocked  
   Non-secure, non-invasive debug lock state   (NIDLOCK) : Unlocked  
   Secure, invasive debug lock state           (SPIDLOCK) : Unlocked  
   Secure, non-invasive debug lock state       (SPNIDLOCK) : Unlocked  
     
   Tamper status          : OK  
   Secure boot            : Disabled  
   Boot status            : 0x20 - OK  
   Command key installed  : True  
   Sign key installed     : False  
   Security state         : Production  
   DONE  
   ```

###### Secure Debug Unlock Using Commander tool (heading level 7)

Use case for secure debug unlock is explained in [secure debug unlock](05-debug-unlock#secure-debug-lockunlock-flow) as pictorially. The steps to securely unlock the device securely are explained below.

1. The Product Company creates a Private/Public Certificate Key pair for each device. Because the key pair is assigned only to a single device, the company may not need to protect the Private Certificate Key as securely as the Private Command Key by the Direct Customer.  
   In this example, the Private/Public Certificate Key pair (`cert_key.pem` and `cert_pubkey.pem`) is generated by running the `util genkey` command.  
   ```sh  
   commander util genkey --type ecc-p256 --privkey cert_key.pem --pubkey cert_pubkey.pem  
   ```  
   ```sh  
   Generating ECC P256 key pair...  
   Writing private key file in PEM format to cert_key.pem  
   Writing public key file in PEM format to cert_pubkey.pem  
   DONE  
   ```
2. The Public Certificate Key (`cert_pubkey.pem`) for each device is passed to the Silicon Labs Direct Customer. The part number and serial number are also required if Direct Customer cannot access the device.If necessary, run the `security status` command to get the device serial number.  
   ```sh  
   commander security status --device sixg301 --serialno 440326972  
   ```  
   ```sh  
   SE ROM version	        : 5.3  
   SE Firmware version	    : 3.3.2  
   Serial number	        : 0000000000000000781c9dfffe589591  
   Debug lock	            : Enabled  
   Device erase	        : Enabled  
   Secure debug unlock	    : Enabled  
   Tamper status	        : OK  
   Secure boot	            : Disabled  
   Boot status	            : 0x20 - OK  
   Command key installed	: True  
   Sign key installed	    : False  
   Security state	        : Production  
   DONE  
   ```
3. The Direct Customer then places that Public Certificate Key in the [access certificate](05-debug-unlock#access-certificate). The access certificate is unique per device because it contains the unique device serial number. This certificate is generated once upon creation of the device, and thereafter, is generally only modified when the Private/Public Certificate Key pair is changed by the Product Company.  
   Run the `security gencert` command with the following parameters from the Product Company to generate an unsigned access certificate (`access_certificate.extsign`) in Security Store:  
   - Device part number  
   - Device serial number  
   - Public Certificate Key  
   ```sh  
   commander security gencert --device sixg301 --deviceserialno 0000000000000000781c9dfffe589591 --cert-pubkey cert_pubkey.pem --extsign  
   ```  
   ```sh  
   Authorization file written to Security Store:  
   C:/Users/<userName>/AppData/Local/SiliconLabs/commander/SecurityStore/ device_0000000000000000781c9dfffe589591/certificate_authorizations.json Cert key written to Security Store:  
   C:/Users/<userName>/AppData/Local/SiliconLabs/commander/SecurityStore/ device_0000000000000000781c9dfffe589591/cert_pubkey.pem  
   Created an unsigned certificate in Security Store:  
   C:/Users/<userName>/AppData/Local/SiliconLabs/commander/SecurityStore/ device_0000000000000000781c9dfffe589591/access_certificate.extsign  
   DONE  
   ```
4. The signing of the access certificate can be done by passing an unsigned access certificate to a Hardware Security Module (HSM) containing the Private Command Key.  
   In this example, the OpenSSL tool is used instead of HSM. Please install openSSL version above 3.5.0. Sign the access certificate (`access_certificate.extsign`) in Security Store with the Private Command Key (`command_key.pem`). The [access certificate signature](05-debug-unlock#access-certificate) is in the `cert_signature.bin` file.  
   ```sh  
   openssl dgst -sha256 -binary -sign command_key.pem -out cert_signature.bin access_certificate.extsign  
   ```  
   Run the `util signcert` command with the following parameters to verify the signature and generate the signed access certificate (`access_certificate.bin`):  
   - Unsigned access certificate  
   - Access certificate signature  
   - Public Command Key  
   ```sh  
   commander util signcert access_certificate.extsign --cert-type access --signature cert_signature.bin  
   --verify command_pubkey.pem --outfile access_certificate.bin  
   ```  
   ```sh  
   R = D97E43FEA278207080D6D0808B46810C1167F123AF1CA9FAF2DE0F4322B97ACE  
   S = FEDFEA11A3C83AFFCD5293283B13A50580862B9F651AAE08012C2BFB6BA8E697  
   Successfully verified signature  
   Successfully signed certificate  
   DONE  
   ```
5. The access certificate is passed to the Product Company. The purpose of the access certificate is to grant overall debug access capabilities to the Product Company and authorize them to allow third parties to debug the device. The Product Company can now use the access certificate to generate the Debug Unlock Token. The same access certificate can be used to generate as many Debug Unlock Tokens as necessary without having to ever go back to the Direct Customer.
6. To create the Debug Unlock Token, a debug session must be started with the device and the challenge value (which is a random number `Challenge 1` in this example) should be read out to generate the challenge response.  
   Run the `security gencommand` command to generate the challenge response without debug access command signature and store it in a file (`command_unsign.bin`).  
   ```sh  
   commander security gencommand --action debug-unlock --unlock-param 1111 -o command_unsign.bin --nostore  
   --device sixg301 --serialno 440326972  
   ```  
   ```sh  
   Unsigned command file written to:  
   command_unsign.bin  
   DONE  
   ```  
   **Notes**:  
   - The data in the `--unlock-param` option are the bits 2 to 5 of debug mode request in the challenge response.  
   - The default value `1111` (reset all debug options) is in place if the `security gencommand` command does not include the `-- un-lock-param` option.
7. The challenge response is then cryptographically hashed (SHA-256) to create a digest. The digest is then signed by the Private Certificate Key to generate the debug access command signature.  
   The signing of the challenge response can be done by passing an unsigned challenge response to a Hardware Security Module (HSM) containing the Private Certificate Key.  
   In this example, OpenSSL is used to sign the challenge response (`command_unsign.bin`) with the Private Certificate Key (`cert_key.pem`). The debug access command signature is in the `command_signature.bin` file.  
   ```sh  
   openssl dgst -sha256 -binary -sign cert_key.pem -out command_signature.bin command_unsign.bin  
   ```
8. Run the `security unlock` command with the access certificate (`access_certificate.bin`) from Direct Customer and debug access command signature (`command_signature.bin`) in step 7 to generate the Debug Unlock Token.  
   ```sh  
   commander security unlock --cert access_certificate.bin --command-signature command_signature.bin -- unlock-param 1111 --device sixg301 --serialno 440326972  
   ```  
   ```sh  
   Certificate written to Security Store:  
   C:/Users/<userName>/AppData/Local/SiliconLabs/commander/SecurityStore/device_0000000000000000781c9dfffe589591/access_certificate.bin  
   R = B4F5F2628B50BBA54ADAB4EB67CD0F933FE4C01E8BA760915D5167E75330F3A2  
   S = 4D7EEA1B6D8EA61F3140198B26060D9F96D32CDCC5CE58CB4611ECED0D21F9B8  
   Command signature is valid  
   Secure debug successfully unlocked  
   Command unlock payload was stored in Security Store  
   DONE  
   ```  
   **Notes**:  
   - Put the required files in the same folder to run the command.  
   - The debug access command signature can be in a Raw or Distinguished Encoding Rules (DER) format.  
   - It requires Simplicity Commander Version 1.11.2 or above to support signature in DER format.  
   - The data in the `--unlock-param` option are the bits 2 to 5 of debug mode request in the Debug Unlock Token. This value **MUST** be equal to the value of `--unlock-param` option in step 6.  
   - The default value `1111` (reset all debug options) is in place if the `security unlock` command does not include the `--unlock- param` option.
9. **(Alternative)** The key protection is not required if the Private Certificate Key is ephemeral. Steps 6 to 8 can be implemented by running the `security unlock` command with the access certificate (`access_certificate.bin`) from the Direct Customer and Private Certificate Key (`cert_key.pem`) to generate the Debug Unlock Token.  
   ```sh  
   commander security unlock --cert access_certificate.bin --cert-privkey cert_key.pem --unlock-param 1111 -- device sixg301 --serialno 440326972  
   ```  
   ```sh  
   Unlocking with unlock payload:  
   C:/Users/<userName>/AppData/Local/SiliconLabs/commander/SecurityStore/device_0000000000000000781c9dfffe589591/challenge_a7be6d84d1a41321b1492e643ba626f1/unlock_payload_0000000000111110.bin  
   Secure debug successfully unlocked  
   DONE  
   ```  
   **Notes**:  
   - The data in the `--unlock-param` option are the bits 2 to 5 of debug mode request in the Debug Unlock Token.  
   - The default value `1111` (reset all debug options) is in place if the `security unlock` command does not include the `--unlock-param` option.
10. The Debug Unlock Token (also known as `Command unlock payload`) file (`unlock_payload_0000000000111110.bin`, where `0000000000111110` is the value of debug mode request) is stored in the Security Store. The location in Windows is `C:\Users\<userName>\AppData\Local\SiliconLabs\commander\SecurityStore\device_<Serial number>\challenge_<Challenge value>`.  
    ![screenshot](/series2-secure-debug/0.3/images/sld714-image68.jpg)  
    Users can also use the `security getpath` command to get the path of the Security Store or a specified device.  
    ```sh  
    commander security getpath --device sixg301  
    ```  
    ```sh  
    C:/Users/<username>/AppData/Local/SiliconLabs/commander/SecurityStore  
    DONE  
    ```  
    ```sh  
    commander security getpath --deviceserialno 0000000000000000781c9dfffe589591  
    ```  
    ```sh  
    C:/Users/<username>/AppData/Local/SiliconLabs/commander/SecurityStore/device_0000000000000000781c9dfffe589591  
    DONE  
    ```
11. The Debug Unlock Token and the device are now delivered to the Debug 3rd Party.  
    Run the `security gencommand` command to create the Security Store to place the Debug Unlock Token file.  
    ```sh  
    commander security gencommand --action debug-unlock --device sixg301 --serialno 440326972  
    ```  
    ```sh  
    Unsigned command file written to Security Store:  
    C:/Users/<userName>/AppData/Local/SiliconLabs/commander/SecurityStore/device_0000000000000000781c9dfffe589591/challenge_a7be6d84d1a41321b1492e643ba626f1/ unlock_command_to_be_signed22_07_2025.bin  
    DONE  
    ```  
    Copy the Debug Unlock Token file (`unlock_payload_0000000000111110.bin`) from Product Company to the Windows Security Store `challenge_<Challenge value>` folder located in `C:\Users\<PC user name>\AppData\Local\SiliconLabs\commander\SecurityStore\device_<Serial number>challenge_<Challenge value>`.
12. The device compares the Debug Unlock Token contents with its internal serial number, challenge value, and Public Command Key to determine the token’s authenticity. If authentic, it will execute the debug access command to unlock the device; otherwise, it will ignore the command.  
    Run the `security unlock` command to unlock the device.  
    ```sh  
    commander security unlock --unlock-param 1111 --device sixg301 --serialno 440326972  
    ```  
    ```sh  
    Unlocking with unlock payload:  
    C:/Users/<userName>AppData/Local/SiliconLabs/commander/SecurityStore/device_0000000000000000781c9dfffe589591/challenge_a7be6d84d1a41321b1492e643ba626f1/unlock_payload_0000000000111110.bin  
    Secure debug successfully unlocked  
    DONE  
    ```  
    **Notes**:  
    - If the security store has multiple tokens for the selected device, use `--unlock-param` option to specify which unlock token is chosen to unlock the device.  
    - Simplicity Commander will only use the token with value `1111` (error if not available) from the security store to unlock the device if the security unlock command does not include the `--unlock-param` option.
13. Run `security status --trustzone` command to check the full debug lock status of the device.  
    ```sh  
    commander security status --trustzone --device sixg301 --serialno 440326972  
    ```  
    ```sh  
    SE ROM version	        : 5.3  
    SE Firmware version	    : 3.3.2  
    Serial number	        : 0000000000000000781c9dfffe589591  
    Debug lock	            : Disabled  
    Device erase	        : Enabled  
    Secure debug unlock     : Disabled  
      
    Debug lock state: Unlocked  
      
    TrustZone Config:  
    Non-secure, invasive debug lock   (DBGLOCK) : Unlocked  
    Non-secure, non-invasive debug lock (NIDLOCK) : Unlocked  
    Secure, invasive debug lock  (SPIDLOCK) : Unlocked  
    Secure, non-invasive debug lock   (SPNIDLOCK): Unlocked  
      
    TrustZone State:  
    Non-secure, invasive debug lock state  (DBGLOCK) : Unlocked  
    Non-secure, non-invasive debug lock state (NIDLOCK) : Unlocked  
    Secure, invasive debug lock state   (SPIDLOCK) : Unlocked  
    Secure, non-invasive debug lock state   (SPNIDLOCK): Unlocked  
      
    Tamper status	      : OK  
    Secure boot	          : Disabled  
    Boot status	          : 0x20 - OK  
    Command key installed : True  
    Sign key installed	  : False  
    DONE  
    ```
14. The Debug 3rd Party can now use this same Debug Unlock Token to unlock the device (step 12), over and over again after each power-on or pin reset, until they have finished debugging the device.
15. Once the Debug 3rd Party has finished debugging, they will send the device back to the Product Company.
16. Once the Product Company receives the device, they will immediately start a debug session, roll the challenge (from `Challenge 1` to `Challenge 2` in this example), and put the device back into the secure debug lock state. Rolling the challenge will effectively invalidate any Debug Unlock Token that has been previously given to any third party.  
    Run the `security rollchallenge` command and reset the device to invalidate the current Debug Unlock Token. The challenge cannot be rolled before it has been used at least once.  
    ```sh  
    commander security rollchallenge --device sixg301 --serialno 440326972  
    ```  
    ```sh  
    Challenge was rolled successfully.  
    DONE  
    ```  
    The unlock token is invalidated after rolling the challenge because any previously issued Debug Unlock Token now contains a different challenge value (`Challenge 1`) than the challenge value currently in the device (`Challenge 2`).  
    The validation process of any previously issued Debug Unlock Token will always fail until a new Debug Unlock Token is issued with a current matching challenge value (`Challenge 2`).

###### Using Simplicity Studio

The security operations are performed in the Security Settings of Simplicity Studio. This application note uses Simplicity Studio v5.11.0.0. The procedures and pictures may be different for the other versions of Simplicity Studio 5.

###### Standard Debug lock/unlock using Simplicity Studio (heading level 7)

1. Right-click the selected debug adapter RB (ID:J-Link serial number) to display the context menu.  
   ![Debug Adapters Context Menu](/series2-secure-debug/0.3/images/sld714-image69.png)
2. Click **Device configuration...** to open the **Configuration of device: J-Link Silicon Labs (serial number)** dialog box. Click the **Security Settings** tab to get the selected device configuration.  
   ![Configuration on Selected Device](/series2-secure-debug/0.3/images/sld714-image70.png)
3. Click [**Enable**] next to **Enable Debug Lock:** to lock the device. The following **Enable Debug Lock Warning** is displayed. Click [**Yes**] to confirm. This configures standard debug lock.  
   ![Enable Debug Lock](/series2-secure-debug/0.3/images/sld714-image71.png)
4. The [**Enable**] controls next to **Enable Secure Debug Unlock:** and **Enable Debug Lock:** are grayed out after standard debug lock is enabled.  
   ![Standard Debug Lock](/series2-secure-debug/0.3/images/sld714-image72.jpg)
5. Click [**Device Erase**] to unlock the device.  
   ![Device Erase](/series2-secure-debug/0.3/images/sld714-image73.png)
6. The device will return to the unlock state. Click [**OK**] to exit.  
   ![Standard Debug Unlock](/series2-secure-debug/0.3/images/sld714-image74.png)

###### Secure Debug Lock Using Simplicity Studio (heading level 7)

1. Run the `util keytotoken` command to convert the Public Command Key file (PEM format) into a text file (`command_pubkey.txt`).  
   Refer to [Secure Debug Lock Using Commander tool)](#secure-debug-unlock-using-commander-tool) before performing these steps.  
   ```sh  
   commander util keytotoken command_pubkey.pem --outfile command_pubkey.txt  
   ```  
   ```sh  
   Writing EC tokens to command_pubkey.txt...  
   DONE  
   ```
2. Open **Security Settings** of the selected device
3. Click the **WriteKey** link next to **Command Key:** to open a dialog box.  
   ![screenshot](/series2-secure-debug/0.3/images/sld714-write-key-link.png)
4. The **Write Command Key** dialog box is displayed.  
   ![screenshot](/series2-secure-debug/0.3/images/sld714-image77.png)
5. Open the `command_pubkey.txt` file generated in step 1.  
   ```sh  
   MFG_SIGNED_BOOTLOADER_KEY_X : 50DF50A09242A49F53251D38E1A368C82EC7CA2D33E6E81C6B3B891B1B9DBFC3  
   MFG_SIGNED_BOOTLOADER_KEY_Y : D5F2D045236CBEF3CB46B13BF7527AA36A26435DA6E8AFAF60037DA21AD7B2E1  
   ```
6. Copy Public Command Key (X-point `50DF...` first, then Y-point `D5F2...`) to **Command Key:** box.  
   ![screenshot](/series2-secure-debug/0.3/images/sld714-image78.png)
7. Click [**Write**] to provision the Public Command Key.
8. Click [**Enable**] next to **Enable Secure Debug Unlock:** to enable the secure debug functionality.  
   ![screenshot](/series2-secure-debug/0.3/images/sld714-image79.png)
9. Click [**Enable**] next to **Enable Debug Lock:** to lock the device. This configures secure debug lock.  
   ![screenshot](/series2-secure-debug/0.3/images/sld714-image81.jpg)
10. Click [**Disable**] next to **Disable Device Erase:** to disable the device erase. The following **Disable Device Erase Warning** is displayed. Click [**Yes**] to confirm.

![screenshot](/series2-secure-debug/0.3/images/sld714-image83.png)

> **Important**: This is an **IRREVERSIBLE** action, and should be the last step in production. While evaluating Secure Debug functionality, it is best to not enable this option.

###### Secure Debug Unlock Token Provision Simplicity Studio (heading level 7)

Use the Debug Unlock Token file (`unlock_payload_0000000000111110.bin`) generated in [Secure Debug Unlock Using Commander Tool](#secure-debug-unlock-using-commander-tool) steps 8 or 9 to unlock the device with Simplicity Studio.

1. Open the `unlock_payload_0000000000111110.bin` file with the Hex File editor.  
   ![screenshot](/series2-secure-debug/0.3/images/sld714-image85.jpg)
2. Click **View** to open the context menu, and then select **Group By** → **Double words** to convert the token into a little-endian format.  
   ![screenshot](/series2-secure-debug/0.3/images/sld714-image86.jpg)
3. Select all (Ctrl+A) and copy (Ctrl+C) the Debug Unlock Token to a text editor.  
   ![screenshot](/series2-secure-debug/0.3/images/sld714-image87.jpg)
4. Use the text editor to remove all the spaces from the token.
5. Right-click the selected debug adapter **RB (ID:J-Link serial number)** to display the context menu.  
   ![screenshot](/series2-secure-debug/0.3/images/sld714-image88.png)
6. Click **Set Unlock Token** to open the **Add Debug Unlock Token** dialog box. Enter the name (e.g., `AN1190 Token`) for this Debug Unlock Token, and copy the content in step 4 to the **Debug Unlock Token:** box. Click [**OK**] to confirm and exit.  
   ![screenshot](/series2-secure-debug/0.3/images/sld714-image89.png)  
   > **Note**: The Simplicity Studio can only keep one Debug Unlock Token on each WSTK.
7. Open Security Settings of the selected device as described in [Standard Debug Lock/Unlock Using Simplicity Studio](#standard-debug-lockunlock-using-simplicity-studio).
8. The token added in step 6 should be displayed on the **Crypto Profile:** field. If not, click the link next to **Crypto Profile:** to select the token from the **Crypto Profile Manager** drop-down list. The Simplicity Studio will automatically add the WSTK J-Link serial number (`-J-Link Silicon Labs (serial number)`) to the token's name.  
   ![screenshot](/series2-secure-debug/0.3/images/sld714-jlink-serial-number.png)
9. Click [**Unlock Debug Port**] to use the token in **Crypto Profile:** to unlock the device (invalid token will display an error message). The device stays in the unlock state until the next power-on or pin reset. Click [**OK**] to exit.  
   ![screenshot](/series2-secure-debug/0.3/images/sld714-image90.png)
10. The Simplicity IDE will automatically use the selected Debug Unlock Token in **Crypto Profile** for debugging and flashing.

After finished debugging, open the Security Settings of the selected device as described in [Standard Debug Lock/Unlock Using Simplicity Studio](#standard-debug-lockunlock-using-simplicity-studio).

###### Roll Challenge Using Simplicity Studio (heading level 7)

1. Click [**Roll Challenge**] to generate a new challenge value to invalidate the Debug Unlock Token added in step 6. Click [**OK**] to exit.  
   ![screenshot](/series2-secure-debug/0.3/images/sld714-image93.png)
2. Right-click the selected debug adapter **RB Board (ID:J-Link serial number)** to display the context menu.  
   ![screenshot](/series2-secure-debug/0.3/images/sld714-image94.png)
3. Click [**Clear Unlock Token**] to delete the WSTK Debug Unlock Token from Simplicity Studio.

###### Using Platform SE - Manager

Simplicity Studio 5 includes the SE Manager platform examples for Secure Tamper. Refer to the corresponding readme file for details about the SE Manager example. This file also includes the procedures to create the project and run the example about the SE Manager example. This file also includes the procedures to create the project and run the example.

<table>
    <thead>
        <tr>
            <th>Category</th>
            <th>SE Manager Platform Example</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Perform Secure Debug</td>
            <td>Platform Security - SoC SE Manager Secure Debug</td>
            <td>Sample application to:
                <ul>
                    <li>Set DEBUG_OPTION for TrustZone aware debug</li>
                    <li>Enable/Disable standard Debug Lock</li>
                    <li>Provision command key</li>
                    <li>Enable/Disable Secure Debug</li>
                    <li>Lock or Unlock debug port securely</li>
                    <li>Disable Device erase</li>
                    <li>Roll challenge</li>
                </ul>
            </td>
        </tr>
    </tbody>
</table>

##### Failure Analysis

The following table describes the different scenarios when returning a Series 2 device to Silicon Labs for failure analysis.

|**State**|**Secure Boot Disabled**|**Secure Boot Enabled (2)**|
|---|---|---|
|Standard debug unlock|Device erase is not necessary for failure analysis.|Device erase is not necessary, but a correctly signed image is required to perform failure analysis.|
|Standard debug lock|Device erase is required to perform failure analysis.|Require device erase and correctly signed image to perform failure analysis.|
|Permanent debug lock|Cannot perform failure analysis.|Cannot perform failure analysis.|
|Secure debug lock (1)|Require debug unlock token to perform failure analysis.|Require debug unlock token and correctly signed image to perform failure analysis.|

**Notes**:

1. Follow the procedures mentioned in [Debug Access Flow](05-debug-unlock#debug-access-flow) to generate a valid debug unlock token for each device returned to Silicon Labs for failure analysis.
2. Secure boot enabled devices, especially with secure boot failure, may limit Silicon Labs' ability to determine the root cause of failure.

#### Production Programming of Series 2 and Series 3 Devices

##### Production Programming of Series 2 and Series 3 Devices

> **Note: This section replaces _AN1222: Production Programming of Series 2 and Series 3 Devices_. Further updates to this application note will be provided here**.

This application note demonstrates how to properly program, provision, and configure Series 2 and Series 3 devices in a production environment.

Series 2 and Series 3 devices contain a Secure Engine, which runs Secure Engine firmware. When a newer version of Secure Engine firmware is released, the firmware may be upgraded either in the production programming process for devices still in manufacturing or via a field update for deployed devices. Keys must be provisioned to the Secure Engine's one-time-programmable (OTP) memory to use the Secure Boot and Secure Debug features.

For more information about Secure Engine, see [Secure Engine Subsystem](https://docs.silabs.com/iot-security/latest/series2-secure-debug/03-secure-engine-subsystem) in _Series 2 and Series 3 Secure Debug_.

###### Key Points

- It is the customer's responsibility to ensure the Secure Engine firmware is up-to-date
- The Secure Engine firmware can be upgraded via the Serial Wire Debug (SWD) interface
- Secure Engine firmware is protected from downgrade
- Secure Engine's OTP memory prevents re-writing of:  
  - GBL Decryption Key  
  - Public Sign Key  
  - Public Command Key  
  - Secure Boot Enable flag and Tamper Configuration

##### Series 2 and Series 3 Device Security Features

Protecting IoT devices against security threats is central to a quality product. Silicon Labs offers several security options to help developers build secure devices, secure application software, and secure paths of communication to manage those devices. Silicon Labs’ security offerings were significantly enhanced by the introduction of the Series 2 products that included a Secure Engine. The Secure Engine is a tamper-resistant component used to securely store sensitive data and keys and to execute cryptographic functions and secure services.

On Series 1 devices, the security features are implemented by the TRNG (if available) and CRYPTO peripherals.

On Series 2 devices, the security features are implemented by the Secure Engine and CRYPTOACC (if available). The Secure Engine may be hardware-based, or virtual (software-based). Throughout this application note, the following abbreviations are used:

- HSE - Hardware Secure Engine
- VSE - Virtual Secure Engine
- SE - Secure Engine (either HSE or VSE)

On Series 3 devices, the security features are implemented by the Secure Engine and SYMCRYPTO (if available). The Secure Engine is hardware-based.

Additional security features are provided by Secure Vault. Three levels of Secure Vault feature support are available, depending on the part and SE implementation, as reflected in the following table:

|**Level (1)**|**SE Support**|**Part (2)**|
|---|---|---|
|Series 3|Series 3 Secure Vault|Refer to (2) for details on supporting devices.|
|Secure Vault High (SVH)|HSE only (HSE-SVH)|“|
|Secure Vault Mid (SVM)|HSE (HSE-SVM)|"|
|"|VSE (VSE-SVM)|"|
|Secure Vault Base (SVB)|N/A|"|

> **Notes**:
> 
> 1. The features of different Secure Vault levels can be found in the [Silicon Labs IoT Security](https://docs.silabs.com/iot-security/latest/iot-security-start/) documentation site.
> 2. [Device Security Features](https://docs.silabs.com/iot-security/latest/iot-endpoint-security-fundamentals/11-series-2-and-series-3-device-security-features).

Secure Vault Mid consists of two core security functions:

- Secure Boot: Process where the initial boot phase is executed from an immutable memory (such as ROM) and where code is authenticated before being authorized for execution.
- Secure Debug access control: The ability to lock access to the debug ports for operational security, and to securely unlock them when access is required by an authorized entity.

Secure Vault High offers additional security options:

- Secure Key Storage: Protects cryptographic keys by “wrapping” or encrypting the keys using a root key known only to the HSE-SVH.
- Anti-Tamper protection: A configurable module to protect the device against tamper attacks.
- Device authentication: Functionality that uses a secure device identity certificate along with digital signatures to verify the source or target of device communications.

Series 3 Secure Vault offers the same features as Secure Vault High, except

- Device identity certificates are only installed for the SE, not the host core
- Support for AXiP/EXiP is added

A Secure Engine Manager and other tools allow users to configure and control their devices both in-house during testing and manufacturing, and after the device is in the field.

###### User Assistance

In support of these products, Silicon Labs offers whitepapers, webinars, and documentation. The following table summarizes the key security documents:

<table>
    <thead>
        <tr>
            <th>Document</th>
            <th>Summary</th>
            <th>Applicability</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/series2-secure-debug/">Series 2 and Series 3 Secure Debug</a></p>
            </td>
            <td>
                <p>How to lock and unlock Series 2 and Series 3 debug access, including background information about the SE</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/">Series 2 and Series 3 Secure Boot with RTSL</a></p>
            </td>
            <td>
                <p>Describes the secure boot process on Series 2 and Series 3 devices using SE</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/efr32-secure-vault-tamper/">Anti-Tamper Protection Configuration and Use</a></p>
            </td>
            <td>
                <p>How to program, provision, and configure the anti-tamper module</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/authenticating-devices-using-device-certificates/">Authenticating Silicon Labs Devices using Device Certificates</a></p>
            </td>
            <td>
                <p>How to authenticate a device using secure device certificates and signatures, at any time during the life of the product</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/efr32-secure-key-storage/">Secure Key Storage</a></p>
            </td>
            <td>
                <p>How to securely "wrap" keys so they can be stored in non-volatile storage.</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Production Programming of Series 2 and Series 3 Devices (this application note)</p>
            </td>
            <td>
                <p>How to program, provision, and configure security information using SE during device production</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>AN1504: Series 3 Security Overview</p>
            </td>
            <td>
                <p>High level overview of the security features included in Series 3 devices</p>
            </td>
            <td>
                <p>Series 3</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>AN1509: Series 3 AXiP</p>
            </td>
            <td>
                <p>How to encrypt and authenticate memory contents</p>
            </td>
            <td>
                <p>Series 3</p>
            </td>
        </tr>
    </tbody>
</table>

###### Key Reference

Public/Private keypairs along with other keys are used throughout Silicon Labs security implementations. Because terminology can sometimes be confusing, the following table lists the key names, their applicability, and the documentation where they are used.

|**Key Name**|**Customer Programmed**|**Purpose**|
|---|---|---|
|Public Sign key (Sign Key Public)|Yes|Secure Boot binary authentication and/or OTA upgrade payload authentication|
|Public Command key (Command Key Public)|Yes|Secure Debug Unlock or Disable Tamper command authentication|
|OTA Decryption key (GBL Decryption key) aka AES-128 Key|Yes|Decrypting GBL payloads used for firmware upgrades|
|Attestation key aka Private Device Key|No|Device authentication for secure identity|
|AXiP Key|No|Used on Series 3 devices for encryption/decryption and authentication of firmware placed in flash|
|EXiP Key|No|Used on Series 3 devices for encryption/decryption of firmware placed in flash|

###### SE Firmware

Silicon Labs strongly recommends installing the latest SE firmware on Series 2 and Series 3 devices to support the required security features. Refer to [Production Programming of Series 2 and Series 3 Devices](https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/) for the procedure to upgrade the SE firmware and [IoT Endpoint Security Fundamentals](https://docs.silabs.com/iot-security/latest/iot-endpoint-security-fundamentals/) for the latest SE Firmware shipped with Series 2 and Series 3 devices and modules.

##### Overview

More steps are involved in the production programming process of Series 2 and Series 3 devices compared to Series 1 devices. The steps vary if the device is to have Secure Boot enabled or disabled. For more information about Secure Boot, see [Series 2 and Series 3 Secure Boot with RTSL](https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/). Enabling Secure Debug is a recommended step in the process. For more information about Secure Debug, see [Series 2 and Series 3 Secure Debug](https://docs.silabs.com/iot-security/latest/series2-secure-debug/).

A general overview of the production programming steps is described in the following sections.

Silicon Labs provides [Custom Part Manufacturing Service (CPMS)](https://docs.silabs.com/iot-security/latest/iot-security-cpms/) to customize the users' security features and settings.

###### Production Programming for Secure Boot-Disabled Device

The following figure illustrates the production programming flow for Secure Boot-disabled devices. It is possible to upgrade Series 2 and Series 3 devices deployed in the field without Secure Boot to Secure Boot with RTSL.

![Series 2 and Series 3 High-Level Production Programming Flowchart for Secure Boot-Disabled Devices](/prod-programming-series2-and-series3/0.2/images/sld952-prod-program-flowchart.png)

Upgrading the SE Firmware and flashing the bootloader and application firmware are required in the production programming process. Provisioning the GBL Decryption Key for GBL payload decryption, Public Sign Key for Secure Boot, Public Command Key for Secure Debug Unlock, and enabling the Debug Lock are strongly recommended.

A more detailed version of the Series 2 and Series 3 production programming flowchart for a Secure Boot-disabled device is illustrated in the following figure.

![Series 2 and Series 3 Step-by-Step Production Programming Flowchart for a Secure Boot-Disabled Device](/prod-programming-series2-and-series3/0.2/images/sld952-prod-program-stepbystep-flowchart.png)

**Notes**:

1. Refer to [Provisioning the GBL Decryption Key in Simplicity Commander](07-key-provisioning#provisioning-the-gbl-decryption-key-in-simplicity-commander) on how to program the GBL Decryption Key to the Series 2 or Series 3 device.
2. The VSE devices store a Public Sign Key copy on the top page of the main flash for Secure Boot (see _Signing for ECDSA-P256-SHA256 Secure Boot_ in [Series 2 and Series 3 Secure Boot with RTSL](https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/)).
3. The Public Command Key can also be used to temporarily disable anti-tamper protection on HSE-SVH devices (see [Anti-Tamper Protection Configuration and Use](https://docs.silabs.com/iot-security/latest/efr32-secure-vault-tamper/)).
4. Enabling the debug lock should be the final step in production, and the following debug lock options are available on Series 2 and Series 3 devices.  
   - [Standard Debug Lock](10-enabling-debug-lock#standard-debug-lock)  
   - [Secure Debug Lock](10-enabling-debug-lock#secure-debug-lock) (Public Command Key was provisioned)  
   - [Permanent Debug Lock](10-enabling-debug-lock#permanent-debug-lock)  
   For more information about these debug lock options, see the section _Debug Lock State Transition_ in [Series 2 and Series 3 Secure Debug](https://docs.silabs.com/iot-security/latest/series2-secure-debug/).
5. For Series 3, the final  step is to close all code regions. See [_Bootloader Firmware Programming](05-bootloader-firmware-programming) and [Application Firmware Programming](06-application-firmware-programming) for instructions.

###### Production Programming for Secure Boot-Enabled Device

The following figure illustrates the production programming flow for Secure Boot-enabled devices.

![Series 2 and Series 3 High-Level Production Programming Flowchart for Secure Boot-Enabled Devices](/prod-programming-series2-and-series3/0.2/images/sld952-high-level-prod-program-flow-secureboot-enabled.png)

Upgrading the SE Firmware and flashing the **SIGNED** bootloader and application firmware are required in the production programming process. Provisioning the Public Sign Key and enabling Secure Boot are also needed in the production programming process to enable the Secure Boot option. Provisioning the GBL Decryption Key for GBL payload decryption, Public Command Key for Secure Debug Unlock, and enabling the Debug Lock are strongly recommended. Provisioning Tamper Configuration (HSE-SVH and Series 3 Secure Vault only) is also recommended.

A more detailed version of the Series 2 and Series 3 production programming flowchart for a Secure Boot-enabled device is illustrated in the following figure.

![Series 2 and Series 3 High-Level Production Programming Flowchart for Secure Boot-Enabled Devices](/prod-programming-series2-and-series3/0.2/images/sld952-stepbystep-prod-program-flow-secureboot-enabled.png)

**Notes**:

1. The device will enter the Secure Boot failed state if the bootloader firmware is either unsigned or incorrectly signed (see [Bootloader Firmware Programming](05-bootloader-firmware-programming)).
2. If the Secure Boot option is enabled in the bootloader, the application firmware must be signed (see [Application Firmware Programming](06-application-firmware-programming)).
3. The VSE devices store a Public Sign Key copy on the top page of the main flash for Secure Boot (see section _Signing for ECDSA-P256-SHA256 Secure Boot_ in [Series 2 and Series 3 Secure Boot with RTSL](https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/)).
4. On HSE-SVH and Series 3 Secure Vault devices, the anti-tamper protection configuration is provisioned with Secure Boot settings (see [Enabling Secure Boot and Tamper Configuration](09-enabling-secure-boot-and-tamper-configuration)).
5. Refer to [Provisioning the GBL Decryption Key in Simplicity Commander](07-key-provisioning#provisioning-the-gbl-decryption-key-in-simplicity-commander) on how to program the GBL Decryption Key to Series 2 or Series 3 devices.
6. The Public Command Key can also be used to temporarily disable anti-tamper protection on HSE-SVH devices (see [Anti-Tamper Protection Configuration and Use](https://docs.silabs.com/iot-security/latest/efr32-secure-vault-tamper/)).
7. Enabling the debug lock should be the final step in production, and the following debug lock options are available on the Series 2 device.  
   - [Standard Debug Lock](10-enabling-debug-lock#standard-debug-lock)  
   - [Permanent Debug Lock](10-enabling-debug-lock#permanent-debug-lock)  
   - [Secure Debug Lock](10-enabling-debug-lock#secure-debug-lock) (Public Command Key was provisioned)  
   For more information about these debug lock options, see the section _Debug Lock State Transition_ in [Series 2 and Series 3 Secure Debug](https://docs.silabs.com/iot-security/latest/series2-secure-debug/).
8. For Series 3, the final step is to close all code regions. See [Bootloader Firmware Programming](05-bootloader-firmware-programming) and [Application Firmware Programming](06-application-firmware-programming) for instructions.

##### Using Simplicity Commander

1. This application note uses Simplicity Commander v1.19.2. The procedures and console output may be different for the other versions of Simplicity Commander. The latest version of Simplicity Commander can be downloaded from [https://www.silabs.com/developers/mcu-programming-options](https://www.silabs.com/developers/mcu-programming-options).  
   ```sh  
   commander --version  
   ```  
   ```sh  
   Simplicity Commander 1v19p2b1907  
     
   JLink DLL version: 8.44  
     
   Qt 5.15.2 Copyright (C) 2017 The Qt Company Ltd.  
     
   EMDLL Version: 0v19p19b793  
     
   mbed TLS version: 2.16.6  
     
   Emulator found with SN=440329507 USBAddr=0  
     
   DONE  
   ```
2. The Simplicity Commander's Command Line Interface (CLI) is invoked by `commander.exe` in the Simplicity Commander folder. The location for Simplicity Studio 5 in Windows is `C:\SiliconLabs\SimplicityStudio\v5\developer\adapter_packs\commander`. For ease of use, it is highly recommended to add the path of `commander.exe` to the system PATH in Windows.
3. If more than one Wireless Starter Kit (WSTK) is connected via USB, the target WSTK must be specified using the `--serialno <J-Link serial number>` option.
4. If the WSTK is in debug mode OUT, the target device must be specified using the `--device <device name>` option. For more information about Simplicity Commander, see [Simplicity Commander Reference Guide](https://docs.silabs.com/simplicity-commander/latest/simplicity-commander-start/).

##### SE Firmware Programming

###### Overview

Consistent with best practices for Internet of Things (IoT) security, the SE Firmware provided with Series 2 and Series 3 devices supports secure firmware updates. Silicon Labs will periodically release new versions of the SE Firmware to fix bugs and patch vulnerabilities, which may require updates to devices on the manufacturing line or to devices already in the field.

Silicon Labs operates under a **Security as a Shared Responsibility Model**. This model provides flexibility to system integrators to manage SE Firmware security updates on their own timetable based on their product's use case, risk assessment, agility of their manufacturing flow, and the agility of their field firmware deployment flow.

Series 2 and Series 3 devices are rarely shipped with the latest SE Firmware installed, meaning system integrators must add SE Firmware programming to their production programming flow.

In all cases, Silicon Labs recommends that system integrators:

Subscribe to security notifications by managing their notification settings in the Silicon Labs Support Portal. This is the easiest method to be notified of SE Firmware updates and discovered vulnerabilities.

Instructions for subscribing to notifications can be found here [https://community.silabs.com/s/article/get-notified-when-a-document-changes](https://community.silabs.com/s/article/get-notified-when-a-document-changes)

- Ensure they are installing the latest SE Firmware release in their manufacturing line.
- Be prepared to deploy [security-related field updates](11-field-upgrade-the-se-firmware) to devices in the field.

###### How to Check the SE Firmware Version on a Device

The SE Firmware version of the device can be found in two ways.

- Simplicity Studio
- Simplicity Commander

This application note mainly uses Simplicity Commander.

###### Check the SE Firmware Version Using Simplicity Commander (heading level 7)

To check the SE Firmware version on the device, issue the Simplicity Commander `security status` command.

```sh
commander security status --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
SE Firmware version : 1.2.14
Serial number	    : 000000000000000014b457fffe045a8e
Debug lock	        : Disabled
Device erase	    : Enabled
Secure debug unlock : Disabled
Tamper status	    : OK
Secure boot	        : Disabled
Boot status	        : 0x20 - OK
DONE
```

In this example, the SE Firmware version on the EFR32MG21A is 1.2.14.

###### How to Find the Latest SE Firmware

Silicon Labs strongly recommends installing the latest SE firmware on Series 2 and Series 3 devices to support the required security features. The latest SE firmware image (`.seu` or `.seuv2` and `.hex`) and release notes can be found in the Windows folder below.

For GSDK v3.2 and lower:

`C:\SiliconLabs\SimplicityStudio\v5\developer\sdks\gecko_sdk_suite\<GSDK VERSION>\util\se_release\public`

For GSDK v4.0 and higher:

`C:\Users\<PC USER NAME>\SimplicityStudio\SDKs\gecko_sdk\util\se_release\public`

For Simplicity SDK

`C:\Users\<PC USER NAME>\SimplicityStudio\SDKs\simplicity_sdk\util\se_release\public`

###### Serial Wire Debug (SWD)

The SE Firmware cannot be directly programmed to the SE using the SWD interface. Instead, an image containing the loader application and SE Firmware is flashed onto the host MCU. The SE Firmware is encrypted, versioned, and signed.

![SWD SE Firmware Upgrade Block Diagram](/prod-programming-series2-and-series3/0.2/images/sld952-swd-se-firmware-upgrade-diagram.png)

Using the SWD interface, the user flashes the loader application onto the host. The host then runs the loader application, which checks the signature and version of the SE Firmware. If the signature check passes and the upgrade's version number is higher than the device's SE Firmware version, the firmware is applied to the SE.

The upgrade will not be applied if the signature check fails or if the upgrade's version number is less than or equal to the device's SE Firmware version. Trying to apply a lower SE Firmware version to the device does no harm, but the upgrade will be ignored. This also means the device's SE Firmware cannot be downgraded.

After the SE Firmware has been upgraded, the loader application can be overwritten with the application firmware via the SWD interface.

As detailed in diagrams in the [Overview](02-overview), the steps to upgrade the SE Firmware are:

1. Connect Hardware: Connect the device's SWD interface with the WSTK and ensure proper connections.
2. Check Version: Check the SE Firmware version already on the device.
3. Flash SE Firmware: Flash the loader application onto the host processor.
4. Run: Allow the loader application to run and install the SE Firmware.
5. Re-Check Version: Ensure the update succeeded.

Each of these steps is described in more detail in the next sections.

###### Connect Hardware (heading level 7)

After connecting the device's SWD interface to the WSTK, try to read the device information using Simplicity Commander, to verify that proper connections were established to the device.

```sh
commander device info --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
Part Number	   : EFR32MG21A010F1024IM32
Die Revision   : A1
Production Ver : 2
Flash Size	   : 1024 kB
SRAM Size	   : 96 kB
Unique ID      : 14b457fffe045a8e
DONE
```

###### Check Version (heading level 7)

To check the SE Firmware version on the device, issue the Simplicity Commander `security status` command.

```sh
commander security status --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
SE Firmware version : 1.2.13
Serial number	    : 000000000000000014b457fffe045a8e
Debug lock	        : Disabled
Device erase	    : Enabled
Secure debug unlock : Disabled
Tamper status	    : OK
Secure boot	        : Disabled
Boot status	        : 0x20 - OK
DONE
```

**Notes**:

- The `Tamper status` item is device dependent.
- Updating SE firmware on Series 3 takes significantly longer than Series 2. If the SE firmware on your device is already up to date, skip the next step for efficiency.

###### Flash SE Firmware (heading level 7)

To flash the SE Firmware upgrade application, run:

```sh
commander flash --masserase s2c1_se_fw_upgrade_app_1v2p14.hex --device EFR32MG21A010F1024 --serialno 440048205
```

where `s2c1_se_fw_upgrade_app_1v2p14.hex` is replaced with the name of the SE Firmware upgrade application file.

```sh
Parsing file s2c1_se_fw_upgrade_app_1v2p14.hex...
Erasing chip...
Flash was erased successfully
Writing 57344 bytes starting at address 0x00000000
Comparing range 0x00000000 - 0x0000DFFF (56 KB)
Programming range 0x00000000 - 0x00001FFF (8 KB)
Programming range 0x00002000 - 0x00003FFF (8 KB)
Programming range 0x00004000 - 0x00005FFF (8 KB)
Programming range 0x00006000 - 0x00007FFF (8 KB)
Programming range 0x00008000 - 0x00009FFF (8 KB)
Programming range 0x0000A000 - 0x0000BFFF (8 KB)
Programming range 0x0000C000 - 0x0000DFFF (8 KB)
DONE
```

###### Run (heading level 7)

Allow the SE Firmware upgrade application to run for at least two seconds on Series 2 devices and for at least 10 seconds on Series 3 devices. After this time has elapsed, the SE Firmware should have been upgraded.

###### Re-Check Version (heading level 7)

Run the `security status` command again to check the upgraded SE Firmware version.

```sh
commander security status --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
SE Firmware version : 1.2.14
Serial number	    : 000000000000000014b457fffe045a8e
Debug lock	        : Disabled
Device erase	    : Enabled
Secure debug unlock : Disabled
Tamper status	    : OK
Secure boot	        : Disabled
Boot status	        : 0x20 - OK
DONE
```

The version is now upgraded to 1.2.14.

##### Bootloader Firmware Programming

If Secure Boot is enabled, a **SIGNED** version of the bootloader firmware must be programmed to the flash.

Instructions on how to sign the bootloader firmware can be found in sections _Signing for ECDSA-P256-SHA256 Secure Boot_ and _Signing for Certificate-Based Secure Boot_ in [Series 2 and Series 3 Secure Boot with RTSL](https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/).

The bootloader starting address is device dependent. For more information about the bootloader starting address, see [Memory Space for Bootloading](https://docs.silabs.com/mcu-bootloader/latest/bootloader-fundamentals/03-memory-space-for-bootloading).

Flashing the bootloader firmware using Simplicity Commander is similar to flashing the SE Firmware upgrade application.

```sh
commander flash --masserase <bootloader file> --device <device name> --serialno <J-Link serial number>
```

where `<bootloader file>` is the name of the bootloader firmware file.

For TrustZone-aware bootloaders, the `<bootloader file>` is the combined image of Secure and Non-secure bootloaders. To check the Boot status of the device, run the security status command.

```sh
commander security status --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
SE Firmware version : 1.2.14
Serial number	    : 000000000000000014b457fffe045a8e
Debug lock	        : Disabled
Device erase	    : Enabled
Secure debug unlock : Disabled
Tamper status	    : OK
Secure boot	        : Disabled
Boot status	    : 0x20 - OK
DONE
```

Any Boot status other than `0x20 – OK` indicates that the secure boot process has failed. It means the bootloader firmware is either unsigned or incorrectly signed. The only way to recover is to flash a correctly-signed image (see section _Recover Devices when Secure Boot Fails_ in [Series 2 and Series 3 Secure Boot with RTSL](https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/)).

For Series 3, make sure that code region 0 is closed.

```sh
commander security readregionconfig -d sixg301
```

```sh
Index        : 0
Size         : 32 kB
Protection   : Encrypted and authenticated
Closed       : False

Index        : 1
Size         : 1984 kB
Protection   : Encrypted and authenticated
Closed       : False

DONE
```

To close region 0, run the following command:

```sh
commander security closeregion 0 -d sixg301
```

> **Note**: the default behavior of the commander flash command is to close regions once programming is complete.

```sh
Successfully closed code region 0 (version 0x00000000)
DONE
```

##### Application Firmware Programming

If the Secure Boot option is enabled in the bootloader, a **SIGNED** version of the application firmware must be programmed to the flash.

Instructions on how to sign the application firmware can be found in sections _Signing for ECDSA-P256-SHA256 Secure Boot_ and _Signing for Certificate-Based Secure Boot_ in [Series 2 and Series 3 Secure Boot with RTSL](https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/).

The application firmware starting address is device dependent. For more information about the application starting address, see section _Memory Space For Bootloading_ in [Bootloader Fundamentals](https://docs.silabs.com/mcu-bootloader/latest/bootloader-fundamentals/).

Flashing the application firmware using Simplicity Commander is similar to flashing the SE Firmware upgrade application.

```sh
commander flash <application file> --device <device name> --serialno <J-Link serial number>
```

where `<application file>` is the name of the application firmware file.

For TrustZone-aware applications, the `<application file>` is the combined image of Secure and Non-secure applications.

> **Note**: Do not use the `--masserase` option to flash the application firmware since it will erase the bootloader at the starting address.

For Series 3 devices, make sure that  all code regions are closed

```sh
commander security closeregion 1 -d simg301
```

If the region was not previously closed, the command completes successfully:

```sh
Successfully closed code region 1 (version 0x00000000)
DONE
```

If the region was already closed, the response is as follows:

```sh
ERROR: DCI command failed due to: Invalid command
DONE
```

**Notes**:

1. The default configuration is for the entire application to reside in region 1, applications that use additional regions must close those regions as well.
2. The default behavior of the commander flash command is to close the region once programming is complete.

##### Key Provisioning

###### Overview

The symmetric GBL Decryption Key is used to decrypt GBL files. All encrypted GBL images on this device must be encrypted with the same 128-bit AES key. _Provisioning the GBL Decryption Key in Simplicity Commander_ below describes different ways to program the GBL Decryption Key to the Series 2 and Series 3 devices.

If the Secure Boot feature is to be used, the Public Sign Key must be provisioned to the device.

If the Secure Debug or tamper feature is to be used, the Public Command Key must be provisioned to the device.

The GBL Decryption Key (HSE device), Public Sign Key, and the Public Command Key are written to one-time-programmable (OTP) memory. Once written, they cannot be changed.

> **Note**: Silicon Labs strongly recommends provisioning these keys for future-proofing even if the device does not use the GBL Encryption, Secure Boot, and Secure Debug features.

###### Provisioning the GBL Decryption Key in Simplicity Commander

To generate the text file for the GBL Decryption Key, run the command:

```sh
commander util genkey --type aes-ccm --outfile aes_key.txt
```

```sh
Using Windows' Cryptographic random number generator
DONE
```

where `aes_key.txt` contains the randomly generated AES-128 key.

To write the GBL Decryption Key to the HSE device, run the command:

```sh
commander security writekey --decrypt aes_key.txt --device EFR32MG21A010F1024 --serialno 440048205
```

This command can be executed only once per device.

```sh
Device has serial number 000000000000000014b457fffe045a8e

================================================================================
Please look through any warnings before proceeding.
THIS IS A ONE-TIME command, any encrypting of GBL files must be done with this key.
Type 'continue' and hit enter to proceed or Ctrl-C to abort:
================================================================================
continue
DONE
```

> **Note**: The GBL Decryption Key cannot be read back from the HSE OTP.

To write the GBL Decryption Key to the `Application Properties` struct of the GBL, run the command:

```sh
commander convert bootloader-uart-xmodem.s37 --aeskey aes_key.txt --outfile bootloader-uart-xmodem.s37
```

```sh
Parsing file bootloader-uart-xmodem.s37...
Writing to bootloader-uart-xmodem.s37...
Overwriting file: bootloader-uart-xmodem.s37...
DONE
```

where `bootloader-uart-xmodem.s37` is the GBL image file.

**Notes**:

- The `--aeskey` option for the convert command requires Simplicity Commander **v1.12.3** or above.
- The GBL Decryption Key can only be added to the GBL with `Application Properties` struct **v1.2** or higher (GSDK ≥ v4.1.0 or with SiSDK).
- This procedure must be implemented before signing the GBL image for Secure Boot.

To write the GBL Decryption Key to the top page of the main flash of a Series 2 VSE device, run the command:

```sh
commander flash --tokengroup znet --tokenfile aes_key.txt --device EFR32MG22C224F512 --serialno 440048205
```

```sh
Writing 8192 bytes starting at address 0x0007e000
Comparing range 0x0007E000 - 0x0007FFFF (8 KB)
Erasing range 0x0007E000 - 0x0007FFFF (1 sector, 8 KB)
Programming range 0x0007E000 - 0x0007FFFF (8 KB)
DONE
```

> **Note**: The MCU Series 2 devices (like EFM32PG22C200F512IM40) require Simplicity Commander Version 1.12.2 or above to support the flash `--tokengroup znet` command.

###### Generate the Sign Key pair

Silicon Labs recommends using an HSM for the creation and storage of the Sign Key pair. The instructions for OpenSSL are shown here for simplicity.

1. Create a keypair for the SECP256r1 curve by running the following command:  
   ```sh  
   openssl ecparam -genkey -name prime256v1 -out sign_key.pem  
   ```
2. Export the public key from this keypair using the following command:

```sh
openssl ec -in sign_key.pem -pubout -out sign_pubkey.pem
```

```sh
read EC key
writing EC key
```

###### Provisioning the Public Sign Key in Simplicity Commander

To write the Public Sign Key to the device, run the command:

```sh
commander security writekey --sign sign_pubkey.pem --device EFR32MG21A010F1024 --serialno 440048205
```

where `sign_pubkey.pem` is the Public Sign Key in Privacy Enhanced Mail (PEM) format. This command can be executed only once per device.

```sh
Device has serial number 000000000000000014b457fffe045a8e

================================================================================
Please look through any warnings before proceeding.
THIS IS A ONE-TIME command, all code to be run on the device must be signed by this key.
Type 'continue' and hit enter to proceed or Ctrl-C to abort:
================================================================================
continue
DONE
```

To read the Public Sign Key on the device, run the command:

```sh
commander security readkey --sign --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
C4AF4AC69AAB9512DB50F7A26AE5B4801183D85417E729A56DA974F4E08A562C
DE6019DEA9411332DC1A743372D170B436238A34597C410EA177024DE20FC819
DONE
```

To generate the Public Sign Key token file for VSE devices, run the command:

```sh
commander util keytotoken sign_pubkey.pem --outfile sign_pubkey.txt
```

```sh
Writing EC tokens to sign_pubkey.txt...
DONE
```

To store a Public Sign Key copy on the top page of the main flash in the VSE device for ECDSA-P256-SHA256 Secure Boot, run the command:

```sh
commander flash --tokengroup znet --tokenfile sign_pubkey.txt --device EFR32MG22C224F512 --serialno 440048205
```

```sh
Writing 8192 bytes starting at address 0x0007e000
Comparing range 0x0007E000 - 0x0007FFFF (8 KB)
Erasing range 0x0007E000 - 0x0007FFFF (1 sector, 8 KB)
Programming range 0x0007E000 - 0x0007FFFF (8 KB)
DONE
```

> **Note**: The MCU Series 2 VSE devices (like EFM32PG22C200F512IM40) require Simplicity Commander Version 1.12.2 or above to support the `flash --tokengroup znet` command.

###### Generate the Command Key Pair

Silicon Labs recommends using an HSM for the creation and storage of the Command Key pair. The instructions for OpenSSL are shown here for simplicity.

1. Create a keypair for the SECP256r1 curve by running the following command:  
   ```sh  
   openssl ecparam -genkey -name prime256v1 -out command_key.pem  
   ```
2. Export the public key from this keypair using the following command:  
   ```sh  
   openssl ec -in command_key.pem -pubout -out command_pubkey.pem  
   ```  
   ```sh  
   read EC key  
   writing EC key  
   ```

###### Provisioning the Public Command Key in Simplicity Commander

To write the Public Command Key to the device, run the command:

```sh
commander security writekey --command command_pubkey.pem --device EFR32MG21A010F1024 --serialno 440048205
```

where `command_pubkey.pem` is the Public Command Key in PEM format. This command can be executed only once per device.

```sh
Device has serial number 000000000000000014b457fffe045a8e

================================================================================
Please look through any warnings before proceeding.
THIS IS A ONE-TIME command which permanently ties debug and tamper access to certificates signed by this key.
Type 'continue' and hit enter to proceed or Ctrl-C to abort:
================================================================================
continue
DONE
```

To read the Public Command Key on the device, run the command:

```sh
commander security readkey --command --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
B1BC6F6FA56640ED522B2EE0F5B3CF7E5D48F60BE8148F0DC08440F0A4E1DCA4
7C04119ED6A1BE31B7707E5F9D001A659A051003E95E1B936F05C37EA793AD63
DONE
```

##### Configuring AXiP/EXiP (Series 3 only)

The Authenticated eXecute in-Place feature is available for Series 3 devices with QSPI flash. This feature is provided to resist attempts to

extract the contents of the executable code in the QSPI flash and modify it. Series 3 can configure up to 8 code regions in flash with

the protection level set separately for each region. By default, SixG301 devices have 2 code regions as follows:

|Flash size|Region 0|Region 1(2)|Protection|
|---|---|---|---|
|2 MB|32 kB|864 kB|AXiP|
|3 MB|32 kB|1408 kB|AXiP|
|4 MB|32 kB|1984 kB|AXiP|
|External|32 kB|1.|AXiP|

**Notes**:

1. Region 1 size = (size of flash – 192 kB (SE Firmware) – 32 kB)/2
2. Sizes shown in this column are logical, not physical, sizes

To verify the AXiP configuration for your device, run the following command:

```sh
commander security readregionconfig --device SixG301 --outfile region-config.yaml
```

```sh
Writing parsed configuration to file region-config.yaml...
DONE
```

To view the configuration, open `region-config.yaml` in a text editor. The default configuration looks like this:

```sh
regions:
  - size_kb: 32
    protection: encrypted_authenticated
  - size_kb: 1408
    protection: encrypted_authenticated
```

This is the recommended configuration for most applications. For instructions on modifying the default configuration, refer to AN1509.

To write a new region configuration to the device, run the following command:

```sh
commander security writeregionconfig region-config.yaml -d simg301
```

```sh
Reading configuration from file region-config.yaml...
Writing region configuration to device...
DONE
```

##### Enabling Secure Boot and Tamper Configuration

The Secure Boot feature verifies the integrity and authenticity of the host application before allowing it to execute. Enabling this feature is **IRREVERSIBLE**, which means once enabled, Secure Boot can no longer be disabled throughout the life of the device. The Secure Boot settings are written to the one-time-programmable (OTP) memory. They cannot be changed once programmed.

On HSE-SVH and Series 3 Secure Vault devices, the anti-tamper configuration is provisioned with Secure Boot settings. The anti-tamper configuration determines the response from the HSE-SVH device if a tamper event occurs.

**Notes**:

- All tamper-related information in the following sections is only valid on HSE-SVH devices.
- For more information about anti-tamper configuration, see [Anti-Tamper Protection Configuration and Use](https://docs.silabs.com/iot-security/latest/efr32-secure-vault-tamper/).
- Except for the EFR32xG21B devices, other HSE-SVH devices require Simplicity Commander Version 1.12.2 or above for tamper configuration.
- Series 3 devices require Simplicity Commander Version 1.18.0, or higher

The `user_configuration.json` is a JSON file that contains the desired Secure Boot settings and anti-tamper configuration. Use the following command on the target device (e.g., EFR32MG21B010F1024) to generate a default configuration file.

```sh
commander security genconfig --nostore -o user_configuration.json --device EFR32MG21B010F1024
--serialno 440048205
```

```sh
DONE
```

> **Note**: The content of the JSON file is device dependent (`--device <device name>`).

The `security genconfig` command above generates a generic configuration file for **EFR32MG21B010F1024** consisting of the properties listed in the tables below. A text editor can be used to modify the default settings shown below to the desired configuration.

```sh
{
    "mcu_flags": { "SECURE_BOOT_ENABLE": true,
        "SECURE_BOOT_VERIFY_CERTIFICATE": false,
        "SECURE_BOOT_ANTI_ROLLBACK": true,
        "SECURE_BOOT_PAGE_LOCK_NARROW": false,
        "SECURE_BOOT_PAGE_LOCK_FULL": true
    },
    "tamper_levels": {
        "FILTER_COUNTER": 0,
        "WATCHDOG": 4,
        "SE_RAM_CRC": 4,
        "SE_HARDFAULT": 4,
        "SOFTWARE_ASSERTION": 4,
        "SE_CODE_AUTH": 4,
        "USER_CODE_AUTH": 0,
        "MAILBOX_AUTH": 0,
        "DCI_AUTH": 0,
        "OTP_READ": 4,
        "SELF_TEST": 4,
        "TRNG_MONITOR": 0,
        "PRS0": 0,
        "PRS1": 0,
        "PRS2": 0,
        "PRS3": 0,
        "PRS4": 0,
        "PRS5": 0,
        "PRS6": 0,
        "PRS7": 0,
        "DECOUPLE_BOD": 4,
        "TEMP_SENSOR": 0,
        "VGLITCH_FALLING": 0,
        "VGLITCH_RISING": 0,
        "SECURE_LOCK": 4,
        "SE_DEBUG": 0,
        "DGLITCH": 0,
        "SE_ICACHE": 4
    },
    "tamper_filter": {
        "FILTER_PERIOD": 0,
        "FILTER_THRESHOLD": 0,
        "RESET_THRESHOLD": 0
    },
    "tamper_flags": {
        "DGLITCH_ALWAYS_ON": false
    }
}

```

> **Note**: For `USER_CODE_AUTH` (user secure boot failed), recommended setting is 0 (Ignore) to avoid boot loops.

**Table: Secure Boot Items (mcu_flags) for Series 2 Devices**

|**Name**|**Description**|
|---|---|
|SECURE_BOOT_ENABLE|If set, verifies the host image on the Cortex-M33 before releasing the Cortex-M33 from reset.|
|SECURE_BOOT_VERIFY_CERTIFICATE|If set, requires certificate-based signing of the host image.|
|SECURE_BOOT_ANTI_ROLLBACK|If set, prevents secure upgrading to a host image with a lower version than the image that is currently stored in flash.|
|SECURE_BOOT_PAGE_LOCK_NARROW|If set, locks flash pages that have been validated by the Secure Boot process to prevent re-flashing by other means than through the SE. Write/erase locks pages from 0 through the page where the Secure Boot host image signature is located, not including the last page if the signature is not on a page boundary.|
|SECURE_BOOT_PAGE_LOCK_FULL|If set, locks flash pages that have been validated by the Secure Boot process to prevent re-flashing by other means than through the SE. Write/erase locks pages from 0 through the page where the Secure Boot host image signature is located, including the last page if the signature is not on a page boundary.|

> **Note**: The host image is usually the Gecko Bootloader (GBL).

**Table: Secure Boot Items (mcu_flags) for Series 3 Secure Vault Devices**

|**Name**|**Description**|
|---|---|
|SECURE_BOOT_ENABLE|If set, verifies the host image on the Cortex-M33 before releasing the Cortex-M33 from reset.|
|SECURE_BOOT_VERIFY_CERTIFICATE|If set, requires certificate-based signing of the host image.|
|SECURE_BOOT_ANTI_ROLLBACK|If set, prevents secure upgrading to a host image with a lower version than the image that is currently stored in flash.|

###### Table: Tamper Items for HSE-SVH and Series 3 Secure Vault Devices

|**Name**|**Description**|
|---|---|
|tamper_levels|The tamper levels of different tamper sources.|
|tamper_filter|The settings for tamper filters.|
|tamper_flags|The settings for tamper flags.|

The following command writes the Secure Boot settings and anti-tamper configuration in `user_configuration.json` file to the device. This command can be executed only once per device.

```sh
commander security writeconfig --configfile user_configuration.json --device EFR32MG21B010F1024
--serialno 440048205
```

```sh
================================================================================

THIS IS A ONE-TIME configuration: Please inspect file before confirming: user_configuration.json
Type 'continue' and hit enter to proceed or Ctrl-C to abort:
================================================================================
continue
DONE
```

To check the device's Secure Boot settings and anti-tamper configuration, run the `security readconfig` command.

```sh
commander security readconfig --serialno 440048205
```

```sh
MCU Flags
Secure Boot                    : Enabled
Secure Boot Verify Certificate : Disabled
Secure Boot Anti Rollback	   : Enabled
Secure Boot Page Lock Narrow   : Disabled
Secure Boot Page Lock Full	   : Enabled

Tamper Levels
FILTER_COUNTER      : 1
WATCHDOG            : 4
SE_RAM_CRC          : 4
SE_HARDFAULT        : 4
SOFTWARE_ASSERTION  : 4
SE_CODE_AUTH	    : 4
USER_CODE_AUTH	    : 0
MAILBOX_AUTH	    : 1
DCI_AUTH	        : 0
OTP_READ	        : 4
SELF_TEST	        : 4
TRNG_MONITOR	    : 1
PRS0	            : 1
PRS1	            : 1
PRS2	            : 2
PRS3	            : 2
PRS4	            : 4
PRS5	            : 4
PRS6	            : 7
PRS7	            : 7
DECOUPLE_BOD	    : 4
TEMP_SENSOR	        : 2
VGLITCH_FALLING	    : 2
VGLITCH_RISING	    : 2
SECURE_LOCK	        : 4
SE_DEBUG	        : 0
DGLITCH	            : 2
SE_ICACHE	        : 4

Tamper Filter
Filter Period    : 10
Filter Threshold : 6
Reset Threshold  : 5

Tamper Flags
Digital Glitch Detector Always On: Disabled
DONE
```

##### Enabling Debug Lock

The debug lock is an important feature to prevent attackers from using the debug interface to perform unauthorized operations on the device. The following sections describe how to apply three different locks to the Series 2 and Series 3 debug interface.

###### Standard Debug Lock

The following command locks the debug interface.

```sh
commander security lock --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
WARNING: Secure debug unlock is disabled. Only way to regain debug access is to run a device erase.
Device is now locked.
DONE
```

To check the debug lock status of the device, run the `security status` command:

```sh
commander security status --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
SE Firmware version : 1.2.14
Serial number	    : 000000000000000014b457fffe045a8e
Debug lock	        : Enabled
Device erase	    : Enabled
Secure debug unlock : Disabled
Tamper status	    : OK
Secure boot	        : Disabled
Boot status	        : 0x20 - OK
DONE
```

###### Permanent Debug Lock

The following command locks the debug interface.

```sh
commander security lock --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
WARNING: Secure debug unlock is disabled. Only way to regain debug access is to run a device erase. Device is now locked.
DONE
```

After locking the device, disable the device erase using the following command. This is an **IRREVERSIBLE** action and should be the last step in production.

```sh
commander security disabledeviceerase --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
================================================================================
THIS IS A ONE-TIME command which Permanently disables device erase.
If secure debug lock has not been set, there is no way to regain debug access to this device.
Type 'continue' and hit enter to proceed or Ctrl-C to abort:
================================================================================
continue
Disabled device erase successfully
DONE
```

To check the debug lock status of the device, run the `security status` command.

```sh
commander security status --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
SE Firmware version : 1.2.14
Serial number	    : 000000000000000014b457fffe045a8e
Debug lock	        : Enabled
Device erase        : Disabled
Secure debug unlock : Disabled
Secure boot         : Disabled
Boot status         : 0x20 - OK
DONE
```

###### Secure Debug Lock

The Secure Debug feature is enabled through the `security lockconfig` command. After locking the device, the security unlock command securely unlocks the device for debugging until the next device reset without erasing flash and RAM contents. For more information about Secure Debug Unlock, see [Series 2 and Series 3 Secure Debug](https://docs.silabs.com/iot-security/latest/series2-secure-debug/).

The following command enables the secure debug unlock.

```sh
commander security lockconfig --secure-debug-unlock enable --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
Secure debug unlock was enabled
DONE
```

For **TrustZone-unaware** applications, after enabling the Secure Debug feature, lock the debug interface using the following command.

```sh
commander security lock --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
Device is now locked.
DONE
```

For **TrustZone-aware** applications, after enabling the Secure Debug feature, set the debug options (e.g., `1100`) and lock the debug interface using the following command.

```sh
commander security lock --trustzone 1100 --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
Writing debug restriction bits:
DBGLOCK:	0
NIDLOCK:	0
SPIDLOCK: 1
SPNIDLOCK: 1
Device is now locked.
DONE
```

**Notes**:

- The `--trustzone` option for the `security lock` command requires Simplicity Commander **≥ v1.13.3**.
- It is strongly recommended to upgrade to SE firmware **≥ v1.2.14** (xG21 and xG22) or **≥ v2.2.1** (other Series 2 devices) so that the debug options cannot be modified after the device is locked.
- Use `commander security lock` without the `--trustzone ####` option if the default setting of debug options (`0000`) is good enough for a TrustZone-aware application.
- For more information about debug options, see the _TrustZone Debug Authentication_ section in [Series 2 and Series 3 Secure Debug](https://docs.silabs.com/iot-security/latest/series2-secure-debug/).

After locking the device, disable the device erase using the following command. This is an **IRREVERSIBLE** action and should be the last step in production.

```sh
commander security disabledeviceerase --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
================================================================================
THIS IS A ONE-TIME command which Permanently disables device erase.
If secure debug lock has not been set, there is no way to regain debug access to this device. Type 'continue' and hit enter to proceed or Ctrl-C to abort:
================================================================================
continue
Disabled device erase successfully
DONE
```

> **Note**: The debug options cannot be reset to the default value `0000` (unlock) if the `device erase option` is disabled.

Run the `security status --trustzone` command to check the full debug lock status of the device.

```sh
commander security status --trustzone --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
SE Firmware version : 1.2.14
Serial number	    : 000000000000000014b457fffe045a8e
Debug lock	        : Enabled
Device erase	    : Disabled
Secure debug unlock : Enabled

Debug lock state: Locked

Non-secure, invasive debug lock     (DBGLOCK) : Unlocked
Non-secure, non-invasive debug lock (NIDLOCK) : Unlocked 
Secure, invasive debug lock	        (SPIDLOCK) : Locked 
Secure, non-invasive debug lock	    (SPNIDLOCK): Locked

Non-secure, invasive debug lock state     (DBGLOCK) : Unlocked
Non-secure, non-invasive debug lock state (NIDLOCK) : Unlocked
Secure, invasive debug lock state	      (SPIDLOCK) : Locked
Secure, non-invasive debug lock state	  (SPNIDLOCK): Locked

Tamper status:      : OK
Secure boot         : Disabled
Boot status         : 0x20 - OK 
DONE
```

> **Note**: For more information about Secure and Non-secure debug locks, see the _TrustZone Debug Authentication_ section in [Series 2 and Series 3 Secure Debug](https://docs.silabs.com/iot-security/latest/series2-secure-debug/).

##### Field Upgrade the SE Firmware

###### Secure Boot-Disabled Device

Simplicity Commander or Gecko Bootloader can be used to upgrade the SE Firmware on a Secure Boot-disabled device. The following table lists the scenarios of SE Firmware upgrade on the Secure Boot-disabled device.

|**Secure Debug**|**Device Erase**|**Debug Lock**|**State**|**SE Firmware Upgrade**|
|---|---|---|---|---|
|Disabled|Enabled|Disabled|Unlock|Simplicity Commander or Gecko Bootloader|
|Disabled|Enabled|Enabled|Standard debug lock|Simplicity Commander or Gecko Bootloader|
|Enabled|Disabled|Enabled|Secure debug lock|Simplicity Commander or Gecko Bootloader|
|Disabled|Disabled|Enabled|Permanent debug lock|Gecko Bootloader|

###### Simplicity Commander (heading level 7)

To flash the SE Firmware upgrade application (e.g., `s2c1_se_fw_upgrade_app_1v2p9.hex`), run:

```sh
commander flash s2c1_se_fw_upgrade_app_1v2p9.hex --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
Parsing file s2c1_se_fw_upgrade_app_1v2p9.hex...
Writing 49152 bytes starting at address 0x00000000
Comparing range 0x00000000 - 0x0000BFFF (48 KB)
Programming range 0x00000000 - 0x00001FFF (8 KB)
Programming range 0x00002000 - 0x00003FFF (8 KB)
Programming range 0x00004000 - 0x00005FFF (8 KB)
Programming range 0x00006000 - 0x00007FFF (8 KB)
Programming range 0x00008000 - 0x00009FFF (8 KB)
Programming range 0x0000A000 - 0x0000BFFF (8 KB)
DONE
```

The device should be unlocked before upgrading the SE Firmware if the standard or secure debug lock has been applied. The sections _Standard Debug Lock and Unlock_ and _Secure Debug Unlock and Roll Challenge_ in [Series 2 and Series 3 Secure Debug](https://docs.silabs.com/iot-security/latest/series2-secure-debug/) describe how to unlock the device.

> **Note**: This method will **OVERWRITE** the bootloader and application firmware on the device. The user should then re-program the [bootloader](05-bootloader-firmware-programming) and [application firmware](06-application-firmware-programming) after the SE Firmware upgrade.

###### Gecko Bootloader (heading level 7)

Refer to section _Generate a GBL Upgrade Image File_ (Secure Engine Upgrade) in [Series 2 and Series 3 Secure Boot with RTSL](https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/) for details.

The Gecko Bootloader can still parse the SE GBL upgrade image file and flash its content to the device even if a debug lock is applied. The application firmware must be updated through the Gecko Bootloader after the SE Firmware upgrade if the SE GBL upgrade image file storage overwrites the existing application. Refer to section "Gecko Bootloader Operation - Secure Engine Upgrade" in [UG266](https://www.silabs.com/documents/public/user-guides/ug266-gecko-bootloader-user-guide.pdf)/[Silicon Labs Gecko Bootloader User's Guide for GSDK 4.0 and Higher (series 1 and 2 devices)](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/) for details.

###### Secure Boot-Enabled Device

Simplicity Commander or Gecko Bootloader can be used to upgrade the SE Firmware on a Secure Boot-enabled device. The following table lists the scenarios of SE Firmware upgrade on the Secure Boot-enabled device.

|**Secure Debug**|**Device Erase**|**Debug Lock**|**State**|**SE Firmware Upgrade**|
|---|---|---|---|---|
|Disabled|Enabled|Disabled|Unlock|Simplicity Commander or Gecko Bootloader|
|Disabled|Enabled|Enabled|Standard debug lock|Simplicity Commander or Gecko Bootloader|
|Disabled|Disabled|Enabled|Permanent debug lock|Gecko Bootloader|
|Enabled|Disabled|Enabled|Secure debug lock|Gecko Bootloader|

> **Note**: Using Simplicity Commander to upgrade the SE Firmware on a Secure Debug Locked device is not recommended. It causes Secure Boot failure since the SE Firmware upgrade erases the signed host image for Secure Boot. The user may have issues when recovering a Secure Boot failure device with Device Erase disabled. See section _Recover Devices when Secure Boot Fails_ in [Series 2 and Series 3 Secure Boot with RTSL](https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/) for details.

###### Simplicity Commander (heading level 7)

A signed SE Firmware upgrade application must be used for the upgrade. Instructions for signing the SE Firmware upgrade application are found in the sections "Signing for ECDSA-P256-SHA256 Secure Boot" and _Signing for Certificate-Based Secure Boot_ in [Series 2 and Series 3 Secure Boot with RTSL](https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/).

If the `SECURE_BOOT_PAGE_LOCK_NARROW` or `SECURE_BOOT_PAGE_LOCK_FULL` in the [Secure Boot Items (mcu_flags) for Series 2](09-enabling-secure-boot-and-tamper-configuration) table was enabled for Secure Boot or the standard debug lock applies, run:

```sh
commander security erasedevice --device EFR32MG21A010F1024 --serialno 440048205
```

to perform a device erase. Issue a power-on or pin reset to complete the device erase process.

```sh
Successfully erased device
DONE
```

To flash the signed SE Firmware upgrade application (`s2c1_se_fw_upgrade_app_1v2p9_signed.hex`), run:

```sh
commander flash s2c1_se_fw_upgrade_app_1v2p9_signed.hex --device EFR32MG21A010F1024 --serialno 440048205
```

```sh
Parsing file s2c1_se_fw_upgrade_app_1v2p9_signed.hex...
Writing 49152 bytes starting at address 0x00000000
Comparing range 0x00000000 - 0x0000BFFF (48 KB)
Erasing range 0x00000000 - 0x00007FFF (4 sectors, 32 KB)
Erasing range 0x00008000 - 0x0000BFFF (2 sectors, 16 KB)
Programming range 0x00000000 - 0x00001FFF (8 KB)
Programming range 0x00002000 - 0x00003FFF (8 KB)
Programming range 0x00004000 - 0x00005FFF (8 KB)
Programming range 0x00006000 - 0x00007FFF (8 KB)
Programming range 0x00008000 - 0x00009FFF (8 KB)
Programming range 0x0000A000 - 0x0000BFFF (8 KB)
DONE
```

**Notes**:

1. This method will **OVERWRITE** the signed bootloader and application firmware on the device. The user should then re-program the **SIGNED** [bootloader](05-bootloader-firmware-programming) and [application firmware](06-application-firmware-programming) after the SE Firmware upgrade.
2. If the `SECURE_BOOT_ANTI_ROLLBACK` in the tables in [Enabling Secure Boot and Tamper Configuration](09-enabling-secure-boot-and-tamper-configuration) was enabled for Secure Boot, the device will prevent the signed SE Firmware upgrade when the host image version (e.g., Gecko Bootloader v1.12.0) is equal to or higher than the SE Firmware version (e.g., v1.2.9). Under this situation, the Gecko Bootloader should be used to upgrade the SE firmware. This method will **OVERWRITE** the bootloader version in SE flash with the SE Firmware version after the upgrade.

```sh
Parsing file s2c1_se_fw_upgrade_app_1v2p9_signed.hex...
Writing 49152 bytes starting at address 0x00000000
Comparing range 0x00000000 - 0x0000BFFF (48 KB)
Erasing range 0x00000000 - 0x00007FFF (4 sectors, 32 KB)
Programming range 0x00000000 - 0x00001FFF (8 KB)
Programming range 0x00002000 - 0x00003FFF (8 KB)
Programming range 0x00004000 - 0x00005FFF (8 KB)
Programming range 0x00006000 - 0x00007FFF (8 KB)
Programming range 0x00008000 - 0x00009FFF (8 KB)
Programming range 0x0000A000 - 0x0000BFFF (8 KB)
JLinkError: Failed to halt CPU.
DONE
```

###### Gecko Bootloader (heading level 7)

Refer to section _Generate a GBL Upgrade Image File_ (Secure Engine Upgrade) in [Series 2 and Series 3 Secure Boot with RTSL](https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/) for details.

The Gecko Bootloader can still parse the SE GBL upgrade image file and flash its content to the device even if a debug lock applies. The application firmware must be updated through the Gecko Bootloader after the SE Firmware upgrade if the SE GBL upgrade image file storage overwrites the existing application. Refer to section _Gecko Bootloader Operation - Secure Engine Upgrade_ in [Developing and Debugging a Gecko Bootloader](https://docs.silabs.com/mcu-bootloader/latest/gecko-bootloader-developing-debugging/) for details.

#### Anti-Tamper Protection Configuration and Use

##### Anti-Tamper Protection Configuration and Use

> **Note: This section replaces _AN1247: Anti-Tamper Protection Configuration and Use_. Further updates to this application note will be provided here**.

This application note describes how to program, provision, and configure the Secure Engine anti-tamper module. Many aspects of the anti-tamper module, including disabling the anti-tamper response when needed, are discussed.

The anti-tamper module is only available on Secure Vault High and Series 3 Secure Vault devices. The external tamper detect module is available on some Secure Vault Mid devices (e.g. xG27), Secure Vault High devices (e.g. xG25B) and Series 3 Secure Vault devices.

###### Key Points

- Tamper responses
- Tamper sources
- Tamper configuration
- Tamper disable
- Examples of provisioning and disabling the anti-tamper module

##### Device Security Features

Protecting IoT devices against security threats is central to a quality product. Silicon Labs offers several security options to help developers build secure devices, secure application software, and secure paths of communication to manage those devices. Silicon Labs’ security offerings were significantly enhanced by the introduction of the Series 2 products that included a Secure Engine, this continues in Series 3. The Secure Engine is a tamper-resistant component used to securely store sensitive data and keys and to execute cryptographic functions and secure services.

On Series 1 devices, the security features are implemented by the TRNG (if available) and CRYPTO peripherals.

On Series 2 devices, the security features are implemented by the Secure Engine and CRYPTOACC (if available). The Secure Engine may be hardware-based, or virtual (software-based). Throughout this document, the following abbreviations are used:

- HSE: Hardware Secure Engine
- VSE: Virtual Secure Engine
- SE: Secure Engine (either HSE or VSE)

On Series 3 devices, the security features are implemented by the Secure Engine and SYMCRYPTO. The Secure Engine and SYMCRYPTO are hardware-based.

Additional security features are provided by Secure Vault. Three levels of Secure Vault feature support are available, depending on the part and SE implementation, as reflected in the following table:

|**Level (1)**|**SE Support**|**Part (2)**|
|---|---|---|
|Series 3 Secure Vault|HSE only|Refer to [Security Fundamentals](https://docs.silabs.com/iot-security/latest/iot-endpoint-security-fundamentals/) for details on supporting devices.|
|Secure Vault High (SVH)|HSE only (HSE-SVH)|“|
|Secure Vault Mid (SVM)|HSE (HSE-SVM)|"|
|"|VSE (VSE-SVM)|"|
|Secure Vault Base (SVB)|N/A|"|

**Notes**:

1. The features of different Secure Vault levels can be found in [https://www.silabs.com/security](https://www.silabs.com/security).
2. [Series 2 and Series 3 Device Security Features](https://docs.silabs.com/iot-security/latest/11-series-2-device-security-features11-series-2-and-series-3-device-security-features/).

Secure Vault Mid consists of two core security functions:

- **Secure Boot**: Process where the initial boot phase is executed from an immutable memory (such as ROM) and where code is authenticated before being authorized for execution.
- **Secure Debug access control**: The ability to lock access to the debug ports for operational security, and to securely unlock them when access is required by an authorized entity.

Secure Vault High offers additional security options:

- **Secure Key Storage**: Protects cryptographic keys by “wrapping” or encrypting the keys using a root key known only to the HSE-SVH.
- **Anti-Tamper protection**: A configurable module to protect the device against tamper attacks.
- **Device authentication**: Functionality that uses a secure device identity certificate along with digital signatures to verify the source or target of device communications.

Series 3 Secure Vault offers the same features as Secure Vault High except:

- Device Authentication is only available for the SE

A Secure Engine Manager and other tools allow users to configure and control their devices both in-house during testing and manufacturing, and after the device is in the field.

###### User Assistance

In support of these products Silicon Labs offers whitepapers, webinars, and documentation. The following table summarizes the key security documents:

<table>
    <thead>
        <tr>
            <th>Document</th>
            <th>Summary</th>
            <th>Applicability</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/series2-secure-debug/">Series 2 and Series 3 Secure Debug</a></p>
            </td>
            <td>
                <p>How to lock and unlock Series 2 and Series 3 debug access, including background information about the SE</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/">Series 2 and Series 3 Secure Boot with RTSL</a></p>
            </td>
            <td>
                <p>Describes the secure boot process on Series 2 and Series 3 devices using SE</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Anti-Tamper Protection Configuration and Use (this application note)</p>
            </td>
            <td>
                <p>How to program, provision, and configure the anti-tamper module</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/authenticating-devices-using-device-certificates/">Authenticating Silicon Labs Devices using Device Certificates</a></p>
            </td>
            <td>
                <p>How to authenticate a device using secure device certificates and signatures, at any time during the life of the product</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/efr32-secure-key-storage/">Secure Key Storage</a></p>
            </td>
            <td>
                <p>How to securely "wrap" keys so they can be stored in non-volatile storage.</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/">Production Programming of Series 2 and Series 3 Devices</a></p>
            </td>
            <td>
                <p>How to program, provision, and configure security information using SE during device production</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>AN1504: Series 3 Security Overview</p>
            </td>
            <td>
                <p>High level overview of the security features included in Series 3 devices</p>
            </td>
            <td>
                <p>Series 3</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>AN1509: Series 3 AXiP</p>
            </td>
            <td>
                <p>How to encrypt and authenticate memory contents</p>
            </td>
            <td>
                <p>Series 3</p>
            </td>
        </tr>
    </tbody>
</table>

###### Key Reference

Public/Private keypairs along with other keys are used throughout Silicon Labs security implementations. Because terminology can sometimes be confusing, the following table lists the key names, their applicability, and the documentation where they are used.

|**Key Name**|**Customer Programmed**|**Purpose**|
|---|---|---|
|Public Sign key (Sign Key Public)|Yes|Secure Boot binary authentication and/or OTA upgrade payload authentication|
|Public Command key (Command Key Public)|Yes|Secure Debug Unlock or Disable Tamper command authentication|
|OTA Decryption key (GBL Decryption key) aka AES-128 Key|Yes|Decrypting GBL payloads used for firmware upgrades|
|Attestation key aka Private Device Key|No|Device authentication for secure identity|
|Authenticated eXecute in Place (AXiP) key|No|Authenticate and decrypt executable in external flash on Series 3 devices|
|Encrypted eXecute in Place (EXiP) key|No|Decrypt executable code in external flash on Series 3 devices|

###### SE Firmware

Silicon Labs strongly recommends installing the latest SE firmware on Series 2 and Series 3 devices to support the required security features. Refer to [Production Programming of Series 2 and Series 3 Devices](https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/) for the procedure to upgrade the SE firmware and [IoT Endpoint Security Fundamentals](https://docs.silabs.com/iot-security/latest/iot-endpoint-security-fundamentals/) for the latest SE Firmware shipped with Series 2 and Series 3 devices and modules.

##### Overview

The HSE-SVH and Series 3 Secure Vault Anti-Tamper module is used to hamper or prevent both reverse engineering and re-engineering of proprietary software systems or applications.

Tamper attacks come from one or more vectors. Common attacks include voltage glitching, magnetic interference, and forced temperature adjustment. The HSE-SVH and Series 3 Secure Vault Anti-Tamper module provides fast hardware detection of external tamper signals such as case opening, glitching, and logical attacks allowing analysis and escalation up to and including bricking the device.

The anti-tamper module connects a number of hardware and software-driven tamper signals to a set of configurable hardware and software responses. This can be used to program the device to automatically respond to external events that could signal that someone is trying to tamper with the device, and very rapidly remove secrets stored in the HSE.

The available tamper signals range from signals based on failed authentication and secure boot to specialized glitch detectors. When any of these signals fire, the tamper block can be configured to trigger several different responses, ranging from triggering an interrupt to erasing the One-Time-Programmable (OTP) memory, removing all HSE secrets and resulting in a permanently destroyed device.

Silicon Labs provides [Custom Part Manufacturing Service (CPMS)](https://www.silabs.com/developers/custom-part-manufacturing-service) to protect the users' privacy by configuring the most effective tamper detection features at the Silicon Labs factory. For more information about CPMS, see [Custom Part Manufacturing Service User's Guide](https://docs.silabs.com/iot-security/latest/iot-security-cpms/).

Some SVM devices (e.g. xG25A and xG27) and SVH devices (e.g. xG25B) and Series 3 Secure Vault devices (e.g. xG301) feature an External Tamper Detect module which is used to detect signals such as case opening. The ETAMPDET signal on SVH and Series 3  Secure Vault devices is routed to the SE as an Anti-Tamper module tamper source, in addition to being a stand-alone module. For more information about ETAMPDET operation, refer to the device reference manual. Examples demonstrating how to use ETAMPDET can be found on the [Silicon Labs peripheral_example github repository](https://github.com/SiliconLabs/peripheral_examples/tree/master/series2/etampdet). The peripheral reflex system (PRS) can also be routed as a tamper source. For more information on PRS, refer to your device’s reference manual.

##### Secure Engine Manager

The Secure Engine Manager provides thread-safe APIs for the SE's mailbox interface. The SE Manager APIs related to tamper operations are listed in the following table.

For the SE's mailbox interface, see _Secure Engine Subsystem_ in [Series 2 and Series 3 Secure Debug](https://docs.silabs.com/iot-security/latest/series2-secure-debug/).

|**SE Manager API**|**Usage**|
|---|---|
|sl_se_init_otp|Initialize SE OTP configuration (including tamper configuration on HSE-SVH and Series 3 Secure Vault devices).|
|sl_se_read_otp|Read SE OTP configuration (including tamper configuration on HSE-SVH and Series 3 Secure Vault devices).|
|sl_se_init_otp_key|Used during device initialization to upload the Public Command Key.|
|sl_se_read_pubkey|Read the stored Public Command Key.|
|sl_se_get_serialnumber|Read out the serial number (16 bytes) of the HSE device.|
|sl_se_get_challenge|Read out the current challenge value (16 bytes) for tamper disable.|
|sl_se_roll_challenge|Used to roll the current challenge value (16 bytes) to invalidate the Disable Tamper Token.|
|sl_se_disable_tamper|Temporarily disable tamper configuration using the Disable Tamper Token.|
|sl_se_get_status|Read the current HSE status (including recorded tamper status on HSE-SVH devices).|
|sl_se_get_reset_cause|Read the EMU->RSTCAUSE register from HSE devices after a tamper reset.|
|sl_se_get_tamper_reset_cause|Read the cached value of the EMU->TAMPERRSTCAUSE register after a tamper reset.|
|sl_se_enter_active_mode|Force the SE to remain active to enable the detection of glitch tamper events on the host Cortex-M33 core (see fourth note below)|
|sl_se_exit_active_mode|Exit active mode and allow the SE to sleep when not performing operations. This will prevent the detection of glitch tamper events when the SE is sleeping. This API should only be used if active mode was entered by calling `sl_se_enter_active_mode`. If active mode is set through a DCI command, it can only be disabled through a DCI command. (see fourth note below)|

**Notes**:

- The `sl_se_get_reset_cause` is only available on EFR32xG21B devices. The `EMU->RSTCAUSE` register can be directly read on other HSE-SVH and Series 3 Secure Vault devices.
- The `sl_se_get_tamper_reset_cause` is unavailable on EFR32xG21B devices, and SE firmware ≥ v2.2.1 is required.
- The SE Manager API document can be found at [https://docs.silabs.com/gecko-platform/latest/platform-security-api/sl-se-manager](https://docs.silabs.com/gecko-platform/latest/platform-security-api/sl-se-manager).
- Does not apply to EFR32MG21B parts.

##### Tamper Responses

A [tamper source](05-tamper-sources) can lead to a series of different autonomous responses from the HSE. These responses are listed in the following table.

|**Level (1)**|**Response (2)**|**Description**|
|---|---|---|
|0|Ignore|No action is taken|
|1|Interrupt|Triggers the SETAMPERHOST interrupt on the host|
|2|Filter|Increases a counter in the tamper filter|
|4|Reset|Resets the device|
|7|Erase OTP|Erases the device's OTP configuration|

**Notes**:

1. Levels 3, 5, and 6 are reserved.
2. These responses are cumulative:  
   - If a filter response is triggered, it will also trigger an interrupt.  
   - If a reset response is triggered, it will supersede the interrupt. The filter counter and interrupt flag are cleared at reset.  
   - If an erase OTP response is triggered, it will erase the OTP and reset the device. The device will fail to boot and become unusable.

###### Interrupt

If a tamper source is configured to respond with the interrupt response or higher (≥ level 1), the `SETAMPERHOST` interrupt line to the host Cortex-M33 will be pulsed and make the NVIC trigger the corresponding interrupt handler (`SETAMPERHOST_IRQHandler`).

After the interrupt has been handled, the tamper status can be found by reading the HSE status (using `sl_se_get_status` in the [SE Manager](03-secure-engine-manager)), which contains a list of all the tamper sources that have been triggered since the last time the status was read. Reading HSE status clears the registered tamper sources.

> **Note**: Enabling the `SEMAILBOXHOST` clock for the tamper source is required to trigger the `SETAMPERHOST` interrupt in most HSE-SVH and Series 3 Secure Vault devices. EFR32xG21B does not require this.

###### Filter

The HSE has a filter to debounce spurious tamper events. The filter has a counter that is periodically reset. If a tamper source is configured to the filter response (level 2), when it is triggered, the counter is increased. If the counter value reaches a configurable threshold, the `Filter counter` tamper source is triggered, which can configure to lead to any other responses (1, 4, or even 7).

Only a single shared filter counter is available, so the cumulative triggering of all tamper sources configured to the filter level will increase the same counter. The filter can be programmed to use one of the trigger thresholds and reset periods provided below. The filter counter is zero upon a tamper or normal reset.

###### Filter Trigger Threshold

- Value (n): 0 to 7
- Filter Trigger Threshold: 256/2^n (256 to 2)

###### Filter Reset Period

- Value (n): 0 to 31
- Filter Reset Period: 32 ms * 2^n (32 ms to ~795.4 days)

###### Example Filter Configuration

For example, consider a device with a Filter Trigger Threshold of 3 and Filter Reset Period of 5. If that device detects 32 (256/2^3) Filter response events in 1.024 seconds (32 ms * 2^5), the `Filter counter` tamper source will trigger.

###### Reset

The reset response resets the HSE and Cortex-M33. After a tamper reset, the last reset cause can be directly read from `EMU-\>RSTCAUSE` register or using `sl_se_get_reset_cause` in the [SE Manager](03-secure-engine-manager). In cases where the reset was caused by a tamper response, the source of the tamper can be determined by calling `sl_se_get_tamper_reset_cause` in the SE Manager. (Note that this API is not available for EFR32xG21B-based parts). See [Tamper Sources](05-tamper-sources) for more information. Tamper reset occurs when the HSE sends a request to the Cortex-M33’s EMU, which issues a hard reset.

If a tamper reset is triggered during boot, this can lead to a boot loop. To debug such a scenario, the HSE has a tamper reset counter and enters diagnostic mode if the counter reaches a programmable threshold. Users can issue a non-tamper reset to clear the tamper reset counter before the programmable threshold is reached.

In diagnostic mode, the Cortex-M33 is held in reset and only DCI commands are available. The device will remain in diagnostic mode until a power-on or pin reset occurs.

For more information on the SE's DCI, see [Secure Engine Subsystem](https://docs.silabs.com/iot-security/latest/series2-secure-debug/03-secure-engine-subsystem/) in _Series 2 and Series 3 Secure Debug_.

###### Erase OTP

The Erase OTP response is the strongest reaction the HSE can take, and it will make the device and all wrapped secrets unrecoverable. After this response, the device will no longer be able to boot or connect to a debugger.

This response should typically only be used in situations where the device believes that it is under an actual attack, for instance through the detection of several voltage or digital glitches in a short time window.

##### Tamper Sources

The following table lists the available tamper sources and the default level on the EFR32xG21B. Refer to Appendix C for the other HSE-SVH and Series 3 devices. The tamper sources with the default level higher than 0 (Ignore) are always in effect even if the user does not initialize the tamper configuration in HSE OTP. Users can keep or escalate the default tamper responses (≥ 0 for Ignore and ≥ 4 for Reset) of any sources when initially configuring the part.

|**Type**|**Number**|**Name**|**Description**|**Default Level**|
|---|---|---|---|---|
|SE Hardware|0|Reserved|—|—|
|"|1|Filter counter|Filter counter reached the configured threshold value|0 (Ignore)|
|"|2|SE watchdog|Internal SE watchdog expired|4 (Reset)|
|"|3|Reserved|—|—|
|"|4|SE RAM CRC|A 2-bit, non-correctable error in the SE RAM has occurred.|4 (Reset)|
|"|5|SE hard fault|The SE core has encountered a hard fault exception indicating that an invalid memory access was attempted.|4 (Reset)|
|"|6|Reserved|—|—|
|SE Software|7|SE software assertion|SE firmware has triggered an assertion, indicating that one of several sanity checks has failed and that normal operation cannot continue without a reset.|4 (Reset)|
|"|8|SE secure boot|Secure boot of SE firmware failed|4 (Reset)|
|"|9|User secure boot|Secure boot of host firmware failed|0 (Ignore)|
|"|10|Mailbox authorization|Unauthorized command received over the Mailbox interface. This can be triggered by either (1) an incorrectly signed debug unlock or tamper disable token or (2) attempting to export a non-exportable key.|0 (Ignore)|
|"|11|DCI authorization|Unauthorized command received over the DCI interface. This can be triggered by either (1) an incorrectly signed debug unlock or tamper disable token or (2) attempting to export a non-exportable key.|0 (Ignore)|
|"|12|OTP read|OTP or flash content could not be properly authenticated.|4 (Reset)|
|"|13|Reserved|—|—|
|"|14|Self test|A check of the integrity of the SE's internal storage failed during boot up.|4 (Reset)|
|"|15|TRNG monitor|The TRNG monitor performs a number of tests on the collected entropy data. If any of these tests fail, this tamper source is triggered.|0 (Ignore)|
|Hardware|16 - 23|PRS0 - 7 [1]|PRS consumer for SE Tamper 0 - 7 asserted|0 (Ignore)|
|"|24|Decouple BOD [1]|Decouple Brown-Out-Detector threshold alert|4 (Reset)|
|"|25|Temperature sensor [1]|SE temperature is monitored to be within 5 degrees C of the limits for the device. If the limit is exceeded, this tamper source will be triggered.|0 (Ignore)|
|"|26|Voltage glitch falling|Voltage glitch detector detected a falling glitch|0 (Ignore)|
|"|27|Voltage glitch rising|Voltage glitch detector detected a rising glitch|0 (Ignore)|
|"|28|Secure lock|This tamper source indicates that the guarding mechanism (comparing the locks with their logical complement) of the debug port locks has failed.|4 (Reset)|

|**Type**|**Number**|**Name**|**Description**|**Default Level**|
|---|---|---|---|---|
|"|29|SE debug|Debug access to SE|0 (Ignore)|
|"|30|Digital glitch|Digital glitch detector detected an event|0 (Ignore)|
|"|31|SE ICACHE|The SE's instruction cache uses a checksum to verify the integrity of the data. This tamper source is triggered if the checksum is invalid.|4 (Reset)|

**Notes**:

- [1] These tamper sources are available down to EM2. Other sources are available in EM1 and above.
- In EFR32xG21B devices, hardware tamper sources 24 to 27 can operate down to Energy Mode 3 (EM3), whereas other hardware tamper sources (16 - 23 and 28 - 31) can be active down to Energy Mode 1 (EM1).
- For devices other than EFR32xG21B see [Appendix C](12-appendix-c-tamper-sources-reference) for tamper sources
- User configuration or tamper disable cannot reduce the tamper response below the default Level.
- The **User secure boot** source gets triggered if secure boot is enabled and host image verification fails. It is likely to put the device in the boot loop if users escalate the tamper response of this source to 4 (Reset).
- The **Mailbox and DCI authorizations** get triggered whenever one of the following conditions has occurred. The mailbox returns `SE_RESPONSE_AUTHORIZATION_ERROR`, and [DCI](https://docs.silabs.com/iot-security/latest/efr32-dci-swd-programming/) returns `AUTH_ERROR = 2`.

1. A mailbox or DCI command tries to exercise a key that it is not allowed to use (e.g., trying to export a non-exportable key).
2. A secure debug access or tamper disable request over the mailbox or DCI is invalidly signed.
3. A malformed HSE firmware upgrade over the mailbox or DCI is attempted.

- The **OTP read** gets triggered if there is an issue when decrypting and authenticating settings in HSE OTP or flash.
- The HSE has redundancy built into the locking mechanism, and the **Secure lock** source is used to detect errors in that redundancy.
- PRS inputs can allow user applications to implement additional tamper sources and feed them into the tamper response mechanism. The **PRS** tamper sources are under the control of the user application and could be reconfigured or disabled if the user application is compromised.
- The **Temperature sensor** source is not completely accurate and is generally only suitable for systems that expect to stay well within the specified temperature range. Users requiring a tighter temperature limit can implement their temperature monitor and provide the results as a tamper source via PRS.

##### Anti-Tamper Configuration

The user can provision the anti-tamper configuration in HSE OTP detailed in the following table through `sl_se_init_otp` in the [SE Manager](03-secure-engine-manager). Tamper configurations must be programmed with secure boot settings and are immutable once written.

For more information on enabling the OTP tamper configuration along with the secure boot settings, see _Enabling Secure Boot and Tamper Configuration_ in [Production Programming of Series 2 and Series 3 Devices](https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/09-enabling-secure-boot-and-tamper-configuration/).

|**Setting**|**Description**|
|---|---|
|Tamper response levels|A user response level for each tamper source (1)|
|Filter settings|The tamper filter counter has two settings: trigger threshold and reset period|
|Digital Glitch Detector Always On|Bit 1 of tamper flag: 0 — Digital glitch detector runs only when the HSE is executing a command; 1 — Digital glitch detector runs even when the HSE is not performing any operations (note that this leads to increased energy consumption)|
|Keep Tamper Alive During Sleep (2)|Bit 2 of tamper flag: 0 — The tamper module stops running in sleep mode; 1 — The tamper module keeps running in sleep mode (down to EM3)|
|Reset threshold|The number of consecutive tamper resets (up to 255) before the part enters diagnostic mode (3)|

**Notes**:

1. The effective response of a tamper source is the maximum value between the default level and user level (Active level = MAX(`default level`, `user level`)). If the user sets the response of a tamper source to a level lower than the default level, the setting will still be saved to HSE OTP but does not take any effect. The HSE returns the user levels instead of active levels of all tamper sources when reading back `sl_se_read_otp` the tamper configuration from the HSE OTP.
2. This flag is not available on EFR32xG21B devices.
3. If the threshold is set to 0, the part will never enter the diagnostic mode due to tamper reset.

##### Typical Configuration

Several of the available [tamper sources](05-tamper-sources) report internal HSE errors. A number of these sources are configured to reset the device (level 4) by default. Custom handling of internal and external tamper sources (default level 0) can be configured to trigger an interrupt (level 1) on the Cortex-M33 or trigger an interrupt and increase a counter in the tamper filter (level 2) as in the following figure for EFR32xG21B devices.

![Custom Handling of Tamper Sources (EFR32xG21B Devices)](/efr32-secure-vault-tamper/0.3/images/sld715-custom-handling-tamper-sources.png)

> **Note**: The actions for level 1 on the right side are implemented by the tamper interrupt handler.

###### Typical Configuration Highlights

- The response of the TRNG monitor depends on the failure rate due to lack of entropy.
- The voltage and digital glitch detectors can see spurious activations. They should typically not be used to drive a high-level tamper response directly. Instead, they should feed their signals into a tamper interrupt, which activates a high-level action (e.g., Reset in this example) through PRS tamper if a certain number of detections (noise filter) occur in a short time window.
- The operating conditions decide the time out of the specification filter for the temperature sensor. For some systems, any time out of specification should trigger a reset.
- Mailbox authorization is handled similarly for voltage and digital glitch detectors.
- A PRS tamper implements a high-level response for a tamper interrupt, which issues a tamper reset (level 4) to prevent or slow further attacks.
- In extreme cases, if the system identifies an attack with high confidence, a PRS tamper can be configured as [Erase OTP](04-tamper-responses#erase-otp) (level 7) to brick the part and prevent further attacks. This is recommended only when the destruction of parts is acceptable and where high confidence of an attack can be achieved.
- Another PRS tamper detects enclosure opening from GPIO. This source feeds into the tamper filter counter (level 2), which will trigger an interrupt cumulative effect and activate a `Filter counter` number 1 response (Reset in this example) if the filter counter reaches the trigger threshold within the filter reset period. This filter counter response approach is less flexible than the interrupt response approach since the trigger threshold and filter reset period are one-time programmable.

##### Tamper Disable

For diagnostic purposes, it may be necessary to disable the tamper response. For example, if a user has configured the part to [Erase OTP](04-tamper-responses#erase-otp) on external tamper detection, disabling the tamper response is required to open the unit and perform failure analysis or field service activities.

After the tamper configuration has been initialized, users can temporarily restore the tamper response to default for a set of tamper sources via a Disable Tamper Token authenticated against the Public Command Key in HSE OTP (similar to secure debug unlock). This is only possible if the Public Command Key has been provisioned in the device.

![Tamper Disable on the EFR32xG21B Devices](/efr32-secure-vault-tamper/0.3/images/sld715-tamper-disable-efr32xg21b-devices.png)

**For default tamper settings on other devices, refer to [Appendix C – Tamper Sources Reference](12-appendix-c-tamper-sources-reference).**

###### Disable Tamper Token

The elements of the Disable Tamper Token are described in the following figure and tables.

![Disable Tamper Token](/efr32-secure-vault-tamper/0.3/images/sld715-disable-tamper-token.png)

|**Element**|**Value**|**Description**|
|---|---|---|
|Disable tamper command|0xfd020001|The command word of the Disable Tamper Token.|
|Tamper disable mask|Device-dependent|The command parameter of the Disable Tamper Token.|
|Access certificate (1)|Device-dependent|See [Access Certificate](#access-certificate).|
|Disable tamper command signature (1)|Device-dependent|See [Challenge Response](#challenge-response).|

**Note**:

1. The disable tamper command payload consists of an access certificate and a disable tamper command signature.

![Tamper Disable Mask](/efr32-secure-vault-tamper/0.3/images/sld715-tamper-disable-mask.png)

**Note**: Set bit to restore the default response of the corresponding tamper source.

The Disable Tamper Token temporarily reverts all masked tamper sources in the figure above to the hard-coded configuration.

The Disable Tamper Token can only restore the escalated user-level configuration to default. It cannot degrade the default level of a tamper source.

###### Access Certificate

The elements of the access certificate are described in the following figure and table.

![Access Certificate](/efr32-secure-vault-tamper/0.3/images/sld715-access-certificate.png)

|**Element**|**Value**|**Description**|
|---|---|---|
|Magic word|0xe5ecce01|A constant value used to identify the access certificate.|
|Authorizations|0x0000003e (1)|A value used to authorize which bit in the debug mode request can be enabled for secure debug.|
|Tamper Authorizations|0xffffffb6 (2)|A value used to authorize which bit in the tamper disable mask can be set to disable the tamper response.|
|Serial number|Device-dependent|A number used to compare against the on-chip serial number for secure debug or tamper disable.|
|Public Certificate Key (3)|Device-dependent|The public key corresponding to the Private Certificate Key (3) used to generate the signature (ECDSA-P256-SHA256) in a challenge response.|
|Access certificate signature|Device-dependent|All the content above is signed (ECDSA-P256-SHA256) by the Private Command Key corresponding to the Public Command Key in the HSE OTP.|

**Notes**:

1. The value allows all debug options to be reset for secure debug. Note that the commands for debug unlock and tamper disable are separate, so the secure debug lock will not be disabled when issuing a tamper disable command.
2. Value that sets available bits in the tamper disable mask for tamper disable.
3. The Private/Public Certificate Key is a randomly generated key pair. It can be ephemeral or retainable.

The Private Certificate Key can be used repeatedly to generate the signature in a challenge response on one device until the Private/ Public Certificate Key pair is discarded. This can reduce the frequency of access to the Private Command Key, allowing more restrictive access control on that key.

For more information about secure debug, see [Series 2 and Series 3 Secure Debug](https://docs.silabs.com/iot-security/latest/series2-secure-debug/).

![Tamper Authorizations](/efr32-secure-vault-tamper/0.3/images/sld715-image43.jpg)

**Notes**:

- Set the bit to enable the corresponding bit in the tamper disable mask.
- The Disable Tamper Token will restore the default response of the corresponding tamper source if the same bit is set in the tamper disable mask and tamper authorizations. If the same bit is not set, the tamper response for that tamper source will be untouched.

###### Challenge Response

The elements of the challenge response are described in the following figure and table.

![Challenge Response](/efr32-secure-vault-tamper/0.3/images/sld715-challenge-response.png)

|**Element**|**Value**|**Description**|
|---|---|---|
|Disable tamper command|0xfd020001|The command word of Disable Tamper Token.|
|Tamper disable mask|Device-dependent|The command parameter of Disable Tamper Token.|
|Challenge|Device-dependent (1)|A random value generated by the HSE.|
|Disable tamper command signature|Device-dependent (2)|All the content above is signed (ECDSA-P256-SHA256) by the Private Certificate Key corresponding to the Public Certificate Key in the access certificate.|

**Notes**:

1. The challenge remains unchanged until it is updated to a new random value by rolling the challenge. The Private Certificate Key can be reused for signing when device challenge is refreshed.
2. This signature is the final argument of the Disable Tamper Token.

###### Tamper Disable Flow

The tamper disable flow is described in the following figure.

![Tamper Disable Flow](/efr32-secure-vault-tamper/0.3/images/sld715-tamper-disable-flow.png)

1. Get the serial number and challenge from the HSE.
2. Generate the access certificate with device serial number.
3. Generate the challenge response with device challenge.
4. Generate the disable tamper command payload with access certificate and disable tamper command signature.
5. Send the Disable Tamper Token to the HSE.
6. Verify the disable tamper command signature using the Public Certificate Key in the access certificate.
7. Verify the serial number and the access certificate signature using the on-chip serial number and Public Command Key in the HSE OTP.
8. Restore default levels on tamper disable mask until the next power-on or pin reset.
9. Roll the challenge to invalidate the current Disable Tamper Token.

> **Note**: Refer to the [Simplicity Commander example](09-examples#using-simplicity-commander) for details on how to follow this flow using Simplicity Commander.

##### Examples

###### Overview

The examples for HSE-SVH and Series 3 Secure Vault Anti-Tamper module are described in the following table.

|**Example**|**Device (Radio Board)**|**HSE Firmware**|**Tool**|
|---|---|---|---|
|Provision Tamper configuration|EFR32MG21B010F1024IM32 (BRD4181C)|Version 1.2.9|SE Manager|
|Provision Public Command Key & Tamper configuration|EFR32MG21B010F1024IM32 (BRD4181C)|Version 1.2.9|Simplicity Commander|
|"|EFR32MG21B010F1024IM32 (BRD4181C)|Version 1.2.9|Simplicity Studio 5|
|Tamper disable and Roll challenge|EFR32MG21B010F1024IM32 (BRD4181C)|Version 1.2.9|SE Manager|
|"|EFR32MG21B010F1024IM32 (BRD4181C)|Version 1.2.9|Simplicity Commander|
|Roll challenge|EFR32MG21B010F1024IM32 (BRD4181C)|Version 1.2.9|Simplicity Studio 5|

> **Note**: Unless specified in the example, these examples can be applied to other HSE-SVH and Series 3 Secure Vault devices.

###### Using Simplicity Commander (heading level 7)

1. This application note uses Simplicity Commander v1.14.6. The procedures and console output may be different on other versions of Simplicity Commander. The latest version of Simplicity Commander can be downloaded from [https://www.silabs.com/developers/mcu-programming-options](https://www.silabs.com/developers/mcu-programming-options/).  
   ```sh  
   commander --version  
   ```  
   ```sh  
   Simplicity Commander 1v14p6b1289  
     
   JLink DLL version: 7.70d  
   Qt 5.12.10 Copyright (C) 2017 The Qt Company Ltd.  
   EMDLL Version: 0v18p9b677 mbed TLS version: 2.16.6  
     
     
   DONE  
   ```
2. The Simplicity Commander's Command Line Interface (CLI) is invoked by `commander.exe` in the Simplicity Commander folder. The location for Simplicity Studio 5 in Windows is `C:\SiliconLabs\SimplicityStudio\v5\developer\adapter_packs\commander`. For ease of use, it is highly recommended to add the path of `commander.exe` to the system PATH in Windows.
3. If more than one Wireless Starter Kit (WSTK or WPK) is connected via USB, the target WSTK must be specified using the `--serialno \<J-Link serial number\>` option.
4. If the WSTK or WPK is in debug mode OUT, the target device must be specified using the --device <device name> option. For more information about Simplicity Commander, see [Simplicity Commander Reference Guide](https://docs.silabs.com/simplicity-commander/latest/simplicity-commander-start/).
5. The minimum recommended version of Simplicity Commander for SixG301 devices is v1.19.2.

###### Using an External Tool (heading level 7)

The tamper disable example uses **OpenSSL** to sign the access certificate and challenge response. The Windows version of OpenSSL can be downloaded from [https://slproweb.com/products/Win32OpenSSL.html](https://slproweb.com/products/Win32OpenSSL.html). This application note uses OpenSSL Version 3.5.0 (Win64).

```sh
openssl version
```

```sh
OpenSSL 3.5.0 8 Apr 2025
```

The OpenSSL's Command Line Interface (CLI) is invoked by openssl.exe in the OpenSSL folder. The location in Windows (Win64) is `C:\Program Files\OpenSSL-Win64\bin`. For ease of use, it is highly recommended to add the path of `openssl.exe` to the system PATH in Windows.

###### Provision Public Command Key and Tamper Configuration

The Public part of the Command Key pair can be generated from the “unsafe” private key delivered with Simplicity Studio, by Simplicity Commander, or by a Hardware Security Module (HSM). Using an HSM is recommended for production systems.

###### Public Command Key Extracted from “Unsafe” Private Key using OpenSSL (heading level 7)

External tools such as openssl can be used to generate a public key from the reference private key provided in Simplicity Studio. Note that this private key is well known and must not be used in production devices.

Run the `openssl ec` command to generate the Public Command Key from the Private Command Key.

```sh
openssl ec -in /c/SiliconLabs/SimplicityStudio/v5/developer/adapter_packs/secmgr/scripts/offline/cmd-unsafe-privkey.pem -pubout -out cmd-unsafe-pubkey.pem
```

###### Command Keypair Generated Randomly Using Simplicity Commander (heading level 7)

Run the `util genkey` command to generate the Public Command Key pair (`command_key.pem` and `command_pubkey.pem`) and Public Command Key token file (`command_pubkey.txt`).

```sh
commander util genkey --type ecc-p256 --privkey command_key.pem --pubkey command_pubkey.pem
--tokenfile command_pubkey.txt
```

```sh
Generating ECC P256 key pair...
Writing private key file in PEM format to command_key.pem
Writing public key file in PEM format to command_pubkey.pem
Writing EC tokens to command_pubkey.txt...
DONE
```

```sh
commander util keytotoken command_pubkey.pem --outfile command_pubkey.txt
```

```sh
Writing EC tokens to command_pubkey.txt...
DONE
```

###### Provision Public Command Key and Tamper Configuration (heading level 7)

1. Run the `security writekey` command to provision the Public Command Key (e.g., `command_pubkey.pem`).  
   ```sh  
   commander security writekey --command command_pubkey.pem --device EFR32MG21B010F1024 --serialno 440030580  
   ```  
   ```sh  
   Device has serial number 000000000000000014b457fffe0f77ce  
     
   ================================================================================  
   Please look through any warnings before proceeding.  
   THIS IS A ONE-TIME command which permanently ties debug and tamper access to certificates signed by this key.  
   Type 'continue' and hit enter to proceed or Ctrl-C to abort:  
   ================================================================================  
   continue  
   DONE  
   ```  
   > **Note**: The Public Command Key cannot be changed once written.
2. Run the `security readkey` command to read the Public Command Key from the HSE OTP for verification with the key in step 1.  
   ```sh  
   commander security readkey --command --device EFR32MG21B010F1024 --serialno 440030580  
   ```  
   ```sh  
   B1BC6F6FA56640ED522B2EE0F5B3CF7E5D48F60BE8148F0DC08440F0A4E1DCA4  
   7C04119ED6A1BE31B7707E5F9D001A659A051003E95E1B936F05C37EA793AD63  
   DONE  
   ```
3. Run the `security genconfig` command to generate a default `user_configuration.json` file for secure boot and tamper configuration.  
   ```sh  
   commander security genconfig --nostore -o user_configuration.json --device EFR32MG21B010F1024 --serialno 440030580  
   ```  
   ```sh  
   Configuration file written to user_configuration.json  
   DONE  
   ```  
   > **Note**: Simplicity Commander Version 1.14.6 or above is required to support tamper configuration for all Series 2 HSE-SVH devices.
4. Use a text editor to modify the default tamper responses in `user_configuration.json` to the desired configuration as below.  
   ```sh  
   {  
   "OPN": "EFR32MG21B010F1024", "VERSION": "1.0.0",  
   "mcu_flags": { "SECURE_BOOT_ANTI_ROLLBACK": false, "SECURE_BOOT_ENABLE": false, "SECURE_BOOT_PAGE_LOCK_FULL": false, "SECURE_BOOT_PAGE_LOCK_NARROW": false, "SECURE_BOOT_VERIFY_CERTIFICATE": false  
   },  
   "tamper_filter": { "FILTER_PERIOD": 10,  
   "FILTER_THRESHOLD": 6,  
   "RESET_THRESHOLD": 5  
   },  
   "tamper_flags": { "DGLITCH_ALWAYS_ON": false  
   },  
   "tamper_levels": { "DCI_AUTH": 0,  
   "DECOUPLE_BOD": 4,  
   "DGLITCH": 2,  
   "FILTER_COUNTER": 1,  
   "MAILBOX_AUTH": 1,  
   "OTP_READ": 4,  
   "PRS0": 1,  
   "PRS1": 1,  
   "PRS2": 2,  
   "PRS3": 2,  
   "PRS4": 4,  
   "PRS5": 4,  
   "PRS6": 7,  
   "PRS7": 7,  
   "SECURE_LOCK": 4,  
   "SELF_TEST": 4,  
   "SE_CODE_AUTH": 4,  
   "SE_DEBUG": 0,  
   "SE_HARDFAULT": 4,  
   "SE_ICACHE": 4,  
   "SE_RAM_CRC": 4,  
   "SOFTWARE_ASSERTION": 4,  
   "TEMP_SENSOR": 2,  
   "TRNG_MONITOR": 1,  
   "USER_CODE_AUTH": 0,  
   "VGLITCH_FALLING": 2,  
   "VGLITCH_RISING": 2,  
   "WATCHDOG": 4  
   }  
   }  
   ```  
   **Notes**:  
   - This example does not enable the secure boot.  
   - The `SECURE_BOOT_PAGE_LOCK_FULL` and `SECURE_BOOT_PAGE_LOCK_NARROW` flags are not supported in Series 3.
5. Run the `security writeconfig` command to program the secure boot and tamper configuration to the HSE OTP. This command can be executed once per device.  
   ```sh  
   commander security writeconfig --configfile user_configuration.json --device EFR32MG21B010F1024 --serialno 440030580  
   ```  
   ```sh  
   THIS IS A ONE-TIME configuration: Please inspect file before confirming:  
   user_configuration.json  
   Type 'continue' and hit enter to proceed or Ctrl-C to abort:  
   ================================================================================  
   continue  
   DONE  
   ```
6. Run the `security readconfig` command to check the secure boot and tamper configuration of the device.  
   ```sh  
   commander security readconfig --serialno 440030580  
   ```  
   ```sh  
   MCU Flags  
   Secure Boot                    : Disabled  
   Secure Boot Verify Certificate : Disabled  
   Secure Boot Anti Rollback      : Disabled  
   Secure Boot Page Lock Narrow   : Disabled  
   Secure Boot Page Lock Full     : Disabled  
     
   Tamper Levels  
     
   FILTER_COUNTER     : 1  
   WATCHDOG           : 4  
   SE_RAM_CRC         : 4  
   SE_HARDFAULT       : 4  
   SOFTWARE_ASSERTION : 4  
   SE_CODE_AUTH	   : 4  
   USER_CODE_AUTH	   : 0  
   MAILBOX_AUTH	   : 1  
   DCI_AUTH	       : 0  
   OTP_READ	       : 4  
   SELF_TEST	       : 4  
   TRNG_MONITOR	   : 1  
   PRS0	           : 1  
   PRS1	           : 1  
   PRS2	           : 2  
   PRS3	           : 2  
   PRS4	           : 4  
   PRS5	           : 4  
   PRS6	           : 7  
   PRS7	           : 7  
   DECOUPLE_BOD	   : 4  
   TEMP_SENSOR	       : 2  
   VGLITCH_FALLING	   : 2  
   VGLITCH_RISING	   : 2  
   SECURE_LOCK	       : 4  
   SE_DEBUG	       : 0  
   DGLITCH	           : 2  
   SE_ICACHE	       : 4  
     
   Tamper Filter  
   Filter Period   : 10  
   Filter Threshold : 6  
   Reset Threshold  : 5  
     
   Tamper Flags  
   Digital Glitch Detector Always On: Disabled  
   DONE  
   ```  
   > **Note**: The `SECURE_BOOT_PAGE_LOCK_FULL` and `SECURE_BOOT_PAGE_LOCK_NARROW` flags are not supported in Series 3.

###### Tamper Disable and Roll Challenge

See Platform Sample app for PRS connections.

The tamper disable was designed with three organizations in mind:

1. The Direct Customer to whom Silicon Labs sells the chip. This chip has the Public Command Key installed in the SE OTP.
2. The Product Company is a customer of the Direct Customer. This is applicable if the Direct Customer is creating a white-labeled product for another company or a sub-component that goes into another company’s product.
3. The Debug 3rd Party could be anyone, internal or external, that the Product Company decides is qualified to debug the device.

Because the Public Command Key is installed into the SE OTP of a large number of devices and cannot be changed, the corresponding Private Command Key must be guarded by a very stringent process. If this Private Command Key is ever leaked, all the devices programmed with the corresponding Public Command Key will be compromised.

A tamper disable use case is described in the following figure, and the signing process is performed by a Hardware Security Module (HSM).

![Tamper Disable Use Case](/efr32-secure-vault-tamper/0.3/images/sld715-tamper-disable-use-case.png)

The tamper disable flow moving across the time axis from left to right is explained below:

1. The Product Company creates a Private/Public Certificate Key pair for each device. Because the key pair is assigned only to a single device, the company may not need to protect the Private Certificate Key as securely as the Private Command Key by the Direct Customer.  
   In this example, the Private/Public Certificate Key pair (`cert_key.pem` and `cert_pubkey.pem`) is generated by running the `util genkey` command.  
   ```sh  
   commander util genkey --type ecc-p256 --privkey cert_key.pem --pubkey cert_pubkey.pem  
   ```  
   ```sh  
   Generating ECC P256 key pair...  
   Writing private key file in PEM format to cert_key.pem  
   Writing public key file in PEM format to cert_pubkey.pem  
   DONE  
   ```
2. The Public Certificate Key (`cert_pubkey.pem`) for each device is passed to the Silicon Labs Direct Customer. The part number and serial number are also required if Direct Customer cannot access the device.  
   Run the security status command to get the device serial number. The `--serialno` option is for the J-Link serial number of the WSTK.  
   ```sh  
   commander security status --device EFR32MG21B010F1024 --serialno 440030580  
   ```  
   ```sh  
   SE Firmware version	   : 1.2.9  
   Serial number	       : 000000000000000014b457fffe0f77ce  
   Debug lock	           : Disabled  
   Device erase	       : Enabled  
   Secure debug unlock	   : Disabled  
   Tamper status	       : OK  
   Secure boot	           : Disabled  
   Boot status	           : 0x20 - OK  
   Command key installed  : True  
   Sign key installed	   : False  
   DONE  
   ```
3. The Direct Customer then places that Public Certificate Key in the access certificate. The access certificate is per device because it contains the unique device serial number. This certificate is generated once upon creation of the device, and thereafter, is generally only modified when the Private/Public Certificate Key pair is changed by the Product Company.  
   The following two steps are **OPTIONAL** for customization of Authorizations and Tamper Authorizations.  
   - (Optional) Run the `security genauth` command to generate the default certificate authorization file (`certificate_authorization.json`).    
     ```sh    
     commander security genauth -o certificate_authorizations.json --nostore --serialno 440030580    
     ```    
     ```sh    
     DONE    
     ```  
   - (Optional) Use a text editor to modify the default Authorizations and Tamper Authorizations in the `json` file.  
   Run the `security gencert` command with the following parameters from the Product Company to generate an unsigned access certificate (`access_certificate.extsign`) in Security Store:  
   - Device part number  
   - Device serial number  
   - Public Certificate Key  
   ```sh  
   commander security gencert --device EFR32MG21B010F1024 --deviceserialno 000000000000000014b457fffe0f77ce  
   --cert-pubkey cert_pubkey.pem --extsign  
   ```  
   ```sh  
   Authorization file written to Security Store:  
   C:/Users/<username>/AppData/Local/SiliconLabs/commander/SecurityStore/device_000000000000000014b457fffe0f77ce/ certificate_authorizations.json  
   Cert key written to Security Store:  
   C:/Users/<username>/AppData/Local/SiliconLabs/commander/SecurityStore/device_000000000000000014b457fffe0f77ce/ cert_pubkey.pem  
   Created an unsigned certificate in Security Store:  
   C:/Users/<username>/AppData/Local/SiliconLabs/commander/SecurityStore/device_000000000000000014b457fffe0f77ce/ access_certificate.extsign  
   DONE  
   ```  
   **Notes**:  
   - The `--extsign` option to create an unsigned access certificate is only available in Simplicity Commander Version 1.11.2 or above.  
   - The unsigned access certificate is generated with the default certificate authorization file (`certificate_authorization.json`) which uses `0x0000003e` for Authorizations and `0xffffffb6` (HSE-SVH device) for Tamper Authorizations.  
   - (Optional) Use `--authorization` option if the customized json file generated in the above optional steps is used.  
   ```sh  
   commander security gencert --device EFR32MG21B010F1024 --authorization certificate_authorizations.json  
   --deviceserialno 000000000000000014b457fffe0f77ce --cert-pubkey cert_pubkey.pem –extsign –outfile access_certificate.extsign  
   ```
4. The signing of the access certificate can be done by passing an unsigned access certificate to a Hardware Security Module (HSM) containing the Private Command Key.  
   In this example, the OpenSSL is used to sign the access certificate (`access_certificate.extsign`) in Security Store with the Private Command Key (`command_key.pem`). The access certificate signature is in the `cert_signature.bin` file.  
   ```sh  
   openssl dgst -sha256 -binary -sign command_key.pem -out cert_signature.bin access_certificate.extsign  
   ```  
   Run the `util signcert` command with the following parameters to verify the signature and generate the signed access certificate (`access_certificate.bin`):  
   - Unsigned access certificate  
   - Access certificate signature  
   - Public Command Key  
   ```sh  
   commander util signcert access_certificate.extsign --cert-type access --signature cert_signature.bin  
   --verify command_pubkey.pem --outfile access_certificate.bin  
   ```  
   ```sh  
   R = 76CDC5BA18E5248FDA5418002F250F149B449829A005D6F0726268016CC53ED4  
   S = E4B8ABA2CF742B0E6CC5BA2C1023D76BEEF3C4A11DA97CC4D23459F32237A206  
   Successfully verified signature  
   Successfully signed certificate  
   DONE  
   ```  
   **Notes**:  
   - Put the required files in the same folder to run the command.  
   - The `util signcert` command for access certificate is only available in Simplicity Commander Version 1.11.2 or above.  
   - The access certificate signature can be in a Raw or Distinguished Encoding Rules (DER) format.
5. The access certificate is passed to the Product Company. The purpose of the access certificate is to grant overall debug access capabilities to the Product Company and authorize them to allow third parties to debug the device. The Product Company can now use the access certificate to generate the Disable Tamper Token. The same access certificate can be used to generate as many Disable Tamper Tokens as necessary without having to ever go back to the Direct Customer.
6. To create the Disable Tamper Token, a debug session must be started with the device and the challenge value (which is a random number `Challenge 1` in this example) should be read out to generate the challenge response.  
   Run the `security gencommand` command to generate the challenge response without disable tamper command signature and store it in a file (`command_unsign.bin`).  
   ```sh  
   commander security gencommand --action disable-tamper --disable-param 0x00fa0000 -o command_unsign.bin  
   --nostore --device EFR32MG21B010F1024 --serialno 440030580  
   ```  
   ```sh  
   Unsigned command file written to:  
   command_unsign.bin  
   DONE  
   ```  
   **Notes**:  
   - The tamper disable mask (`0x00fa0000`) is based on the Tamper platform example on EFR32xG21B devices (See readme.md in the Platform sample).  
   - If the `--disable-param` option is not provided, it will restore all tamper sources (`0xffffffb6`) by default.
7. The challenge response is then cryptographically hashed (SHA-256) to create a digest. The digest is then signed by the Private Certificate Key to generate the disable tamper command signature.  
   The signing of the challenge response can be done by passing an unsigned challenge response to a Hardware Security Module (HSM) containing the Private Certificate Key.  
   In this example, the OpenSSL is used to sign the challenge response (`command_unsign.bin`) with the Private Certificate Key (`cert_key.pem`). The disable tamper command signature is in the `command_signature.bin` file.  
   ```sh  
   openssl dgst -sha256 -binary -sign cert_key.pem -out command_signature.bin command_unsign.bin  
   ```
8. Run the `security disabletamper` command with the access certificate (`access_certificate.bin`) from Direct Customer and disable tamper command signature (`command_signature.bin`) in step 7 to generate the Disable Tamper Token.  
   ```sh  
   commander security disabletamper --disable-param 0x00fa0000 --cert access_certificate.bin  
   --command-signature command_signature.bin EFR32MG21B010F1024 --serialno 440030580  
   ```  
   ```sh  
   Certificate written to Security Store:  
   C:/Users/<username>/AppData/Local/SiliconLabs/commander/SecurityStore/ device_000000000000000014b457fffe0f77ce/access_certificate.bin  
   R = A70834D97640A92510D151765F0EED6C6A05CB8BE81E06E905C230ED24E71659  
   S = 9B69C113C2B7DEE60BF0BC7D72719F7F9465840D68EADBBB4F9BCE7A1267B936  
   Command signature is valid  
   Tamper successfully disabled.  
   Command disable tamper payload was stored in Security Store  
   DONE  
   ```  
   **Notes**:  
   - Put the required files in the same folder to run the command.  
   - The disable tamper command signature can be in a Raw or Distinguished Encoding Rules (DER) format.  
   - Simplicity Commander Version 1.11.2 or above is required to support signature in DER format.
9. (Alternative) Key protection is not required if the Private Certificate Key is ephemeral. Steps 6 to 8 can be implemented by running the `security disabletamper` command with the access certificate (`access_certificate.bin`) from the Direct Customer and Private Certificate Key (`cert_key.pem`) to generate the Disable Tamper Token.  
   ```sh  
   commander security disabletamper --disable-param 0x00fa0000 --cert access_certificate.bin --cert-privkey  
   cert_key.pem --device EFR32MG21B010F1024 --serialno 440030580  
   ```  
   ```sh  
   Certificate written to Security Store:  
   C:/Users/<username>/AppData/Local/SiliconLabs/commander/SecurityStore/device_000000000000000014b457fffe0f77ce/access_certificate.bin  
   Cert key written to Security Store:  
   C:/Users/<username>/AppData/Local/SiliconLabs/commander/SecurityStore/device_000000000000000014b457fffe0f77ce/cert_pubkey.pem  
   Created unsigned disable tamper command  
   Signed disable tamper command using  
   C:/Users/<username>/AppData/Local/SiliconLabs/commander/SecurityStore/device_000000000000000014b457fffe0f77ce/cert_key.pem  
   Tamper successfully disabled.  
   Command disable tamper payload was stored in Security Store  
   DONE  
   ```
10. The Disable Tamper Token (aka `Command disable tamper payload`) file (`tamper_payload_111110100000000000000000.bin`, where `111110100000000000000000` is `0x00fa0000` for tamper disable mask) is stored in the Security Store. The location in Windows is `C:\Users\<PC user name>\AppData\Local\SiliconLabs\commander\SecurityStore\device_<Serial number>\challenge_<Challenge value>`.  
    ![screenshot](/efr32-secure-vault-tamper/0.3/images/sld715-image71.jpg)  
    Users can also use the `security getpath` command to get the path of the Security Store or a specified device.  
    ```sh  
    commander security getpath  
    ```  
    ```sh  
    C:/Users/<username>/AppData/Local/SiliconLabs/commander/SecurityStore  
    DONE  
    ```  
    ```sh  
    commander security getpath --deviceserialno 0000000000000000588e81fffe70350d  
    ```  
    ```sh  
    C:/Users/<username>/AppData/Local/SiliconLabs/commander/SecurityStore/device_0000000000000000588e81fffe70350d  
    DONE  
    ```
11. The Disable Tamper Token and the device are now delivered to the Debug 3rd Party.  
    Run the `security gencommand` command to create the Security Store to place the Disable Tamper Token file.  
    ```sh  
    commander security gencommand --action disable-tamper --disable-param 0x00fa0000  
    --device EFR32MG21B010F1024 --serialno 440030580  
    ```  
    ```sh  
    Unsigned command file written to Security Store:  
    C:/Users/<username>/AppData/Local/SiliconLabs/commander/SecurityStore/device_000000000000000014b457fffe0f77ce/challenge_8e7f73e6322edda06b62997155334f29/ disable_tamper_command_to_be_signed09_08_2021.bin  
    DONE  
    ```  
    Copy the Disable Tamper Token file (`tamper_payload_111110100000000000000000.bin`) from Product Company to the Windows Security Store `challenge_<Challenge value>` folder located in `C:\Users\<PC user name>\AppData\Local\SiliconLabs\commander\SecurityStore\device_<Serial number>\challenge_<Challenge value>`.
12. The device compares the Disable Tamper Token contents with its internal serial number, challenge value, and Public Command Key to determine the token’s authenticity. If authentic, it will execute the disable tamper command to restore the default levels on the tamper disable mask (`0xfa000000`); otherwise, it will ignore the command.  
    Run the `security disabletamper` command to disable the tamper.  
    ```sh  
    commander security disabletamper --disable-param 0x00fa0000 --device EFR32MG21B010F1024  
    --serialno 440030580  
    ```  
    ```sh  
    Disabling tamper with tamper payload:  
    C:/Users/<username>/AppData/Local/SiliconLabs/commander/SecurityStore/device_000000000000000014b457fffe0f77ce/challenge_8e7f73e6322edda06b62997155334f29/ tamper_payload_111110100000000000000000.bin  
    Tamper successfully disabled.  
    DONE  
    ```  
    > **Note**: Users can verify the Disable Tamper Token by following steps 4 to 6 in [Tamper Disable](08-tamper-disable) if the EFR32xG21B device is running in the Normal mode of the SE Manager Tamper platform example.
13. The Debug 3rd Party can now use this same Disable Tamper Token to disable the tamper (step 12), over and over again after each power-on or pin reset, until they have finished debugging the device.
14. Once the Debug 3rd Party has finished debugging, they will send the device back to the Product Company.
15. Once the Product Company receives the device, they will immediately start a debug session to roll the challenge (from `Challenge 1` to `Challenge 2` in this example). Rolling the challenge will effectively invalidate any Disable Tamper Token that has been previously given to any third party.  
    Run the `security rollchallenge` command and reset the device to invalidate the current Disable Tamper Token. The challenge cannot be rolled before it has been used at least once; that is, by running the `security disabletamper` or `security unlock` command.  
    ```sh  
    commander security rollchallenge --device EFR32MG21B010F1024 --serialno 440030580  
    ```  
    ```sh  
    Challenge was rolled successfully.  
    DONE  
    ```  
    The unlock token is invalidated after rolling the challenge because any previously issued Disable Tamper Token now contains a different challenge value (`Challenge 1`) than the challenge value currently in the device (`Challenge 2`).  
    The validation process of any previously issued Disable Tamper Token will always fail until a new Disable Tamper Token is issued with a current matching challenge value (`Challenge 2`).  
    > **Note**: Direct Customer can directly use the Private Command Key on the connected chip to generate the Disable Tamper Token in Security Store. But it has a high risk (cannot use HSM) to leak the Private Command Key to a 3rd party when using this approach.  
    ```sh  
    commander security disabletamper --disable-param 0x00fa0000 --command-key command_key.pem  
    --device EFR32MG21B010F1024 --serialno 440030580  
    ```

##### Appendix A: Examples with Simplicity Studio

###### Overview

The security operations are performed in the Security Settings of Simplicity Studio. This application note uses Simplicity Studio v5.4.0.0. The procedures and pictures may be different on the other version of Simplicity Studio 5.

1. Right-click the selected debug adapter **RB (ID:J-Link serial number)** to display the context menu.  
   ![Debug Adapters Context Menu](/efr32-secure-vault-tamper/0.3/images/sld715-image72.jpg)
2. Click **Device configuration...** to open the **Configuration of device: J-Link Silicon Labs (serial number)** dialog box. Click the **Security Settings** tab to get the selected device configuration.  
   ![Configuration on Selected Device](/efr32-secure-vault-tamper/0.3/images/sld715-image73.png)

###### Provision Public Command Key and Tamper Configuration

This example focuses on provisioning the Public Command Key and tamper configuration. It skips the procedures for provisioning of the Public Sign Key and Secure Boot Enabling.

1. Run the `util keytotoken` command to convert the Public Command Key file (PEM format) into a text file (`command_pubkey.txt`).  
   ```sh  
   commander util keytotoken command_pubkey.pem --outfile command_pubkey.txt  
   ```  
   ```sh  
   Writing EC tokens to command_pubkey.txt...  
   DONE  
   ```
2. Open the **Security Settings** of the selected device as described in [Using Simplicity Studio](09-examples#using-simplicity-commander).
3. Click [**Start Provisioning Wizard...**] in the upper right corner to display the **Secure Initialization** dialog box.  
   ![screenshot](/efr32-secure-vault-tamper/0.3/images/sld715-image74.jpg)
4. Click [**Edit**] to open the **Tamper Source Configuration** dialog box. Use the dropdown menus to modify the default tamper responses to the desired configuration. Click [**OK**] to exit.  
   ![screenshot](/efr32-secure-vault-tamper/0.3/images/sld715-image75.png)
5. Click [**Next >**]. The **Security Keys** dialog box is displayed.  
   ![screenshot](/efr32-secure-vault-tamper/0.3/images/sld715-image76.jpg)
6. Using a text editor, open the `command_pubkey.txt` file generated in step 1.  
   ```sh  
   MFG_SIGNED_BOOTLOADER_KEY_X : B1BC6F6FA56640ED522B2EE0F5B3CF7E5D48F60BE8148F0DC08440F0A4E1DCA4  
   MFG_SIGNED_BOOTLOADER_KEY_Y : 7C04119ED6A1BE31B7707E5F9D001A659A051003E95E1B936F05C37EA793AD63  
   ```
7. Check **Enable Writing Command Key**. Copy the Public Command Key (X-point `B1BC...` first, then Y-point `7C04...`) to the **Key:** box under **Command Key:**.  
   ![screenshot](/efr32-secure-vault-tamper/0.3/images/sld715-image77.jpg)  
   > **Note**: This example does not enable the secure boot (not checking **Enable Writing Sign Key** option).
8. Click [**Next >**]. The **Secure Locks** dialog box is displayed. The **Debug locks** are set by default. Uncheck **Enable secure debug unlock** and **Enable debug lock**.  
   ![screenshot](/efr32-secure-vault-tamper/0.3/images/sld715-image78.jpg)
9. Click [**Next >**] to display the **Summary** dialog box. Verify the tamper configuration and Public Command Key in the **Provisioning Summary** are correct.  
   ![screenshot](/efr32-secure-vault-tamper/0.3/images/sld715-image79.png)
10. If the information displayed is correct, click [**Provision**]. Click [**Yes**] to confirm.  
    ![screenshot](/efr32-secure-vault-tamper/0.3/images/sld715-image80.png)  
    > **Note**: The Public Command Key and tamper configuration cannot be changed once written.
11. The **Provisioning Status** is displayed in the **Summary** dialog box.  
    ![screenshot](/efr32-secure-vault-tamper/0.3/images/sld715-image81.png)
12. Click [**Done**] to exit the provisioning process. The device configuration is updated.  
    ![screenshot](/efr32-secure-vault-tamper/0.3/images/sld715-image82.png)

###### Tamper Disable and Roll Challenge

1. Open **Security Settings** of the selected device as described in [Using Simplicity Studio](09-examples#using-simplicity-studio).
2. Click [**Roll Challenge**] to generate a new challenge value to invalidate the Disable Tamper Token for tamper disable. Click [**OK**] to exit.  
   ![screenshot](/efr32-secure-vault-tamper/0.3/images/sld715-image83.jpg)

##### Appendix B: Platform Examples

###### Overview

Simplicity Studio 5 includes the [SE Manager platform examples](https://docs.silabs.com/simplicity-studio-5-users-guide/latest/ss-5-users-guide-getting-started/start-a-project#examples) for Secure Tamper. Refer to the corresponding readme file for details about the SE Manager example. This file also includes the procedures to create the project and run the example.

<table>
    <thead>
        <tr>
            <th>Category</th>
            <th>SE Manager Platform Example</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Tamper provisioning and runtime operations</td>
            <td>SoC SE Manager Tamper</td>
            <td>
                Sample application to:
                <ul>
                    <li>provision tamper settings</li>
                    <li>temporarily disable tamper and roll challenge</li>
                    <li>Implement an interrupt handler for a filtered tamper source</li>
                </ul>
            </td>
        </tr>
    </tbody>
</table>

###### Adapting PRS Tamper for Custom Hardware

The platform examples are designed to work with Silicon Labs development kits. To adapt the code to work with custom hardware a few steps are required as follows. In the sample application these steps are found in `init_tamper_prs` of the `app_se_manager_tamper.c` source file:

1. Set up the GPIO pin as an input. The sample application demonstrates calls to set up the GPIO. It is recommended to install the ‘simple button’ software component to ensure that the necessary drivers are installed.
2. Set up an unused PRS channel as a producer. The sample application uses PRS channels 6, 7 and 8. Refer to the reference manual for your device to determine the source and signal for GPIO. The sample application demonstrates API calls for Series 2 and Series 3 devices.
3. Connect the PRS channel chosen in the previous step to the desired tamper signal. Tamper signals are named `CONSUMER_SETAMPER_TAMPERSR\<bit\>`, where bit is the bit position of the PRS tamper signal. Refer to [Tamper Sources](05-tamper-sources) for the bit positions of PRS tamper signals in the EFR32xG21B and [Appendix C: Tamper Sources Reference](12-appendix-c-tamper-sources-reference) for the bit positions of PRS tamper signals in other devices. The sample application demonstrates calls to perform this task.

##### Appendix C: Tamper Sources Reference

This section provides the tamper sources for devices other than EFR32xG21B.

###### Tamper Sources on Other Series 2 HSE-SVH Devices

|**Type**|**Number**|**Name**|**Description**|**Default Level**|
|---|---|---|---|---|
|SE Hardware|0|Reserved|—|—|
|"|1|Filter counter|Filter counter reached the configured threshold value|0 (Ignore)|
|"|2|SE watchdog|Internal SE watchdog expired|4 (Reset)|
|"|3|Reserved|—|—|
|"|4|SE RAM ECC2|A 2-bit, non-correctable error in the SE RAM has occurred.|4 (Reset)|
|"|5|SE hard fault|The SE core has encountered a hard fault exception indicating that an invalid memory access was attempted.|4 (Reset)|
|"|6|Reserved|—|—|
|SE Software|7|SE software assertion|SE firmware has triggered an assertion, indicating that one of several sanity checks has failed and that normal operation cannot continue without a reset.|4 (Reset)|
|"|8|SE secure boot|Secure boot of SE firmware failed|4 (Reset)|
|"|9|User secure boot|Secure boot of host firmware failed|0 (Ignore)|
|"|10|Mailbox authorization|Unauthorized command received over the Mailbox interface. This can be triggered by either (1) an incorrectly signed debug unlock or tamper disable token or (2) attempting to export a non-exportable key.|0 (Ignore)|
|"|11|DCI authorization|Unauthorized command received over the DCI interface. This can be triggered by either (1) an incorrectly signed debug unlock or tamper disable token or (2) attempting to export a non-exportable key.|0 (Ignore)|
|"|12|OTP read|OTP or flash content could not be properly authenticated|4 (Reset)|
|"|13|Reserved|—|—|
|"|14|Self test|A check of the integrity of the SE's internal storage failed during boot up.|4 (Reset)|
|"|15|TRNG monitor|The TRNG monitor performs a number of tests on the collected entropy data. If any of these tests fail, this tamper source is triggered.|0 (Ignore)|
|Hardware|16|Secure lock|This tamper source indicates that the guarding mechanism (comparing the locks with their logical complement) of the debug port locks has failed.|4 (Reset)|
|"|17|Digital glitch|Digital Glitch detector detected an event|0 (Ignore)|
|"|18|Voltage glitch|Voltage Glitch Detector detected an event|0 (Ignore)|
|"|19|SE ICACHE|The SE's instruction cache uses a checksum to verify the integrity of the data. This tamper source is triggered if the checksum is invalid.|4 (Reset)|

|**Type**|**Number**|**Name**|**Description**|**Default Level**|
|---|---|---|---|---|
|"|20|SE RAM ECC1|SE RAM 1-bit ECC error occurred|0 (Ignore)|
|"|21|BOD [1]|Brown-Out-Detector threshold alert|4 (Reset)|
|"|22|Temperature sensor [1]|SE temperature is monitored to be within 5 degrees C of the limits for the device. If the limit is exceeded, this tamper source will be triggered.|0 (Ignore)|
|"|23|DPLL fall|DPLL lock failed low|0 (Ignore)|
|"|24|DPLL rise|DPLL lock failed high|0 (Ignore)|
|"|25|PRS0 or ETAMPDET|PRS consumer for SE Tamper 25 or ETAMPDET asserted|0 (Ignore)|
|"|26 - 31|PRS1 - 6 or PRS0 - 5 [1]|PRS consumer for SE Tamper 26 - 31 asserted|0 (Ignore)|

**Notes**:

- [1] These tamper sources are available down to EM2. Other sources are available in EM1 and above.
- On EFR32xG23B and later devices, the default behavior is to detect tamper events only when the SE core is active. To detect tamper events when the SE is not performing operations, call `sl_se_enter_active_mode()`. This prevents the SE from sleeping and will result in higher current draw.
- In other HSE-SVH devices, tamper sources 25 to 31 are used for External Tamper Detect (ETAMPDET) if present and PRS consumers. Devices with ETAMPDET (e.g. EFR32xG25B) will have 6 PRS consumers (26 to 31) and devices without ETAMPDET will have 7 PRS consumers (25 to 31).
- The **ETAMPDET** source gets triggered when any of the ETAMPDET channels are asserted.

###### Tamper Sources on SixG301 Devices

|**Type**|**Number**|**Name**|**Description**|**Default Level**|
|---|---|---|---|---|
|SE Hardware|0|Reserved|—|—|
|"|1|Filter counter|Filter counter reached the configured threshold value|0 (Ignore)|
|"|2|SE watchdog|Internal SE watchdog expired|4 (Reset)|
|"|3|Crypto error|HostSymCrypto (1)|4 (Reset)|
|"|4|SE RAM ECC2|A 2-bit, non-correctable error in the SE RAM has occurred.|4 (Reset)|
|"|5|Reserved|--|-|
|"|6|SE major fault|SE major fault detected(2)|4 (Reset)|
|SE Software|7|L2 ICACHE|L2 instruction cache error|4 (Reset)|
|"|8|Reserved|--|-|
|"|9|User secure boot|Secure boot of host firmware failed|0 (Ignore)|
|"|10|Mailbox authorization|Unauthorized command received over the Mailbox interface. This can be triggered by either (1) an incorrectly signed debug unlock or tamper disable token or (2) attempting to export a non-exportable key.|0 (Ignore)|
|"|11|DCI authorization|Unauthorized command received over the DCI interface. This can be triggered by either (1) an incorrectly signed debug unlock or tamper disable token or (2) attempting to export a non-exportable key.|0 (Ignore)|
|"|12|SE Software Assert|SE software triggers an assert|4 (Reset)|
|"|13|Reserved|—|—|
|"|14|Self test|A check of the integrity of the SE's internal storage failed during boot up.|4 (Reset)|
|"|15|TRNG monitor|The TRNG monitor performs a number of tests on the collected entropy data. If any of these tests fail, this tamper source is triggered.|0 (Ignore)|
|Hardware|16|Secure lock|This tamper source indicates that the guarding mechanism (comparing the locks with their logical complement) of the debug port locks has failed.|4 (Reset)|
|"|17|Glitch Detector|Any tamper detection|0 (Ignore)|
|"|18|OTP Alarm|OTP alarm triggered(3)|4 (Reset)|
|"|19|SE ICACHE|The SE's instruction cache uses a checksum to verify the integrity of the data. This tamper source is triggered if the checksum is invalid.|4 (Reset)|
|“|20|SE RAM ECC 1|SE RAM 1-bit ECC error|0 (Ignore)|
| |21|Brownout detect|Brown-out-detector threshold alert|4 (Reset)|
| |22|Temperature sensor|On-device temperature sensor|0 (Ignore)|
| |23|DPLL Lock|DPLL lock failure|0 (Ignore)|
| |24|SoC PLL|SoC PLL failure|0 (Ignore)|
| |25|eTamper detect|External tamper detect|0 (Ignore)|
| |26|KSU ECC 1|KSU ECC 1-bit error|0 (Ignore)|
| |27|KSU ECC 2|KSU ECC 2-bit error|4 (Reset)|
| |28|QSPI Reseed|QSPI reseed error|4 (Reset)|
| |29|PRS0|PRS channel 0 asserted|0 (Ignore)|
| |30|PRS1|PRS channel 1 asserted|0 (Ignore)|
| |31|PRS2|PRS channel 2 asserted|0 (Ignore)|

**Notes**:

1. HOSTSYMCRYPTO is the host cryptographic accelerator referred to as SYMCRYPTO in SixG301 reference manual.
2. The SE Major fault is triggered by any unrecoverable error in the SE code.
3. The OTP alarm is triggered if the OTP data cannot be read and verified.

#### Authenticating Silicon Labs Devices using Device Certificates

##### Authenticating Silicon Labs Devices Using Device Certificates

> **Note: This section replaces _AN1268: Authenticating Silicon Labs Devices Using Device Certificates_. Further updates to this application note will be provided here**.

This application note describes how to authenticate a device as a genuine Silicon Labs product at any time during its life. The digital certificates for secure identity are stored in the device and the Silicon Labs Server. This secure identity feature is only available on Secure Vault High devices.

###### Key Points

- Secure identity on Secure Vault High devices
- Device certificate options
- Entity Attestation Token (EAT)
- Remote authentication process
- Examples for certificate chain verification and remote authentication

##### Introduction

One of the biggest challenges for connected devices is post-deployment authentication. Silicon Labs’ factory trust provisioning service with optional secure programming provides a secure device identity certificate, analogous to a birth certificate, for each individual silicon die during integrated circuit (IC) manufacturing. This enables post-deployment security, authenticity, and attestation-based health checks. The device certificate can be used to verify the authenticity of the device for its lifetime. When the certificate is checked, a digital signature confirms that the certificate received has not been tampered with.

The digital signature and certificates are major cryptographic tools to verify the device is authentic. These tools are described in the following sections.

###### Digital Signature

The digital signature is used to protect integrity and authenticity of an electronic message.

###### Digital Signature Example (heading level 7)

Alice wants to give data to Bob, and Bob wants to make sure that the data came from Alice and has not been tampered with. Alice has a private/public key pair, and has previously given Bob her public key.

![Digital Signature](/authenticating-devices-using-device-certificates/0.2/images/sld790-digital-signature.png)

1. Alice  generates the hash (for example SHA256) of the data.
2. Alice's private key is used to sign the hash to create a signature. The hash is signed instead of the data itself because the signing operation is slow. Therefore it is more efficient to sign the hash instead of the arbitrarily large data.
3. The signature is attached to the end of the data.
4. The data and signature are given to Bob.
5. Bob independently generates the hash of the data.
6. The signature is verified with the hash and Alice’s public key, which results in a true or false outcome indicating if the data is valid.

> **Note**: This scheme requires distribution of Alice's public key.

###### Digital Certificates and Chain of Trust

In [Digital Signature Example](#digital-signature), Bob already had access to Alice’s public key, which he trusted. However, it is not always feasible to pre-share a public key with everyone for secure identity verification, and no mechanism exists to revoke or inactivate the public key in case it gets stolen.

A digital certificate is simply a small, verifiable data file that contains identity credentials and a public key. That data is then signed either with the corresponding private key, or a different private key. The digital certificate can be used to prove the ownership of a public key.

- If it is signed using the corresponding private key, it is called a self-signed certificate.
- If it is signed by another private key, the owner of that private key is acting as a Certificate Authority (CA).
- A Certificate Authority (CA) is a trusted third party by both the owner and party relying on the certificate.

Concatenation of digital certificates builds a chain of trust.

- At the root of the chain is a self-signed certificate called a root certificate or a CA certificate.
- The root or CA certificate can be used to sign another certificate.

![Digital Certificates and Chain of Trust](/authenticating-devices-using-device-certificates/0.2/images/sld790-digital-certificates-and-chain-of-trust.png)

> **Note**: The private key is never included as part of the certificate – it must be stored separately and kept private. The security of the scheme relies on protecting the private keys.

###### Digital Certificates Verification

This section illustrates the process shown in [Digital Signature Example](#digital-signature), but using digital certificates.

###### Digital Certificates Verification Example (heading level 7)

Alice wants to give data to Bob, signed with her private key. Alice has a digital certificate signed by a trusted third party (CA) in addition to her private key. Bob has a certificate from the trusted CA but nothing else is previously shared.

![Digital Certificate Verification](/authenticating-devices-using-device-certificates/0.2/images/sld790-digital-certificate-verification.png)

1. Alice uses her private key to sign the data.
2. Alice gives the data, signature, and her certificate to Bob.
3. Bob first verifies that Alice's certificate is valid, to prove Alice is the owner of the certificate's public key. This is done by verifying that Alice's certificate contains a valid signature created by the CA.
4. Bob then verifies the signature of the data using the public key in Alice's certificate.

> **Note**: The hash process in [Digital Signature Example](#digital-signature-example) is skipped in this example.

##### Secure Identification using MCU Device Certificates on Series 2 and Series 3 Devices

The goal of secure identification is to prove the ownership of a device's unique public key to an external service. It enables the external service to identify the device as legitimate and to authenticate device-generated data or messages.

###### Chain of Trust

A certificate chain of trust establishes a verifiable link between a device and a trusted authority by organizing certificates in a hierarchical structure. At the top of the certificate chain is a root certificate authority (CA), which is implicitly trusted by the system verifying the device. The factory and batch certificates, are used to delegate trust while limiting exposure of the root private key. At the end of the chain is the MCU device certificate, which uniquely identifies a device and is cryptographically bound to a private device certificate key securely stored on the device. During authentication, each certificate in the chain is validated by verifying its signature against the issuing certificate, allowing a remote party to confirm that the device identity ultimately traces back to a trusted root authority.

The chain of trust on Silicon Labs Series 2 and 3 devices is illustrated in the following figure. All certificates are in standard X.509 format, conform to [RFC-3280](https://datatracker.ietf.org/doc/html/rfc3280), TLS compliant with standard endpoint authentication methods, and use the signature algorithm ECDSA-prime256v1 with SHA256. A certificate can be revoked if required, for example when a private key is compromised or a security issue is identified. When revocation occurs, the certificate’s identifier is added to the Certificate Revocation List (CRL) stored on the Silicon Labs server. During certificate validation, remote devices or services consult the CRL, and any certificate listed as revoked is considered invalid and is no longer trusted for authentication or secure communication.

![Chain of Trust](/authenticating-devices-using-device-certificates/0.2/images/sld790-chain-of-trust.png)

###### Storage of the Certificates in the Chain of Trust (heading level 7)

The certificates that make up the certificate chain are stored either in a server or the embedded device. Their storage location depends on the certificate’s purpose and the specific security feature subset enabled on the device. Refer to the table below for more details.

|Certificate|Issuer|Storage|Notes|
|---|---|---|---|
|Root Certificate|Root CA|Silicon Labs Server|Silicon Labs is a Certificate Authority.|
|Factory Certificate|Root CA|Silicon Labs Server|The factory certificate is static per factory.|
|Batch Certificate|Factory CA|Certificate stored on Device (location varies per device, see table below), Private Key stored in Silicon Labs HSM|The batch certificate is rolled per production batch.|
|MCU Device Certificate|Batch CA|Stored on Device (location varies per device, see table below)|The MCU device certificate is a unique cryptographic identity.|

###### Storage of the Batch and MCU Device Certificates on Series 2 and 3 Devices (heading level 8)

|Device Security Level|Batch and MCU Device Certificate Location|Availability|
|---|---|---|
|Series 2 Secure Vault High (HSE)|Both Certs stored in SE OTP|Programmed by default|
|Series 2 Secure Vault Mid (VSE and HSE)|Both Certs are injected in device flash|Only available through CPMS certificate injection|
|Series 3 Secure Vault (SixG301)|Both Certs are injected in device flash|Only available through CPMS certificate injection|

**Note**: For more information on CPMS, refer to the [CPMS User's Guide](https://docs.silabs.com/iot-security/latest/iot-security-cpms/).

###### Device Certificate

The device certificate is an X.509 certificate encoded in DER format, with an approximate size of 0.5 kB. This certificate is programmed into the device during manufacturing, either by default or custom-ordered through CPMS, and is stored in SE OTP or Flash, respectively. Because OTP memory cannot be altered after programming, the device certificate is immutable on HSE Secure Vault High devices, which prevents tampering or replacement after the device leaves the factory, ensuring a unique device identity.

Each device certificate uniquely identifies a device. The certificate’s Common Name (CN) field encodes the device’s 64-bit unique identifier (EUI), cryptographically binding the certificate to that specific device. In addition, the Issuer Common Name includes a batch number that identifies the factory and manufacturing batch in which the device was produced, enabling traceability and auditability across production runs. These fields can be customized via CPMS for further identification of individual products or manufacturers.

The device certificate has a validity period of 100 years starting from the device manufacture date. This extended validity is intentional and aligns with the expected lifetime of long-lived embedded devices, eliminating the need for certificate renewal in the field while still maintaining a unique device identity.

An example device certificate for a Series 2 device is shown in the following figure. The boxes in the image below highlight the following fields, in order they appear from the beginning of the certificate:

- The issuer of the device certificate (in this case, the Batch CA)
- The validity period
- The Common Name and device's EUI number
- The public key stored in the device certificate
- The certificate signature

![Device Certificate Example](/authenticating-devices-using-device-certificates/0.2/images/sld790-device-certificate-example.png)

The device certificate contains a device-specific public key, while the corresponding private key is generated and stored in different locations depending on security feature support of the device. See the table listed below for more details.

###### Storage of the Private Device Certificate Key (heading level 7)

Protecting the device certificate private key is fundamental to maintaining a secure device identity. The device certificate private key is the cryptographic secret that proves ownership of the device certificate; if it is exposed, copied, or misused, the device identity can be cloned or impersonated, undermining authentication. As a result, the strength of the overall identity is directly tied to how well this private key is protected throughout the device lifecycle—from manufacturing and provisioning to deployment and field operation. This section describes methods available to protect the device certificate private key on Series 2 and Series 3 devices.

|Device Family|Device Certificate Private Key Storage Location|Protections Available|Notes|
|---|---|---|---|
|Series 2 HSE - SVH|Stored securely within SE OTP|Private key never leaves SE OTP|Standard on all Series 2 HSE - SVH devices|
|Series 2 HSE - SVM|Stored in Flash|Wrap the private key using TrustZone|Provisioned to user-defined location in flash via CPMS|
|Series 2 VSE - SVM|Stored in Flash|Wrap the private key using TrustZone|Provisioned to user-defined location in flash via CPMS|
|Series 3 Secure Vault|Stored in Flash|Wrap the private key using Secure Key Storage Feature|Provisioned to user-defined location in flash via CPMS|

###### Secure Identity Signatures and Verification

###### Signing and Verification Process (heading level 7)

The signing and verification processes for the certificate chain are shown in the figures below.

The certificate signing process is done during the manufacturing process in order to establish the certificate chain of trust. The root of trust is the self-signed root certificate. The root certificate private key is used to sign the factory certificate, the factory certificate private key is used to sign the batch certificate, and so on. Once signed, the device certificate and batch certificates are stored on the device, either in flash or the SE depending on device security level, forming the chain that can later be presented during attestation.

![Signing for Certificates](/authenticating-devices-using-device-certificates/0.2/images/sld790-signing-for-certificates.png)

The certificate chain verification process is done as the first part of the remote authentication process in order to validate the certificate chain of trust. The verification process begins by verifying the device certificate signature with the batch certificate public key, then the batch certificate signature is verified with the factory certificate public key, and so on. Once validated, this proves the device certificate issued is authentic by a trusted root. The rest of the remote authentication process is covered in detail in ([Remote Authentication Process](06-r-remoteauthenticationprocess)).

![Verification for Certificates](/authenticating-devices-using-device-certificates/0.2/images/sld790-verification-for-certificates.png)

##### Device Certificate Options

The HSE-SVH devices are each programmed with a MCU device certificate during IC production. The device certificate is signed with the private batch key allowing the device certificate to be validated against the Silicon Labs certificate chain [Verification for Certificates](03-r-secureidentity#signing-and-verification) and [Certificate Chain Verification](08-r-examples#certificate-chain-verification).

The device certificate private key never leaves the Secure Key Storage on the HSE-SVH device.
Three device certificate options (standard, modified, and external) are provided to meet different requirements. Silicon Labs provides [Custom Part Manufacturing Service (CPMS)](https://www.silabs.com/developers/custom-part-manufacturing-service) to program custom certificates at Silicon Labs factories. For more information about CPMS, see [Custom Part Manufacturing Service User's Guide](https://docs.silabs.com/iot-security/1.0.0/iot-security-cpms/).

###### Standard MCU Device Certificate

- Comes standard with Series 2 HSE-SVH devices.  
  - Can be injected into other Series 2 (HSE or VSE SVM devices) and Series 3 devices using CPMS.
- Cryptographically proves the device is an authentic Silicon Labs device.
- Does not protect against overproduction or counterfeit products that are built with authentic Silicon Labs devices.
- Signed to a Silicon Labs Certificate Authority (CA).
- The device can prove that it possesses the private key associated with the public key in its certificate by signing the response to a given challenge ([Remote Authentication Process](06-r-remoteauthenticationprocess) and [Certificate Chain Verification and Remote Authentication](08-r-examples#certificate-chain-verification-and-remote-authentication)).

![Standard Device Certificate](/authenticating-devices-using-device-certificates/0.2/images/sld790-standard.png)

###### Modified MCU Device Certificate

- Available as a customization service via CPMS on Series 2 and Series 3 devices.
- Cryptographically proves the device is an authentic Silicon Labs device that was produced for a specific OEM.
- Protects against overproduction by Contract Manufacturer (CM).
- Device Certificate X.509 fields can be customized, with some restrictions. See the [CPMS User's Guide](https://docs.silabs.com/iot-security/latest/iot-security-cpms/custom-certificates) for more details.
- Signed to a Silicon Labs Certificate Authority (CA).

![Modified Device Certificate](/authenticating-devices-using-device-certificates/0.2/images/sld790-modified.png)

###### External Chain - MCU Device Certificate

- Available as a customization service via CPMS on Series 2 and Series 3 devices.
- Cryptographically proves the device is an authentic Silicon Labs device that was produced for a specific OEM.
- Protects against overproduction by Contract Manufacturer (CM).
- Factory Certificate is custom for each OEM.
- Device Certificate and Factory Certificate X.509 fields can be customized, with some restrictions. See the [CPMS User's Guide](https://docs.silabs.com/iot-security/latest/iot-security-cpms/custom-certificates) for more details.
- Signed to a OEM Certificate Authority (CA).
- Root Certificate Authority is OEM-specified.
- Electronic delivery of all batch and device certificates signed under this OEM factory certificate is supported.

![External Device Certificate](/authenticating-devices-using-device-certificates/0.2/images/sld790-external.png)

##### Entity Attestation Tokens

In addition to the MCU Device Certificate identity, some Silicon Labs devices support both MCU device certificates and Secure Engine (SE) device certificates. The SE certificate is signed into the same batch certificate as the MCU device certificate, and is unique per device.

See table below for details on device support:

|Device Security Level|SE Device Certificate and Private Key Location|Availability|
|---|---|---|
|Series 2 Secure Vault High|Stored in SE OTP|Programmed by default|
|Series 2 Secure Vault Mid (VSE and HSE)|Not programmed on SVM devices|Unsupported on SVM|
|Series 3 Secure Vault (SixG301)|Stored in SE OTP|Programmed by default|

MCU device certificates are intended for application-level identity authentication through challenge-response authentication.

SE device certificates are intended for PSA-compliant attestation. PSA Entity Attestation Tokens (EAT) must be signed using the SE attestation private key, stored within the SE, to ensure claims are rooted in hardware-enforced security mechanisms. This token can either be a PSA Initial Attestation Token (IAT) or a Silicon Labs Security Configuration Token. Each of these tokens can be used to authenticate different claims of the SE or other security features of the device. These tokens can be used in additon to the MCU Device identity for many use cases, including secure provisioning of assets in an untrusted environment.

###### PSA EAT

An Entity Attestation Token (EAT) is a mini-report that is cryptographically signed. An EAT is encoded in either one of two standardized data formats: a Concise Binary Object Representation ([CBOR](https://www.rfc-editor.org/info/rfc7049)) or in the text-based format JSON. A digital signature is then used to protect its content. The technical specification defining the content of the EAT, which are claims about the hardware and the software running on a device, is specified by the Internet Engineering Task Force ([IETF](https://datatracker.ietf.org/doc/html/draft-ietf-rats-eat-11#ref-RATS.Architecture)).

An EAT is a collection of Key ID-Value pairs relating to device pedigree or any other information one wants the device to attest. Collected data can originate from the Root of Trust (RoT), any protected area, or non-protected areas.

The EAT must be signed following the structure of the CBOR Object Signing and Encryption ([COSE](https://www.rfc-editor.org/info/rfc8152)) specification. For asymmetric key algorithms, the signature structure must be COSE-Sign1. A COSE-Sign1 is a CBOR encoded, self-secured data blob that contains headers, a payload, and a signature.

The primary need for EAT verification is to check correct formatting and verify signatures as for any token. In addition, though, the verifier can operate a policy where values of some of the claims in this profile can be compared to reference values, registered with the verifier for a given deployment, to confirm that the device is endorsed by the manufacturer supply chain.

###### PSA IAT (heading level 7)

The following tables describe EAT claims that are used in the [PSA IAT](https://www.ietf.org/archive/id/draft-tschofenig-rats-psa-token-08.txt).

> **Note**: The actual claims returned from the tokens are SE firmware version dependent.

<table>
    <caption>Claims of PSA Initial Attestation Token</caption>
    <thead>
        <tr>
            <th>Key ID</th>
            <th>Claim</th>
            <th>Description</th>
            <th>Value</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>-75000</p>
            </td>
            <td>
                <p>Profile definition</p>
            </td>
            <td>
                <p>Name of a document that describes the profile of the report.</p>
            </td>
            <td>
                <p>PSA_IOT_PROFILE_1</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>-75001</p>
            </td>
            <td>
                <p>Client ID</p>
            </td>
            <td>
                <p>Represents the Partition ID of the caller.</p>
            </td>
            <td>
                <p>See note below</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>-75002</p>
            </td>
            <td>
                <p>Security lifecycle</p>
            </td>
            <td>
                <p>Represents the current life cycle stage of the PSA RoT.</p>
            </td>
            <td>
                <p>Device dependent</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>-75003</p>
            </td>
            <td>
                <p>Implementation ID</p>
            </td>
            <td>
                <p>Uniquely identifies the underlying immutable PSA RoT.</p>
            </td>
            <td>
                <p>Device dependent (32 bytes)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>-75004</p>
            </td>
            <td>
                <p>Boot seed</p>
            </td>
            <td>
                <p>Represents a random value created at system boot time.</p>
            </td>
            <td>
                <p>Random bytes (32 bytes)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>-75006</p>
            </td>
            <td>
                <p>Software components</p>
            </td>
            <td>
                <p>A list of Software components represents all the software loaded by PSA RoT.</p>
            </td>
            <td>
                <p>See the software components table below.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>-75008</p>
            </td>
            <td>
                <p>Auth challenge</p>
            </td>
            <td>
                <p>Input object from the caller. For example, this can be a cryptographic nonce or a hash of locally attested data. The length must be 32, 48, or 64 bytes.</p>
            </td>
            <td>
                <p>Random bytes or hash (32/48/64 bytes)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>-75009</p>
            </td>
            <td>
                <p>Instance ID</p>
            </td>
            <td>
                <p>Unique identifier of the instance.</p>
            </td>
            <td>
                <p>Device EUI-64 unique ID with type byte 0x06 (9 bytes)</p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**:
> 
> - Key ID 75001: Client ID if present. Otherwise the value 1 for a token requested by a secure bus master and -1 for a non-secure master.
> - Key ID 75002 (For the definitions of these lifecycle states, please refer to the ARM [Platform Security Model](https://developer.arm.com/documentation/den0128/0101b/?lang=en)):
>   - UNKNOWN (`0x0000`)
>   - ASSEMBLY_AND_TEST (`0x1000`)
>   - PSA_ROT_PROVISIONING (`0x2000`)
>   - SECURED (`0x3000`)
>   - NON_PSA_ROT_DEBUG (`0x4000`)
>   - RECOVERABLE_PSA_ROT_DEBUG (`0x5000`)
>   - DECOMMISSIONED (`0x6000`)
> - Key ID 75003:
>   - Word[0]: Die revision
>   - Word[1]: HSE OTP version
>   - Word[2]: Bit indicating it is an HSE-SVH device
>   - Word[3]: Production version
>   - Word[4:7]: Reserved (zeros)

<table>
    <caption>Software Components</caption>
    <thead>
        <tr>
            <th>Key ID</th>
            <th>Type</th>
            <th>Description</th>
            <th>Value</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>1</p>
            </td>
            <td>
                <p>Measurement type</p>
            </td>
            <td>
                <p>A short string represents the role of this software component.</p>
            </td>
            <td>
                <p>See note below</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>2</p>
            </td>
            <td>
                <p>Measurement value</p>
            </td>
            <td>
                <p>Represents a hash of the invariant software component in memory at startup time.</p>
            </td>
            <td>
                <p>See note below</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>4</p>
            </td>
            <td>
                <p>Version</p>
            </td>
            <td>
                <p>The issued software version is in the form of a text string.</p>
            </td>
            <td>
                <p>See note below</p>
            </td>
        </tr>
    </tbody>
</table>

**Notes**:

- Key ID 1:  
  - HSE always exists — **PRoT**  
  - If secure booted Gecko Bootloader exists at flash starting address — **BL**  
  - If secure booted application exists at flash starting address — **ARoT**
- Key ID 2: SHA-256 hash (32 bytes) of the firmware (HSE, Gecko Bootloader, or application)
- Key ID 4: Version of the firmware (HSE, Gecko Bootloader, or application)

###### Security Configuration Token

The following tables describe EAT claims that are used in the Security Configuration Token.

<table>
    <caption>Claims of Security Configuration Token</caption>
    <thead>
        <tr>
            <th>Key ID</th>
            <th>Claim</th>
            <th>Description</th>
            <th>Value</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>-75000</p>
            </td>
            <td>
                <p>Profile definition</p>
            </td>
            <td>
                <p>Name of a document that describes the profile of the report.</p>
            </td>
            <td>
                <p>SILABS_1</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>-75008</p>
            </td>
            <td>
                <p>Auth challenge</p>
            </td>
            <td>
                <p>Input object from the caller. For example, this can be a cryptographic nonce or a hash of locally attested data. The length must be 32 bytes.</p>
            </td>
            <td>
                <p>Random bytes or hash (32 bytes)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>-75009</p>
            </td>
            <td>
                <p>Instance ID</p>
            </td>
            <td>
                <p>Unique identifier of the instance.</p>
            </td>
            <td>
                <p>Device EUI-64 unique ID with type byte 0x06 (9 bytes)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>-76000</p>
            </td>
            <td>
                <p>SE status</p>
            </td>
            <td>
                <p>Device HSE status.</p>
            </td>
            <td>
                <p>Device dependent (36 bytes)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>-76001</p>
            </td>
            <td>
                <p>OTP configuration</p>
            </td>
            <td>
                <p>Device HSE OTP configuration if provisioned.</p>
            </td>
            <td>
                <p>Device dependent (24 bytes)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>-76002</p>
            </td>
            <td>
                <p>Sign Key</p>
            </td>
            <td>
                <p>Public Sign Key in HSE OTP if provisioned.</p>
            </td>
            <td>
                <p>Device dependent (64 bytes)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>-76003</p>
            </td>
            <td>
                <p>Command Key</p>
            </td>
            <td>
                <p>Public Command Key in HSE OTP if provisioned.</p>
            </td>
            <td>
                <p>Device dependent (64 bytes)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>-76004</p>
            </td>
            <td>
                <p>Tamper settings</p>
            </td>
            <td>
                <p>Current applied tamper settings.</p>
            </td>
            <td>
                <p>Device dependent (16 bytes)</p>
            </td>
        </tr>
    </tbody>
</table>

**Notes**:

- All custom Silicon Labs claims will have a base of 76000.
- Key ID 76000: Refer to the _Get Status_ section in [Programming Series 2 Devices using the Debug Challenge Interface (DCI) and Serial Wire Debug (SWD)](https://docs.silabs.com/iot-security/latest/efr32-dci-swd-programming/05-se-command-list#get-status) for the description (HSE-SVH) of the value.
- Key ID 76001: Refer to the _Read User Configuration_ section in [Programming Series 2 Devices using the Debug Challenge Interface (DCI) and Serial Wire Debug (SWD)](https://docs.silabs.com/iot-security/latest/efr32-dci-swd-programming/05-se-command-list#read-user-configuration) for the description (HSE-SVH) of the value.
- Key ID 76002 and 76003: Refer to Key Reference for Public Sign Key and Public Command Key.
- Key ID 76004: One nibble per tamper source. Refer to the _Anti-Tamper Configuration_ section in [Programming Series 2 Devices using the Debug Challenge Interface (DCI) and Serial Wire Debug (SWD)](https://docs.silabs.com/iot-security/latest/efr32-dci-swd-programming/05-se-command-list#anti-tamper-configuration) for the description of the value.

##### Remote Authentication Process

Remote authentication is used to manage attestation by requesting that the device sign a challenge or [PSA EAT](05-r-entityattestationtoken). The remote device requests a challenge (random nonce) that is either signed directly, if using the MCU identity, or packaged into one of the EAT Tokens, if using the SE identity for authentication. The MCU Device Certificate Private Key is used to sign a challenge for authentication, the SE Device Certificate Private Key is used to sign a EAT (either the PSA IAT or security config token)

![Remote Authentication Process](/authenticating-devices-using-device-certificates/0.2/images/sld790-remote-authentication-process.png)

1. The remote device requests the SE or MCU device certificate and batch certificate from the Silicon Labs device.
2. The remote device looks up the factory certificate and root certificate from the Silicon Labs Server.
3. The remote device validates each certificate in the chain using the public key of each Issuer ([Verification for Certificates](03-r-secureidentity#signing-and-verification-signing-and-verification)).
4. The remote device then sends an attestation request to the device. The device uses the MCU Device Certificate Private Key on the chip to sign the challenge or the SE Device Certificate Private key to sign the EAT and sends the signature of challenge or EAT to the remote device.
5. The remote device requires a small library to validate the signature of challenge or EAT using the Device Certificate Public Key  in the device certificate.

##### Secure Engine Manager

The Secure Engine Manager provides thread-safe APIs for the SE's mailbox interface. The following table lists the SE Manager APIs related to secure identity. The SE Manager API document can be found at [https://docs.silabs.com/gecko-platform/latest/service/api/group-sl-se-manager](https://docs.silabs.com/gecko-platform/latest/service/api/group-sl-se-manager).

For the SE's mailbox interface, see section _Secure Engine Subsystem_ in [Series 2 Secure Debug](https://docs.silabs.com/iot-security/latest/series2-secure-debug/03-r-secureelement).

<table>
    <caption>SE Manager API for Security Identity</caption>
    <thead>
        <tr>
            <th>SE Manager API</th>
            <th>Usage</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>sl_se_read_pubkey</p>
            </td>
            <td>
                <p>Read stored Public Device Key in the HSE-SVH device.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_read_cert</p>
            </td>
            <td>
                <p>Read stored certificates (DER format) in the HSE-SVH device.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_read_cert_size</p>
            </td>
            <td>
                <p>Read the size of stored certificates in the HSE-SVH device.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_attestation_get_psa_iat_token</p>
            </td>
            <td>
                <p>Get the PSA attestation token from the HSE with the given nonce.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_attestation_get_psa_iat_token_size</p>
            </td>
            <td>
                <p>Get the size of a PSA attestation token with the given nonce.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_attestation_get_config_token</p>
            </td>
            <td>
                <p>Get the security configuration token from the HSE with the given nonce.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_attestation_get_config_token_size</p>
            </td>
            <td>
                <p>Get the size of a security configuration token with the given nonce.</p>
            </td>
        </tr>
    </tbody>
</table>

##### Examples

###### Overview

The secure device authentication examples in this section are demonstrated using the following device and SE FW versions.

<table>
    <caption>Secure Attestation/Identity Examples</caption>
    <thead>
        <tr>
            <th>Example</th>
            <th>Device (Radio Board)</th>
            <th>HSE Firmware</th>
            <th>Tool</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Certificate chain verification using MCU Device Identity</p>
            </td>
            <td>
                <p>EFR32MG21B010F1024IM32 (BRD4195A)</p>
            </td>
            <td>
                <p>Version 1.2.16</p>
            </td>
            <td>
                <p>Simplicity Commander and OpenSSL</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Certificate chain verification using MCU Device Identity</p>
            </td>
            <td>
                <p>EFR32MG21B010F1024IM32 (BRD4195A)</p>
            </td>
            <td>
                <p>Version 1.2.16</p>
            </td>
            <td>
                <p>Simplicity Commander</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Certificate chain verification using MCU Device Identity</p>
            </td>
            <td>
                <p>EFR32MG21B010F1024IM32 (BRD4195A)</p>
            </td>
            <td>
                <p>Version 1.2.16</p>
            </td>
            <td>
                <p>Simplicity Studio 5</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Certificate chain verification &amp; Remote authentication</p>
            </td>
            <td>
                <p>EFR32MG21B010F1024IM32 (BRD4195A)</p>
            </td>
            <td>
                <p>Version 1.2.16</p>
            </td>
            <td>
                <p>SE Manager and MBedTLS</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Entity Attestation Token (EAT)</p>
            </td>
            <td>
                <p>EFR32MG21B010F1024IM32 (BRD4195A)</p>
            </td>
            <td>
                <p>Version 1.2.16</p>
            </td>
            <td>
                <p>SE Manager</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Entity Attestation Token (EAT)</p>
            </td>
            <td>
                <p>EFR32MG21B010F1024IM32 (BRD4195A)</p>
            </td>
            <td>
                <p>Version 1.2.16</p>
            </td>
            <td>
                <p>Simplicity Commander</p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**: Unless specified in the example, these examples can apply to other HSE-SVH devices.

###### Root and Factory Certificates for Verification (heading level 7)

Users can download the device root certificate (`Device-Root-CA-chain.pem`) and factory certificate (`Factory-chain.pem`) from [https://www.silabs.com/certificate-authority](https://www.silabs.com/certificate-authority).

![Cert Server](/authenticating-devices-using-device-certificates/0.2/images/sld790-cert-server.png)

For Simplicity Studio v5.3.0.0 and higher, the device root certificate (`device-root-prod.pem`) and factory certificate (`factory-prod.pem`) can be found in the Windows folder below.

C:\SiliconLabs\SimplicityStudio\v5\offline\common\certificates

###### Using Simplicity Commander (heading level 7)

1. This application note uses Simplicity Commander v1.23.0. The procedures and console output may be different on the other versions of Simplicity Commander. The latest version of Simplicity Commander can be downloaded from [https://www.silabs.com/developers/mcu-programming-options](https://www.silabs.com/developers/mcu-programming-options).  
   ```sh  
   commander --version  
   ```  
   ```sh  
   Simplicity Commander 1v23p0b1960  
     
   JLink DLL version: 8.94  
   Qt 5.15.2 Copyright (C) 2017 The Qt Company Ltd.  
   EMDLL Version: 0v20p6b826  
   mbed TLS version: 2.16.6  
     
   DONE  
     
   ```
2. The Simplicity Commander's Command Line Interface (CLI) is invoked by `commander.exe` in the Simplicity Commander folder. The location for Simplicity Studio 5 in Windows is `C:\SiliconLabs\SimplicityStudio\v5\developer\adapter_packs\commander`. For ease of use, it is highly recommended to add the path of `commander.exe` to the system `PATH` in Windows.
3. If more than one Wireless Starter Kit (WSTK) is connected via USB, the target WSTK must be specified using the `--serialno \<J-Link serial number>` option.
4. If the WSTK is in debug mode OUT, the target device must be specified using the `--device \<device name>` option.

For more information about Simplicity Commander, see [Simplicity Commander Reference Guide](https://docs.silabs.com/simplicity-commander/latest/simplicity-commander-start/).

###### Using an External Tool (heading level 7)

The certificate chain verification with MCU device identity example below uses OpenSSL to validate the certificate chain. The Windows version of OpenSSL can be downloaded from [https://slproweb.com/products/Win32OpenSSL.html](https://slproweb.com/products/Win32OpenSSL.html). This application note uses OpenSSL Version 1.1.1h (Win64).

```sh
openssl version
```

```sh
OpenSSL 1.1.1h  22 Sep 2020
```

The OpenSSL's Command Line Interface (CLI) is invoked by `openssl.exe` in the OpenSSL folder. The location in Windows (Win64) is `C:\Program Files\OpenSSL-Win64\bin`. For ease of use, it is highly recommended to add the path of `openssl.exe` to the system `PATH` in Windows.

###### Using Platform Examples (heading level 7)

Simplicity Studio 5 includes the [SE Manager platform examples](https://docs.silabs.com/simplicity-studio-5-users-guide/latest/ss-5-users-guide-getting-started/start-a-project#examples) for secure identity and attestation. This application note uses platform example of GSDK v3.2.3. The console output may be different on the other versions of GSDK.

Refer to the corresponding `readme.html` file for details about each SE Manager platform example. This file also includes the procedures to create the project and run the example.

###### Certificate Chain Verification with MCU Device Identity

Certificate chain verification is the process of making sure a given certificate chain is well-formed, valid, properly signed, and trustworthy. The certificate signature is verified using the public key in the issuer certificate ([Verification for Certificates](03-r-secureidentity#signing-and-verification)).

**Note**: The commands below will only verify the MCU Device Identity Certificate on devices with the MCU Device Certificate stored in SE OTP (Series 2 SVH).

###### Using Simplicity Commander and OpenSSL to verify the MCU Device Certificate (heading level 7)

1. Run the `security readcert` command to save the batch certificate in PEM format.  
   ```sh  
   commander security readcert batch -o batch.pem --serialno 440030580  
   ```  
   ```sh  
   Writing certificate to batch.pem...  
   DONE  
   ```
2. Run the `security readcert` command to save the device certificate in PEM format.  
   ```sh  
   commander security readcert mcu -o device.pem --serialno 440030580  
   ```  
   ```sh  
   Writing certificate to device.pem...  
   DONE  
   ```
3. Get the root certificate (`device-root-prod.pem`) and factory certificate (`factory-prod.pem`) from the certificate folder in [Simplicity Studio](08-r-examples#root-and-factory-certificates-for-verification).
4. Use OpenSSL to display the certificate information (e.g., `device.pem`).  
   ```sh  
   openssl x509 -in device.pem -text -noout  
   ```  
   ```sh  
   Certificate:  
       Data:  
           Version: 3 (0x2)  
           Serial Number:  
               66:f8:5a:e6:b4:ef:6e:49:d3:36:95:63:c9:c3:99:13:e4:71:93:f6  
           Signature Algorithm: ecdsa-with-SHA256  
           Issuer: CN = Batch 1001317, O = Silicon Labs Inc., C = US  
           Validity  
               Not Before: Nov 19 15:10:33 2019 GMT  
               Not After : Nov 19 15:10:33 2119 GMT  
           Subject: C = US, O = Silicon Labs Inc., CN = EUI:14B457FFFE0F77CE DMS:086AEC3C645836BFB04D312F S:SE0 ID:MCU  
           Subject Public Key Info:  
               Public Key Algorithm: id-ecPublicKey  
                   Public-Key: (256 bit)  
                   pub:  
                       04:5c:4b:c9:b0:b3:ff:fa:99:81:c5:99:be:ff:ae:  
                       77:74:1a:f4:30:f1:1e:0e:2d:df:96:4b:ff:d2:46:  
                       fa:fa:e7:23:4b:79:cb:0a:c7:71:13:fa:7c:39:5f:  
                       e2:18:9e:4e:06:43:88:a7:9c:65:53:f3:a3:a1:06:  
                       81:e6:06:f2:11  
                   ASN1 OID: prime256v1  
                   NIST CURVE: P-256  
           X509v3 extensions:  
               X509v3 Basic Constraints: critical  
                   CA:FALSE  
               X509v3 Key Usage: critical  
                   Digital Signature, Non Repudiation, Key Encipherment  
               X509v3 Extended Key Usage: critical  
                   TLS Web Client Authentication  
       Signature Algorithm: ecdsa-with-SHA256  
           30:44:02:20:57:12:a4:84:d8:37:b8:c0:44:8f:16:ac:c1:a3:  
           be:a9:f1:16:38:9f:b9:a2:57:e6:12:49:bf:96:a9:a9:d2:b8:  
           02:20:5f:ae:22:f5:00:05:49:b1:da:ee:4a:84:48:70:27:97:  
           1c:40:2d:85:5f:f2:12:b3:8b:4a:d7:9a:ee:60:81:7c  
   ```
5. Use OpenSSL to verify the certificate chain from steps 1 to 3.

```sh
openssl verify -show_chain -CAfile device-root-prod.pem -untrusted factory-prod.pem -untrusted batch.pem device.pem
```

```sh
device.pem: OKChain:
depth=0: C = US, O = Silicon Labs Inc., CN = EUI:14B457FFFE0F7777 DMS:086AEC3CE650543EE73568DA S:SE0 ID:MCU (untrusted)
depth=1: CN = Batch 1001317, O = Silicon Labs Inc., C = US (untrusted)
depth=2: CN = Factory, O = Silicon Labs Inc., C = US (untrusted)
depth=3: CN = Device Root CA, O = Silicon Labs Inc., C = US
```

###### Using Simplicity Commander to Verify the MCU Device Certificate and Batch Certificate (heading level 7)

Run the `security readcert` command to display the key information about the on-chip certificates (e.g., mcu, batch).

```sh
commander security readcert mcu --serialno 440030580
```

```sh
cert. version     : 3
serial number     : 87:30:83:3C:C9:60:E2:E4:1A:05:0D:17:1F:F0:38:47:7F:29:C4:46
issuer name       : CN=Batch 1172331, O=Silicon Labs Inc., C=US
subject name      : C=US, O=Silicon Labs Inc., CN=EUI:B43522FFFE950215 DMS:086AEC3CBCC884B8D38D896A S:SE0 ID:MCU
issued  on        : 2022-09-15 23:00:53
expires on        : 2122-09-15 23:00:53
signed using      : ECDSA with SHA256
EC key size       : 256 bits
basic constraints : CA=false
key usage         : Digital Signature, Non Repudiation, Key Encipherment
ext key usage     : TLS Web Client Authentication

DONE
```

```sh
commander security readcert batch --serialno 440030580
```

```sh
cert. version     : 3
serial number     : 26:88:29:A3:A7:3D:34:59
issuer name       : CN=Factory, O=Silicon Labs Inc., C=US
subject name      : CN=Batch 1172331, O=Silicon Labs Inc., C=US
issued  on        : 2022-08-23 15:57:18
expires on        : 2118-09-16 17:32:00
signed using      : ECDSA with SHA256
EC key size       : 256 bits
basic constraints : CA=true, max_pathlen=0
key usage         : Digital Signature, Key Cert Sign

DONE
```

###### Using Simplicity Studio v5 to Verify the MCU Device Certificate (heading level 7)

This application note uses Simplicity Studio v5.2.1.1. The procedures and pictures may be different on the other versions of Simplicity Studio 5.

1. Right-click the selected debug adapter **RB (ID:J-Link serial number)** to display the context menu.  
   ![Device Config](/authenticating-devices-using-device-certificates/0.2/images/sld790-device-config.png)
2. Click **Device configuration...** to open the **Configuration of device: J-Link Silicon Labs (serial number)** dialog box. Click the **Security Settings** tab to get the selected device configuration.
3. The **MCU Certificate:** will display **Validated Successfully** if it passed the certificate chain verification process.  
   ![Cert Studio](/authenticating-devices-using-device-certificates/0.2/images/sld790-cert-studio.png)
4. Click **Certificate Details...** to browse the details of different certificates (e.g., Device MCU Certificate in the figure below).

![Device Cert](/authenticating-devices-using-device-certificates/0.2/images/sld790-device-cert.png)

###### Using SE Manager Examples for Certificate Chain Verification and Remote Authentication

The SE Manager Secure Identity platform example uses APIs in [SE Manager](07-r-secureenginemanager) and MBedTLS to emulate the processes in [Remote Authentication Process](06-r-remoteauthenticationprocess).

Click the `View Project Documentation` link to open the `readme.html` file.

![Si Example](/authenticating-devices-using-device-certificates/0.2/images/sld790-si-example.png)

The device simulates the operations in the remote device to eliminate the communications between different parties in this example. The factory certificate and root certificate are hard-coded in the `app_mbedtls_x509.c` file.

The MCU Device Certificate Private Key in the Secure Key Storage on the chip is used to sign the challenge from the remote device. Therefore this example can only run as-is on a chip with the [Standard MCU Device Certificate](04-r-devicecertoptions). It must be modified to be used on devices with an injected MCU Device Certificate, such as Series 2 SVM and Series 3 Secure Vault devices.

###### Step 1 in the Remote Authentication Process (heading level 7)

```sh
SE Manager Secure Identity Example - Core running at 38000 kHz.
  . SE manager initialization... SL_STATUS_OK (cycles: 6 time: 0 us)

  . Secure Vault High device:
  + Read size of on-chip certificates... SL_STATUS_OK (cycles: 5296 time: 139 us)
  + Read on-chip device certificate... SL_STATUS_OK (cycles: 5138 time: 135 us)
  + Parse the device certificate (DER format)... SL_STATUS_OK (cycles: 167043 time: 4395 us)
  + Get the public device key in device certificate... OK
  + Read on-chip batch certificate... SL_STATUS_OK (cycles: 5080 time: 133 us)
  + Parse the batch certificate (DER format)... SL_STATUS_OK (cycles: 173151 time: 4556 us)
```

###### Steps 2 and 3 in the Remote Authentication Process (certificate chain printout is disabled) (heading level 7)

```sh
. Remote device:
  + Parse the factory certificate (PEM format)... SL_STATUS_OK (cycles: 5373122 time: 141 ms)
  + Parse the root certificate (PEM format)... SL_STATUS_OK (cycles: 5448802 time: 143 ms)
  + Verify the certificate chain with root certificate... SL_STATUS_OK (cycles: 958730 time: 25229 us)
```

###### Steps 2 and 3 in the Remote Authentication Process (certificate chain printout is enabled) (heading level 7)

```sh
. Remote device:
  + Parse the factory certificate (PEM format)... SL_STATUS_OK (cycles: 5373935 time: 141 ms)
  + Parse the root certificate (PEM format)... SL_STATUS_OK (cycles: 5449622 time: 143 ms)
  + Verify requested for (Depth 3) ... OK
      cert. version     : 3
      serial number     : 12:E6:A2:A5:9C:AA:27:F9
      issuer name       : CN=Device Root CA, O=Silicon Labs Inc., C=USsubject name      : CN=Device Root CA, O=Silicon Labs Inc., C=US
      issued  on        : 2018-10-10 17:32:00
      expires on        : 2118-09-16 17:32:00
      signed using      : ECDSA with SHA256
      EC key size       : 256 bits
      basic constraints : CA=true, max_pathlen=2
      key usage         : Digital Signature, Key Cert Sign, CRL Sign
  + Verify requested for (Depth 2) ... OK
      cert. version     : 3
      serial number     : 24:DC:7B:40:0C:32:9C:0A
      issuer name       : CN=Device Root CA, O=Silicon Labs Inc., C=USsubject name      : CN=Factory, O=Silicon Labs Inc., C=US
      issued  on        : 2018-10-10 17:33:00
      expires on        : 2118-09-16 17:32:00
      signed using      : ECDSA with SHA256
      EC key size       : 256 bits
      basic constraints : CA=true, max_pathlen=1
      key usage         : Digital Signature, Key Cert Sign, CRL Sign
  + Verify requested for (Depth 1) ... OK
      cert. version     : 3
      serial number     : 23:09:DA:39:B4:78:05:AA
      issuer name       : CN=Factory, O=Silicon Labs Inc., C=USsubject name      : CN=Batch 1001317, O=Silicon Labs Inc., C=US
      issued  on        : 2019-10-17 21:20:20
      expires on        : 2118-09-16 17:32:00
      signed using      : ECDSA with SHA256
      EC key size       : 256 bits
      basic constraints : CA=true, max_pathlen=0
      key usage         : Digital Signature, Key Cert Sign
  + Verify requested for (Depth 0) ... OK
      cert. version     : 3
      serial number     : 66:F8:5A:E6:B4:EF:6E:49:D3:36:95:63:C9:C3:99:13:E4:71:93:F6
      issuer name       : CN=Batch 1001317, O=Silicon Labs Inc., C=USsubject name      : C=US, O=Silicon Labs Inc., CN=EUI:14B457FFFE0F77CE DMS:086AEC3C645836BFB04D312F S:SE0 ID:MCU
      issued  on        : 2019-11-19 15:10:33
      expires on        : 2119-11-19 15:10:33
      signed using      : ECDSA with SHA256
      EC key size       : 256 bits
      basic constraints : CA=false
      key usage         : Digital Signature, Non Repudiation, Key Encipherment
      ext key usage     : TLS Web Client Authentication
  + Verify the certificate chain with root certificate... SL_STATUS_OK (cycles: 9703861 time: 255 ms)
```

> **Note**: The longer processing time (255 ms) is due to the certificate chain printout.

###### Steps 4 and 5 (signature of a challenge) in the Remote Authentication Process (heading level 7)

```sh
. Remote authentication:
  + Create a 16 bytes challenge (random number) in remote device for signing... SL_STATUS_OK (cycles: 3700 time: 97 us)
  + Sign challenge with private device key in Secure Vault High device... SL_STATUS_OK (cycles: 221983 time: 5841 us)
  + Get public device key in Secure Vault High device... SL_STATUS_OK (cycles: 199788 time: 5257 us)
  + Verify signature with public device key in Secure Vault High device... SL_STATUS_OK (cycles: 229054 time: 6027 us)
  + Verify signature with public device key in remote device... SL_STATUS_OK (cycles: 230442 time: 6064 us)

  . SE manager deinitialization... SL_STATUS_OK (cycles: 6 time: 0 us)
```

###### Using SE Manager Examples to Generate Entity Attestation Tokens (EAT)

These examples demonstrate how to retrieve the [EAT tokens](05-r-entityattestationtoken) from the Series 2 SVH or Series 3 Secure Vault device.

###### SE Manager - Attestation Platform Example (heading level 7)

The SE Manager Attestation platform example uses APIs in [SE Manager](07-r-secureenginemanager) to retrieve the PSA initial attestation token and security configuration token from the SE. This example is only available on devices with an SE Device Certificate programmed, which includes Series 2 SVH and Series 3 Secure Vault (SixG301) devices.

Click the `View Project Documentation` link to open the `readme.html` file.

![Att Example](/authenticating-devices-using-device-certificates/0.2/images/sld790-att-example.png)

Press `SPACE` to cycle the challenge size for the PSA attestation token. Press `ENTER` to make a selection and run the program.

```sh
SE Manager Attestation Example - Core running at 38000 kHz.
Initializing SE Manager...
SL_STATUS_OK (cycles: 10 time: 0 us)

Select nonce size for the IAT token (32, 48 or 64 bytes).
Press SPACE to cycle through the options.
Press ENTER to make a selection.
    Current nonce size:  32
    Selected nonce size: 32
Calling sl_se_attestation_get_psa_iat_token...

SL_STATUS_OK (cycles: 661072 time: 17396 us)
```

**PSA Initial Attestation Token** (Refer to [Entity Attestation Token (EAT)](05-r-entityattestationtoken) for additional details)

```sh
PSA IAT token
=============
-------------------------------------------------------------------
Raw token:
d28443a10126a058e4a83a000124ff58204ca14d0bc8601cad2e511de1964e93
9338b6fc20f8231aa178ca79519b0ffae73a000124f7715053415f494f545f50
524f46494c455f313a00012500490614b457fffe0f77ce3a000124f8013a0001
24f91920003a000124fa5820011c00010600000001000000f2030f0000000000
0000000000000000000000003a000124fb58204922b7bbd31c0c81c9b0485ccf
b5396ec24ffa877ece441e11c947b791218cf83a000124fd81a3016450526f54
046830303031303230390258206d39caedba129297062b820ba6d85b3e432c44
3c8a8a31d3c6232be6906d38dc584030f9d61523204793965fc9eb2be788db9d
2b02692d877673c86ebffbfb6769984515d2f1a287a92d2c134c1024f20f018d
be952a2ccae7ed2980a9f242d02c9c

COSE_Sign1 structure:
d2    ; tag(18)
  84    ; array(4)
    43    ; byte_str(3)
      a10126
    a0    ; map(0)
    58    ; byte_str(228)
      a83a000124ff58204ca14d0bc8601cad2e511de1964e939338b6fc20f8231aa1
    78ca79519b0ffae73a000124f7715053415f494f545f50524f46494c455f313a
    00012500490614b457fffe0f77ce3a000124f8013a000124f91920003a000124
    fa5820011c00010600000001000000f2030f0000000000000000000000000000
    0000003a000124fb58204922b7bbd31c0c81c9b0485ccfb5396ec24ffa877ece
    441e11c947b791218cf83a000124fd81a3016450526f54046830303031303230
    390258206d39caedba129297062b820ba6d85b3e432c443c8a8a31d3c6232be6
    906d38dc
    58    ; byte_str(64)
      30f9d61523204793965fc9eb2be788db9d2b02692d877673c86ebffbfb676998
    4515d2f1a287a92d2c134c1024f20f018dbe952a2ccae7ed2980a9f242d02c9c
-------------------------------------------------------------------
Token claims:
a8    ; map(8)
  3a    ; int(-75008)
  58    ; byte_str(32)
    4ca14d0bc8601cad2e511de1964e939338b6fc20f8231aa178ca79519b0ffae7
  3a    ; int(-75000)
  71    ; text_str(17)
    "PSA_IOT_PROFILE_1"
  3a    ; int(-75009)
  49    ; byte_str(9)
    0614b457fffe0f77ce
  3a    ; int(-75001)
  01    ; int(1)
  3a    ; int(-75002)
  19    ; int(8192)
  3a    ; int(-75003)
  58    ; byte_str(32)
    011c00010600000001000000f2030f0000000000000000000000000000000000
  3a    ; int(-75004)
  58    ; byte_str(32)
    4922b7bbd31c0c81c9b0485ccfb5396ec24ffa877ece441e11c947b791218cf8
  3a    ; int(-75006)
  81    ; array(1)
    a3    ; map(3)
      01    ; int(1)
      64    ; text_str(4)
        "PRoT"
      04    ; int(4)
      68    ; text_str(8)
        "00010209"
      02    ; int(2)
      58    ; byte_str(32)
        6d39caedba129297062b820ba6d85b3e432c443c8a8a31d3c6232be6906d38dc

List of claims printed with human-friendly names:
  ARM PSA Nonce, Claim ID: -75008
  58    ; byte_str(32)
    4ca14d0bc8601cad2e511de1964e939338b6fc20f8231aa178ca79519b0ffae7

  ARM PSA Profile ID, Claim ID: -75000
  71    ; text_str(17)
    "PSA_IOT_PROFILE_1"

  ARM PSA / IETF EAT UEID, Claim ID: -75009
  49    ; byte_str(9)
    0614b457fffe0f77ce

  ARM PSA Partition ID, Claim ID: -75001
  01    ; int(1)

  ARM PSA Lifecycle, Claim ID: -75002
  19    ; int(8192)

  ARM PSA Implementation ID, Claim ID: -75003
  58    ; byte_str(32)
    011c00010600000001000000f2030f0000000000000000000000000000000000

  ARM PSA Boot seed, Claim ID: -75004
  58    ; byte_str(32)
    4922b7bbd31c0c81c9b0485ccfb5396ec24ffa877ece441e11c947b791218cf8

  ARM PSA Software components, Claim ID: -75006
  81    ; array(1)
    a3    ; map(3)
      01    ; int(1)
      64    ; text_str(4)
        "PRoT"
      04    ; int(4)
      68    ; text_str(8)
        "00010209"
      02    ; int(2)
      58    ; byte_str(32)
        6d39caedba129297062b820ba6d85b3e432c443c8a8a31d3c6232be6906d38dc
```

**Security Configuration Token** (Refer to [Entity Attestation Token (EAT)](05-r-entityattestationtoken) for additional details)

```sh
-------------------------------------------------------------------

Calling sl_se_attestation_get_config_token...
SL_STATUS_OK (cycles: 541281 time: 14244 us)

Config token
============
-------------------------------------------------------------------
Raw token:
d28443a10126a0590133a83a000124ff5820c3e3664dcc47711bf81734bc95f0
87d81dd841d73fc805fc9237c7b3dfa25c503a000124f76853494c4142535f31
3a00012500490614b457fffe0f77ce3a000128df582400000001000000000000
0000000000000000002000010209ffffffff00000025000000003a000128e058
1800000000100444400401041411224477242204420a0600053a000128e15840
c4af4ac69aab9512db50f7a26ae5b4801183d85417e729a56da974f4e08a562c
de6019dea9411332dc1a743372d170b436238a34597c410ea177024de20fc819
3a000128e25840b1bc6f6fa56640ed522b2ee0f5b3cf7e5d48f60be8148f0dc0
8440f0a4e1dca47c04119ed6a1be31b7707e5f9d001a659a051003e95e1b936f
05c37ea793ad633a000128e350150444400401041411224477142204425840b7
47d98be9cef8a91af0292a479a3fa499527018b97ac1188ddefb0fa6fcb9b3d1
d4159240a8663c8803a2ef7cebdf7644fa3394cf1057d612e1b3977d9de92d

COSE_Sign1 structure:
d2    ; tag(18)
  84    ; array(4)
    43    ; byte_str(3)
      a10126
    a0    ; map(0)
    59    ; byte_str(307)
    a83a000124ff5820c3e3664dcc47711bf81734bc95f087d81dd841d73fc805fc
    9237c7b3dfa25c503a000124f76853494c4142535f313a00012500490614b457
    fffe0f77ce3a000128df58240000000100000000000000000000000000000020
    00010209ffffffff00000025000000003a000128e05818000000001004444004
    01041411224477242204420a0600053a000128e15840c4af4ac69aab9512db50
    f7a26ae5b4801183d85417e729a56da974f4e08a562cde6019dea9411332dc1a
    743372d170b436238a34597c410ea177024de20fc8193a000128e25840b1bc6f
    6fa56640ed522b2ee0f5b3cf7e5d48f60be8148f0dc08440f0a4e1dca47c0411
    9ed6a1be31b7707e5f9d001a659a051003e95e1b936f05c37ea793ad633a0001
    28e35015044440040104141122447714220442
    58    ; byte_str(64)
    b747d98be9cef8a91af0292a479a3fa499527018b97ac1188ddefb0fa6fcb9b3
    d1d4159240a8663c8803a2ef7cebdf7644fa3394cf1057d612e1b3977d9de92d
-------------------------------------------------------------------
Token claims:
a8    ; map(8)
  3a    ; int(-75008)
  58    ; byte_str(32)
    c3e3664dcc47711bf81734bc95f087d81dd841d73fc805fc9237c7b3dfa25c50
  3a    ; int(-75000)
  68    ; text_str(8)
    "SILABS_1"
  3a    ; int(-75009)
  49    ; byte_str(9)
    0614b457fffe0f77ce
  3a    ; int(-76000)
  58    ; byte_str(36)
    000000010000000000000000000000000000002000010209ffffffff0000002500000000
  3a    ; int(-76001)
  58    ; byte_str(24)
    00000000100444400401041411224477242204420a060005
  3a    ; int(-76002)
  58    ; byte_str(64)
    c4af4ac69aab9512db50f7a26ae5b4801183d85417e729a56da974f4e08a562c
  de6019dea9411332dc1a743372d170b436238a34597c410ea177024de20fc819
  3a    ; int(-76003)
  58    ; byte_str(64)
    b1bc6f6fa56640ed522b2ee0f5b3cf7e5d48f60be8148f0dc08440f0a4e1dca4
  7c04119ed6a1be31b7707e5f9d001a659a051003e95e1b936f05c37ea793ad63
  3a    ; int(-76004)
  50    ; byte_str(16)
    15044440040104141122447714220442

List of claims printed with human-friendly names:
  ARM PSA Nonce, Claim ID: -75008
  58    ; byte_str(32)
    c3e3664dcc47711bf81734bc95f087d81dd841d73fc805fc9237c7b3dfa25c50

  ARM PSA Profile ID, Claim ID: -75000
  68    ; text_str(8)
    "SILABS_1"

  ARM PSA / IETF EAT UEID, Claim ID: -75009
  49    ; byte_str(9)
    0614b457fffe0f77ce

  SE Status, Claim ID: -76000
  58    ; byte_str(36)
    000000010000000000000000000000000000002000010209ffffffff0000002500000000

  OTP Configuration, Claim ID: -76001
  58    ; byte_str(24)
    00000000100444400401041411224477242204420a060005

  OTP MCU Boot key, Claim ID: -76002
  58    ; byte_str(64)
    c4af4ac69aab9512db50f7a26ae5b4801183d85417e729a56da974f4e08a562c
  de6019dea9411332dc1a743372d170b436238a34597c410ea177024de20fc819

  OTP MCU Auth key, Claim ID: -76003
  58    ; byte_str(64)
    b1bc6f6fa56640ed522b2ee0f5b3cf7e5d48f60be8148f0dc08440f0a4e1dca4
  7c04119ed6a1be31b7707e5f9d001a659a051003e95e1b936f05c37ea793ad63

  Current applied tamper settings, Claim ID: -76004
  50    ; byte_str(16)
    15044440040104141122447714220442
-------------------------------------------------------------------

Exiting...
SL_STATUS_OK (cycles: 8 time: 0 us)
```

> **Note**: The reserved tamper source in ID 76004 returns a value of 0 or 5.

###### Using Simplicity Commander to Verify the SE Device Certificate (heading level 7)

The SE Device certificate can be read from the device using the readcert command.

```sh
commander security readcert se --serialno 440030580
```

```sh
cert. version     : 3
serial number     : B0:38:8C:E3:41:FB:B8:CD:DB:0D:A8:6A:BA:2A:81:88:AA:01:47:81
issuer name       : CN=Batch 1172331, O=Silicon Labs Inc., C=US
subject name      : C=US, O=Silicon Labs Inc., CN=EUI:B43522FFFE950215 DMS:086AEC3C122247BA83B66768 S:SE0 ID:SE
issued  on        : 2022-09-15 23:00:53
expires on        : 2122-09-15 23:00:53
signed using      : ECDSA with SHA256
EC key size       : 256 bits
basic constraints : CA=false
key usage         : Digital Signature, Non Repudiation, Key Encipherment
ext key usage     : TLS Web Client Authentication

DONE
```

Run the `security attestation` command to retrieve and validate the security configuration token from the SE. This command is currently supported only on Series 2 Secure Vault High devices.

```sh
commander security attestation --serialno 440030580
```

```sh
Certificate chain successfully validated up to Silicon Labs device root certificate.

-75008 ARM PSA nonce                   : e2d87794046ad3e752164dd8ecea63f27f0ee82af1112e13bc3ac2537ff366a2
-75000 ARM PSA Profile ID              : SILABS_1
-75009 ARM PSA/IETF EAT UEID           : 06b43522fffe950215
-76000 SE status                       : 000000000000000000000000000000020000002000010210000000000000000200000000
-76001 OTP configuration               : 00000000100444400401041411224477242204420a060005
-76003 MCU command key                 : b1bc6f6fa56640ed522b2ee0f5b3cf7e5d48f60be8148f0dc08440f0a4e1dca47c04119ed6a1be31b7707e5f9d001a659a051003e95e1b936f05c37ea793ad63
-76004 Current applied tamper settings : 15044440040104141122447714220442

Successfully validated signature of attestation token.
DONE
```

> **Note**: The reserved tamper source in ID 76004 returns a value of 0 or 5.

#### Secure Key Storage

##### Secure Key Storage

> **Note: This section replaces _AN1271: Secure Key Storage_. Further updates to this application note will be provided here**.

Secure Key Storage is a feature in  High devices that allows for the protection of cryptographic keys by key wrapping. User keys are encrypted by the device's root key for non-volatile storage for later usage. This prevents the need for a key to be stored in plaintext format on the device, preventing attackers from gaining access to the keys through traditional flash-extraction or application attacks, and allowing for a potentially unlimited number of keys to be securely stored in any available storage.

Series 2 devices can use TrustZone to implement Secure Key Storage, so this feature is now also available on  Mid devices.

This document describes the operation and usage of this feature, and provides comparisons with other key storage methods.

###### Key Points

- Keys are encrypted or 'wrapped' with a  root key
- root key is not stored on the device, instead it is generated on each reset
- Wrapped keys are confidential to the , and can be stored in non-volatile memory safely
- Wrapped keys can be cached into  for usage at a later time
- TrustZone Secure Key Storage

##### Series 2 Device Security Features

Protecting IoT devices against security threats is central to a quality product. Silicon Labs offers several security options to help developers build secure devices, secure application software, and secure paths of communication to manage those devices. Silicon Labs’ security offerings were significantly enhanced by the introduction of the Series 2 products that included a Secure Engine. The Secure Engine is a tamper-resistant component used to securely store sensitive data and keys and to execute cryptographic functions and secure services.

On Series 1 devices, the security features are implemented by the TRNG (if available) and CRYPTO peripherals.

On Series 2 devices, the security features are implemented by the Secure Engine and CRYPTOACC (if available). The Secure Engine may be hardware-based, or virtual (software-based). Throughout this document, the following abbreviations are used:

- HSE - Hardware Secure Engine
- VSE - Virtual Secure Engine
- SE - Secure Engine (either HSE or VSE)

Additional security features are provided by Secure Vault. Three levels of Secure Vault feature support are available, depending on the part and SE implementation, as reflected in the following table:

<table>
    <thead>
        <tr>
            <th>Level (1)</th>
            <th>SE Support</th>
            <th>Part (2)</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Secure Vault High (SVH)</p>
            </td>
            <td>
                <p>HSE only (HSE-SVH)</p>
            </td>
            <td>
                <p>Refer to <a href="https://docs.silabs.com/iot-security/latest/iot-endpoint-security-fundamentals/">IoT Endpoint Security Fundamentals</a> for details on supporting devices.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Secure Vault Mid (SVM)</p>
            </td>
            <td>
                <p>HSE (HSE-SVM)</p>
            </td>
            <td>
                <p>"</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>"</p>
            </td>
            <td>
                <p>VSE (VSE-SVM)</p>
            </td>
            <td>
                <p>"</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Secure Vault Base (SVB)</p>
            </td>
            <td>
                <p>N/A</p>
            </td>
            <td>
                <p>"</p>
            </td>
        </tr>
    </tbody>
</table>

**Notes**:

1. The features of different Secure Vault levels can be found in [https://www.silabs.com/security](https://www.silabs.com/security).
2. [IoT Endpoint Security Fundamentals](https://docs.silabs.com/iot-security/latest/iot-endpoint-security-fundamentals/).

Secure Vault Mid consists of two core security functions:

- Secure Boot: Process where the initial boot phase is executed from an immutable memory (such as ROM) and where code is authenticated before being authorized for execution.
- Secure Debug access control: The ability to lock access to the debug ports for operational security, and to securely unlock them when access is required by an authorized entity.

Secure Vault High offers additional security options:

- Secure Key Storage: Protects cryptographic keys by "wrapping" or encrypting the keys using a root key known only to the HSE-SVH.
- Anti-Tamper protection: A configurable module to protect the device against tamper attacks.
- Device authentication: Functionality that uses a secure device identity certificate along with digital signatures to verify the source or target of device communications.

A Secure Engine Manager and other tools allow users to configure and control their devices both in-house during testing and manufacturing, and after the device is in the field.

###### User Assistance

In support of these products Silicon Labs offers whitepapers, webinars, and documentation. The following table summarizes the key security documents:

<table>
    <thead>
        <tr>
            <th>Document</th>
            <th>Summary</th>
            <th>Applicability</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/series2-secure-debug/">Series 2 and Series 3 Secure Debug</a></p>
            </td>
            <td>
                <p>How to lock and unlock Series 2 and Series 3 debug access, including background information about the SE</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/">Series 2 and Series 3 Secure Boot with RTSL</a></p>
            </td>
            <td>
                <p>Describes the secure boot process on Series 2 and Series 3 devices using SE</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/efr32-secure-vault-tamper/">Anti-Tamper Protection Configuration and Use</a></p>
            </td>
            <td>
                <p>How to program, provision, and configure the anti-tamper module</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/authenticating-devices-using-device-certificates/">Authenticating Silicon Labs Devices using Device Certificates</a></p>
            </td>
            <td>
                <p>How to authenticate a device using secure device certificates and signatures, at any time during the life of the product</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Secure Key Storage (this application note)</p>
            </td>
            <td>
                <p>How to securely "wrap" keys so they can be stored in non-volatile storage.</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/">Production Programming of Series 2 and Series 3 Devices</a></p>
            </td>
            <td>
                <p>How to program, provision, and configure security information using SE during device production</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>AN1504: Series 3 Security Overview</p>
            </td>
            <td>
                <p>High level overview of the security features included in Series 3 devices</p>
            </td>
            <td>
                <p>Series 3</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>AN1509: Series 3 AXiP</p>
            </td>
            <td>
                <p>How to encrypt and authenticate memory contents</p>
            </td>
            <td>
                <p>Series 3</p>
            </td>
        </tr>
    </tbody>
</table>

###### Key Reference

Public/Private keypairs along with other keys are used throughout Silicon Labs security implementations. Because terminology can sometimes be confusing, the following table lists the key names, their applicability, and the documentation where they are used.

<table>
    <thead>
        <tr>
            <th>Key Name</th>
            <th>Customer Programmed</th>
            <th>Purpose</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Public Sign key (Sign Key Public)</p>
            </td>
            <td>
                <p>Yes</p>
            </td>
            <td>
                <p>Secure Boot binary authentication and/or OTA upgrade payload authentication</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Public Command key (Command Key Public)</p>
            </td>
            <td>
                <p>Yes</p>
            </td>
            <td>
                <p>Secure Debug Unlock or Disable Tamper command authentication</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>OTA Decryption key (GBL Decryption key) aka AES-128 Key</p>
            </td>
            <td>
                <p>Yes</p>
            </td>
            <td>
                <p>Decrypting GBL payloads used for firmware upgrades</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Attestation key aka Private Device Key</p>
            </td>
            <td>
                <p>No</p>
            </td>
            <td>
                <p>Device authentication for secure identity</p>
            </td>
        </tr>
    </tbody>
</table>

###### SE Firmware

Silicon Labs strongly recommends installing the latest SE firmware on Series 2 and Series 3 devices to support the required security features. Refer to [Production Programming of Series 2 and Series 3 Devices](https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/) for the procedure to upgrade the SE firmware and [IoT Endpoint Security Fundamentals](https://docs.silabs.com/iot-security/latest/iot-endpoint-security-fundamentals/) for the latest SE Firmware shipped with Series 2 and Series 3 devices and modules.

##### Introduction

The HSE isolates cryptographic functions and data from the host Cortex-M33 core. It is used to accelerate cryptographic operations as well as to provide a method to securely store keys. This application note will cover the Secure Key Storage feature of the HSE-SVH devices.

The HSE contains one-time programmable memory (OTP) key storage slots for three specific keys:

1. The Public Sign Key, used for Secure Boot and Secure Upgrades
2. The Public Command Key, used for Secure Debug unlock and tamper disable
3. The Symmetric OTA Decryption Key, used for Over-The-Air updates

These keys are one-time programmable, and, after programming, are persistent for the lifetime of the device.

HSE-SVH devices also contain four volatile storage slots for any other user keys. These slots are not persistent through a reset. In the case where a key needs persistent storage, the key must be stored outside of the HSE in non-volatile storage. After a device reset, the key can be loaded into the HSE volatile key storage for usage by index, or used in-place (passed to the HSE on every requested operation). Without any secure key storage mechanism, the user key stored in non-volatile storage is opened to storage-extraction attacks (such as gaining access to and downloading device flash), as well as application-level attacks (i.e., taking control of the user application or privileges in a manner that allows access to the keys).

With Secure Key Storage, a user can only access a key from the HSE in a 'wrapped' format. In this format, the key is encrypted by a device-unique root key, only available to the HSE. This allows a user to store a key confidentially in non-volatile storage to provide key persistence. Using Secure Key Storage, the plaintext key is never stored in non-volatile memory, preventing storage-extraction attacks from obtaining the key. After a device reset, the wrapped key can be loaded into the HSE for usage without ever exposing the plaintext key to the application, which also prevents application-level attacks from exposing the key.

SVM devices can only support Secure Key Storage through the use of [TrustZone](04-r-storagecomparisons-trustzone). GSDK v4.2.2 is the first version to support TrustZone software development on Series 2 devices.

Silicon Labs provides [Custom Part Manufacturing Service (CPMS)](https://www.silabs.com/developers/custom-part-manufacturing-service) to inject custom secret keys on the chips during manufacturing. For more information about CPMS, see the [Custom Part Manufacturing Service User's Guide](https://docs.silabs.com/iot-security/latest/iot-security-cpms/).

##### HSE Secure Key Storage

The following sections demonstrate three methods for key storage: ARM® TrustZone®, plaintext, and Secure Key Storage.

> **Note**: In the following examples, AES key usage is demonstrated. However, any other key types supported by the device can also be used for key storage.

###### Key Generation and Usage

In HSE-SVH devices, cryptographic functions are performed by the HSE. In order to perform these functions, the HSE must have access to any user keys needed. Keys can be generated and used by the HSE in multiple ways:

1. External storage, in-place usage:  
   1. A user generates a plaintext key and stores it in device memory.  
   2. The user provides a key descriptor to the HSE that points to this key for a specific cryptographic operation.  
   3. The HSE performs the cryptographic operation using this key, but does not store it in any HSE volatile storage slot.
2. External storage with HSE import:  
   1. A user generates a plaintext key and stores it in device memory.  
   2. The user provides a key descriptor to the HSE that points to this key, as well as a slot number to store the key.  
   3. The HSE imports this key into a volatile key storage slot or can optionally save it in wrapped form in device memory.  
   4. The user requests that the HSE performs a cryptographic function by providing the index of the storage slot or a pointer to the wrapped key in device memory.
3. Internal HSE key generation:  
   1. The user commands the HSE to generate a new key within one of the HSE's volatile key slots or can optionally save it in wrapped form in device memory.  
   2. The user requests that the HSE performs a cryptographic function by providing the index of the storage slot or a pointer to the wrapped key in device memory.

**Notes**:

- In each case, to provide persistent storage for the key, the key must be stored in non-volatile memory.
- [Plain Key Storage](#plaintext-key-storage) and [Secure Key Storage](#secure-key-storage) provide details on key generation and usage with HSE-SVH device.

###### Plaintext Key Storage

###### Plaintext Key Import (heading level 7)

The simplest manner to store a key is to save it in plaintext form. The steps to store and use a key stored in plaintext form are as follows:

1. A user key is generated and imported into device memory. For persistent storage, this must be non-volatile storage, such as device flash.
2. After a device reset, the HSE volatile key storage will be empty. The plaintext key is imported ([method 2](#key-generation-and-usage)) into a slot for usage. Alternatively, the key could be used in place ([method 1](#key-generation-and-usage)) from non-volatile storage on a per-operation basis.  
   ![Plaintext Key Import](/efr32-secure-key-storage/0.2/images/sld791-plaintext-key-import.png)

###### Plaintext Key Usage (heading level 7)

In order to use the key for a cryptographic operation, the following procedure is used.

1. The user passes data to be processed (in this specific example, AES encrypted data) to the HSE.
2. The user requests that a cryptographic operation be performed on this data using one of the keys stored in the HSE volatile key storage slots ([method 2](#key-generation-and-usage)). Alternatively, the key can be passed to the HSE directly for a singular cryptographic operation ([method 1](#key-generation-and-usage)).
3. The HSE performs the cryptographic operation.
4. The output of the cryptographic operation is passed back to the user for processing.  
   ![Plaintext Key Usage](/efr32-secure-key-storage/0.2/images/sld791-plaintext-key-usage.png)

This method exposes the keys to two major vulnerabilities:

1. Access to device storage gives access to the keys. In this case, an attack that gains access to the flash contents will expose the user key.
2. Since the application has access to the keys, compromising the application or device privileges can compromise the keys. Such an attack might not directly access device memory, but take control of the application in a way that causes the application to expose the key to an attacker.

###### Secure Key Storage

With Secure Key Storage, the user key, using the HSE, can be accessed in an encrypted, or 'wrapped' form. Only the HSE has access to the HSE root key used to decrypt, or 'unwrap', the wrapped key. This HSE root key is not stored on the device during power-down, but rather reconstructed after each reset. Key wrapping allows a user to securely store a key in non-volatile memory, limiting the number of keys that can be stored only by the amount of storage the user has available.

> **Note**: The reconstructed root key after each reset is IDENTICAL and UNIQUE on each HSE-SVH device.

###### Wrap an External Key (heading level 7)

To wrap an externally-generated key:

1. After power-on, the device's unique root key is reconstructed with output from the Physically Unclonable Function (PUF).
2. A user key is generated and imported into device memory. In this example, the key is imported into RAM for easy deletion, and the added security that, if device power is removed, the key will be lost.
3. The user key is passed to the HSE, where it is encrypted with the HSE's root key.
4. The wrapped key is passed back to the user application for storage in non-volatile memory (in this case, device flash).
5. The plaintext key can now be deleted from the device. From this point forward, only the HSE will have access to the plaintext key.  
   ![External Key Import, Wrapping, and Storage](/efr32-secure-key-storage/0.2/images/sld791-external-key-import-wrapping-and-storage.png)

###### Generate an Internal Wrapped Key (heading level 7)

Instead of importing an external key, the HSE can generate a new key directly into one of its volatile key storage slots. This key can then be exported in wrapped form for secure persistent storage.

1. The user requests that the HSE generates a new key into one of its storage slots using the True Random Number Generator (TRNG).
2. The key is encrypted with the HSE's root key.
3. The wrapped key is passed back to the user application for non-volatile storage (flash, in this case).  
   ![Internally Generated Key Wrapping and Storage](/efr32-secure-key-storage/0.2/images/sld791-internally-generated-key-wrapping-and-storage.png)

###### Wrapped Key Import (heading level 7)

In order to import a wrapped key into the HSE for usage:

1. The wrapped key is passed to the HSE.
2. The wrapped key is decrypted ("unwrapped") with the HSE's root key.
3. The plaintext key is stored in a volatile key storage slot.  
   ![Wrapped Key Import](/efr32-secure-key-storage/0.2/images/sld791-wrapped-key-import.png)

###### Wrapped Key Usage (heading level 7)

In order to use the key for a cryptographic operation, the same steps are followed as when using a plaintext key that has been imported into the HSE:

1. The user passes data to be processed (in this specific example, AES encrypted data) to the HSE.
2. The user requests that a cryptographic operation be performed on this data using one of the keys stored in the HSE volatile key storage slots. Alternatively, the wrapped key can be passed to the HSE directly for a singular cryptographic operation. In this case, the key will be unwrapped before being used, but will not be stored for future operations.
3. The HSE performs the cryptographic operation.
4. The output of the cryptographic operation is passed back to the user for processing.  
   ![Wrapped Key Usage](/efr32-secure-key-storage/0.2/images/sld791-wrapped-key-usage.png)

###### Secure Key Storage Advantages

Secure Key Storage confers the following benefits over other key storage methods:

1. Access to device memory does not expose user keys.
2. Compromising the user application does not expose user keys, since the user application itself does not have access to the plaintext keys.
3. The number of user keys that can be securely stored is only limited by the amount of storage available to the user, including external storage.

###### Operation Details

###### Root Key Generation (heading level 7)

Secure Key Storage depends on the HSE to encrypt / decrypt (wrap / unwrap) user keys with its own symmetric root key. The symmetric key used for this wrapping and unwrapping must be highly secure as it can expose all other key material in the system. The HSE key Management system uses a Physically Unclonable Function (PUF) to generate a persistent device-unique seed on power up to dynamically reconstruct this critical root key. The key is only visible to the AES encryption engine, and it is not retained when the device loses power.

###### Access a Wrapped Key (heading level 7)

By default, a key in an HSE storage slot can be exported to the application as a plaintext key. To prevent this, the user can use the key descriptor to set a user key to [non-exportable](05-r-securekeystorageimplementation#secure-key-storage-implementations). This option prevents any request to export the wrapped key in plaintext from HSE, so the user application can only access the key encrypted by the HSE's root key. The HSE also tags the key with information to identify the wrapped key. Since only the HSE can access the root key to unwrap the user key, the plaintext key is non-accessible to the user application.

> **Note**: Wrapped keys are slightly larger than the equivalent plaintext key, as some additional metadata is required to identify the wrapped key to the HSE.

###### Wrapped Key Storage and Usage (heading level 7)

Once a key has been wrapped, it can be safely stored anywhere - device flash, RAM, external storage, etc. The number of keys that can be securely stored is only limited by the available storage space. A wrapped key can later be imported into a HSE volatile storage slot for usage, or used in-place. Once the key is wrapped and stored, the plaintext key available to the application can be deleted. From here, only the HSE will have the ability to unwrap and use the key.

With access to the wrapped key, the HSE can use this key in one of two ways:

1. A user can request that a cryptographic operation be performed using the key stored in memory. In this case, the HSE will import the key, unwrap it, and then perform the cryptographic operation. The key will not be stored within the HSE.
2. A user can import the wrapped key into a HSE volatile storage slot. In this case, the key is unwrapped by the HSE and stored in plaintext in a volatile slot. The user can then later request that a cryptographic function be performed by the HSE by referencing the volatile slot index. This provides a performance increase over using wrapped keys in place, as the HSE does not need to import and unwrap the key on each requested operation.

###### Password Protection (heading level 7)

When defining a key descriptor for a new key, or when importing an existing key into HSE, the user can choose to require a password to allow use of the key. The password field in the key descriptor structure is eight bytes in length. If unspecified, the key will use the default password of all zeros.

After importing a key with a password, failing to provide the correct password when performing a cryptographic operation will result in HSE returning an invalid credentials error, and no operation will be performed.

##### TrustZone Secure Key Storage

In Series 2 devices, key management can be handled by a feature called TrustZone. TrustZone divides the device memory map into a Secure Processing Environment (SPE) and a Non-secure Processing Environment (NSPE). User code is executed from the NSPE, which cannot access any part of the SPE. The SPE is used to store cryptographic keys securely and to control other Secure operations.

The following sections describe using TrustZone on Series 2 devices for Secure Key Storage. Refer to [Series 2 TrustZone](https://docs.silabs.com/mcu-bootloader/latest/series2-trustzone/) for details about TrustZone implementation on Series 2 devices.

###### TrustZone Root Key Generation (HSE and VSE)

![Wrapped TRK](/efr32-secure-key-storage/0.2/images/sld791-wrapped-trk.png)

1. The TrustZone Root Key (TRK) is generated by the True Random Number Generator (TRNG) in Series 2 devices.
2. The PUF-derived key (HSE and xG27 VSE devices) or padded unique device serial number (xG22 VSE devices) is used to wrap (AES-GCM) the TRK.
3. The wrapped TRK is stored in the SE Non-volatile memory (NVM), and the TRK in RAM is deleted.  
   - The wrapped TRK already existed if the shipped Series 2 device with SE firmware version supports this key.  
   - The wrapped TRK will be generated when upgrading from a SE firmware version that did not support this key to the one that does.  
   - The wrapped TRK will be renewed after performing a Device Erase.

> **Note**: The Physically Unclonable Function (PUF) is not retained when the device loses power, so the TRK wrapped by the PUF-derived key is not vulnerable to a storage-extraction attack.

###### TrustZone Root Key Usage (HSE)

![HSE Unwrapped TRK](/efr32-secure-key-storage/0.2/images/sld791-hse-unwrapped-trk.png)

1. The Secure application in the host uses a non-exportable built-in key to access the wrapped TRK in HSE NVM for cryptographic operations.
2. The PUF-derived key is used to decrypt (AES-GCM) the wrapped TRK in HSE NVM.
3. The unwrapped TRK in the HSE is the master key of a Key Derivation Function (KDF).
4. The encryption key in SPE for Secure Key Storage is derived from the KDF CMAC.

**Notes**:

- All cryptographic operations are performed by the HSE (security co-processor).
- Only the HSE can access the unwrapped TRK for KDF, so this key will not expose the Secure application in the host.

###### TrustZone Root Key Usage (VSE)

![VSE Unwrapped TRK](/efr32-secure-key-storage/0.2/images/sld791-vse-unwrapped-trk.png)

1. The wrapped TRK in VSE NVM is accessed by the VSE Root mode firmware.
2. The PUF-derived key (xG27) or padded unique device serial number (xG22) is used to decrypt (AES-GCM) the wrapped TRK in VSE NVM.
3. Unwrapped TRK is transferred to the shared RAM when switching from VSE Root mode to User mode. The VSE user mode Secure application stores this key to the Secure RAM in SPE and deletes this key in the shared RAM.
4. The unwrapped TRK in the Secure RAM is the master key of a Key Derivation Function (KDF).
5. The encryption key in SPE for Secure Key Storage is derived from the KDF CMAC.

> **Note**: On VSE devices, all cryptographic operations are performed by the Cryptographic Accelerator (CRYPTOACC) peripheral.

For more information about the HSE and VSE, refer to the section _Secure Engine Subsystem_ in [Series 2 Secure Debug](https://docs.silabs.com/iot-security/latest/series2-secure-debug/03-r-secureelement).

###### TrustZone Secure Key Storage (HSE and VSE)

The TRK allows a user to securely store a key in the Non-secure flash, limiting the number of keys that can be saved only by the amount of Non-secure storage. The following figure describes using the TRK to encrypt a plaintext key and store it in Non-secure NVM.![TZ SKS](/efr32-secure-key-storage/0.2/images/sld791-tz-sks.png)

1. After power-on, the device's TRK (wrapped in HSE NVM and unwrapped in VSE Secure RAM) is available for the SPE.
2. A user key is generated and imported into the device's Non-secure memory. In this example, the key is imported into Non-secure RAM for easy deletion, and the key is lost if device power is removed.
3. Call the PSA Crypto API (`psa_import_key()` or `psa_generate_key()`) through the Secure Gateway (SG) in Non-secure Callable (NSC) memory to generate a key for crypto operations.
4. The plaintext key is passed in the PSA Crypto API to the SPE, where it is encrypted (AES-GCM) with the encryption key derived (KDF CMAC) from the TRK.
5. The encrypted key is stored to the NVM region in the NSPE through the PSA Internal Trusted Storage (ITS) and [NVM3](https://docs.silabs.com/gecko-platform/latest/driver/api/group-nvm3) drivers.
6. The plaintext key can now be deleted from the Non-secure RAM.
7. Only the PSA Crypto API in the SPE can retrieve the encrypted key from NVM in the NSPE and decrypt it for crypto operations in the SPE.

> **Note**: Ignore steps 2 and 6 if the plaintext key is randomly generated by the PSA Crypto.

##### Secure Key Storage Implementations

Users can use Secure Engine Manager (SE Manager) or PSA Crypto in the following figure to access the secure key storage on HSE-SVH devices. SE Manager APIs for secure key storage and crypto are usually not considered external APIs. PSA Crypto API abstracts the entropy sources, crypto primitives, and even advanced security features like secure key storage from the calling functions.

Silicon Labs recommends using PSA Crypto API for secure key storage and cryptography whenever possible. It makes the solution more portable and hardware agnostic. In some cases, however, setting up tamper and initializing the secure boot can only be implemented by the SE Manager APIs.

![Secure Engine Manager and PSA Crypto](/efr32-secure-key-storage/0.2/images/sld791-secure-engine-manager-and-psa-crypto.png)

<table>
    <thead>
        <tr>
            <th>Component</th>
            <th>Functionality</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>EMLIB (em_se.c)</p>
            </td>
            <td>
                <p>Abstracts the mailbox interface: how to construct, send and receive low-level HSE mailbox commands.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SE Manager</p>
            </td>
            <td>
                <p>On top of EMLIB, it abstracts the HSE command set: translates function calls into mailbox messages. The SE Manager also provides thread synchronization.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>PSA Accelerator Drivers</p>
            </td>
            <td>
                <p>A translation layer to map the PSA Crypto HSE interface and crypto acceleration calls to SE Manager calls.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>PSA Crypto API</p>
            </td>
            <td>
                <p>Platform independent cryptographic hardware acceleration support by implementing standardized APIs.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>PSA ITS Driver</p>
            </td>
            <td>
                <p>The key management functionality in PSA Crypto needs access to non-volatile memory for persistent storage of plaintext or wrapped keys. NVM3 gets wrapped by this translation layer, mapping the PSA ITS (Internal Trusted Storage) interface to NVM3 calls.</p>
            </td>
        </tr>
    </tbody>
</table>

For the SE's mailbox interface, see section _Secure Engine Subsystem_ in [Series 2 Secure Debug](https://docs.silabs.com/iot-security/latest/series2-secure-debug/03-r-secureelement).

For more information about NVM3, see [https://docs.silabs.com/gecko-platform/latest/driver/api/group-nvm3](https://docs.silabs.com/gecko-platform/latest/driver/api/group-nvm3).

For more information about PSA Crypto, see [Integrating Crypto Functionality Using PSA Crypto Compared to Mbed TLS](https://docs.silabs.com/iot-security/latest/mbedtls-psa-crypto-porting-guide/).

###### SE Manager API

The following table lists the SE Manager APIs related to Secure Key Storage operations. The SE Manager API document can be found at [https://docs.silabs.com/gecko-platform/latest/service/api/group-sl-se-manager](https://docs.silabs.com/gecko-platform/latest/service/api/group-sl-se-manager).

<table>
    <thead>
        <tr>
            <th>SE Manager API</th>
            <th>Usage</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>sl_se_generate_key</p>
            </td>
            <td>
                <p>Generate a new key and store it either in a volatile HSE storage slot or as a wrapped key.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_import_key</p>
            </td>
            <td>
                <p>Import a plaintext key and store it either in a volatile HSE storage slot or as a wrapped key.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_export_key</p>
            </td>
            <td>
                <p>Export a volatile or wrapped key back to plaintext if allowed. It will fail for a key that has been flagged as SL_SE_KEY_FLAG_NON_EXPORTABLE.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_transfer_key</p>
            </td>
            <td>
                <p>Transfer a volatile or wrapped key to another storage option (volatile HSE storage slot or a wrapped key) if allowed.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_delete_key</p>
            </td>
            <td>
                <p>Delete a key from a volatile HSE storage slot.</p>
            </td>
        </tr>
    </tbody>
</table>

###### PSA Crypto API

The following table lists the PSA Crypto APIs related to Secure Key Storage operations. The PSA Crypto API document can be found at [https://docs.silabs.com/mbed-tls/latest/](https://docs.silabs.com/mbed-tls/latest/).

For more information about PSA Crypto APIs on Secure Key Storage, see [Integrating Crypto Functionality Using PSA Crypto Compared to Mbed TLS](https://docs.silabs.com/iot-security/latest/mbedtls-psa-crypto-porting-guide/).

<table>
    <thead>
        <tr>
            <th>PSA Crypto API</th>
            <th>Usage</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>psa_generate_key</p>
            </td>
            <td>
                <p>Generate a new plaintext or wrapped key and store it either in volatile or non-volatile memory.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>psa_import_key</p>
            </td>
            <td>
                <p>Import a plaintext key and save it in plaintext or wrapped form. It can store either in volatile or non-volatile memory.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>psa_export_key</p>
            </td>
            <td>
                <p>Export a key back to plaintext if allowed. The policy on the key must have the usage flag<code>PSA_KEY_USAGE_EXPORT</code>set.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>psa_copy_key</p>
            </td>
            <td>
                <p>Copy key material from one location to another, which may have a different lifetime (e.g., volatile to non-volatile).</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>psa_destroy_key</p>
            </td>
            <td>
                <p>Destroy a key from both volatile memory and, if applicable, non-volatile storage.</p>
            </td>
        </tr>
    </tbody>
</table>

###### SE Manager API Versus PSA Crypto API

The following table compares the SE Manager APIs with PSA Crypto APIs on Secure Key Storage.

<table>
    <thead>
        <tr>
            <th>Item</th>
            <th>SE Manager API</th>
            <th>PSA Crypto API</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Availability</p>
            </td>
            <td>
                <p>Only on HSE devices</p>
            </td>
            <td>
                <p>Platform independent</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>API</p>
            </td>
            <td>
                <p>Silicon Labs proprietary</p>
            </td>
            <td>
                <p>Standardized by ARM®</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Key Storage</p>
            </td>
            <td>
                <p>Volatile (RAM) memory only</p>
            </td>
            <td>
                <p>Volatile (RAM) or non-volatile (flash) memory</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Wrapped Key Cache</p>
            </td>
            <td>
                <p>Can use a volatile HSE storage slot</p>
            </td>
            <td>
                <p>Not yet implemented</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Password Protection</p>
            </td>
            <td>
                <p>Can define in a key descriptor</p>
            </td>
            <td>
                <p>Not yet defined in PSA Crypto</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Custom ECC Curve</p>
            </td>
            <td>
                <p>Can define in a key descriptor</p>
            </td>
            <td>
                <p>Not yet defined in PSA Crypto</p>
            </td>
        </tr>
    </tbody>
</table>

###### PSA Crypto Key Types with TrustZone Secure Key Storage

The following tables describes the storage differences between key storage with and without TrustZone on SVM and SVH devices.

**Table: TrustZone Secure Key Storage (SKS) on SVM Devices**

<table>
    <thead>
        <tr>
            <th>Key Type</th>
            <th>Storage without TrustZone SKS</th>
            <th>Storage with TrustZone SKS</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Volatile Plaintext</p>
            </td>
            <td>
                <p>RAM</p>
            </td>
            <td>
                <p>Secure RAM (2)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Persistent Plaintext</p>
            </td>
            <td>
                <p>NVM</p>
            </td>
            <td>
                <p>Encrypted in NS NVM (2)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Volatile Wrapped</p>
            </td>
            <td>
                <p>Not supported</p>
            </td>
            <td>
                <p>Not supported</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Persistent Wrapped</p>
            </td>
            <td>
                <p>Not supported</p>
            </td>
            <td>
                <p>Not supported</p>
            </td>
        </tr>
    </tbody>
</table>

**Table: TrustZone Secure Key Storage (SKS) on SVH Devices**

<table>
    <thead>
        <tr>
            <th>Key Type</th>
            <th>Storage without TrustZone SKS</th>
            <th>Storage with TrustZone SKS</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Volatile Plaintext</p>
            </td>
            <td>
                <p>Plaintext key in RAM</p>
            </td>
            <td>
                <p>Plaintext key in Secure RAM</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Persistent Plaintext</p>
            </td>
            <td>
                <p>Plaintext key in NVM</p>
            </td>
            <td>
                <p>Encrypted plaintext key in NS NVM</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Volatile Wrapped</p>
            </td>
            <td>
                <p>Wrapped key in RAM (1)</p>
            </td>
            <td>
                <p>Wrapped key in Secure RAM</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Persistent Wrapped</p>
            </td>
            <td>
                <p>Wrapped key in NVM (1)</p>
            </td>
            <td>
                <p>Encrypted wrapped key in NS NVM</p>
            </td>
        </tr>
    </tbody>
</table>

**Notes**:

- The NVM or NS NVM is at the last part of the main flash.
- It is possible to replace the wrapped key solution on the SVH device (1) with TrustZone Secure Key Storage on the SVM device (2), but this is a less secure approach.

##### Examples

Simplicity Studio 5 includes the [SE Manager and PSA Crypto platform examples](https://docs.silabs.com/simplicity-studio-5-users-guide/latest/ss-5-users-guide-getting-started/start-a-project#examples) for Secure Key Storage. Refer to the corresponding `readme` file for details about each SE Manager and PSA Crypto platform example. This file also includes the procedures to create the project and run the example.

**Table: Platform Examples for Secure Key Storage**

<table>
    <thead>
        <tr>
            <th>Category</th>
            <th>SE Manager Platform Example</th>
            <th>PSA Crypto Platform Example</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td rowspan="2">
                <p>Key Handling</p>
            </td>
            <td>
                <p>SE Manager Symmetric Key Handling</p>
            </td>
            <td>
                <p>PSA Crypto Symmetric Key</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SE Manager Asymmetric Key Handling</p>
            </td>
            <td>
                <p>PSA Crypto Asymmetric Key</p>
            </td>
        </tr>
        <tr>
            <td rowspan="4">
                <p>Symmetric Key Usage</p>
            </td>
            <td rowspan="4">
                <p>SE Manager Block Cipher</p>
            </td>
            <td>
                <p>PSA Crypto AEAD</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>PSA Crypto Cipher</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>PSA Crypto KDF</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>PSA Crypto MAC</p>
            </td>
        </tr>
        <tr>
            <td rowspan="2">
                <p>Asymmetric Key Usage</p>
            </td>
            <td>
                <p>SE Manager Digital Signature (ECDSA and EdDSA)</p>
            </td>
            <td>
                <p>PSA Crypto DSA</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SE Manager Key Agreement (ECDH)</p>
            </td>
            <td>
                <p>PSA Crypto ECDH</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>X.509 Certificate</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>PSA Crypto X.509</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>TrustZone Secure Key Storage</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>tz_psa_crypto_ecdh_ws</p>
            </td>
        </tr>
    </tbody>
</table>

#### Programming Series 2 Devices Using DCI and SWD

##### Programming Series 2 Devices using the Debug Challenge Interface (DCI) and Serial Wire Debug (SWD)

> **Note: This section replaces _AN1303: Programming Series 2 Devices using the Debug Challenge Interface (DCI) and Serial Wire Debug (SWD)_. Further updates to this Quick-Start Guide will be provided here**.

This application note describes how to provision and configure Series 2 devices through the dedicated Debug Challenge Interface (DCI). The process to use the Serial Wire Debug (SWD) interface for programming the internal flash memory of Series 2 devices is also included.

For details on how to use the SWD interface to program devices, see [AN0062: Programming Internal Flash over the Serial Wire Debug Interface](https://www.silabs.com/documents/public/application-notes/an0062.pdf).

###### Key Points

- DCI overview
- SWD interface overview
- DCI programming examples to provision and configure Series 2 devices
- SWD programming examples to program Series 2 devices
- How to add a new Series 2 device to the programmer

##### Series 2 Device Security Features

Protecting IoT devices against security threats is central to a quality product. Silicon Labs offers several security options to help developers build secure devices, secure application software, and secure paths of communication to manage those devices. Silicon Labs’ security offerings were significantly enhanced by the introduction of the Series 2 products that included a Secure Engine. The Secure Engine is a tamper-resistant component used to securely store sensitive data and keys and to execute cryptographic functions and secure services.

On Series 1 devices, the security features are implemented by the TRNG (if available) and CRYPTO peripherals.

On Series 2 devices, the security features are implemented by the Secure Engine and CRYPTOACC (if available). The Secure Engine may be hardware-based, or virtual (software-based). Throughout this document, the following abbreviations are used:

- HSE - Hardware Secure Engine
- VSE - Virtual Secure Engine
- SE - Secure Engine (either HSE or VSE)

Additional security features are provided by Secure Vault. Three levels of Secure Vault feature support are available, depending on the part and SE implementation, as reflected in the following table:

|**Level (1)**|**SE Support**|**Part (2)**|
|---|---|---|
|Secure Vault High (SVH)|HSE only (HSE-SVH)|Refer to _IoT Endpoint Security Fundamentals_ for details on supporting devices.|
|Secure Vault Mid (SVM)|HSE (HSE-SVM)|"|
|"|VSE (VSE-SVM)|"|
|Secure Vault Base (SVB)|N/A|"|

Notes:

1. The features of different Secure Vault levels can be found in [https://www.silabs.com/security](https://www.silabs.com/security).
2. [IoT Endpoint Security Fundamentals](https://docs.silabs.com/iot-security/latest/iot-endpoint-security-fundamentals/).

Secure Vault Mid consists of two core security functions:

- **Secure Boot**: Process where the initial boot phase is executed from an immutable memory (such as ROM) and where code is authenticated before being authorized for execution.
- **Secure Debug access control**: The ability to lock access to the debug ports for operational security, and to securely unlock them when access is required by an authorized entity.

Secure Vault High offers additional security options:

- **Secure Key Storage**: Protects cryptographic keys by “wrapping” or encrypting the keys using a root key known only to the HSE-SVH.
- **Anti-Tamper protection**: A configurable module to protect the device against tamper attacks.
- **Device authentication**: Functionality that uses a secure device identity certificate along with digital signatures to verify the source or target of device communications.
- Advanced cryptographic acceleration (i.e., ChaCha20/Poly1305)

A Secure Engine Manager and other tools allow users to configure and control their devices both in-house during testing and manufacturing, and after the device is in the field.

###### User Assistance

In support of these products Silicon Labs offers whitepapers, webinars, and documentation. The following table summarizes the key security documents:

<table>
    <thead>
        <tr>
            <th><strong>Document</strong></th>
            <th><strong>Summary</strong></th>
            <th><strong>Applicability</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/series2-secure-debug/">Series 2 and Series 3 Secure Debug</a></p>
            </td>
            <td>
                <p>How to lock and unlock Series 2 and Series 3 debug access, including background information about the SE</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/">Series 2 and Series 3 Secure Boot with RTSL</a></p>
            </td>
            <td>
                <p>Describes the secure boot process on Series 2 and Series 3 devices using SE</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/efr32-secure-vault-tamper/">Anti-Tamper Protection Configuration and Use</a></p>
            </td>
            <td>
                <p>How to program, provision, and configure the anti-tamper module</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/authenticating-devices-using-device-certificates/">Authenticating Silicon Labs Devices using Device Certificates</a></p>
            </td>
            <td>
                <p>How to authenticate a device using secure device certificates and signatures, at any time during the life of the product</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/efr32-secure-key-storage/">Secure Key Storage</a></p>
            </td>
            <td>
                <p>How to securely "wrap" keys so they can be stored in non-volatile storage.</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/">Production Programming of Series 2 and Series 3 Devices</a></p>
            </td>
            <td>
                <p>How to program, provision, and configure security information using SE during device production</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/mbedtls-psa-crypto-porting-guide/">Integrating Crypto Functionality Using PSA Crypto Compared to Mbed TLS</a></p>
            </td>
            <td>
                <p>How to port firmware from the Mbed TLS cryptographic library to PSA Crypto</p>
            </td>
            <td>
                <p>Secure Vault Mid and High</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/series2-trustzone/">Series 2 TrustZone</a></p>
            </td>
            <td>
                <p>How to TrustZone to separate trusted and non-trusted firmware.</p>
            </td>
            <td>
                <p>Secure Vault Mid and High</p>
            </td>
        </tr>
    </tbody>
</table>

###### Key Reference

Public/Private keypairs along with other keys are used throughout Silicon Labs security implementations. Because terminology can sometimes be confusing, the following table lists the key names, their applicability, and the documentation where they are used.

|**Key Name**|**Customer Programmed**|**Purpose**|
|---|---|---|
|Public Sign key (Sign Key Public)|Yes|Secure Boot binary authentication and/or OTA upgrade payload authentication|
|Public Command key (Command Key Public)|Yes|Secure Debug Unlock or Disable Tamper command authentication|
|OTA Decryption key (GBL Decryption key) aka AES-128 Key|Yes|Decrypting GBL payloads used for firmware upgrades|
|Attestation key aka Private Device Key|No|Device authentication for secure identity|

###### SE Firmware

Silicon Labs strongly recommends installing the latest SE firmware on Series 2 devices to support the required security features. Refer to [Production Programming of Series 2 and Series 3 Devices](https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/) for the procedure to upgrade the SE firmware and [IoT Endpoint Security Fundamentals](https://docs.silabs.com/iot-security/latest/iot-endpoint-security-fundamentals/) for the latest SE Firmware shipped with Series 2 devices and modules.

##### Introduction

The latest SE firmware image (`.seu` and `.hex`) and release notes can be found in the Windows folder below. For GSDK v3.2 and lower:

`C:\SiliconLabs\SimplicityStudio\v5\developer\sdks\gecko_sdk_suite\<GSDK VERSION>\util\se_release\public`

For GSDK v4.0 and higher:

`C:\Users\<PC USER NAME>\SimplicityStudio\SDKs\gecko_sdk\util\se_release\public`

Silicon Labs provides [Custom Part Manufacturing Service (CPMS)](https://www.silabs.com/developers/custom-part-manufacturing-service) to customize the users' security features and settings.

The Debug Challenge Interface (DCI) is used to configure the security features of the Series 2 devices, whereas the Serial Wire Debug (SWD) interface is used to program the flash memory of Series 2 devices. A general overview of the DCI and SWD programming steps is described in the following sections.

##### Debug Challenge Interface (DCI)

Interaction with the SE is performed over a command interface that is available through a dedicated Debug Challenge Interface (DCI). The DCI is intended to be used for various [SE commands](05-se-command-list#se-command-list). The DCI is open while the SE is running.

###### DCI Connection

The DCI is made available through a connection on the Serial Wire Debug (SWD) port. The steps involved in connecting the DCI through the SWD port are as follows.

1. Send the JTAG-to-SWD switching sequence.
2. Read the IDCODE register (SWD DP register 0) to retrieve an identification value. On the Series 2 devices with a Cortex-M33 core, this value is `0x6BA02477`.
3. Use the ABORT register (SWD DP register 0 = `0x0000001E`) to clear the error and sticky flag conditions.
4. Use the STAT register (SWD DP register 1 = `0x50000000`) to generate a system and debug domain power-up request.
5. Use the SELECT register (SWD DP register 2 = `0x01000000`) to set the SWD interface in the chip to communicate with the DCI.
6. Use the CSW register (SWD AP register 0 = `0x22000002`) to set the transfer size to 32-bit.

See [AN0062: Programming Internal Flash over the Serial Wire Debug Interface](https://www.silabs.com/documents/public/application-notes/an0062.pdf) for more information about the SWD port registers.

![CI Connection Flowchart](/efr32-dci-swd-programming/0.1/images/sld815-dci-connection-flowchart.png)

###### DCI Registers

The following table lists three registers to interact with the DCI through the SWD interface.

**DCI Register**

|**Register**|**Description**|**Fields**|**Address**|**Remarks**|
|---|---|---|---|---|
|DCI_WDATA|Write data to the DCI|WDATA [31:0]|0x1000|Command|
|DCI_RDATA|Read data from the DCI|RDATA [31:0]|0x1004|Response|
|DCI_STATUS|Status of DCI accesses|Bit 0 = WPENDING|0x1008|Write Request to the DCI is pending. Additional writes to DCI_WDATA are discarded when this bit is asserted.|
|"|"|Bit 8 = RDATAVALID|"|Response from the DCI is valid when this bit is asserted. Cleared on a read of DCI_RDATA.|

###### DCI Calls

###### Command (heading level 7)

All DCI calls start by writing a 32-bit word containing the length of the DCI data followed by the 32-bit Command ID into the DCI. The length includes the length word itself, so the minimum value is 8 (4 bytes of length and 4 byes of Command ID). Then a variable length command payload (if applicable) is transferred into the DCI.

**DCI Command**

|**Command Word**|**Description**|
|---|---|
|Word 0|Length of packet in bytes (including word 0)|
|Word 1|Command ID|
|Words 2 – N|Command payload if applicable|

###### Response (heading level 7)

On completion, a 32-bit word consisting of the response length [15:0] in bytes and response code [31:16] is read from the DCI followed by the response payload, if present.

**DCI Response**

|**Response Word**|**Description**|
|---|---|
|Word 0|Total response length (including word 0) and response code: [15:0] – Total length in bytes; [31:16] – Response code|
|Words 1 – N|Response payload if applicable|

All executed commands return a response code that classifies the result of the operation. The basic meaning of these response codes is given in the following table.

**DCI Response Codes**

|**Response Code**|**Status**|**Description**|
|---|---|---|
|0|SE_RESPONSE_OK|Command executed successfully or signature was successfully validated.|
|1|SE_RESPONSE_INVALID_COMMAND|Command was not recognized as a valid command, or is not allowed in the current context.|
|2|SE_RESPONSE_AUTHORIZATION_ERROR|User did not provide the required credentials to be allowed to execute the command.|
|3|SE_RESPONSE_INVALID_SIGNATURE|Signature validation command failed to verify the given signature as being correct.|
|4|SE_RESPONSE_BUS_ERROR|A command started in non-secure mode is trying to access secure memory.|
|5|SE_RESPONSE_INTERNAL_ERROR|Internal SE error.|
|6|SE_RESPONSE_CRYPTO_ERROR|Error in crypto operation.|
|7|SE_RESPONSE_INVALID_PARAMETER|One of the passed parameters is deemed invalid (for example, out of bounds), or the number of parameters is incorrect.|
|8|SE_RESPONSE_INTEGRITY_ERROR|Operation cannot be completed due to the SE having an invalid internal state.|
|9|SE_RESPONSE_SECUREBOOT_ERROR|The host application failed secure boot check.|
|10|SE_RESPONSE_SELFTEST_ERROR|Failure during self-test.|
|11|SE_RESPONSE_NOT_INITIALIZED|Feature or item is not present or not initialized.|

###### DCI Operation

###### DCI Write (heading level 7)

The user can write the DCI_WDATA register to pass command to the SE. The steps involved in writing a command to DCI are as follows.

1. Connect to DCI according to [DCI Connection Flowchart](#dci-connection).
2. For each word in the command, follow these steps in sequence:  
   1. Set the DCI to read from [DCI_STATUS](#dci-calls) by setting SWD AP register 1 to 0x1008.  
   2. Read DCI_STATUS by reading from SWD AP register 3.  
   3. If [WPENDING](#dci-calls) is high, go back to step (a). If WPENDING is low, continue. If [RDATAVALID](#dci-registers) is high, then the SE has started issuing a reply and the current command needs to be aborted.  
   4. Set the DCI to write to [DCI_WDATA](#dci-registers) by setting SWD AP register 1 to 0x1000.  
   5. Write the command word to SWD AP register 3.  
   ![DCI Write Flowchart](/efr32-dci-swd-programming/0.1/images/sld815-dci-write-flowchart.png)

###### DCI Read (heading level 7)

The user can read the DCI_RDATA register to retrieve responses from the SE to a previously written command. If a user has sent a command according to [DCI Write Flowchart on page 8](#dci-write), the steps to read the response from the DCI are as follows.

1. Set the DCI to read from [DCI_STATUS](#dci-registers) by setting SWD AP register 1 to 0x1008.
2. Read DCI_STATUS by reading from SWD AP register 3.
3. If [RDATAVALID](#dci-registers) is high, a response word is available to be read from DCI_RDATA. If RDATAVALID is low, go back to step (2) because the SE has not begun to reply.
4. Set the DCI to read from [DCI_RDATA](#dci-registers) by setting SWD AP register 1 to 0x1004.
5. Read the first response word from the command to SWD AP register 3.
6. Total length of the response (including the first word) is in the lower 16 bits of that word. If this is larger than 4, for each response word:  
   a. Set the DCI to read from DCI_STATUS by setting SWD AP register 1 to 0x1008.  
   b. Read DCI_STATUS by reading from SWD AP register 3.  
   c. If RDATAVALID is high, a response word is available to be read from DCI_RDATA. If RDATAVALID is low, go back to step (a) because the process is polling faster than the SE can send data.  
   d. Set the DCI to read from DCI_RDATA by setting SWD AP register 1 to 0x1004.  
   e. Read the sequential response word to SWD AP register 3.

![DCI Read Flowchart](/efr32-dci-swd-programming/0.1/images/sld815-dci-read-flowchart.png)

##### Serial Wire Debug (SWD) Interface

The flash memory on Series 2 devices is divided into two blocks: the main block and the information block. Program code is normally written to the main block. The information block is available for special user data. Throughout this document, the flash or main flash is referred to as the main block and user data is referred to as the information block.

Program the Series 2 flash memory by writing directly to the device's Memory System Controller (MSC) registers over the Serial Wire Debug (SWD) interface. This method is simple and easy to upgrade to support new Series 2 devices.

The program must set the MSC bit in the CMU CLKEN1 register (if available) to enable the clock source for the Memory System Controller (MSC).

Series 2 devices with a Cortex-M33 core return the value `0x84770001` when reading the AP Identification Register (IDR) through SWD.

See [AN0062: Programming Internal Flash over the Serial Wire Debug Interface](https://www.silabs.com/documents/public/application-notes/an0062.pdf) for more information on how to access the SWD interface of the target device and how to use this interface to program devices.

###### Flash Erase

There are two ways to erase the flash of the target device through SWD interface:

###### Page Erase (heading level 7)

A page erase can be initiated from software using the ERASEPAGE bit in the MSC WRITECMD register. The page erase operations require that the address of main flash or user data is written into the MSC ADDRB register. To reduce the time needed for the flash erase process, the program should avoid erasing the target main flash page by page, especially for devices with larger flash memories.

###### Mass Erase (heading level 7)

A mass erase can be initiated from software using the ERASEMAIN0 bit in the MSC WRITECMD register. This erases the entire flash (excluding the user data).

![Flash-Erase-Flowchart](/efr32-dci-swd-programming/0.1/images/sld815-flash-erase-flowchart.png)

The flash base address of the Series 2 device is either `0x00000000` or `0x08000000.` The timing for page erase or mass erase on each Series 2 device might vary. Refer to the device-specific datasheets for details.

###### Flash Write

The write operation requires that the address be written into the MSC ADDRB register. After each 32-bit word is written, the internal address register is incremented automatically by 4. When a word is written to the MSC WDATA register, the WDATAREADY bit of the MSC STATUS register is cleared. When this status bit is set, software can write the next word.

The flash program time (32-bit word) of each Series 2 device might vary. Refer to the device-specific datasheets for details.

To reduce the time for the flash write process, the program can omit polling the WDATAREADY bit in the MSC STATUS register after writing each 32-bit word, because the register read process is time consuming. The alternative is to add a fixed microseconds delay between each write to make sure the maximum write time can be met.

![Flash-Write-Flowchart](/efr32-dci-swd-programming/0.1/images/sld815-flash-write-flowchart.png)

###### Flash Verify (heading level 7)

The program verifies the target flash contents with a buffer to make sure that no errors occurred during the programming process. The autoincrement of the Transfer Address Register (TAR) is for burst reads within the TAR wraparound boundary. The TAR must be initialized at every TAR wraparound boundary to set up the next flash read address.

![Flash Verify Flowchart](/efr32-dci-swd-programming/0.1/images/sld815-flash-verify-flowchart.png)

##### SE Command List

This application note does not include a complete list of commands for DCI. The following sections contain information about each command's operation and arguments for DCI production programming. The [command](03-debug-challenge-interface-dci#command) and [response](03-debug-challenge-interface-dci#response) payload may be device-specific (e.g., Initialize OTP and Get Status).

For more information about secure boot, see [Series 2 Secure Boot with RTSL](https://docs.silabs.com/iot-security/latest/series2-secure-boot-with-rtsl/). For more information about debug lock and secure debug, see [Series 2 Secure Debug](https://docs.silabs.com/iot-security/latest/series2-secure-debug/).

###### SE Image Check

This command can be used to check the SE image before starting the upgrade process, in order to be able to abort early if the image is invalid or inapplicable.

> **Note**: This command is only available on SE firmware version ≥ v1.2.2 (xG21 or xG22 devices).

**SE Image Check Command**

|**ID [31:16]**|**Option 1 [15:8]**|**Option 2 [7:0]**|**Command payload**|**Response payload**|
|---|---|---|---|---|
|0x4302|0x00|0x00|Address in internal flash where the SE upgrade image is stored - 4 bytes:|None|

###### SE Image Apply

This command can be used to perform an upgrade of the SE firmware where the existing firmware will be overwritten with the one stored in the internal flash if the upgrade image is valid and applicable. The system is restarted and no [response code](03-debug-challenge-interface-dci) is returned if the SE image is successfully upgraded.

> **Note**: This command is only available on SE firmware version ≥ v1.2.2 (xG21 or xG22 devices).

**SE Image Apply Command**

|**ID [31:16]**|**Option 1 [15:8]**|**Option 2 [7:0]**|**Command payload**|**Response payload**|
|---|---|---|---|---|
|0x4303|0x00|0x00|Address in internal flash where the SE upgrade image is stored - 4 bytes:|None|

###### Apply Lock

This command enables the debug lock for the part.

**Apply Lock Command**

|**ID [31:16]**|**Option 1 [15:8]**|**Option 2 [7:0]**|**Command payload**|**Response payload**|
|---|---|---|---|---|
|0x430C|0x00|0x00|None|None|

###### Enable Secure Debug

This command enables the secure debug functionality. This command must be used before the debug port is locked and will fail if executed after locking debug access.

**Enable Secure Debug Command**

|**ID [31:16]**|**Option 1 [15:8]**|**Option 2 [7:0]**|**Command payload**|**Response payload**|
|---|---|---|---|---|
|0x430D|0x00|0x00|None|None|

###### Disable Secure Debug

This command disables the secure debug functionality and is available even after the debug port has been locked.

**Disable Secure Debug Command**

|**ID [31:16]**|**Option 1 [15:8]**|**Option 2 [7:0]**|**Command payload**|**Response payload**|
|---|---|---|---|---|
|0x430E|0x00|0x00|None|None|

###### Erase Device

This command performs a device mass erase and resets the debug configuration to its initial unlocked state. It is only available if the [Disable Device Erase](#disable-device-erase) command has not been executed.

This command clears and verifies the main flash and RAM of the system, excluding the user data and one-time programmable (OTP) commissioning information in the SE.

**Erase Device Command**

|**ID [31:16]**|**Option 1 [15:8]**|**Option 2 [7:0]**|**Command payload**|**Response payload**|
|---|---|---|---|---|
|0x430F|0x00|0x00|None|None|

###### Disable Device Erase

This command disables the [Erase Device](#erase-device) command. This command does not lock the debug interface to the part, but it is a permanent action for the part. This is a one-time command.

**Disable Device Erase Command**

|**ID [31:16]**|**Option 1 [15:8]**|**Option 2 [7:0]**|**Command payload**|**Response payload**|
|---|---|---|---|---|
|0x4310|0x00|0x00|None|None|

###### Read Serial Number

This command is used to read the Silicon Labs-provisioned serial number of the device.

**Read Serial Number Command**

|**ID [31:16]**|**Option 1 [15:8]**|**Option 2 [7:0]**|**Command payload**|**Response payload**|
|---|---|---|---|---|
|0xFE00|0x00|0x00|None|16 bytes serial number|

###### Get Status

This command is used to read out the status information from the SE.

**Get Status Command**

|**ID [31:16]**|**Option 1 [15:8]**|**Option 2 [7:0]**|**Command payload**|**Response payload**|
|---|---|---|---|---|
|0xFE01|0x00|0x00|None|Varies by SE type, see following.|

**VSE-SVM - total 20 bytes:**

- Boot status - 4 bytes
- VSE firmware version - 4 bytes
- MCU firmware version - 4 bytes
- Debug lock status - 4 bytes
- Secure boot configuration - 4 bytes

**HSE-SVM or HSE-SVH - total 36 bytes**:

- 16 bytes for HSE-SVM: Reserved
- 16 bytes for HSE-SVH:  
  - Tamper status - 4 bytes  
  - Tamper time stamp - 4 bytes  
  - Tamper raw status - 4 bytes  
  - Time stamp - 4 bytes
- Boot status - 4 bytes
- HSE firmware version - 4 bytes
- MCU firmware version - 4 bytes
- Debug lock status - 4 bytes
- Secure boot configuration - 4 bytes

> **Note**:
> 
> - Tamper status is a set of 32 flags that indicate which tamper events have occurred.
> - Tamper time stamp is a HSE timer counter value for the last tamper event.
> - Tamper raw status is encoded the same as tamper status but is an immediate value of the tamper event sources.
> - The time stamp is a HSE timer counter value.
> - Boot status:
> - Bit [7:0] - 0x20 if boot is successful.
> - Bit [:] (for xG21 or xG22 devices only) - The [response code](03-debug-challenge-interface-dci) if SE firmware version ≥ v1.2.0
> - VSE or HSE firmware version:
> - Bit [7:0] - Patch version
> - Bit [15:8] - Minor version
> - Bit [23:16] - Major version
> - Bit [31:24] - Series 2 device family (0 for xG21, 1 for xG22, 2 for xG23, etc.)
> - MCU firmware version: Bit [31:0] - The MCU firmware version is not available if all set to 1 (0xFFFFFFFF)
> - Debug lock status:
> - Bit [0] - Debug lock (configuration status) is enabled if set.
> - Bit [1] - Device erase is enabled if set.
> - Bit [2] - Secure debug is enabled if set.
> - Bit [5] - Debug lock (hardware status) is enabled if set.
> - Secure boot configuration:
> - Bit [31:0] - SE OTP is not yet configured if all set to 1 (0xFFFFFFFF)
> - Bit [31:0] - SE OTP has been configured if Bit [31:1] are 0, secure boot is enabled if Bit [0] is set

###### Read User Configuration

This command is used to read non-reconfigurable user settings on the SE OTP for secure boot and tamper response.

> **Note**: This command is only available on SE firmware versions ≥ v1.2.2 (xG21 or xG22 devices).

**Read User Configuration Command**

|**ID [31:16]**|**Option 1 [15:8]**|**Option 2 [7:0]**|**Command payload**|**Response payload**|
|---|---|---|---|---|
|0xFE04|0x00|0x00|None|Varies by SE type, see following.|

**VSE-SVM - total 4 bytes**:

- [MCU flags](#mcu-flags) - 4 bytes

**HSE-SVM - total 24 bytes**:

- [MCU flags](#mcu-flags) - 4 bytes
- Reserved - 20 bytes

**HSE-SVH - total 24 bytes**:

- [MCU flags](#mcu-flags) - 4 bytes
- [Tamper response levels]() (2 signals per byte) - 16 bytes
- [Filter reset period]() - 1 byte
- [Filter trigger threshold]() - 1 byte
- [Tamper flags]() - 1 byte
- [Tamper reset threshold]() - 1 byte

###### Initialize OTP

This command is used during factory initialization, to upload device-specific settings to the SE OTP. This is a one-time command.

**Initialize OTP Command**

|**ID [31:16]**|**Option 1 [15:8]**|**Option 2 [7:0]**|**Command payload**|**Response payload**|
|---|---|---|---|---|
|0xFF00|0x00|0x01|Varies by SE type, see following.|None|

**VSE-SVM - total 12 bytes**:

1. Parity (equal to item 3) - 4 bytes
2. Length of the following content - 4 bytes
3. [MCU flags](#mcu-flags) - 4 bytes

**HSE-SVM - total 32 bytes**:

1. Parity (the XOR of 32-bit words from item 3 and 4) - 4 bytes
2. Length of the following content - 4 bytes
3. [MCU flags](#mcu-flags) - 4 bytes
4. Reserved - 20 bytes

**HSE-SVH - total 32 bytes**:

1. Parity (the XOR of 32-bit words from item 3 to 8) - 4 bytes
2. Length of the following content - 4 bytes
3. [MCU flags](#mcu-flags) - 4 bytes
4. [Tamper response levels]() (2 signals per byte) - 16 bytes
5. [Filter reset period]() - 1 byte
6. [Filter trigger threshold]() - 1 byte
7. [Tamper flags]() - 1 byte
8. [Tamper reset threshold]() - 1 byte

###### MCU Flags (heading level 7)

The parameters of the MCU flags are described in the following tables.

**Parameters of MCU Flags**

|**Fields**|**Description**|
|---|---|
|Bit [15:0]|Reserved|
|Bit [16]|SECURE_BOOT_ENABLE|
|Bit [17]|SECURE_BOOT_VERIFY_CERTIFICATE|
|Bit [18]|SECURE_BOOT_ANTI_ROLLBACK|
|Bit [19]|SECURE_BOOT_PAGE_LOCK_NARROW|
|Bit [20]|SECURE_BOOT_PAGE_LOCK_FULL|
|Bit [31:21]|Reserved|

**MCU Flags for Series 2 Devices**

|**Name**|**Description**|
|---|---|
|SECURE_BOOT_ENABLE|If set, verifies the image on the Cortex-M33 before releasing the Cortex-M33 from reset.|
|SECURE_BOOT_VERIFY_CERTIFICATE|If set, requires certificate-based signing of the host application.|
|SECURE_BOOT_ANTI_ROLLBACK|If set, prevents secure upgrading to a host image with a lower version than the image that is currently stored in flash.|
|SECURE_BOOT_PAGE_LOCK_NARROW|If set, locks flash pages that have been validated by the secure boot process to prevent re-flashing by means other than through the SE.|
|"|Write/erase locks pages from 0 through the page where the secure boot signature of the application is located, not including the last page if the signature is not on a page boundary.|
|SECURE_BOOT_PAGE_LOCK_FULL|If set, locks flash pages that have been validated by the secure boot process to prevent re-flashing by means other than through the SE.|
|"|Write/erase locks pages from 0 through the page where the secure boot signature of the application is located, including the last page if the signature is not on a page boundary.|

###### Anti-Tamper Configuration (heading level 7)

The 16 bytes of tamper response levels on HSE-SVH devices are described in the following tables.

**Tamper Source Response Level on HSE-SVH (xG21B) Devices**

![Tamper Source Response Level on HSE-SVH (xG21B) Devices](/efr32-dci-swd-programming/0.1/images/sld815-tamper-source-response-level-on-hse-svh-xg21b-devices.png)

**Tamper Source Response Level on Other HSE-SVH Devices**

![Tamper Source Response Level on Other HSE-SVH Devices](/efr32-dci-swd-programming/0.1/images/sld815-tamper-source-response-level-on-other-hse-svh-devices.png)

###### Initialize Public Key

This command is used to initialize the user public key(s) to the SE OTP. This is a one-time command.

**Initialize Public Key Command**

|**ID [31:16]**|**Option 1 [15:8]**|**Option 2 [7:0]**|**Command payload**|**Response payload**|
|---|---|---|---|---|
|0xFF07|Key type, see following|0x01|See following|None|

**Key type:**

- 0x01 - Public Sign Key
- 0x02 - Public Command Key

**Command payload - total 68 bytes**:

1. Parity (the XOR of 32-bit words from item 2) - 4 bytes
2. Public key in option 1 - 64 bytes

###### Read Public Key

This command can be used to read out one of the public keys that are permanently stored in SE OTP.

**Read Public Key Command**

|**ID [31:16]**|**Option 1 [15:8]**|**Option 2 [7:0]**|**Command payload**|**Response payload**|
|---|---|---|---|---|
|0xFF08|Key type, see following|0x01|None|64 bytes: public key in option 1|

**Key type:**

- 0x01 - Public Sign Key
- 0x02 - Public Command Key

###### Initialize AES Key

This command is used to initialize a 128-bit symmetric key to the SE OTP. This is a one-time command.

> **Note**: This command is only available on HSE devices.

**Initialize AES Key Command**

|**ID [31:16]**|**Option 1 [15:8]**|**Option 2 [7:0]**|**Command payload**|**Response payload**|
|---|---|---|---|---|
|0xFF0B|Key type, see following|0x01|See following|None|

**Key type:**

- 0x05 - AES-128 key

**Command payload - total 20 bytes**:

1. Parity (the XOR of 32-bit words from item 2) - 4 bytes
2. Symmetric key in option 1 - 16 bytes

###### Set Debug Restrictions

This command is used to set the restrictions for the debug port.

**Set Debug Restrictions Command**

|**ID [31:16]**|**Option 1 [15:8]**|**Option 2 [7:0]**|**Command payload**|**Response payload**|
|---|---|---|---|---|
|0x4312|0x00|0x00|See following|None|

Command Payload – total 4 bytes. Debug restriction bit mask is defined in the table below.

**Debug Port Restriction Bits**

<table>
    <thead>
        <tr>
            <th><strong>Bit</strong></th>
            <th><strong>Name</strong></th>
            <th><strong>Description</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>0</p>
            </td>
            <td>
                <p>DBGLOCK</p>
            </td>
            <td>
                <p>Non-secure, invasive debug lock.<br>
                    If this bit is set, it is not possible to debug the non-secure state in a way that is intrusive to program execution.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>1</p>
            </td>
            <td>
                <p>NIDLOCK</p>
            </td>
            <td>
                <p>Non-secure, non-invasive debug lock.<br>
                    If this bit is set, it is not possible to observe the non-secure state of the M33 using trace.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>2</p>
            </td>
            <td>
                <p>SPIDLOCK</p>
            </td>
            <td>
                <p>Secure, invasive debug lock.<br>
                    If this bit is set, it is not possible to debug the secure state in a way that is intrusive to program execution.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>3</p>
            </td>
            <td>
                <p>SPNIDLOCK</p>
            </td>
            <td>
                <p>Secure, non-invasive debug lock.<br>
                    If this bit is set, it is not possible to observe the secure state of the M33 using trace.<br>
                    (If SPIDLOCK is open, SPNIDLOCK will also remain open.)</p>
            </td>
        </tr>
    </tbody>
</table>

###### Read Lock Status

This command is used to read the lock status of the debug port.

**Read Lock Status Command**

|**ID [31:16]**|**Option 1 [15:8]**|**Option 2 [7:0]**|**Command payload**|**Response payload**|
|---|---|---|---|---|
|0x4311|0x00|0x00|See following|None|

Debug port lock status – total 4 bytes:

- Bit [0] - Debug lock (configuration status) is enabled if set
- Bit [1] - Device erase enabled is enabled if set
- Bit [2] – Secure debug lock enabled is enabled if set
- Bit [3:4] – Reserved
- Bit [5] – Debug lock hardware status is enabled if set
- Bit [6] – Invasive debug lock is enabled if set
- Bit [7] – Non-invasive debug lock is enabled if set
- Bit [8] - Secure invasive debug lock is enabled if set
- Bit [9] - Secure non-invasive debug lock is enabled if set

##### Series 2 DCI and SWD Programming Examples

The programming examples in this application note use the [Series 2 DCI and SWD Programming](#software-overview) platform example of GSDK v4.0.1. The hardware and software implementation may be different on other versions of the GSDK.

For more information about production programming steps, see section "_Overview_" in [Production Programming of Series 2 and Series 3 Devices](https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/).

###### Hardware Overview

The Wireless Starter Kit (WSTK) with the [BRD4182A Radio Board](https://www.silabs.com/documents/public/user-guides/ug430-brd4182a-user-guide.pdf) (EFR32MG22C224F512IM40) is used as the hardware platform of the DCI and SWD programmer.

The programmer uses GPIO to emulate the DCI and SWD to program the target device. The UART interface handles the user interface. The VCOM_TX and VCOM_RX are routed to the WSTK virtual COM port by setting the VCOM_ENABLE line high.

![Block Diagram of DCI and SWD Programmer](/efr32-dci-swd-programming/0.1/images/sld815-block-diagram-of-dci-and-swd-programmer.png)

**Resources of BRD4182A Used by Programmer**

|**GPIO (SoC Peripheral)**|**WSTK Peripheral**|**Function**|**EXP Header Connection**|
|---|---|---|---|
|PA01 (DBG_SWCLK)|DBG_TCK_SWCLK|Programmer debug SWCLK|—|
|PA02 (DBG_SWDIO)|DBG_TCK_SWDIO|Programmer debug SWDIO|—|
|PA05 (US1_TX)|VCOM_TX|WSTK Virtual COM port TX|Pin 12|
|PA06 (US1_RX)|VCOM_RX|WSTK Virtual COM port RX|Pin 14|
|PB04|VCOM_ENABLE|WSTK Virtual COM port enable|—|
|PC03|—|Target device RESETn|Pin 10|
|PD02|UIF_LED0|Target device SWCLK (shared with WSTK LED0)|Pin 11|
|PD03|UIF_LED1|Target device SWDIO (shared with WSTK LED1)|Pin 13|

The following figure shows the interconnection between programmer and target device. The programmer is the WSTK with the BRD4182A Radio Board and the target device is the WSTK with one of the Series 2 radio boards below.

- EFR32MG21A — BRD4181A (J101 P16 for SWCLK)
- EFR32MG21B — BRD4181C (J101 P16 for SWCLK)
- EFR32MG22 — BRD4182A (J101 P16 for SWCLK)
- EFR32FG23A — BRD4263B (J101 P20 for SWCLK)
- EFR32FG23B — BRD4263C (J101 P20 for SWCLK)

![Programmer Connection Diagram](/efr32-dci-swd-programming/0.1/images/sld815-programmer-connection-diagra.png)

The programmer powers the target device on the Series 2 radio board through VMCU. Therefore power switch (SW700) of the target device WSTK should be in the BAT position.

**Programmer Connection Diagram**

The programmer can work on other Series 2 radio boards or kits. However, the following limitations may apply.

- Some firmware images (`xg2*_app_image[] and xg2*_signed_imag[]`) in [Hard-coded Firmware Images](#firmware-images) are hardware-dependent (GPIOs for LEDs, push buttons, and UART). Therefore, they may not function properly on other radio boards or kits.
- The SWCLK and SWDIO of the target radio boards or kits may be on different pins or connectors. Therefore, the connection diagram above may not apply (refer to the board-specific schematics for details).

###### Software Overview

Users must select the BRD4182A radio board to access the `Series 2 DCI and SWD Programming` platform example in Simplicity Studio 5. Click the `View Project Documentation` link to open the `readme` file. This file includes the procedures to create the project and run the example.

![View Project Documentation](/efr32-dci-swd-programming/0.1/images/sld815-image19.jpg)

###### Bit-Bang (heading level 7)

The main overhead on writing directly to MSC registers is to emulate the [SWCLK and SWDIO](#hardware-overview) signals by bit-banging GPIO pins. The programmer must use the GPIOs in the same port group (0-7 or 8-15) for SWCLK and SWDIO to speed up this emulation process. The software writes to the entire port at once when bit-banging the SWCLK and SWDIO signals. Therefore, the programmer cannot use the spare pins of this port for other purposes.

###### Security Keys (heading level 7)

The following table describes the security keys (hard-coded in `app_dci_task.c`) used in the programming examples. Users can modify these keys to adapt to the application requirements. The files (`.prv` and `.pem`) of security keys can be found in the Windows folder `C:\SiliconLabs\SimplicityStudio\v5\developer\adapter_packs\secmgr\scripts\offline.`

**Hard-Coded Security Keys**

|**Array**|**Usage**|**Source**|
|---|---|---|
|aes_key[]|Decrypt GBL payloads|A 16 bytes AES-128 key in encrypt-unsafe-key.prv (binary file).|
|public_sign_key[]|Secure boot|A 64 bytes Public Sign Key, the corresponding Private Sign Key in root-sign-unsafe-privkey.pem.|
|public_command_key[]|Secure debug unlock and Disable tamper|A 64 bytes Public Command Key, the corresponding Private Command Key in cmd-unsafe-privkey.pem.|

The public key can be derived from the private key by using [OpenSSL](https://slproweb.com/products/Win32OpenSSL.html).

```sh
openssl ec -in rootsign-unsafe-privkey.pem -pubout -text > public_sign_key.txt
```

```sh
openssl ec -in cmd-unsafe-privkey.pem -pubout -text > public_command_key.txt
```

###### OTP Settings (heading level 7)

The following table describes the OTP settings (hard-coded in `app_dci_task.c`) used in the programming examples. Users can modify these values to adapt to the application requirements.

**Hard-Coded OTP Settings**

|**Array**|**OTP Settings**|**Value**|
|---|---|---|
|vse_svm_conf[]|VSE-SVM|MCU flags: Secure boot enable and Secure boot anti-rollback (0x00050000)|
|hse_svm_conf[]|HSE-SVM|MCU flags: Secure boot enable and Secure boot anti-rollback (0x00050000)|
|"|"|Reserved: 0x00000000, 0x00000000, 0x00000000, 0x00000000|
|hse_svh_xg21b_conf[]|HSE-SVH (xG21B)|MCU flags: Secure boot enable and Secure boot anti-rollback (0x00050000)|
|"|"|Tamper source levels: 0x40440410, 0x14040104, 0x77442211, 0x42042224|
|"|"|Filter reset period: 10|
|"|"|Filter trigger threshold: 6|
|"|"|Tamper flags: 0|
|"|"|Tamper reset threshold: 5|
|hse_svh_other_conf[]|HSE-SVH (others)|MCU flags: Secure boot enable and Secure boot anti-rollback (0x00050000)|
|"|"|Tamper source levels: 0x40440410, 0x14040104, 0x22414224, 0x74422112|
|"|"|Filter reset period: 10|
|"|"|Filter trigger threshold: 6|
|"|"|Tamper flags: 0|
|"|"|Tamper reset threshold: 5|

> **Note**: Refer to the tables below for details about OTP settings.

- [Parameters of MCU Flags](05-se-command-list#mcu-flags)
- [Tamper Source Response Level on HSE-SVH (xG21B) Devices](05-se-command-list#anti-tamper-configuration)
- [Tamper Source Response Level on Other HSE-SVH Devices](05-se-command-list#anti-tamper-configuration)
- [Anti-Tamper Configuration Settings](05-se-command-list#anti-tamper-configuration)

###### Firmware images (heading level 7)

The following table describes the firmware images (hard-coded in `app_firmware_image.c`) used in the programming examples. Users can modify these images to adapt to the application requirements.

**Hard-Coded Firmware Images**

|**Array**|**Size (Bytes)**|**Usage**|
|---|---|---|
|xg21_hse_image[]|41125|The xG21 HSE upgrade firmware image (v1.2.9)|
|xg21_app_image[]|9212|The xG21 application firmware image (Blink Bare-metal platform example)|
|xg21_signed_image[]|11192|A signed xG21 UART XMODEM Bootloader image (v1.12.0)|
|erase_xg21_userdata[]|3068|Application firmware image to erase xG21 user data|
|write_xg21_userdata[]|4228|Application firmware image to program xG21 user data|
|prog_xg21_hse_upgrade[]|6956|Application firmware image to upgrade xG21 HSE firmware|
|xg22_vse_image[]|16549|The xG22 VSE upgrade firmware image (v1.2.7)|
|xg22_app_image[]|10756|The xG22 application firmware image (Blink Bare-metal platform example)|
|xg22_signed_image[]|14128|A signed xG22 UART XMODEM Bootloader image (v1.12.0)|
|prog_xg22_vse_upgrade[]|9320|Application firmware image to upgrade xG22 VSE firmware|
|xg23_hse_image[]|88221|The xG23 HSE upgrade firmware image (v2.1.4)|
|xg23_app_image[]|13560|The xG23 application firmware image (Blink Bare-metal platform example)|
|xg23_signed_image[]|16248|A signed xG23 UART XMODEM Bootloader image (v1.12.0)|
|prog_xg23_hse_upgrade[]|11696|Application firmware image to upgrade xG23 HSE firmware|

> **Note**:
> 
> - The HSE or VSE upgrade firmware image must be stored to the device's internal flash in [.seu](02-introduction#introduction) format.
> - The firmware image (`.seu` format) can be converted to a C source file using the SEGGER free utility [`Bin2C.exe`](https://www.segger.com/free-utilities/bin2c/). The last **NULL** (`0x00`) character in the converted firmware image array should be discarded.
> 
> ![Bin2C.exe](/efr32-dci-swd-programming/0.1/images/sld815-image20.png)
> 
> - The programmer uses the bootloader image signed by the Private Sign Key ([rootsign-unsafe-privkey.pem](#security-keys)) to recover a secure boot failure device.
> - Refer to [Add a New Series Device to the Programmer](07-add-a-new-series-2-device-to-the-programmer#add-a-new-series-2-device-to-the-programmer) for instructions on adding firmware images for newer devices.

###### Compile Options (heading level 7)

The following tables describe the compile options in header files to set up the software and hardware environment for the programming examples.

**Compile Options in app_dci_swd.h**

|**Parameter**|**Usage**|**Default Setting**|
|---|---|---|
|RESET_PULSE|Pin reset pulse width in microseconds|1000 µs (1 ms)|
|RESET_DELAY|Delay in microseconds after issuing a soft or pin reset|50000 µs (50 ms)|
|DCI_RETRY_COUNT|Number of times to retry a DCI read or write operation. It must be high enough to receive a response from the SE command.|1001000|
|SWCLK_PORT|GPIO port for SWCLK|3 (Port D)|
|SWCLK_PIN|GPIO pin for SWCLK|2 (PD02)|
|SWDIO_PORT|GPIO port for SWDIO|3 (Port D)|
|SWDIO_PIN|GPIO pin for SWDIO|3 (PD03)|
|RESET_PORT|GPIO port for RESETn|2 (Port C)|
|RESET_PIN|GPIO pin for RESETn|3 (PC03)|

**Compile Options in app_firmware_image.h**

|**Parameter**|**Usage**|**Default Setting**|
|---|---|---|
|SE_START_ADDR|SE upgrade firmware image start address (aligned with 8 kB flash page size)|0x00060000|
|SE_UPGRADE_DELAY|Delay in microseconds after issuing a command to upgrade the SE firmware|2500000 µs (2.5 s)|
|USER_DATA_DELAY|Delay in microseconds after issuing a soft reset to run the application to erase or write the user data on xG21 devices|1000000 µs (1 s)|

> **Note**: Make sure the device has enough flash space to accommodate the SE firmware image when setting the SE_START_ADDR.

**Compile Options in app_swd_task.h**

|**Parameter**|**Usage**|**Default Setting**|
|---|---|---|
|ERASE_DELAY|Delay in microseconds after a flash page erase or mass erase|12000 µs (12 ms)|
|ERASE_LOOPCNT|It must be high enough to cover the page erase or mass erase time|1000|
|SKIP_POLLING|Skip polling WDATAREADY bit in the MSC STATUS register after writing 32-bit word to target device flash|1 (Skip)|
|WRITE_DELAY|Fix delay in microseconds after writing a 32-bit word to target device flash (if SKIP_POLLING = 1)|11 µs|

> **Note**:
> 
> - Refer to [Flash Erase](04-serial-wire-debug-swd-interface#flash-erase) for details about page erase and mass erase.
> - Refer to [Flash Write](04-serial-wire-debug-swd-interface#flash-write) for details about `SKIP_POLLING` and `WRITE_DELAY`.

###### Menu Operation

The `SPACE` and `ENTER` presses from the terminal program are used to manipulate the menu of the programmer.

###### Interface Menu (heading level 7)

```sh
Series 2 DCI and SWD Programming Examples - Core running at 80000 kHz.
 . Current interface selection is DEBUG CHALLENGE INTERFACE (DCI).
 + Press SPACE to pick an interface (DCI/SWD), press ENTER to select the task of the chosen interface.
```

**DCI Menu**

```sh
. Current DCI task is GET SE STATUS.
 + Press SPACE to cycle through the DCI tasks, press ENTER to run the selected DCI task.
 + Current DCI task is READ SE OTP CONFIGURATION.
 + Current DCI task is READ SERIAL NUMBER.
 + Current DCI task is READ PUBLIC SIGN KEY.
 + Current DCI task is READ PUBLIC COMMAND KEY.
 + Current DCI task is READ LOCK STATUS.
 + Current DCI task is SET DEBUG RESTRICTIONS.
 + Current DCI task is ENABLE SECURE DEBUG.
 + Current DCI task is DISABLE SECURE DEBUG.
 + Current DCI task is LOCK DEVICE.
 + Current DCI task is ERASE DEVICE (UNLOCK).
 + Current DCI task is RECOVER SECURE BOOT FAILURE DEVICE.
 + Current DCI task is UPGRADE SE FIRMWARE THROUGH DCI.
 + Current DCI task is INITIALIZE AES-128 KEY (HSE).
 + Current DCI task is INITIALIZE PUBLIC SIGN KEY.
 + Current DCI task is INITIALIZE PUBLIC COMMAND KEY.
 + Current DCI task is INITIALIZE SE OTP.
 + Current DCI task is DISABLE DEVICE ERASE.
```

From task `ENABLE SECURE DEBUG` to task `UPGRADE SE FIRMWARE THROUGH DCI`:

```sh
 + Current DCI task is ENABLE SECURE DEBUG.
 + Press ENTER to confirm or press SPACE to abort.
```

From task `INITIALIZE AES-128 KEY (HSE)` to task `DISABLE DEVICE ERASE`:

```sh
+ Current DCI task is DISABLE DEVICE ERASE.
 + Warning: This is a ONE-TIME command and the operation is IRREVERSIBLE!
 + Press ENTER to confirm or press SPACE to abort.
```

**SWD Interface Menu**

```sh
Series 2 DCI and SWD Programming Examples - Core running at 80000 kHz.
 . Current interface selection is DEBUG CHALLENGE INTERFACE (DCI).
 + Press SPACE to pick an interface (DCI/SWD), press ENTER to select the task of the chosen interface.
 + Current interface selection is SERIAL WIRE DEBUG (SWD) INTERFACE.

 . Current SWD task is ERASE MAIN FLASH.
 + Press SPACE to cycle through the SWD tasks, press ENTER to run the selected SWD task.
 + Current SWD task is PROGRAM MAIN FLASH.
 + Current SWD task is ERASE USER DATA.
 + Current SWD task is PROGRAM USER DATA.
 + Current SWD task is UPGRADE SE FIRMWARE THROUGH APPLICATION FIRMWARE.
```

```sh
 + Current SWD task is ERASE MAIN FLASH.
 + Press ENTER to confirm or press SPACE to abort.
```

**Error Handling**

```sh
 + Press ENTER to issue a pin reset to the target, press SPACE to skip.
```

###### DCI Programming Examples (heading level 7)

The following DCI programming examples are based on EFR32MG21B (BRD4181C) as the target device [Programmer Connection Diagram on page 23](#hardware-overview). Software is compiled with -O2 optimization in Simplicity IDE of Simplicity Studio 5.

**Get SE Status**

- To get the current [SE status](05-se-command-list#get-status).

```sh
 . Current DCI task is GET SE STATUS.
 + Press SPACE to cycle through the DCI tasks, press ENTER to run the selected DCI task.
 . Read target device SE status... OK
 + SE firmware version : 00010209
 + MCU firmware version : NA
 + Debug lock : Disabled
 + Debug lock state : False
 + Device Erase : Enabled
 + Secure debug : Disabled
 + Secure boot : Disabled
 + Boot status : 0x20 - Command successful.
```

###### Read SE OTP Configuration (heading level 7)

- To read the current [SE OTP configuration](05-se-command-list#read-user-configuration).  
  ```sh  
  . Current DCI task is READ SE OTP CONFIGURATION.  
  + Press SPACE to cycle through the DCI tasks, press ENTER to run the selected DCI task.  
    
  . Read target device user (SE OTP) configuration... OK  
  + Secure boot : Disabled  
  + Secure boot verify certificate : Disabled  
  + Secure boot anti-rollback : Disabled  
  + Secure boot page lock narrow : Disabled  
  + Secure boot page lock full : Disabled  
  + Tamper source level  
  Filter counter : 1  
  SE watchdog : 4  
  SE RAM CRC : 4  
  SE hard fault : 4  
  SE software assertion : 4  
  SE secure boot : 4  
  User secure boot : 0  
  Mailbox authorization : 1  
  DCI authorization : 0  
  OTP read : 4  
  Self test : 4  
  TRNG monitor : 1  
  PRS0 : 1  
  PRS1 : 1  
  PRS2 : 2  
  PRS3 : 2  
  PRS4 : 4  
  PRS5 : 4  
  PRS6 : 7  
  PRS7 : 7  
  Decouple BOD : 4  
  Temperature sensor : 2  
  Voltage glitch falling : 2  
  Voltage glitch rising : 2  
  Secure lock : 4  
  SE debug : 0  
  Digital glitch : 2  
  SE ICACHE : 4  
  + Reset period for the tamper filter counter: ~32 ms x 1024  
  + Activation threshold for the tamper filter: 4  
  + Digital glitch detector always on: Disabled  
  + Tamper reset threshold: 5  
  ```
- SE will return the [SE_RESPONSE_INVALID_COMMAND](03-debug-challenge-interface-dci#response) code if the SE OTP data has not been initialized or SE firmware version is less than v1.2.2 (xG21 or xG22 devices).

```sh
 . Read target device user (SE OTP) configuration... Failed - Unsupported command.
 + Press ENTER to issue a pin reset to the target, press SPACE to skip.
```

**Read Serial Number**

- To read the [serial number](05-se-command-list#read-serial-number) of the Series 2 device.

```sh
 . Current DCI task is READ SERIAL NUMBER.
 + Press SPACE to cycle through the DCI tasks, press ENTER to run the selected DCI task.
 . Read target device serial number... OK
 + The serial number (16 bytes): 000000000000000014B457FFFE0F77CE
```

###### Read Public Sign Key (heading level 7)

- To read the [Public Sign Key](05-se-command-list#read-public-key) in SE OTP.

```sh
 . Current DCI task is READ PUBLIC COMMAND KEY.
 + Press SPACE to cycle through the DCI tasks, press ENTER to run the selected DCI task.
 . Read target device public command key... OK
 + The public command key (64 bytes): B1BC6F6FA56640ED522B2EE0F5B3CF7E5D48F60BE8148F0DC08440F0A4E1DCA4
 7C04119ED6A1BE31B7707E5F9D001A659A051003E95E1B936F05C37EA793AD63
```

- SE will return the [SE_RESPONSE_INTERNAL_ERROR](03-debug-challenge-interface-dci#response) code if the Public Sign Key has not been provisioned.

```sh
 . Read target device public command key... Failed - Internal SE error.
 + Press ENTER to issue a pin reset to the target, press SPACE to skip.
```

**Read Public Command Key**

- To read the [Public Command Key](05-se-command-list#read-public-key) in SE OTP.

```sh
 . Current DCI task is READ PUBLIC COMMAND KEY.
 + Press SPACE to cycle through the DCI tasks, press ENTER to run the selected DCI task.
 . Read target device public command key... OK
 + The public command key (64 bytes): B1BC6F6FA56640ED522B2EE0F5B3CF7E5D48F60BE8148F0DC08440F0A4E1DCA4
 7C04119ED6A1BE31B7707E5F9D001A659A051003E95E1B936F05C37EA793AD63
```

- SE will return the [SE_RESPONSE_INTERNAL_ERROR](03-debug-challenge-interface-dci#response) code if the Public Command Key has not been provisioned.

```sh
 . Read target device public command key... Failed - Internal SE error.
 + Press ENTER to issue a pin reset to the target, press SPACE to skip.
```

**Read Lock Status**

- To read the lock state of the debug port.

```sh
+ Current DCI task is READ LOCK STATUS.
 . Debug port lock state
 + Debug Locked, config status : Disabled.
 + Device erase : Enabled.
 + Secure debug unlock : Enabled.
 + Debug port : Unlocked.
 + Invasive Debug Lock : Unlocked.
 + Non-invasive Debug Lock : Unlocked.
 + Secure Invasive Debug Lock : Unlocked.
 + Secure Non-invasive Debug Lock : Unlocked.
```

**Set Debug Restrictions**

- To set debug restrictions.

```sh
 + Setting the lockbit for Secure, Non-Invasive Debug
 . Set the debug restriction bits of the target device... OK
 . Read target device SE status... OK
 + SE firmware version : 0001020E
 + MCU firmware version : NA
 + Debug lock : Disabled
 + Debug lock state : False
 + Device Erase : Enabled
 + Secure debug : Enabled
 + Secure boot : Disabled and SE OTP is not configured
 + Boot status : 0x20 - Command successful.
Series 2 DCI and SWD Programming Examples - Core running at 80000 kHz.
 . Current interface selection is DEBUG CHALLENGE INTERFACE (DCI).
 + Press SPACE to pick an interface (DCI/SWD), press ENTER to select the task of the chosen interface.
```

###### Enable Secure Debug (heading level 7)

- To [enable](05-se-command-list#enable-secure-debug) the secure debug functionality.

```sh
 + Current DCI task is ENABLE SECURE DEBUG.
 + Press ENTER to confirm or press SPACE to abort.
 . Enable secure debug of the target device... OK
 . Read target device SE status... OK
 + SE firmware version : 00010209
 + MCU firmware version : NA
 + Debug lock : Disabled
 + Debug lock state : False
 + Device Erase : Enabled
 + Secure debug : Enabled
 + Secure boot : Disabled
 + Boot status : 0x20 - Command successful.
```

- SE will return the [SE_RESPONSE_INVALID_PARAMETER](03-debug-challenge-interface-dci#response) code if the Public Command Key has not been provisioned.

```sh
 . Enable secure debug of the target device... Failed - Parameters are invalid or buffer is too small.
 + Press ENTER to issue a pin reset to the target, press SPACE to skip.
```

- SE will return the [SE_RESPONSE_INVALID_COMMAND](03-debug-challenge-interface-dci#response) code if the secure debug has already enabled.

```sh
 . Enable secure debug of the target device... Failed - Unsupported command.
 + Press ENTER to issue a pin reset to the target, press SPACE to skip.
```

**Disable Secure Debug**

- To [disable](05-se-command-list#disable-secure-debug) the secure debug functionality.

```sh
 + Current DCI task is DISABLE SECURE DEBUG.
 + Press ENTER to confirm or press SPACE to abort.
 . Disable secure debug of the target device... OK
 . Read target device SE status... OK
 + SE firmware version : 00010209
 + MCU firmware version : NA
 + Debug lock : Disabled
 + Debug lock state : False
 + Device Erase : Enabled
 + Secure debug : Disabled
 + Secure boot : Disabled
 + Boot status : 0x20 - Command successful.
```

**Lock Device**

- To enable the [debug lock](05-se-command-list#apply-lock) of the Series 2 device.

```sh
 + Current DCI task is LOCK DEVICE.
 + Press ENTER to confirm or press SPACE to abort.
 . Lock target device... OK
 . Read target device SE status... OK
 + SE firmware version : 00010209
 + MCU firmware version : NA
 + Debug lock : Enabled
 + Debug lock state : True
 + Device Erase : Enabled
 + Secure debug : Disabled
 + Secure boot : Disabled
 + Boot status : 0x20 - Command successful.
```

**Erase Device (Unlock)**

- To perform a device mass erase and [unlock](05-se-command-list#erase-device) the standard debug lock of the Series 2 device.

```sh
 + Current DCI task is ERASE DEVICE (UNLOCK).
 + Press ENTER to confirm or press SPACE to abort.
 . Erase (unlock) target device... OK
 . Read target device SE status... OK
 + SE firmware version : 00010209
 + MCU firmware version : NA
 + Debug lock : Disabled
 + Debug lock state : False
 + Device Erase : Enabled
 + Secure debug : Disabled
 + Secure boot : Disabled
 + Boot status : 0x20 - Command successful.
```

- SE will return the [SE_RESPONSE_INVALID_COMMAND](03-debug-challenge-interface-dci#erase-device) code if the [Device Erase](05-se-command-list#disable-device-erase) is disabled.

```sh
 . Erase (unlock) target device... Failed - Unsupported command.
 + Press ENTER to issue a pin reset to the target, press SPACE to skip.
```

**Recover Secure Boot Failure Device**

- To get the current [SE status](05-se-command-list#get-status).

```sh
 . Current DCI task is GET SE STATUS.
 + Press SPACE to cycle through the DCI tasks, press ENTER to run the selected DCI task.
 . Read target device SE status... OK
 + SE firmware version : 00010209
 + MCU firmware version : NA
 + Debug lock : Disabled
 + Debug lock state : False
 + Device Erase : Enabled
 + Secure debug : Disabled
 + Secure boot : Enabled
 + Boot status : 0x14 - Failure while checking the host for secure boot.
```

- Issue a [device erase](05-se-command-list#erase-device) to unlock the target device through DCI.

```sh
 + Current DCI task is RECOVER SECURE BOOT FAILURE DEVICE.
 + Press ENTER to confirm or press SPACE to abort.

 . Issue a device erase through DCI.
 + Erase target device... OK
```

- A correctly-signed firmware image [xg21_signed_image[]](#firmware-images) is programmed to the target device main flash through the SWD interface to recover a secure boot failure device.

```sh
 + Initialize DP... OK - IDCODE = 0x6BA02477
 + Read AP... OK - IDR = 0x84770001
 + Set up AHB-AP and halt target... OK
 + Get device information... OK - Target device is EFR32MG21B010F1024 and unique ID is 0x14B457FFFE0F7762
 . Program a correctly-signed image to recover device.
 + The xG21 signed firmware image size is 11192 bytes and start address is 0x00000000.
 + Erase-Program-Verify the xG21 main flash for signed firmware image... OK (cycles: 13616519 time: 170 ms)
 . Read target device SE status... OK
 + SE firmware version : 00010209
 + MCU firmware version : 010C0000
 + Debug lock : Disabled
 + Debug lock state : False
 + Device Erase : Enabled
 + Secure debug : Disabled
 + Secure boot : Enabled
 + Boot status : 0x20 - Command successful.
```

- The device cannot be recovered if the image is not correctly-signed.

```sh
 . Read target device SE status... OK
 + SE firmware version : 00010209
 + MCU firmware version : 010C0000
 + Debug lock : Disabled
 + Debug lock state : False
 + Device Erase : Enabled
 + Secure debug : Disabled
 + Secure boot : Enabled
 + Boot status : 0x12 - Failure while checking the host for secure boot.
```

**Upgrade SE Firmware Through DCI**

- A SE upgrade firmware image [xg21_hse_image[]](#firmware-images) is programmed to the target device main flash at [SE_START_ADDR](#compile-options) through the SWD interface. It will overwrite the data from `SE_START_ADDR` to `SE_START_ADDR` + the size of `xg21_hse_image[]`.

```sh
 + Current DCI task is UPGRADE SE FIRMWARE THROUGH DCI.
 + Press ENTER to confirm or press SPACE to abort.
 . Program a SE firmware image to the target device.
 + Initialize DP... OK - IDCODE = 0x6BA02477
 + Read AP... OK - IDR = 0x84770001
 + Set up AHB-AP and halt target... OK
 + Get device information... OK - Target device is EFR32MG21B010F1024 and unique ID is 0x588E81FFFE7034E5
 + The xG21 HSE firmware image version: 00010209
 + The xG21 HSE firmware image size is 41125 bytes and start address is 0x00060000.
 + Erase-Program-Verify the xG21 main flash for HSE firmware image... OK (cycles: 52365323 time: 654 ms)
```

- [Validate](05-se-command-list#se-image-check) the SE upgrade firmware image and start the [upgrade](05-se-command-list#se-image-apply) process if the image is valid.

```sh
 . Validate SE firmware image in the target device... OK
 . Upgrade SE firmware image, delay few seconds to check SE status... Done
 . Read target device SE status... OK
 + SE firmware version : 00010209
 + MCU firmware version : NA
 + Debug lock : Disabled
 + Debug lock state : False
 + Device Erase : Enabled
 + Secure debug : Disabled
 + Secure boot : Disabled
 + Boot status : 0x20 - Command successful.
```

- SE will return the [SE_RESPONSE_INVALID_PARAMETER](03-debug-challenge-interface-dci#response) code if the SE upgrade firmware image is invalid.

```sh
 . Validate SE firmware image in the target device... Failed - Parameters are invalid or buffer is too small.
 + Press ENTER to issue a pin reset to the target, press SPACE to skip.
```

- SE will return the [SE_RESPONSE_INVALID_COMMAND](03-debug-challenge-interface-dci#response) code if the current SE firmware version is less than v1.2.2 (xG21 or xG22 devices).

```sh
 . Validate SE firmware image in the target device... Failed - Unsupported command.
 + Press ENTER to issue a pin reset to the target, press SPACE to skip.

```

###### Initialize AES-128 Key (HSE) (heading level 7)

- To [initialize](05-se-command-list#initialize-aes-key) the [aes_key[]](#security-keys) to HSE OTP.

```sh
 + Current DCI task is INITIALIZE AES-128 KEY (HSE).
 + Warning: This is a ONE-TIME command and the operation is IRREVERSIBLE!
 + Press ENTER to confirm or press SPACE to abort.
 . Initialize AES-128 key of the target device... OK
```

- SE will return the [SE_RESPONSE_INVALID_PARAMETER](03-debug-challenge-interface-dci#response) code if the AES-128 key has already been initialized.

```sh
 . Initialize AES-128 key of the target device... Failed - Parameters are invalid or buffer is too small.
 + Press ENTER to issue a pin reset to the target, press SPACE to skip.
```

**Initialize Public Sign Key**

- To [initialize](05-se-command-list#initialize-public-key) the [public_sign_key[]](#security-keys) to SE OTP.

```sh
 + Current DCI task is INITIALIZE PUBLIC SIGN KEY.
 + Warning: This is a ONE-TIME command and the operation is IRREVERSIBLE!
 + Press ENTER to confirm or press SPACE to abort.
 . Initialize public sign key of the target device... OK
 . Read target device public sign key... OK
 + The public sign key (64 bytes): C4AF4AC69AAB9512DB50F7A26AE5B4801183D85417E729A56DA974F4E08A562C
 DE6019DEA9411332DC1A743372D170B436238A34597C410EA177024DE20FC819
```

- SE will return the [SE_RESPONSE_INVALID_PARAMETER](03-debug-challenge-interface-dci#response) code if the Public Sign Key has already been initialized.

```sh
 . Initialize public sign key of the target device... Failed - Parameters are invalid or buffer is too small.
 + Press ENTER to issue a pin reset to the target, press SPACE to skip.
```

**Initialize Public Command Key**

- To [initialize](05-se-command-list#initialize-public-key) the [public_command_key[]](#security-keys) to SE OTP.

```sh
 + Current DCI task is INITIALIZE PUBLIC COMMAND KEY.
 + Warning: This is a ONE-TIME command and the operation is IRREVERSIBLE!
 + Press ENTER to confirm or press SPACE to abort.
 . Initialize public command key of the target device... OK
 . Read target device public command key... OK
 + The public command key (64 bytes): B1BC6F6FA56640ED522B2EE0F5B3CF7E5D48F60BE8148F0DC08440F0A4E1DCA4
 7C04119ED6A1BE31B7707E5F9D001A659A051003E95E1B936F05C37EA793AD63
```

- SE will return the [SE_RESPONSE_INVALID_PARAMETER](03-debug-challenge-interface-dci#response) code if the Public Command Key has already been initialized.

```sh
 . Initialize public command key of the target device... Failed - Parameters are invalid or buffer is too small.
 + Press ENTER to issue a pin reset to the target, press SPACE to skip.
```

###### Initialize SE OTP (heading level 7)

- To [initialize](05-se-command-list#initialize-otp) the [hse_svh_xg21b_conf[]](#otp-settings) to the HSE OTP.

```sh
 + Current DCI task is INITIALIZE SE OTP.
 + Warning: This is a ONE-TIME command and the operation is IRREVERSIBLE!
 + Press ENTER to confirm or press SPACE to abort.
 . Initialize SE OTP of the target device... OK
 . Read target device user (SE OTP) configuration... OK
 + Secure boot : Enabled
 + Secure boot verify certificate : Disabled
 + Secure boot anti-rollback : Enabled
 + Secure boot page lock narrow : Disabled
 + Secure boot page lock full : Disabled
 + Tamper source level
 Filter counter : 1
 SE watchdog : 4
 SE RAM CRC : 4
 SE hard fault : 4
 SE software assertion : 4
 SE secure boot : 4
 User secure boot : 0
 Mailbox authorization : 1
 DCI authorization : 0
 OTP read : 4
 Self test : 4
 TRNG monitor : 1
 PRS0 : 1
 PRS1 : 1
 PRS2 : 2
 PRS3 : 2
 PRS4 : 4
 PRS5 : 4
 PRS6 : 7
 PRS7 : 7
 Decouple BOD : 4
 Temperature sensor : 2
 Voltage glitch falling : 2
 Voltage glitch rising : 2
 Secure lock : 4
 SE debug : 0
 Digital glitch : 2
 SE ICACHE : 4
 + Reset period for the tamper filter counter: ~32 ms x 1024
 + Activation threshold for the tamper filter: 4
 + Digital glitch detector always on: Disabled
 + Tamper reset threshold: 5
```

- SE will return the [SE_RESPONSE_INVALID_COMMAND](03-debug-challenge-interface-dci#response) code if the SE OTP has already programmed.

```sh
 . Initialize SE OTP of the target device... Failed - Unsupported command.
 + Press ENTER to issue a pin reset to the target, press SPACE to skip.
```

- SE will return the [SE_RESPONSE_INVALID_PARAMETER](03-debug-challenge-interface-dci#response) code if secure boot option is enabled and the Public Sign Key has not been provisioned.

```sh
 . Initialize SE OTP of the target device... Failed - Parameters are invalid or buffer is too small.
 + Press ENTER to issue a pin reset to the target, press SPACE to skip.
```

**Disable Device Erase**

- To [disable](05-se-command-list#disable-device-erase) the [Erase Device](05-se-command-list#erase-device) command.

```sh
 + Current DCI task is DISABLE DEVICE ERASE.
 + Warning: This is a ONE-TIME command and the operation is IRREVERSIBLE!
 + Press ENTER to confirm or press SPACE to abort.
 . Disable device erase of the target device... OK
 . Read target device SE status... OK
 + SE firmware version : 00010209
 + MCU firmware version : NA
 + Debug lock : Disabled
 + Debug lock state : False
 + Device Erase : Disabled
 + Secure debug : Enabled
 + Secure boot : Disabled
 + Boot status : 0x20 - Command successful.
```

###### SWD Programming Examples

The following SWD programming examples are based on EFR32MG21B (BRD4181C) and EFR32MG22 (BRD4182A) as target devices ([Programmer Connection Diagram](#hardware-overview)). Software is compiled with -O2 optimization in the Simplicity IDE of Simplicity Studio 5.

If the secure boot option is enabled in the SE OTP or bootloader, the application firmware must be signed.

Some operations are implemented by the SE Manager APIs in an application firmware that is programmed to the target device.

- For more information about SE Manager, see [Series 2 Secure Debug](https://docs.silabs.com/iot-security/latest/series2-secure-debug/).
- The SE Manager is available in Gecko SDK Suite 3.0.0 or later.
- The SE Manager APIs are fully described in the Silicon Labs online documentation located at [https://docs.silabs.com/gecko-plat-](https://docs.silabs.com/gecko-platform/latest/service/api/group-sl-se-manager) [form/latest/service/api/group-sl-se-manager](https://docs.silabs.com/gecko-platform/latest/service/api/group-sl-se-manager).

**Erase Main Flash**

- This example demonstrates the [mass erase](04-serial-wire-debug-swd-interface#flash-erase) operation through the SWD interface.

```sh
 + Current SWD task is ERASE MAIN FLASH.
 + Press ENTER to confirm or press SPACE to abort.
 . Connect to the target device through the SWD interface.
 + Initialize DP... OK - IDCODE = 0x6BA02477
 + Read AP... OK - IDR = 0x84770001
 + Set up AHB-AP and halt target... OK
 + Get device information... OK - Target device is EFR32MG21B010F1024 and unique ID is 0x14B457FFFE0F77CE
 . Erase main flash of the target device... OK (cycles: 1511120 time: 18889 us)
```

**Program Main Flash**

- This example demonstrates the [flash erase](04-serial-wire-debug-swd-interface#flash-erase), [flash write](04-serial-wire-debug-swd-interface#flash-write), and [flash verify](04-serial-wire-debug-swd-interface) operations through the SWD interface. In this example, an application firmware image [xg21_app_image[]](#firmware-images) is programmed to the target device main flash.

```sh
 + Current SWD task is PROGRAM MAIN FLASH.
 + Press ENTER to confirm or press SPACE to abort.
 . Connect to the target device through the SWD interface.
 + Initialize DP... OK - IDCODE = 0x6BA02477
 + Read AP... OK - IDR = 0x84770001
 + Set up AHB-AP and halt target... OK
 + Get device information... OK - Target device is EFR32MG21B010F1024 and unique ID is 0x14B457FFFE0F77CE
 . Program an application firmware image to the target device.
 + The xG21 application firmware image size is 9212 bytes and start address is 0x00000000.
 + Erase-Program-Verify the xG21 main flash for application firmware image... OK (cycles: 11388469 time: 142 ms)
 + Issue a soft reset to run the application firmware... OK
```

**Erase User Data (xG21 Devices)**

- For xG21 devices (BRD4181C), the user data can only be erased by issuing a command to the HSE through the SE Manager API.
- In this example, an application firmware image [erase_xg21_userdata[]](#firmware-images) (see source code below) is programmed to the target device main flash.

```sh
#include "em_chip.h"
#include "em_cmu.h"
#include "sl_se_manager.h"
#include "sl_se_manager_util.h"
/***************************************************************************//**
 * Main function
 ******************************************************************************/
int main(void)
{
 // Command context
 sl_se_command_context_t cmd_ctx;
 // Switch SYSCLK to 38 MHz HFRCO
 CMU_HFRCODPLLBandSet(cmuHFRCODPLLFreq_38M0Hz);
 // Initialize SE Manager
 sl_se_init();
 // Erase user page
 sl_se_erase_user_data(&cmd_ctx);
 
 while (1) ;
}
```

- The original application firmware image on the application start address will be overwritten.
- Issue a soft reset to run the application above to erase the user data.

```sh
 + Current SWD task is ERASE USER DATA.
 + Press ENTER to confirm or press SPACE to abort.
 . Connect to the target device through the SWD interface.
 + Initialize DP... OK - IDCODE = 0x6BA02477
 + Read AP... OK - IDR = 0x84770001
 + Set up AHB-AP and halt target... OK
 + Get device information... OK - Target device is EFR32MG21B010F1024 and unique ID is 0x14B457FFFE0F77CE
 . Erase xG21 user data through application firmware.
 + The xG21 application firmware image size is 3068 bytes and start address is 0x00000000.
 + Erase-Program-Verify the xG21 main flash for application firmware image... OK (cycles: 4473314 time: 55916 us)
 + Issue a soft reset to run the application firmware to erase the xG21 user data... OK
```

**Erase User Data (non xG21 Devices)**

- For non xG21 devices (BRD4182A), the user data can be erased the same way as any page in the main flash.

```sh
 + Current SWD task is ERASE USER DATA.
 + Press ENTER to confirm or press SPACE to abort.
 . Connect to the target device through the SWD interface.
 + Initialize DP... OK - IDCODE = 0x6BA02477
 + Read AP... OK - IDR = 0x84770001
 + Set up AHB-AP and halt target... OK
 + Get device information... OK - Target device is EFR32MG22C224F512 and unique ID is 0x680AE2FFFE287808
 . Erase xG22 user data... OK (cycles: 2027306 time: 25341 us)
```

**Program User Data (xG21 Devices)**

- For xG21 devices (BRD4181C), the user data can only be written by issuing a command to the HSE through the SE Manager API.
- In this example, an application firmware image write_xg21_userdata[] (see source code below) is programmed to the target device  
  main flash.

```sh
#include "em_chip.h"
#include "em_cmu.h"
#include "sl_se_manager.h"
#include "sl_se_manager_util.h"
// User data
SL_ALIGN(4) static const uint32_t user_data[256] SL_ATTRIBUTE_ALIGN(4) = {
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55, 0x55AA55AA, 0xAA55AA55,
};
/***************************************************************************//**
 * Main function
 ******************************************************************************/
int main(void)
{
 // Command context
 sl_se_command_context_t cmd_ctx;
 // Switch SYSCLK to 38 MHz HFRCO
 CMU_HFRCODPLLBandSet(cmuHFRCODPLLFreq_38M0Hz);
 // Initialize SE Manager
 sl_se_init();
 // Erase user data
 sl_se_erase_user_data(&cmd_ctx);
 
 // Write user data
 sl_se_write_user_data(&cmd_ctx, 0, (uint32_t *)user_data, sizeof(user_data));
 while (1) ;
}
```

- The original application firmware image on the application start address will be overwritten.
- Issue a soft reset to run the application above to write the user data.

```sh
 + Current SWD task is PROGRAM USER DATA.
 + Press ENTER to confirm or press SPACE to abort.
 . Connect to the target device through the SWD interface.
 + Initialize DP... OK - IDCODE = 0x6BA02477
 + Read AP... OK - IDR = 0x84770001
 + Set up AHB-AP and halt target... OK
 + Get device information... OK - Target device is EFR32MG21B010F1024 and unique ID is 0x14B457FFFE0F77CE
 . Program xG21 user data through application firmware.
 + The xG21 application firmware image size is 4228 bytes and start address is 0x00000000.
 + Erase-Program-Verify the xG21 main flash for application firmware image... OK (cycles: 5777749 time: 72221 us)
 + Issue a soft reset to run the application firmware to program the xG21 user data... OK
```

**Program User Data (non xG21 Devices)**

- For non xG21 devices (BRD4182A), the user data can be written the same way as any page in the main flash.

```sh
 + Current SWD task is PROGRAM USER DATA.
 + Press ENTER to confirm or press SPACE to abort.
 . Connect to the target device through the SWD interface.
 + Initialize DP... OK - IDCODE = 0x6BA02477
 + Read AP... OK - IDR = 0x84770001
 + Set up AHB-AP and halt target... OK
 + Get device information... OK - Target device is EFR32MG22C224F512 and unique ID is 0x680AE2FFFE287808
 . Program xG22 user data.
 + User data size is 1024 bytes and start address is 0x0FE00000.
 + Erase-Program-Verify the xG22 user data... OK (cycles: 2179418 time: 27242 us)
```

**Upgrade SE Firmware Through Application Firmware**

- For xG21 or xG22 target devices, the SE firmware can only be upgraded by issuing a command to the SE through the SE Manager API if the SE firmware version is less than v1.2.2.
- Connect to the target device through the SWD interface.
- An application firmware image [prog_xg21_hse_upgrade[]](#firmware-images) (see source code below) is programmed to the target device main flash.

```sh
#include "em_chip.h" #include "em_cmu.h" #include "sl_se_manager.h"

#include "sl_se_manager_util.h"

// SE firmware image start address

#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG) \ && (_SILICON_LABS_32B_SERIES_2_CONFIG \< 3)

#define SE_START_ADDR (**0x00060000UL**)

#else

#define SE_START_ADDR (**0x08060000UL**)

#endif

// Current SE firmware version static uint32_t current_version;

// Upgrade SE firmware version

static volatile uint32_t upgrade_version;

/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*//\*\*

\* Main function

\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/ int main(void)

{

// Command context sl_se_command_context_t cmd_ctx;

// Switch SYSCLK to 38 MHz HFRCO CMU_HFRCODPLLBandSet(cmuHFRCODPLLFreq_38M0Hz);

// Initialize SE Manager sl_se_init();

// Get current SE firmware version

if (sl_se_get_se_version(&cmd_ctx, &current_version) != 0) { goto exit;

}

// Get upgrade SE firmware version

upgrade_version = \*((uint32_t \*)SE_START_ADDR + 3);

// Check if upgrade version \> current version if (upgrade_version \<= current_version) {

goto exit;

}

#if !defined(CRYPTOACC_PRESENT)

// Validate the SE firmware image

if (sl_se_check_se_image(&cmd_ctx, (uint32_t \*)SE_START_ADDR) != 0) { goto exit;

}

#endif

// Upgrade the SE firmware image sl_se_apply_se_image(&cmd_ctx, (uint32_t \*)SE_START_ADDR);

exit:

while (1) ;

}
```

- The original application firmware image on the application start address will be overwritten.

```sh
 + Current SWD task is UPGRADE SE FIRMWARE THROUGH APPLICATION FIRMWARE.
 + Press ENTER to confirm or press SPACE to abort.
 . Connect to the target device through the SWD interface.
 + Initialize DP... OK - IDCODE = 0x6BA02477
 + Read AP... OK - IDR = 0x84770001
 + Set up AHB-AP and halt target... OK
 + Get device information... OK - Target device is EFR32MG21B010F1024 and unique ID is 0x60A423FFFEA0773C
 . Program an application firmware image to the target device to upgrade the SE firmware.
 + The xG21 HSE upgrade application firmware image size is 6956 bytes and start address is 0x00000000.
 + Erase-Program-Verify the xG21 main flash for application to upgrade
 HSE firmware... OK (cycles: 8840993 time: 110 ms)
```

- A SE upgrade firmware image [xg21_hse_image[]](#firmware-images) is programmed to the target device main flash at [SE_START_ADDR](#compile-options). The application start address cannot overlap with `SE_START_ADDR`. This means the `SE_START_ADDR` must be greater than the application start address plus the size [6956 bytes](#firmware-images) of the application firmware image (`prog_xg21_hse_upgrade[]`).

```sh
 . Program a SE firmware image to the target device.
 + The xG21 HSE firmware image version: 00010209
 + The xG21 HSE firmware image size is 41125 bytes and start address is 0x00060000.
 + Erase-Program-Verify the xG21 main flash for HSE firmware image... OK (cycles: 52363897 time: 654 ms)
```

- Issue a pin reset to run the application firmware in `prog_xg21_hse_upgrade[]`.
- Check the SE firmware version after a few seconds to verify the SE firmware has been upgraded.

```sh
 + Issue a pin reset to run the application to upgrade the SE firmware.
 + Delay few seconds to check SE status... Done
 . Read target device SE status... OK
 + SE firmware version : 00010209
 + MCU firmware version : NA
 + Debug lock : Disabled
 + Debug lock state : False
 + Device Erase : Enabled
 + Secure debug : Disabled
 + Secure boot : Disabled and SE OTP is not configured
 + Boot status : 0x20 - Command successful.
```

- The SE firmware cannot be downgraded so the upgrade will be ignored if SE firmware with the same or a lower version is applied to the device.

###### Benchmark

The application firmware in [Hard-coded Firmware Images on page 26](#firmware-images) is replaced by a 256 kB image for benchmarking. The test results in the following table are based on the conditions below.

- [Series 2 DCI and SWD Programming](#software-overview) platform example of GSDK v4.0.1
- Erase, program, and verify the main flash
- [Compile options](#compile-options) are set to default values
- Software is compiled with -O2 in Simplicity IDE (GNU ARM v10.2.1) of Simplicity Studio 5
- The programmer (EFR32MG22C224F512IM40) is running at 80 MHz

**Erase-Program-Verify Time for Different Target Devices**

|**Target Device**|**Application Firmware Image Size**|**Erase-Program-Verify Time**|
|---|---|---|
|EFR32MG21B (BRD4181C)|256 kB (xg21_app_image[])|3.68 s|
|EFR32MG22 (BRD4182A)|256 kB (xg22_app_image[])|3.68 s|
|EFR32FG23B (BRD4263C)|256 kB (xg23_app_image[])|3.74 s|

> **Note**: The following section describes how to add firmware images for newer devices. Users requiring performance data for devices not listed here can add their own 256 kb image to perform the test.

##### Add a New Series 2 Device to the Programmer

The programmer in this application note uses EFR32MG22C224F512IM40 as a host controller. The defines in the following table for EFR32MG22C224F512 should usually apply to the new Series 2 devices.

**Defines for EFR32MG22C224F512**

|**Item**|**Define**|**Value**|
|---|---|---|
|Main flash base address|FLASH_MEM_BASE in efr32mg22c224f512im40.h|0x00000000|
|User data base address|USERDATA_BASE in efr32mg22c224f512im40.h|0x0FE00000|
|MSC base address|MSC_BASE in efr32mg22c224f512im40.h|0x40030000|
|Offset and bitfields of MSC registers|MSC Register Map in xG22 reference manual|—|
|DEVINFO base address|DEVINFO_BASE in efr32mg22c224f512im40.h|0x0FE08000|
|Offset and bitfields of DEVINFO registers|DEVINFO Register Map in xG22 reference manual|—|
|CMU base address|CMU_BASE in efr32mg22c224f512im40.h|0x40008000|
|Offset of CMU_CLKEN1_SET register|CMU Register Map in xG22 reference manual|0x00001068|
|Bitmask to enable MSC clock|CMU_CLKEN1_MSC in efr32mg22_cmu.h|0x00020000|
|AP IDR (Cortex-M33)|S2_AHBAP_ID in app_dci_swd.h|0x84770001|
|TAR wrap mask (Cortex-M33)|TAR_WRAP_1K in app_swd_task.h|0x3FF|

Users need to define new items if any defines in the table above do not match the new Series 2 device. And users may require tuning the settings in [Compile Options](06-series-2-dci-and-swd-programming-examples#compile-options) for the new Series 2 device.

The following procedures describe how to add the xG23 device to the programmer.

1. Add `XG23_FAMILY` (`0x17` for 23) to `app_swd_task.h.` It is `0x15` for xG21, `0x16` for xG22, `0x17` for xG23, etc.  
   ```sh  
   /// Device family of xG23  
   #define XG23_FAMILY (0x00170000UL)  
   ```
2. The  items  below  are  different  from  the  EFR32MG22C224F512  after  checking  the  xG23  header  files  (e.g., efr32fg23b010f512im48.h) and reference manual.  
   - Main [flash base address](04-serial-wire-debug-swd-interface#flash-erase) — Add `FLASH_BASE_XG23` to `app_swd_task.h.`  
   ```sh  
   /// Flash start address of xG23  
   #define FLASH_BASE_XG23 (0x08000000UL)  
   ```  
   - Bitmask to enable MSC clock — Add `CLKEN1_MSC_XG23` to `app_swd_task.h.`  
   ```sh  
   /// MSC bit of xG23 CMU_CLKEN1_SET  
   #define CLKEN1_MSC_XG23 (0x00010000UL)  
   ```
3. Add code to `app_swd_task.c` to enable [MSC clock](04-serial-wire-debug-swd-interface#serial-wire-debug-swd-interface) and set flash base address.  
   ```sh  
   // Enable MSC clock if device is xG23  
   if (buf0 == XG23_FAMILY) {  
   write_mem((uint32_t)&(CMU->CLKEN1_SET), CLKEN1_MSC_XG23);  
   // Check UDLOCKBIT on xG23  
   if (read_mem((uint32_t)&(MSC->MISCLOCKWORD)) & MSC_MISCLOCKWORD_UDLOCKBIT) {  
   RAISE(SWD_ERROR_USERDATA_LOCK);  
   }  
   // Flash start address for xG23  
   flash_start_addr = FLASH_BASE_XG23;  
   }  
   ```
4. Add `DEVICE_XG23` (ASCII code of character "3") to `app_process.h.` It is "1" for xG21, "2" for xG22, "3" for xG23, etc.  
   ```sh  
   /// xG23 device  
   #define DEVICE_XG23 (0x33)  
   ```
5. The [hse_svm_conf[]](06-series-2-dci-and-swd-programming-examples#otp-settings) can apply to xG23A (HSE-SVM) devices. Add || `defined(DEVICE_XG23)` to `hse_svm_conf[]` in `app_dci_ta sk.c.`  
   ```sh  
   #if defined(DEVICE_XG21) || defined(DEVICE_XG23)  
   /// HSE-SVM user configuration  
   static const uint32_t hse_svm_conf[HSE_USER_CONF_SIZE] = {  
   0x00000018, // 24 bytes data below  
   SECURE_BOOT_ENABLE_MASK + ANTI_ROLLBACK_MASK, // MCU settings  
   0x00000000, // 20 bytes reserved data  
   0x00000000,  
   0x00000000,  
   0x00000000,  
   0x00000000  
   };  
   #endif  
   ```
6. The [hse_svh_xg21b_conf[]](06-series-2-dci-and-swd-programming-examples#otp-settings) cannot apply to xG23B (HSE-SVH) devices. Add [hse_svh_other_conf[]](06-series-2-dci-and-swd-programming-examples#otp-settings) to `app_dci_task.c.`  
   ```sh  
   #if defined(DEVICE_XG23)  
   /// Other HSE-SVH user configuration  
   static const uint32_t hse_svh_other_conf[HSE_USER_CONF_SIZE] = {  
   0x00000018, // 24 bytes data below  
   SECURE_BOOT_ENABLE_MASK + ANTI_ROLLBACK_MASK, // MCU settings  
   0x40440410, // Tamper settings  
   0x14040104,  
   0x22414224,  
   0x74422112,  
   0x0500060A  
   };  
   #endif  
   ```
7. Add code (based on `case DEVICE_XG21:`, replace `hse_svh_xg21b_conf` with `hse_svh_other_conf`) to `app_dci_task.c` to set up a command buffer for [OTP configuration](05-se-command-list#initialize-otp).  
   ```sh  
   #if defined(DEVICE_XG23)  
   case DEVICE_XG23:  
   // Check SVM or SVH  
   if (*(cmd_buf + 2) == 'A') {  
   *cmd_buf = INIT_HSE_OTP_LENGTH;  
   *(++cmd_buf) = COMMAND_INIT_OTP;  
   // Calculate parity of OTP configuration  
   *(++cmd_buf) = 0;  
   for (i = 1; i < HSE_USER_CONF_SIZE; i++) {  
   *cmd_buf ^= hse_svm_conf[i];  
   }  
   // Copy OTP configuration to buffer  
   memcpy((uint32_t *)(++cmd_buf), (uint32_t *)hse_svm_conf,  
   INIT_HSE_OTP_LENGTH);  
   } else if (*(cmd_buf + 2) == 'B') {  
   *cmd_buf = INIT_HSE_OTP_LENGTH;  
   *(++cmd_buf) = COMMAND_INIT_OTP;  
   // Calculate parity of OTP configuration  
   *(++cmd_buf) = 0;  
   for (i = 1; i < HSE_USER_CONF_SIZE; i++) {  
   *cmd_buf ^= hse_svh_other_conf[i];  
   }  
   // Copy OTP configuration to buffer  
   memcpy((uint32_t *)(++cmd_buf), (uint32_t *)hse_svh_other_conf,  
   INIT_HSE_OTP_LENGTH);  
   } else {  
   RAISE(SWD_ERROR_UNKNOWN_DEVICE);  
   }  
   break;  
   #endif  
   ```
8. Add [firmware images](06-series-2-dci-and-swd-programming-examples#firmware-images) to `app_firmware_image.c` and `app_firmware_image.h.`  
   ![Add firmware image](/efr32-dci-swd-programming/0.1/images/sld815-image21.png)
9. Add functions to `app_firmware_image.c` and `app_firmware_image.h` to get the address and size of firmware images.  
   ![Add functions](/efr32-dci-swd-programming/0.1/images/sld815-image22.png)
10. The `tamper_source_xg21b[]` cannot be applied to xG23B (HSE-SVH) devices. Add `tamper_source_other[]` to `app_process.c.`  
    ```sh  
    #if defined(DEVICE_XG23)  
    /// Strings for tamper sources of other HSE-SVH devices  
    static const char *tamper_source_other[TAMPER_SIGNAL_NUM] = {  
    NULL,  
    "Filter counter : ",  
    "SE watchdog : ",  
    NULL,  
    "SE RAM ECC 2 : ",  
    "SE hard fault : ",  
    NULL,  
    "SE software assertion : ",  
    "SE secure boot : ",  
    "User secure boot : ",  
    "Mailbox authorization : ",  
    "DCI authorization : ",  
    "OTP Read : ",  
    NULL,  
    "Self test : ",  
    "TRNG monitor : ",  
    "Secure lock : ",  
    "Digital glitch : ",  
    "Voltage glitch : ",  
    "SE ICACHE : ",  
    "SE RAM ECC 1 : ",  
    "BOD : ",  
    "Temperature sensor : ",  
    "DPLL lock fail low : ",  
    "DPLL lock fail high : ",  
    "PRS0 : ",  
    "PRS1 : ",  
    "PRS2 : ",  
    "PRS3 : ",  
    "PRS4 : ",  
    "PRS5 : ",  
    "PRS6 : "  
    };  
    #endif  
    ```
11. Add  || `defined(DEVICE_XG23)` and  code  (based  on  `case DEVICE_XG21:`,  replace  `tamper_source_xg21b` with `tamper_source_other`) to function `print_otp_conf()` in `app_process.c` to print out the [tamper configuration](05-se-command-list#anti-tamper-configuration).  
    ```sh  
    #if defined(DEVICE_XG21) || defined(DEVICE_XG23)  
    uint32_t i;  
    uint32_t j;  
    uint32_t k;  
    #endif  
    ...  
    #if defined(DEVICE_XG23)  
    case DEVICE_XG23:  
    for (i = 0, j = 0, k = 2; i < TAMPER_SIGNAL_NUM; i++, j += 4) {  
    if (j == 32) {  
    j = 0;  
    k++;  
    }  
    cmd_resp_buf[8] = (cmd_resp_buf[k] >> j) & 0x0f;  
    if (tamper_source_other[i] != NULL) {  
    printf(" %s %lu\n", tamper_source_other[i], cmd_resp_buf[8]);  
    }  
    }  
    break;  
    #endif  
    ...  
    #if defined(DEVICE_XG21) || defined(DEVICE_XG23)  
    // Common tamper parameters  
    printf(" + Reset period for the tamper filter counter: ~32 ms x %u\n",  
    1 << (cmd_resp_buf[6] & COUNTER_PERIOD_MASK));  
    printf(" + Activation threshold for the tamper filter: %d\n",  
    256 / (1 << ((cmd_resp_buf[6] & COUNTER_THRESHOLD_MASK) >> COUNTER_THRESHOLD_SHIFT)));  
    if (cmd_resp_buf[6] & GLITCH_DETECTOR_MASK) {  
    printf(" + Digital glitch detector always on: Enabled\n");  
    } else {  
    printf(" + Digital glitch detector always on: Disabled\n");  
    }  
    if (device_name[DEVICE_INDEX] > DEVICE_XG22) {  
    if (cmd_resp_buf[6] & SLEEP_ALIVE_MASK) {  
    printf(" + Keep tamper alive during sleep: Enabled\n");  
    } else {  
    printf(" + Keep tamper alive during sleep: Disabled\n");  
    }  
    }  
    printf(" + Tamper reset threshold: %lu\n", cmd_resp_buf[6] >> TAMPER_RESET_SHIFT);  
    #endif  
    ```
12. Add code (take `case DEVICE_XG21:` as reference) to function `prog_main_flash_app()` in `app_process.c.`  
    ```sh  
    #if defined(DEVICE_XG23)  
    case DEVICE_XG23:  
    printf(" + The xG23 application firmware image size is %lu bytes "  
    "and start address is 0x%08lX.\n", get_xg23_app_size(),  
    get_flash_start_addr());  
    printf(" + Erase-Program-Verify the xG23 main flash for "  
    "application firmware image... ");  
    cmd_resp_buf[0] = prog_flash(get_flash_start_addr(),  
    get_xg23_app_size(),  
    (uint32_t *)get_xg23_app_addr());  
    print_cycle_time();  
    break;  
    #endif  
    ```
13. Add code (take `case DEVICE_XG21:` as reference) to function `prog_main_flash_se()` in `app_process.c.`  
    ```sh  
    #if defined(DEVICE_XG23)  
    case DEVICE_XG23:  
    printf(" + The xG23 HSE firmware image version: %08lX\n",  
    *((uint32_t *)get_xg23_hse_addr() + 3));  
    printf(" + The xG23 HSE firmware image size is %lu bytes and start "  
    "address is 0x%08lX.\n", get_xg23_hse_size(),  
    SE_START_ADDR + get_flash_start_addr());  
    printf(" + Erase-Program-Verify the xG23 main flash for HSE "  
    "firmware image... ");  
    cmd_resp_buf[0] = prog_flash(SE_START_ADDR + get_flash_start_addr(),  
    get_xg23_hse_size(),  
    (uint32_t *)get_xg23_hse_addr());  
    print_cycle_time();  
    break;  
    #endif  
    ```
14. Add code (take `case DEVICE_XG21:` as reference) to function `prog_main_flash_se_app()` in `app_process.c.`  
    ```sh  
    #if defined(DEVICE_XG23)  
    case DEVICE_XG23:  
    printf(" + The xG23 HSE upgrade application firmware image size is "  
    "%lu bytes and start address is 0x%08lX.\n",  
    get_xg23_hse_upgrade_app_size(), get_flash_start_addr());  
    printf(" + Erase-Program-Verify the xG23 main flash for "  
    "application to upgrade \n");  
    printf(" HSE firmware... ");  
    cmd_resp_buf[0] = prog_flash(get_flash_start_addr(),  
    get_xg23_hse_upgrade_app_size(),  
    (uint32_t *)get_xg23_hse_upgrade_app_addr());  
    print_cycle_time();  
    break;  
    #endif  
    ```
15. Add code (take `case DEVICE_XG21:` as reference) to function `prog_main_flash_signed()` in `app_process.c.`  
    ```sh  
    #if defined(DEVICE_XG23)  
    case DEVICE_XG23:  
    printf(" + The xG23 signed firmware image size is %lu bytes "  
    "and start address is 0x%08lX.\n",  
    get_xg23_signed_size(),  
    get_flash_start_addr());  
    printf(" + Erase-Program-Verify the xG23 main flash for "  
    "signed firmware image... ");  
    cmd_resp_buf[0] = prog_flash(get_flash_start_addr(),  
    get_xg23_signed_size(),  
    (uint32_t *)get_xg23_signed_addr());  
    print_cycle_time();  
    break;  
    #endif  
    ```
16. For xG23 devices, the user data can be erased the same way as any page in the main flash. Add code (take `case DEVICE_XG22:` as reference) to function `erase_user_data()` in `app_process.c.`  
    ```sh  
    #if defined(DEVICE_XG23)  
    case DEVICE_XG23:  
    printf("\n . Erase xG23 user data... ");  
    cmd_resp_buf[0] = erase_flash(true);  
    print_cycle_time();  
    hard_reset_target();  
    break;  
    #endif  
    ```
17. For xG23 devices, the user data can be written the same way as any page in the main flash. Add code (take `case DEVICE_XG22:` as reference) to function `prog_user_data()` in `app_process.c.`  
    ```sh  
    #if defined(DEVICE_XG23)  
    case DEVICE_XG23:  
    printf("\n . Program xG23 user data.\n");  
    printf(" + User data size is %lu bytes and start address is 0x%08lX.\n",  
    get_userdata_size(), USERDATA_BASE);  
    printf(" + Erase-Program-Verify the xG23 user data... ");  
    cmd_resp_buf[0] = prog_flash(USERDATA_BASE,  
    get_userdata_size(),  
    (uint32_t *)get_userdata_addr());  
    print_cycle_time();  
    hard_reset_target();  
    break;  
    #endif  
    ```
18. There is no need to change the default settings in [Compile Options](06-series-2-dci-and-swd-programming-examples#compile-options) for xG23 devices.  
    > **Note**: The 512 kB flash of EFR32MG22C224F512 is not enough to store the [firmware images](06-series-2-dci-and-swd-programming-examples#firmware-images) when the programmer needs to support more Series 2 devices. Users can selectively comment out the defines in `app_process.h` to save memory for the required Series 2 devices.  
    ```sh  
    /// xG21 device  
    #define DEVICE_XG21 (0x31)  
      
    /// xG22 device  
    define DEVICE_XG22 (0x32)  
      
    /// xG23 device  
    #define DEVICE_XG23 (0x33)  
      
    ```

##### Use J-Link Commander to Run DCI Command

J-Link Commander from [Segger](https://www.segger.com/downloads/jlink/) is a command line-based utility that supports simple commands to verify and communicate with the target connection. There are two ways to run the DCI command through the J-Link Commander.

1. [J-Link Command File](https://wiki.segger.com/J-Link_Commander#Using_J-Link_Command_Files) (*.jlink)
2. [J-Link Script File](https://wiki.segger.com/J-Link_script_files#Using_J-Link_script_files) (*.JLinkScript)

This application note uses J-Link Commander v7.66g. The J-Link Commander's command line interface is invoked by JLink.exe located in `C:\Program Files\SEGGER\JLink_V766g` (Windows).

###### J-Link Command File

The J-Link Command file below is an example to invoke the J-Link Commander in batch processing mode for the [Erase Device](05-se-command-list) operation. The delay (`sleep 500`) in milliseconds after writing the command word is device dependent. It may be shorter or longer.

```sh
connect
swdwritedp 2 0x01000000 ; Select DCI AP
swdwriteap 1 0x1008 ; DCI STATUS
swdreadap 3 
swdreaddp 3 ; Poll till DCI_STATUS.WPENDING (bit 0) is low
swdwriteap 1 0x1000 ; DCI WDATA
swdwriteap 3 0x08 ; Command word 0
sleep 500 ; Delay if necessary
swdwriteap 1 0x1008 ; DCI STATUS
swdreadap 3 
swdreaddp 3 ; Poll till DCI_STATUS.WPENDING (bit 0) is low
swdwriteap 1 0x1000 ; DCI WDATA
swdwriteap 3 0x430F0000 ; command word 1 (Erase Device)
sleep 500 ; Delay if necessary
swdwriteap 1 0x1008 ; DCI STATUS
swdreadap 3 
swdreaddp 3 ; Poll till DCI_STATUS.RDATAVALID (bit 8) is high
swdwriteap 1 0x1004 ; DCI RDATA
swdreadap 3 ; First word
swdreaddp 3 ; Status code is upper 16 bits, total length is lower 16 bits
exit
```

The example below is to run the J-Link Command file (assume `EraseDevice.jlink` is in the J-Link Commander folder) via the Windows DOS command prompt. The target device is EFR32MG21A, and the target interface is 1000 kHz SWD. The SWD speed is hardware dependent; for example, the length of the wires between the programmer and the device debug pins. The speed (frequency) can be higher for shorter wires and lower for longer wires.

```sh
JLink.exe -device EFR32MG21AXXXF1024 -if SWD -speed 1000 -CommandFile EraseDevice.jlink
```

```sh
SEGGER J-Link Commander V7.66g (Compiled Jul 7 2022 10:44:29)
DLL version V7.66g, compiled Jul 7 2022 10:42:43
J-Link Command File read successfully.
Processing script file...
J-Link>connect
J-Link connection not established yet but required for command.
Connecting to J-Link via USB...O.K.
Firmware: Silicon Labs J-Link Pro OB compiled Sep 16 2020 17:10:58
Hardware version: V4.00
S/N: 440048205
License(s): RDI, FlashBP
IP-Addr: DHCP (no addr. received yet)
VTref=3.326V
Device "EFR32MG21AXXXF1024" selected.
Connecting to target via SWD
Found SW-DP with ID 0x6BA02477
DPv0 detected
CoreSight SoC-400 or earlier
Scanning AP map to find all available APs
AP[3]: Stopped AP scan as end of AP map has been reached
AP[0]: AHB-AP (IDR: 0x84770001)
AP[1]: APB-AP (IDR: 0x54770002)
AP[2]: AHB-AP (IDR: 0x84770001)
Iterating through AP map to find AHB-AP to use
AP[0]: Core found
AP[0]: AHB-AP ROM base: 0xE00FE000
CPUID register: 0x410FD213. Implementer code: 0x41 (ARM)
Feature set: Mainline
Found Cortex-M33 r0p3, Little endian.
FPUnit: 8 code (BP) slots and 0 literal slots
Security extension: implemented
Secure debug: enabled
CoreSight components:
ROMTbl[0] @ E00FE000
[0][0]: E00FF000 CID B105100D PID 000BB4C9 ROM Table
ROMTbl[1] @ E00FF000
[1][0]: E000E000 CID B105900D PID 000BBD21 DEVARCH 47702A04 DEVTYPE 00 Cortex-M33
[1][1]: E0001000 CID B105900D PID 000BBD21 DEVARCH 47701A02 DEVTYPE 00 DWT
[1][2]: E0002000 CID B105900D PID 000BBD21 DEVARCH 47701A03 DEVTYPE 00 FPB
[1][3]: E0000000 CID B105900D PID 000BBD21 DEVARCH 47701A01 DEVTYPE 43 ITM
[1][5]: E0041000 CID B105900D PID 002BBD21 DEVARCH 47724A13 DEVTYPE 13 ETM
[1][6]: E0042000 CID B105900D PID 000BBD21 DEVARCH 47701A14 DEVTYPE 14 CSS600-CTI
[0][1]: E0040000 CID B105900D PID 000BBD21 DEVARCH 00000000 DEVTYPE 11 Cortex-M33
[0][2]: E00FD000 CID B105F00D PID 001BB101 TSG
Cortex-M33 identified.
J-Link>swdwritedp 2 0x01000000 ; Select DCI AP
Write DP register 2 = 0x01000000
J-Link>swdwriteap 1 0x1008 ; DCI STATUS
Write AP register 1 = 0x00001008
J-Link>swdreadap 3
Read AP register 3 = 0x000000B1
J-Link>swdreaddp 3 ; Poll till DCI_STATUS.WPENDING (bit 0) is low
Read DP register 3 = 0x00000000
J-Link>swdwriteap 1 0x1000 ; DCI WDATA
Write AP register 1 = 0x00001000
J-Link>swdwriteap 3 0x08 ; Command word 0
Write AP register 3 = 0x00000008
J-Link>sleep 500 ; Delay if necessary
Sleep(500)
J-Link>swdwriteap 1 0x1008 ; DCI STATUS
Write AP register 1 = 0x00001008
J-Link>swdreadap 3
Read AP register 3 = 0x00000000
J-Link>swdreaddp 3 ; Poll till DCI_STATUS.WPENDING (bit 0) is low
Read DP register 3 = 0x00000000
J-Link>swdwriteap 1 0x1000 ; DCI WDATA
Write AP register 1 = 0x00001000
J-Link>swdwriteap 3 0x430F0000 ; command word 1 (Erase Device)
Write AP register 3 = 0x430F0000
J-Link>sleep 500 ; Delay if necessary
Sleep(500)
J-Link>swdwriteap 1 0x1008 ; DCI STATUS
Write AP register 1 = 0x00001008
J-Link>swdreadap 3
Read AP register 3 = 0x00000000
J-Link>swdreaddp 3 ; Poll till DCI_STATUS.RDATAVALID (bit 8) is high
Read DP register 3 = 0x00000100
J-Link>swdwriteap 1 0x1004 ; DCI RDATA
Write AP register 1 = 0x00001004
J-Link>swdreadap 3 ; First word
Read AP register 3 = 0x00000100
J-Link>swdreaddp 3 ; Status code is upper 16 bits, total length is lower 16 bits
Read DP register 3 = 0x00000004
J-Link>exit
Script processing completed.
```

###### J-Link Script File (heading level 7)

The J-Link Script file below is an example to customize some actions performed by the J-Link Commander for the [Read Serial Number](05-se-command-list#read-serial-number) operation.

```sh
// Function to select DCI AP register bank 0
int SelectDciAp(void)
{
 int stat;
 int value;
 JLINK_SYS_Report("- Select DCI AP register bank 0\n");
 value = 0x01000000; 
 stat = JLINK_CORESIGHT_WriteDP(2, value);
 if (stat < 0) {
 JLINK_SYS_Report1("- DP 2 write failed: ", value);
 }
 return stat;
}
// Function to write command to DCI_WDATA (0x1000) 
int WriteCommandWord(int command)
{
 int stat;
 int value;
 // Poll DCI_STATUS (0x1008) WPENDING bit (bit 0)
 do {
 value = 0x00001008;
 stat = JLINK_CORESIGHT_WriteAP(1, value);
 if (stat < 0) {
 JLINK_SYS_Report1("- AP 1 write failed: ", value);
 return stat;
 }
 JLINK_CORESIGHT_ReadAP(3);
 value = JLINK_CORESIGHT_ReadDP(3);
 if (value == -1) {
 JLINK_SYS_Report("- DP 3 read failed");
 return value;
 }
 if ((value & 0x0100) != 0) {
 JLINK_SYS_Report("- RDATAVALID is high, command aborted");
 return -1;
 } 
 } while ((value & 0x01) != 0);
 
 // Write command
 value = 0x00001000;
 stat = JLINK_CORESIGHT_WriteAP(1, value);
 if (stat < 0) {
 JLINK_SYS_Report1("- AP 1 write failed: ", value);
 return stat;
 }
 value = command;
 stat = JLINK_CORESIGHT_WriteAP(3, value);
 if (stat < 0) {
 JLINK_SYS_Report1("- AP 3 write failed: ", value);
 return stat;
 }
 JLINK_SYS_Report1("- Write command word ", command); 
 return stat;
}
// Function to read response from DCI_RDATA (0x1004)
int ReadResponse(void)
{
 int stat;
 int value;
 int count;
// Poll DCI_STATUS (0x1008) RDATAVALID bit (bit 8) 
 do {
 value = 0x00001008;
 stat = JLINK_CORESIGHT_WriteAP(1, value);
 if (stat < 0) {
 JLINK_SYS_Report1("- AP 1 write failed: ", value);
 return stat;
 }
 JLINK_CORESIGHT_ReadAP(3);
 value = JLINK_CORESIGHT_ReadDP(3);
 if (value == -1) {
 JLINK_SYS_Report("- DP 3 read failed");
 return value;
 }
 } while ((value & 0x0100) != 0x0100);
 
 // Read first 32-bit response word 
 value = 0x00001004;
 stat = JLINK_CORESIGHT_WriteAP(1, value);
 if (stat < 0) {
 JLINK_SYS_Report1("- AP 1 write failed: ", value);
 return stat;
 }
 JLINK_CORESIGHT_ReadAP(3);
 value = JLINK_CORESIGHT_ReadDP(3);
 if (value == -1) {
 JLINK_SYS_Report("- DP 3 read failed");
 return value;
 }
 // Get response count
 count = value & 0x00FF; 
 count = count >> 2;
 JLINK_SYS_Report1("- Number of 32-bit response word: ", count);
 // Get response code
 JLINK_SYS_Report1("- Response: ", value);
 stat = value >>16; 
 if (stat != 0) {
 JLINK_SYS_Report1("- Command error: ", stat);
 return -1;
 }
 
 // Read following 32-bit response word
 count = count - 1;
 while (count != 0) {
 // Poll DCI_STATUS (0x1008) RDATAVALID bit (bit 8) 
 do {
 value = 0x00001008;
 stat = JLINK_CORESIGHT_WriteAP(1, value);
 if (stat < 0) {
 JLINK_SYS_Report1("- AP 1 write failed: ", value);
 return stat;
 }
 JLINK_CORESIGHT_ReadAP(3);
 value = JLINK_CORESIGHT_ReadDP(3);
 if (value == -1) {
 JLINK_SYS_Report("- DP 3 read failed");
 return value;
 }
 } while ((value & 0x0100) != 0x0100);
 
 // Read 32-bit response word 
 value = 0x00001004;
 stat = JLINK_CORESIGHT_WriteAP(1, value);
 if (stat < 0) {
 JLINK_SYS_Report1("- AP 1 write failed: ", value);
 return stat;
  }
 JLINK_CORESIGHT_ReadAP(3);
 value = JLINK_CORESIGHT_ReadDP(3);
 if (value == -1) {
 JLINK_SYS_Report("- DP 3 read failed");
 return value;
 }
 JLINK_SYS_Report1("- Response: ", value);
 count = count - 1;
 }
 return 0;
}
// Function to connect DCI
int ConnectDci(void)
{ 
 int stat;
 int value;
 
 JLINK_SYS_Report("\n******************************************************");
 JLINK_SYS_Report("Connect to Series 2 Device DCI\n");
 JLINK_SYS_Report("- Select SWD by sending SWD switching sequence\n");
 stat = JLINK_CORESIGHT_Configure(""); 
 if (stat < 0) {
 JLINK_SYS_Report("- SWD connection failed");
 return stat;
 }
 // Manually select debug interface 
 JLINK_SYS_Report("- Clear sticky error flags\n");
 value = 0x0000001E; 
 stat = JLINK_CORESIGHT_WriteDP(0, value); 
 if (stat < 0) {
 JLINK_SYS_Report1("- DP 0 write failed: ", value);
 return stat;
 }
 JLINK_SYS_Report("- Power up system and debug\n");
 value = 0x50000000; 
 stat = JLINK_CORESIGHT_WriteDP(1, value); 
 if (stat < 0) {
 JLINK_SYS_Report1("- DP 1 write failed: ", value);
 return stat;
 }
 
 value = JLINK_CORESIGHT_ReadDP(0); 
 if (value != 0x6BA02477) {
 JLINK_SYS_Report1("- The connected device does not have a Secure Element (ID Code): ", value);
 return -1;
 }
 JLINK_SYS_Report1("- Read SWD-DP ID code: ", value);
 JLINK_SYS_Report("- Select DCI AP register bank 0\n");
 value = 0x01000000; 
 stat = JLINK_CORESIGHT_WriteDP(2, value); 
 if (stat < 0) {
 JLINK_SYS_Report1("- DP 2 write failed: ", value);
 return stat;
 }
 
 JLINK_SYS_Report("- Set up AP defaults\n");
 value = 0x22000002; 
 stat = JLINK_CORESIGHT_WriteAP(0, value); 
 if (stat < 0) {
 JLINK_SYS_Report1("- AP 0 write failed: ", value); 
 return stat;
 }
 JLINK_SYS_Report("Connect to Series 2 Device DCI OK\n");
 JLINK_SYS_Report("******************************************************");
 return 0;
}
// Function to run DCI command
void RunDciCommand(void)
{
 JLINK_SYS_Report("\n******************************************************");
 JLINK_SYS_Report("Read Serial Number\n");
 if (SelectDciAp() == -1) {
 JLINK_SYS_Report("- Select DCI AP failed\n");
 JLINK_SYS_Report("******************************************************\n\n");
 return;
 }
 if (WriteCommandWord(0x00000008) == -1) {
 JLINK_SYS_Report("- Write DCI command (length) failed\n");
 JLINK_SYS_Report("******************************************************\n\n");
 return;
 }
 if (WriteCommandWord(0xFE000000) == -1) {
 JLINK_SYS_Report("- Write DCI command (ID) failed\n");
 JLINK_SYS_Report("******************************************************\n\n");
 return;
 }
 if (ReadResponse() == -1) {
 JLINK_SYS_Report("- Read DCI command response failed\n");
 JLINK_SYS_Report("******************************************************\n\n");
 return;
 }
 JLINK_SYS_Report("Read Serial Number Done\n");
 JLINK_SYS_Report("******************************************************\n\n");
}
// Replace ConfigTargetSettings() in J-Link DLL
void ConfigTargetSettings(void)
{
 if (ConnectDci() == -1) {
 JLINK_SYS_Report("- Connect to DCI failed\n");
 JLINK_SYS_Report("******************************************************\n\n");
 return;
 }
 RunDciCommand();
}
```

The example below is to run the J-Link Script file (assume `ReadSerialNo.JLinkScript` is in the J-Link Commander folder) via the Windows DOS command prompt. The target device is EFR32MG21A, and the target interface is 1000 kHz SWD. The SWD speed is hardware dependent; for example, the length of the wires between the programmer and the device debug pins. It can be higher for shorter wires and lower for longer wires.

```sh
JLink -device EFR32MG21AXXXF1024 -if SWD -speed 1000 -autoConnect 1 -JLinkScriptFile ReadSerialNo.JlinkScript

```

```sh
SEGGER J-Link Commander V7.66g (Compiled Jul 7 2022 10:44:29)
DLL version V7.66g, compiled Jul 7 2022 10:42:43
Connecting to J-Link via USB...O.K.
Firmware: Silicon Labs J-Link Pro OB compiled Sep 16 2020 17:10:58
Hardware version: V4.00
S/N: 440048205
License(s): RDI, FlashBP
IP-Addr: DHCP (no addr. received yet)
VTref=3.328V
Device "EFR32MG21AXXXF1024" selected.
Connecting to target via SWD
ConfigTargetSettings() start
******************************************************
Connect to Series 2 Device DCI
- Select SWD by sending SWD switching sequence
- Clear sticky error flags
- Power up system and debug
- Read SWD-DP ID code: 0x6BA02477
- Select DCI AP register bank 0
- Set up AP defaults
```

```sh
Connect to Series 2 Device DCI OK
******************************************************
******************************************************
Read Serial Number
- Select DCI AP register bank 0
- Write command word 0x00000008
- Write command word 0xFE000000
- Number of 32-bit response word: 0x00000005
- Response: 0x00000014
- Response: 0x00000000
- Response: 0x00000000
- Response: 0xFF818E58
- Response: 0xE53470FE
Read Serial Number Done
******************************************************
ConfigTargetSettings() end
```

#### Series 2 TrustZone

##### Series 2 TrustZone

> **Note: This section replaces _AN1374: TrustZone_. Further updates to this user guide will be provided here**.

ARMv8-M TrustZone is a technology that provides a foundation for improved system security in embedded applications. It allows the ARMv8-M to be aware of the security states of the system. Series 2 devices use the Cortex-M33 core to implement the ARMv8-M TrustZone security extension, which provides the ability to restrict access to peripherals and memory regions based on the processor security attribute. TrustZone works with the MPU, which controls privileged/unprivileged execution of code to provide a complete security solution.

ARMv8-M TrustZone is an extensive topic. The references below are publicly available on the [ARM Developer Documentation](https://developer.arm.com/docs) website.

- [ARMv8-M Architecture Reference Manual](https://developer.arm.com/documentation/ddi0553/latest)
- [ARMv8-M Architecture Technical Overview](https://community.arm.com/cfs-file/__key/telligent-evolution-components-attachments/01-2142-00-00-00-00-66-90/Whitepaper-_2D00_-ARMv8_2D00_M-Architecture-Technical-Overview.pdf)
- [ARM Cortex-M33 Processor Technical Reference Manual](https://developer.arm.com/documentation/100230/latest)
- [System Design with ARMv8-M](https://developer.arm.com/documentation/100767/0100/System-Design-for-ARMv8-M)
- [TrustZone technology for ARMv8-M Architecture](https://developer.arm.com/documentation/100690/latest/)
- [ARM Cortex-M33 Devices Generic User Guide](https://developer.arm.com/documentation/100235/latest)
- [Secure software guidelines for ARMv8-M](https://developer.arm.com/documentation/100720/0300)
- [Software Development in ARMv8-M Architecture](https://community.arm.com/cfs-file/__key/telligent-evolution-components-attachments/01-2142-00-00-00-01-27-19/ARM-Cortex-_2D00_-session-11-_2D00_-Yiu-_2D00_-Software-Development-in-ARMv8_2D00_M-Architecture.pdf)Reading guides:
- Beginner
- Minimal experience with TrustZone, starting with [TrustZone Basics](02-r-basic)
- Intermediate - Have a basic understanding of the TrustZone technology, starting with [Bus Level Security](03-r-bls)
- Advanced - Developed experience on TrustZone, starting with [TrustZone Implementation](05-r-implementation)-Demo - Starting with [TrustZone Platform Examples](05-r-implementation)

###### Key Points

- TrustZone Basics
- Bus Level Security (BLS)
- Secure and Privileged Programming Model
- TrustZone Implementation
- Upgrade Existing Application to TrustZone
- TrustZone Platform Examples

##### Series 2 Device Security Features

Protecting IoT devices against security threats is central to a quality product. Silicon Labs offers several security options to help developers build secure devices, secure application software, and secure communication paths to manage those devices. Silicon Labs’ security offerings were significantly enhanced by the introduction of the Series 2 products that included a Secure Engine. The Secure Engine is a tamper-resistant component used to securely store sensitive data and keys, and to execute cryptographic functions and secure services.

On Series 2 devices, the security features are implemented by the Secure Engine and CRYPTOACC (if available). The Secure Engine may be hardware-based or virtual (software-based). Throughout this document, the following abbreviations are used:

- HSE - Hardware Secure Engine
- VSE - Virtual Secure Engine
- SE - Secure Engine (either HSE or VSE)

Additional security features are provided by Secure Vault. Three levels of Secure Vault feature support are available, depending on the part and SE implementation, as reflected in the following table:

<table>
    <thead>
        <tr>
            <th>
                <p>Level (1)</p>
            </th>
            <th>
                <p>SE Support</p>
            </th>
            <th>
                <p>Part</p>
            </th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Secure Vault High (SVH)</p>
            </td>
            <td>
                <p>HSE only (HSE-SVH)</p>
            </td>
            <td>
                <p>Refer to <a href="https://docs.silabs.com/iot-security/latest/iot-endpoint-security-fundamentals/">IoT Endpoint Security Fundamentals</a> for details on supporting devices.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Secure Vault Mid (SVM)</p>
            </td>
            <td>
                <p>HSE (HSE-SVM)</p>
            </td>
            <td>
                <p>"</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Secure Vault Mid (SVM)</p>
            </td>
            <td>
                <p>VSE (VSE-SVM)</p>
            </td>
            <td>
                <p>"</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Secure Vault Base (SVB)</p>
            </td>
            <td>
                <p>N/A</p>
            </td>
            <td>
                <p>"</p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**: 1. The features of different Secure Vault levels can be found in [https://www.silabs.com/security](https://www.silabs.com/security).

Secure Vault Mid consists of two core security functions:

- Secure Boot: Process where the initial boot phase is executed from an immutable memory (such as ROM) and where code is authenticated before being authorized for execution.
- Secure Debug Access Control: The ability to lock access to the debug ports for operational security, and to securely unlock them when access is required by an authorized entity.

Secure Vault High offers additional security options:

- Secure Key Storage: Protects cryptographic keys by "wrapping" or encrypting the keys using a root key known only to the HSE-SVH.
- Anti-Tamper protection: A configurable module to protect the device against tamper attacks.
- Device authentication: Functionality that uses a secure device identity certificate along with digital signatures to verify the source or target of device communications.

Series 2 devices require a specific [SE firmware version](06-r-migration) to support the TrustZone implementation. Refer to [Production Programming of Series 2 and Series 3 Devices](https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/) to learn how to upgrade the SE firmware and [IoT Endpoint Security Fundamentals](https://docs.silabs.com/iot-security/latest/iot-endpoint-security-fundamentals/) for the latest SE Firmware shipped with Series 2 devices and modules.

Series 2 devices use Cortex-M33 core to implement the ARMv8-M Mainline TrustZone security extension and refer to TrustZone as [Bus Level Security](03-r-bls). The following table lists the configuration of TrustZone related components in the Series 2 Cortex-M33 core.

<table>
    <thead>
        <tr>
            <th>
                <p>Component</p>
            </th>
            <th>
                <p>Series 2 Configuration</p>
            </th>
            <th>
                <p>Description</p>
            </th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Security Extension (TrustZone)</p>
            </td>
            <td>
                <p>Enabled</p>
            </td>
            <td>
                <p>The security extension cannot be disabled, and the entire memory after RESET is Secure by default.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Memory Protection Unit (MPU)</p>
            </td>
            <td>
                <p>16 regions (maximum)</p>
            </td>
            <td>
                <p>The MPU regions for both Secure and Non-secure MPUs.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Security Attribution Unit (SAU)</p>
            </td>
            <td>
                <p>8 regions (maximum)</p>
            </td>
            <td>
                <p>The SAU regions for Non-secure and Non-secure Callable.</p>
            </td>
        </tr>
    </tbody>
</table>

##### TrustZone Basics

###### Introduction

TrustZone for ARMv8-M adds extra states to the Cortex-M processor operations to ensure there is  a Secure and Non-secure state. These security states are orthogonal to the existing Thread and Handler modes, thereby having both a Thread and Handler mode in both Secure and Non-secure states. The Thread mode can also be either Privileged or Unprivileged.

![Operation States and Modes of TrustZone Implementation](/series2-trustzone/0.2/images/sld717-operation-states-and-modes-of-trustzone-implementation.png)

_Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved._

TrustZone for ARMv8-M is an optional architecture extension. By default, the system starts up in a Secure state if the processor implements the TrustZone security extension. The division of Secure and Non-secure worlds is memory-map based (security state depends on the address of the fetched instruction), and the transitions happen automatically. It is also possible to leave the Non-secure state unused and execute the whole application in the Secure state.

###### Memory Security Attributes

TrustZone classifies memory into four security attributes as described in the following table.

<table>
    <thead>
        <tr>
            <th>
                <p>Security Attribute</p>
            </th>
            <th>
                <p>Processor State</p>
            </th>
            <th>
                <p>Description</p>
            </th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Non-secure (NS)</p>
            </td>
            <td>
                <p>Non-secure</p>
            </td>
            <td>
                <p>Non-secure and Secure software can access these memory regions.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Secure (S)</p>
            </td>
            <td>
                <p>Secure</p>
            </td>
            <td>
                <p>Secure software can access these memory regions. Non-secure software cannot gain access to the Secure memory.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Non-secure Callable (NSC)</p>
            </td>
            <td>
                <p>Secure</p>
            </td>
            <td>
                <p>Secure memory with an NSC attribute provides entry points for Secure APIs that can be called from a Non-secure space. It is a region of memory that contains the Secure Gateway (SG) veneers that allow Non-secure code to call secure functions that exist in Secure code. Non-secure software cannot read/write to an NSC memory but can branch into it if the branch target is an SG instruction.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Exempted</p>
            </td>
            <td>
                <p>Secure/Non-secure</p>
            </td>
            <td>
                <p>Non-secure and Secure software can access these memory regions (exempted from security checking). Exempted regions are typically used by debugging components that do not pose any security risk (e.g., system ROM table) when accessed by the Non-secure software.</p>
            </td>
        </tr>
    </tbody>
</table>

> **Note:** The [Non-secure Callable](#switching-from-non-secure-to-secure-state) is also known as Secure Non-secure Callable (Secure NSC) to declare that this region resides in Secure memory.

###### Banked Register

The concept of a banked register in ARMv8-M between Secure and Non-secure states means that there are two copies of the register, and the core automatically uses the copy that belongs to the current security state. When a register is banked, the `_S` and `_NS` suffixes are used in the ARMv8-M architecture to identify whether the resource is for the Secure state or Non-secure state.

###### General-Purpose Registers (heading level 7)

The Cortex-M processors have 16 general-purpose registers (R0 - R15) for data processing (R0 - R12) and control. The following figure shows the general-purpose register view of the ARMv8-M system with TrustZone. Refer to the [ARM Cortex-M33 Devices Generic User Guide](https://developer.arm.com/documentation/100235/0100/The-Cortex-M33-Processor/Programmer-s-model/Core-registers) for details about these registers.

![General-Purpose Register View with TrustZone](/series2-trustzone/0.2/images/sld717-general-purpose-register-view-with-trustzone.png)

_Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved._

The Secure or Non-secure state can access the data processing registers R0 - R12 and special usage registers R13 - R15. The register R13 (banked SP) is the stack pointer alias, and the actual stack pointer (`MSP_NS`, `PSP_NS`, `MSP_S`, `PSP_S`) accessed depends on the state (Secure or Non-secure) and mode (Handler or Thread) as described in the following figure.

In addition, stack limit registers ([special registers](#special-purpose-registers)) enable hardware to detect stack overflow conditions. Two pairs of [stack limit registers](#special-purpose-registers) (`MSPLIM_NS` and `PSPLIM_NS`, `MSPLIM_S` and `PSPLIM_S`) are implemented, one per security state, to protect the Main Stack Pointer (MSP) and Process Stack Pointer (PSP).

![Banked Registers in the General-Purpose Registers](/series2-trustzone/0.2/images/sld717-banked-registers-in-the-general-purpose-registers.png)

_Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved._

In Thread mode, execution can be privileged or unprivileged. The stack pointer used can be the MSP or PSP, depending on the `SPSEL` bit in the [CONTROL](#special-purpose-registers) register. When in Handler mode, the processor is Privileged. The stack pointer is always MSP.

It is possible to directly [access](https://arm-software.github.io/CMSIS_5/Core/html/group__Core__Register__gr.html) the stack pointers (MSP and PSP) and stack limit registers (MSPLIM and PSPLIM), providing that the processor is in a privileged state. If the processor is in a Secure privileged state, the software can also access the Non-secure stack pointers (`MSP_NS` and `PSP_NS`) through [Core Register Access Functions](https://arm-software.github.io/CMSIS_5/Core/html/group__coreregister__trustzone__functions.html) in CMSIS-Core.

###### Special-Purpose Registers (heading level 7)

Except for the general-purpose registers, there are several special-purpose registers for conditional flags, interrupt masking, control, and stack pointer limit. The following figure shows the special-purpose registers view of the ARMv8-M system with TrustZone. Refer to the [ARM Cortex-M33 Devices Generic User Guide](https://developer.arm.com/documentation/100235/0100/The-Cortex-M33-Processor/Programmer-s-model/Core-registers) for details about these registers.

![Special-purpose Registers View with TrustZone](/series2-trustzone/0.2/images/sld717-special-purpose-registers-view-with-trustzone.png)

Image:_[https://documentation-service.arm.com/](https://documentation-service.arm.com/static/5e7cd7b67158f500bd5c4eea?token=).Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved._

The Combined Program Status Register (xPSR) consists of the Application Program Status Register (APSR), Interrupt Program Status Register (IPSR), and Execution Program Status Register (EPSR).

Some of the special-purpose registers are banked between Secure and Non-secure states. Special-purpose registers are not memory-mapped and can be [accessed](https://arm-software.github.io/CMSIS_5/Core/html/group__Core__Register__gr.html) using Core Register Access Functions in CMSIS-Core (except for EPSR in xPSR).

Secure privileged software can also access the Non-secure interrupt masking registers (`PRIMASK_NS`, `FAULTMASK_NS`, and `BASEPRI_NS`), CONTROL register (`CONTROL_NS`), and stack limit registers (`MSPLIM_NS` and `PSPLIM_NS`) through [Core Register Access Functions](https://arm-software.github.io/CMSIS_5/Core/html/group__coreregister__trustzone__functions.html) in CMSIS-Core.

###### System Private Peripheral Bus (PPB) (heading level 7)

The banking of registers is usually used to separate the Secure and Non-secure information of the system components inside the processor. The following figure shows the System Private Peripheral Bus (PPB) registers view of the ARMv8-M system with TrustZone. Refer to the [ARM Cortex-M33 Devices Generic User Guide](https://developer.arm.com/documentation/100235/0100/The-Cortex-M33-Peripherals/About-the-Cortex-M33-peripherals) for details about the System PPB registers.

![System Private Peripheral Bus (PPB) Registers View with TrustZone](/series2-trustzone/0.2/images/sld717-system-private-peripheral-bus-ppb-registers-view-with-trustzone.png)

**System components for debugging and trace operations (`0xE0000000` to `0xE0002FFF`)**:

- Instrumentation Trace Macrocell (ITM)
- Data Watch point and Trace unit (DWT)
- Flash Patch and Breakpoint unit (FPB)

**System Control Space (SCS)**:

- The registers in SCS address spaces are memory-mapped and can be accessed using pointers in software
- Secure SCS (`0xE000E000` to `0xE000EFFF`) - Secure software using this address space to access the banked Secure SCS registers (e.g., `SCB->CPUID`)
- Non-secure SCS (`0xE000E000` to `0xE000EFFF`) - Non-secure software using this address space to access the banked Non-secure SCS registers (e.g., `SCB->CPUID`)
- Non-secure alias SCS (`0xE002E000` to `0xE002EFFF`) - Secure software using this address space to access the Non-secure SCS registers (e.g., `SCB_NS->CPUID`)

The following table describes some core peripherals in the SCS and corresponding [data structures](https://arm-software.github.io/CMSIS_5/Core/html/annotated.html) defined in the CMSIS-Core header file to access the registers of core peripherals in two SCS address spaces.

<table>
    <thead>
        <tr>
            <th>
                <p>Core Peripheral</p>
            </th>
            <th>
                <p>Data Structure for Secure and NS SCS</p>
            </th>
            <th>
                <p>Data Structure for NS Alias SCS</p>
            </th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Implementation Control Block</p>
            </td>
            <td>
                <p>SCnSCB (0xE000E004)</p>
            </td>
            <td>
                <p>SCnSCB_NS (0xE002E004)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SysTick Timer</p>
            </td>
            <td>
                <p>SysTick (0xE000E010)</p>
            </td>
            <td>
                <p>SysTick_NS (0xE002E010)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Nested Vectored Interrupt Controller</p>
            </td>
            <td>
                <p>NVIC (0xE000E100)</p>
            </td>
            <td>
                <p>NVIC_NS (0xE002E100)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>System Control Block</p>
            </td>
            <td>
                <p>SCB (0xE000ECFC)</p>
            </td>
            <td>
                <p>SCB_NS (0xE002ECFC)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Memory Protection Unit</p>
            </td>
            <td>
                <p>MPU (0xE000ED90)</p>
            </td>
            <td>
                <p>MPU_NS (0xE002ED90)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Security Attribution Unit</p>
            </td>
            <td>
                <p>SAU (0xE000EDD0)</p>
            </td>
            <td>
                <p>-</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Debug Control Block</p>
            </td>
            <td>
                <p>CoreDebug (0xE000EDF0)</p>
            </td>
            <td>
                <p>CoreDebug_NS (0xE002EDF0)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Software Interrupt Generation</p>
            </td>
            <td>
                <p>STIR (0xE000EF00)</p>
            </td>
            <td>
                <p>STIR_NS (0xE002EF00)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Floating-Point Extension</p>
            </td>
            <td>
                <p>FPU (0xE000EF34)</p>
            </td>
            <td>
                <p>FPU_NS (0xE002EF34)</p>
            </td>
        </tr>
    </tbody>
</table>

**Notes**:

- The SCB is a group of system control registers for the various usages below.  
  - System Control Register (SCR) to configure processor low power mode  
  - Fault Status Register (xFSR) to provide fault status information  
  - [Vector Table Offset Register (VTOR)](#type-of-exceptions) for vector table relocation
- The [SAU](03-r-bls#security-attribution-unit) register is accessible from the Secure state only.
- The STIR register is not physically banked.
- Core peripherals such as SysTick, SCB, and MPU are duplicated. One instance is Secure and the other one is Non-secure.
- Secure software can use the corresponding functions for ARMv8-M in CMSIS-Core to configure the Non-secure [NVIC](https://arm-software.github.io/CMSIS_5/Core/html/group__nvic__trustzone__functions.html) and [SysTick](https://arm-software.github.io/CMSIS_5/Core/html/group__systick__trustzone__functions.html) through the Non-secure alias SCS.

**Debug or vendor specific components (`0xE0040000` to `0xE00FFFFF`)**:

- Optional debug components (e.g., ETM)
- [External Private Peripheral Bus (EPPB)](03-r-bls#external-secure-attribution-unit-esau) allows designers to add their own debug or vendor-specific components
- [System ROM Table](03-r-bls#external-secure-attribution-unit-esau) is a simple lookup table that enables debug tools to extract the addresses of debug and trace components

###### Secure Attribution Unit (SAU), Implementation Defined Attribution Unit (IDAU), and Memory Protection Unit (MPU)

Two units determine the security attribute of an address:

1. The internal programmable [Secure Attribution Unit (SAU)](https://developer.arm.com/documentation/100690/0201/Attribution-units--SAU-and-IDAU-).
2. The external Implementation Defined Attribution Unit (IDAU), through the IDAU interface, returns the security attribute and region number of an address.

![mpu](/series2-trustzone/0.2/images/sld717-mpu.png)

Three possible configurations to define the security attribute of an address:

1. Internal SAU only
2. External IDAU only
3. A combination of the internal [SAU](03-r-bls#security-attribution-unit) and external IDAU

**Notes**:

- Series 2 devices use configuration 3.
- IDAU in Series 2 devices is the [External Secure Attribution Unit (ESAU)](03-r-bls#external-secure-attribution-unit-esau).

The [Memory Protection Unit (MPU)](https://developer.arm.com/documentation/100699/0100/) is a programmable unit that allows privileged software to define memory access permission. If the TrustZone is enabled, there can be up to two MPUs, one for Secure and one for Non-secure.

- The number of [MPU regions](01-series-2-security-features) for the Secure and the Non-secure MPU can be different.
- The MPU registers are memory-mapped and are placed in the [System Control Space (SCS)](#system-private-peripheral-bus-ppb).
- Secure software can use the [MPU Functions for ARMv8-M](https://arm-software.github.io/CMSIS_5/Core/html/group__mpu8__functions.html) in CMSIS-Core to configure the Non-secure MPU through the [Non-secure alias SCS](#system-private-peripheral-bus-ppb) (`0xE002ED90` - `0xE002EDC4`).

<table>
    <thead>
        <tr>
            <th>Software</th>
            <th>Non-secure MPU Registers</th>
            <th>Secure MPU Registers</th>
            <th>MemManage Fault</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Non-secure privileged</p>
            </td>
            <td>
                <p>0xE000ED90 - 0xE000EDC4</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>Non-secure MPU violation</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Secure privileged</p>
            </td>
            <td>
                <p>0xE002ED90 - 0xE002EDC4</p>
            </td>
            <td>
                <p>0xE000ED90 - 0xE000EDC4</p>
            </td>
            <td>
                <p>Secure MPU violation</p>
            </td>
        </tr>
    </tbody>
</table>

###### Exceptions and Interrupts

###### Type of Exceptions (heading level 7)

The following table describes the types of exceptions in the TrustZone implemented system.

<table>
    <thead>
        <tr>
            <th>Section</th>
            <th>Guidance</th>
            <th>Type</th>
            <th>Default State</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>1 (-)</p>
            </td>
            <td>
                <p>Reset</p>
            </td>
            <td>
                <p>Secure only</p>
            </td>
            <td>
                <p>Secure</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>2 (-14)</p>
            </td>
            <td>
                <p>NMI</p>
            </td>
            <td>
                <p>Configurable</p>
            </td>
            <td>
                <p>Secure</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>3 (-13)</p>
            </td>
            <td>
                <p>HardFault</p>
            </td>
            <td>
                <p>Configurable</p>
            </td>
            <td>
                <p>Secure</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>4 (-12)</p>
            </td>
            <td>
                <p>MemManage Fault</p>
            </td>
            <td>
                <p>Banked</p>
            </td>
            <td>
                <p>Banked</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>5 (-11)</p>
            </td>
            <td>
                <p>BusFault</p>
            </td>
            <td>
                <p>Configurable</p>
            </td>
            <td>
                <p>Secure</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>6 (-10)</p>
            </td>
            <td>
                <p>UsageFault</p>
            </td>
            <td>
                <p>Banked</p>
            </td>
            <td>
                <p>Banked</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>7 (-9)</p>
            </td>
            <td>
                <p>SecureFault</p>
            </td>
            <td>Secure only<p></p>
            </td>
            <td>
                <p>Secure</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>11 (-5)</p>
            </td>
            <td>
                <p>SVCall</p>
            </td>
            <td>
                <p>Banked</p>
            </td>
            <td>
                <p>Banked</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>12 (-4)</p>
            </td>
            <td>
                <p>DebugMonitor</p>
            </td>
            <td>
                <p>Configurable</p>
            </td>
            <td>
                <p>Secure</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>14 (-2)</p>
            </td>
            <td>
                <p>PendSV</p>
            </td>
            <td>
                <p>Banked</p>
            </td>
            <td>
                <p>Banked</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>15 (-1)</p>
            </td>
            <td>
                <p>SysTick</p>
            </td>
            <td>
                <p>Banked</p>
            </td>
            <td>
                <p>Banked</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>16 - 495 (0 - 479)</p>
            </td>
            <td>
                <p>IRQ0 - IRQ479</p>
            </td>
            <td>
                <p>Configurable</p>
            </td>
            <td>
                <p>Secure</p>
            </td>
        </tr>
    </tbody>
</table>

**Notes**:

- "Secure only" means the system exceptions can only trigger in the Secure state.
- "Configurable" means the system exceptions and interrupts can be configured to target either the Secure state or the Non-secure state.
- Banked means the system exceptions can have Secure and Non-secure versions. Both can be triggered and executed independently and have different priority level settings.

###### Exception Priorities (heading level 7)

It may cause a security issue if the Non-secure software uses high priority levels to mask the Secure interrupts. To avoid this issue, TrustZone introduces a programmable bit in the `AIRCR` register called `PRIS` (Prioritize Secure exception) for Secure software to prioritize, if needed, Secure exceptions and interrupts.

The `AIRCR.PRIS` is set to 0 out of reset, which means Secure and Non-secure exceptions/interrupts share the same configurable programmable priority level space (columns 2 and 3 in the following table). When the `AIRCR.PRIS` is set to 1, all Non-secure configurable exceptions/interrupts are placed in the lower half of the priority level space so that Secure exceptions/interrupts can potentially have higher priorities (columns 2 and 4 in the following table).

<table>
    <thead>
        <tr>
            <th>Priority Value</th>
            <th>Secure Priority</th>
            <th>Non-secure Priority (PRIS = 0)</th>
            <th>Non-secure Priority (PRIS = 1)</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>0</p>
            </td>
            <td>
                <p>0</p>
            </td>
            <td>
                <p>0 (0x00)</p>
            </td>
            <td>
                <p>128 (0x80)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>1</p>
            </td>
            <td>
                <p>32</p>
            </td>
            <td>
                <p>32 (0x20)</p>
            </td>
            <td>
                <p>144 (0x90)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>2</p>
            </td>
            <td>
                <p>64</p>
            </td>
            <td>
                <p>64 (0x40)</p>
            </td>
            <td>
                <p>160 (0xA0)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>3</p>
            </td>
            <td>
                <p>96</p>
            </td>
            <td>
                <p>96 (0x60)</p>
            </td>
            <td>
                <p>176 (0xB0)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>4</p>
            </td>
            <td>
                <p>128</p>
            </td>
            <td>
                <p>128 (0x80)</p>
            </td>
            <td>
                <p>192 (0xC0)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>5</p>
            </td>
            <td>
                <p>160</p>
            </td>
            <td>
                <p>160 (0xA0)</p>
            </td>
            <td>
                <p>208 (0xD0)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>6</p>
            </td>
            <td>
                <p>192</p>
            </td>
            <td>
                <p>192 (0xC0)</p>
            </td>
            <td>
                <p>224 (0xE0)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>7</p>
            </td>
            <td>
                <p>224</p>
            </td>
            <td>
                <p>224 (0xE0)</p>
            </td>
            <td>
                <p>240 (0xF0)</p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**: This table uses three bits (Bit [7:5]) of the group priority level (`AIRCR.PRIGROUP`) to limit the maximum number of preemption levels to 8. A lower priority value indicates a higher priority.

###### Vector Tables (heading level 7)

The following figure shows two vector tables for Secure and Non-secure exceptions and interrupts. The vector table offset is defined by a Vector Table Offset Register (`VTOR` at `0xE000ED08`), which can only be programmed in the privileged state.

![vector-table](/series2-trustzone/0.2/images/sld717-vector-table.png)

Image: _[Vector Table](https://developer.arm.com/documentation/100235/0100/The-Cortex-M33-Processor/Exception-model/Vector-table).Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved._

**Notes**:

- The `VTOR_S` defines the address of the Secure vector table in Secure memory, and the [Secure Main Stack Pointer](#general-purpose-registers) (`MSP_S`) is the default stack for the Secure exception handler.
- The `VTOR_NS` defines the address of the Non-secure vector table in Non-secure memory, and the [Non-secure Main Stack Pointer](#general-purpose-registers) (`MSP_NS`) is the default stack for the Non-secure exception handler.
- Secure privileged software can access the `VTOR_NS` using the [Non-secure SCB alias](#system-private-peripheral-bus-ppb) (`0xE002ED08`).
- The [System Control Space](#system-private-peripheral-bus-ppb) contains registers for the SysTick timer, NVIC, and SCB.
- The interrupt masking registers (PRIMASK, FAULTMASK, and BASEPRI) are [banked](#special-purpose-registers) between security states. The priority level space is shared between the Secure and the Non-secure world, setting an interrupt mask register on one side can block some, or all, of the exceptions on the other side.
- Interrupts (`IRQ0` - `IRQ479`) are defined as Secure by default. Each interrupt can be configured as Secure or Non-secure and is determined by the Interrupt Target Non-secure (`NVIC_ITNS`) register, which is only programmable in the Secure software.

###### State Transitions in Exceptions and Interrupts (heading level 7)

The following figure shows transitions between the [processor states](#introduction) in ARMv8-M TrustZone.![State Transitions](/series2-trustzone/0.2/images/sld717-state-transitions.png)

Image (left): _[Switching-between-Secure-and-Non-secure-states](https://developer.arm.com/documentation/100690/0201/Switching-between-Secure-and-Non-secure-states).Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved._

1. Secure Thread → Secure Handler or Non-secure Thread to Non-secure Handler  
   - No security state transition.  
   - The exception sequence is almost identical to the exception stacking mechanism of current Cortex-M processors.  
   - The Interrupt Service Routine (ISR) is executed in the current security state (either Secure or Non-secure).
2. Non-secure Thread → Secure Handler or Non-secure Handler → Secure Handler  
   - The transition from Non-secure to Secure state.  
   - The exception sequence is almost identical to the exception stacking mechanism of current Cortex-M processors.  
   - The ISR is executed in a Secure state.
3. Secure Thread → Non-secure Handler or Secure Handler → Non-secure Handler  
   - The transition from Secure to Non-secure state.  
   - To avoid an information leak when transitioning from the Secure to Non-secure state. The processor automatically pushes all general-purpose registers into the Secure stack and erases the contents of all general-purpose registers before executing the Non-secure ISR. The processor pops the contents of all general-purpose registers from the Secure stack when returning from the Non-secure ISR (right side in [Figure 2.6 State Transitions on page 12](#state-transitions-in-exceptions-and-interrupts)). It incurs a slightly longer interrupt latency.  
   - The ISR is executed in a Non-secure state.
4. Secure Privileged Thread ↔ Non-secure Privileged Thread or Secure Unprivileged Thread ↔ Non-secure Unprivileged Thread  
   - The transition from Secure to Non-secure state or Non-secure to Secure state.  
   - The [Function calls and returns](#switching-between-secure-and-non-secure-states) can be used when the privileged level remains the same.

> **Note**: Subject to interrupt priority, there are no restrictions regarding whether a Non-secure or Secure interrupt can occur when the processor runs Non-secure or Secure code.

###### Switching Between Secure and Non-secure States

The TrustZone allows direct calling between Secure and Non-secure software. The following figure shows how to use an API function call to trigger security state transitions. The state transitions can also happen because of [exceptions and interrupts](#state-transitions-in-exceptions-and-interrupts).

![swich state](/series2-trustzone/0.2/images/sld717-switch-state.png)

Image: _[Switching-between-Secure-and-Non-secure-states](https://developer.arm.com/documentation/100690/0201/Switching-between-Secure-and-Non-secure-states).Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved._

###### Switching from Non-secure to Secure State (heading level 7)

When the Non-secure program calls a Secure software, the first instruction must be a Secure Gateway (`SG`) instruction residing in Non-secure Callable memory. The Secure Gateway entry points (veneers) decouple the address of the `SG` instructions in the Non-secure Callable memory region from the rest of the Secure code. It can eliminate the risk of having inadvertent entry points when the Secure software contains a pattern that matches the opcode of the `SG` instruction.

![switch-image](/series2-trustzone/0.2/images/sld717-switch-s.png)

Image (right): _Whitepaper - ARMv8-M Architecture Technical Overview. Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved._

The bit 0 of the [Link Register (LR)](#special-purpose-registers) is cleared to zero by `SG` instruction to indicate that returning from this function transits from Secure to Non-secure. The processor is still in the Non-secure state when the `SG` instruction is executed. The `BXNS LR` instruction is used when returning since a normal `BX LR` instruction interprets it as an unsupported execution mode change. A [SecureFault](#vector-tables) exception is triggered if the processor returns to a Secure address. It prevents a hacker from calling a Secure API with a fake return address pointing to a Secure program location. If bit 0 of LR is 1, the `BXNS LR` instruction behaves like a normal `BX LR`. Therefore, Secure code can call a Secure API in the NSC region even it is not a usual practice.

<table>
    <thead>
        <tr>
            <th>Program</th>
            <th>Call Instruction</th>
            <th>SG Instruction</th>
            <th>Return Instruction</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Non-secure call Non-secure</p>
            </td>
            <td>
                <p>BL or BLX</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>BX LR (Return to Non-secure state)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Non-secure call Secure</p>
            </td>
            <td>
                <p>BL or BLX</p>
            </td>
            <td>
                <p>Clear bit 0 of LR</p>
            </td>
            <td>
                <p>BXNS LR (Return to Non-secure state)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Secure call Secure</p>
            </td>
            <td>
                <p>BL or BLX</p>
            </td>
            <td>
                <p>Set bit 0 of LR</p>
            </td>
            <td>
                <p>BXNS LR (Return to Secure state)</p>
            </td>
        </tr>
    </tbody>
</table>

To help software developers create Secure APIs in C/C++, the [Cortex-M Security Extension (CMSE)](https://developer.arm.com/documentation/ecm0359818/latest) defines a C function attribute called `cmse_nonsecure_entry`.

- GCC — `__attribute__((cmse_nonsecure_entry))`
- IAR — `__cmse_nonsecure_entry`

###### Test Target (TT) Instruction (heading level 7)

The software can use an ARMv8-M instruction called Test Target (TT) and the region number generated by the SAU or the IDAU to determine if a contiguous range of memory shares common security attributes and privilege levels.

The TT instruction returns the [SAU/IDAU](#secure-attribution-unit-sau-implementation-defined-attribution-unit-idau-and-memory-protection-unit-mpu) region number, security attributes (S/NS), and MPU region number after passing the start and end addresses of the memory range to the TT instruction. The software can determine whether the memory range has required security attributes and resides in the same region number.

![tt-instruction](/series2-trustzone/0.2/images/sld717-tt-instruction.png)

Image: _[Test-target-instruction](https://developer.arm.com/documentation/100690/0201/Test-target-instruction).Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved._

This mechanism allows security checking at the beginning of the API service (instead of during the operation) to determine if the memory referenced by a pointer from Non-secure software points to the Non-secure address. It prevents Non-secure software from using those APIs in Secure software to access or modify Secure data.

To make these operations easier in a C/C++ programming environment, the [Cortex-M Security Extension (CMSE)](https://developer.arm.com/documentation/ecm0359818/latest) has defined a range of [intrinsic functions](https://www.keil.com/support/man/docs/armclang_ref/armclang_ref_pge1446715440722.htm) for dealing with pointer checks with the TT instructions.

###### Switching from Secure to Non-secure State (heading level 7)

When the Secure program calls a Non-secure software, the Secure program must use a `BLXNS <reg>` instruction to invoke the process. If bit 0 of the `<reg>` is 0, the processor must switch to the Non-secure state when branching to the target address. During the state transition, the return address and some processor state information are pushed onto the Secure stack, while the return address on the [Link Register (LR)](#special-purpose-registers) is set to a special value called `FNC_RETURN` (`0xFEFFFFFF`).

The Non-secure function completes by performing a branch (`BX LR`) to the `FNC_RETURN` address (bit 0 is 1 to indicate the function was called from the Secure state). It automatically triggers the unstacking of the actual return address from the Secure stack and returns to the calling function. The `FNC_RETURN` hides the return address of the Secure program from the Non-secure software to avoid the leakage of any secret information. It also prevents Non-secure software from modifying the Secure return address stored in the Secure stack.

![switch_ns](/series2-trustzone/0.2/images/sld717-switch-ns.png)

Image: _[Switching-between-Secure-and-Non-secure-states](https://developer.arm.com/documentation/100690/0201/Switching-between-Secure-and-Non-secure-states).Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved._

To help software developers declare Non-secure function pointers in C/C++, the [Cortex-M Security Extension (CMSE)](https://developer.arm.com/documentation/ecm0359818/latest) defines a C function attribute called `cmse_nonsecure_call`.

- GCC: `__attribute__((cmse_nonsecure_call))`
- IAR: `__cmse_nonsecure_call`

###### Software Flow

The following figure describes a software flow example in a TrustZone implemented system.

![Execution Flow of a TrustZone Implemented System](/series2-trustzone/0.2/images/sld717-execution-flow-of-a-trustzone-implemented-system.png)

Image: _[Software Development in ARMv8-M Architecture](https://community.arm.com/cfs-file/__key/telligent-evolution-components-attachments/01-2142-00-00-00-01-27-19/ARM-Cortex-_2D00_-session-11-_2D00_-Yiu-_2D00_-Software-Development-in-ARMv8_2D00_M-Architecture.pdf).Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved._

1. The system starts executing code in the Secure state after a power-on or reset (Secure boot).  
   - The Secure [stack pointer](#general-purpose-registers) (`MSP_S`) is set from the address of the Secure vector table (`VTOR_S`).  
   - The Secure Reset Handler pointed by the `VTOR_S` is called.  
   - Perform various initialization tasks such as C startup code.  
   - Place peripherals and associated interrupts in either Secure or Non-secure applications.  
   - Program [SAU/IDAU](04-r-programming) to partition the entire memory into Secure, Non-secure Callable, and Non-secure regions.  
   - Program the address of the Non-secure vector table (`VTOR_NS`).  
   - Initialize the two first entries of the table for the Non-secure stack pointer (`MSP_NS`) and Reset Handler to emulate a Non-secure reset.
2. The Secure firmware branches to the entry point (Reset Handler pointed by the `VTOR_NS`) of the Non-secure application.  
   - The Non-secure software has its Reset Handler.  
   - Perform various initialization tasks such as C startup code and hardware initialization (e.g., Non-secure peripherals).  
   - It does not conflict with initialization from the Secure code as the stack and heap spaces of Secure and Non-secure code are separated.
3. During the execution of Non-secure applications, the application could call Secure APIs through the [Secure Gateway (SG) veneer](#switching-from-non-secure-to-secure-state) in the Non-secure Callable region.
4. In some cases, Secure APIs might need to call [Non-secure call-back functions](#switching-from-non-secure-to-secure-state) (e.g., a hardware driver).

##### Bus Level Security (BLS)

###### System Design

The following figure shows two system designs:

- The sample system contains an ARMv8-M processor and the required components to support TrustZone.
- Bus Level Security (BLS) on Series 2 devices implements the concepts introduced in the ARM TrustZone sample system. BLS enforces Secure and privileged programming models and uses security components (colored blocks) to configure the security attribute and privileged level of peripherals and Bus Masters.

![ARMv8-M TrustZone Implementation](/series2-trustzone/0.2/images/sld717-armv8-m-trustzone-implementation.png)

###### ARMv8-M Processor (heading level 7)

The ARMv8M processor is TrustZone capable of Secure and Non-secure states. It has a dedicated internal [SAU](#security-attribution-unit) that is fully programmable up to 8 different memory regions. Out of reset, the processor is in a Secure state and every transaction is a Secure transaction.

ARMv8-M Processor in Series 2 devices is the Cortex-M33.

###### System Security Controller (heading level 7)

The system security controller is the central location for all security settings in the system. Each type of controller, IDAU, and wrapper receives its security configuration and bus response configuration from this block.

System Security Controller in Series 2 devices is the [Security Management Unit (SMU)](#security-management-unit-smu).

###### Implementation Defined Attribution Unit (IDAU) (heading level 7)

The [IDAU](02-r-basic) generates the security attribute for a given address. All IDAUs in the system have the same memory partitioning. The IDAU is intended only for ARMv8-M cores and utilizes the entire IDAU interface for the core. The lite IDAU uses only the Secure and Non-secure interface from the IDAU and is intended for Non-ARMv8-M Bus Masters.

IDAU in Series 2 devices is the [External Secure Attribution Unit (ESAU)](#external-secure-attribution-unit-esau).

###### Security Wrapper (heading level 7)

The Security Wrapper gives a legacy Bus Master the ability to drive security attribution. The security wrapper outputs the transaction address to the lite IDAU which returns the security attribute of the address. If the wrapper is configured as Non-secure, any transactions to a Secure address are blocked.

Security Wrapper in Series 2 devices is the [Bus Master Protect Unit (BMPU)](#bus-master-protection-unit-bmpu).

###### Memory Protection Controller (MPC) (heading level 7)

MPC has a security configuration for a per block of memory or memory above and below the watermark. If the security attribute of the block or memory region does not match the security attribute of the address, the transaction is blocked. This controller is used in a system that alias RAM or flash memory locations. This controller is not needed when the memory region size is programmable in an IDAU.

Series 2 devices have a programmable flash and RAM region in the [ESAU](#external-secure-attribution-unit-esau) (equivalent to IDAU) and are not implementing this block.

###### Peripheral Protection Controller (PPC) (heading level 7)

PPC has a security configuration for every peripheral. If the security attribute of the selected peripheral does not match the security attribute of the address, the transaction is blocked. This controller is used in systems that alias the peripheral memory locations.

PPC in Series 2 devices is the [Peripheral Protection Unit (PPU)](#peripheral-protection-unit-ppu).

Hardware security is now extended to the peripheral bus system of the processor. Each component on the bus can verify and propagate the security level for each bus operation.  The following sections describe the individual security component for BLS on Series 2 devices.

###### Security Management Unit (SMU)

The SMU is the only user-facing block in the BLS architecture and houses all the configuration and status for the [ESAUs, BMPUs, and PPUs](#bus-level-security-bls).

- Thirteen memory regions ([ESAU](#external-secure-attribution-unit-esau))
- Per Bus Master privileged and security attribute ([BMPU](#bus-master-protection-unit-bmpu))
- Interrupt flag for Bus Master security fault (fault table in BMPU section)
- Per peripheral privileged and security attribute ([PPU](#peripheral-protection-unit-ppu))
- Interrupt flags for privileged, security, and instruction peripheral access faults (fault tables in PPU section)
- Separate Secure and Privileged IRQ

The SMU configurations can be [locked](04-r-programming) down and protected from runaway code. The `SMU_LOCK` register resets to UNLOCK. Any write other than the unlock code (`0xACCE55`) locks all SMU registers from further updates. The `SMU_STATUS` register contains a `SMULOCK` bitfield with the current lock state of the SMU.

The `SMU_M33CTRL` register can [lock](04-r-programming) down internal security and privileged configurations below.

- Cortex-M33 SAU
- Non-secure MPU
- Secure MPU
- Non-secure Vector Table Offset Register (VTOR)
- Secure AIRCR register

Interrupt flags in the `SMU_IF` register can generate a [Secure or Privileged interrupt](04-r-programming) in the table below when its corresponding interrupt enable bit in the `SMU_IEN` register is set and `IRQn` is enabled.

<table>
    <thead>
        <tr>
            <th>Enable Bit in SMU_IEN Register</th>
            <th>IRQn</th>
            <th>Interrupt Handler</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>BMPUSEC, PPUSEC</p>
            </td>
            <td>
                <p>SMU_SECURE_IRQn</p>
            </td>
            <td>
                <p>SMU_SECURE_IRQHandler()</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>PPUPRIV, PPUINST</p>
            </td>
            <td>
                <p>SMU_PRIVILEGED_IRQn</p>
            </td>
            <td>
                <p>SMU_PRIVILEGED_IRQHandler()</p>
            </td>
        </tr>
    </tbody>
</table>

Each interrupt flag in the `SMU_IF` register can be cleared by writing 1 to the corresponding bit of the `SMU_IF_CLR` register.

###### External Secure Attribution Unit (ESAU)

The ESAU is responsible for determining the memory region and [security attribute](02-r-basic) of a given address. Referring to [ARMv8-M TrustZone Implementation on page 16](03-r-bls#system-design), the Cortex-M33 interfaces with an ESAU  and the BMPUs of other Bus Masters interface with lite ESAUs to determine the security attribute of all transactions. The following figure describes the security attributes of different memory regions defined by the ESAU on Series 2 devices.

![System Memory Map of Series 2 Device with TrustZone](/series2-trustzone/0.2/images/sld717-system-memory-map-of-series-2-device-with-trustzone.png)

**Notes**:

- For Series 2 devices with base address `0x08000000` in region 0, the memory address from `0x0` to `0x07FFFFFF` is an invalid region.
- The invalid regions are deemed as Secure.
- The NSC and Exempted attributes are only available to the ESAU, and all lite ESAUs in the system view these attributes as [Secure](05-r-implementation).

The ESAU divides the memory map into 13 memory regions and has a maximum of 6 Non-secure regions.

- Four Movable Region Boundaries (MRBs) determine the size of 6 regions.
- Two regions have configurable security attributes.
- Each memory region consists of a base address that specifies the start of the region and a limit address that specifies the end of the region plus one (**+ 1**).
- The address is valid if it falls between the base (≥ base) and limit (< limit) of a region.
- If the memory region is not defined, it is deemed invalid and Secure.

The MRBs distinguish the Secure, Non-secure Callable, and Non-secure regions in flash and RAM. The two configurable regions determine if the Info flash and Cortex-M33 [EPPB](02-r-basic) regions are Secure or Non-secure. The MRBs have a specific programming sequence. Any [misprogramming](04-r-programming) results in a `SMUPRGERR` in the `SMU_STATUS` register.

###### ARMv8-M CODE Regions (heading level 7)

- [Regions 0, 1, and 2](04-r-programming) are in the Main space of flash. Region 3 is the info space of flash.
- The mrb01 (`ESAUMRB01` in `SMU_ESAURMBR01` register) determines the end of region 0 and the start of region 1.
- The mrb12 (`ESAUMRB12` in `SMU_ESAURMBR12` register) determines the end of region 1 and the start of region 2.
- The size of region 3 is device-dependent.
- Three regions' security attributes are static, and one region is configurable. Region 0 is always Secure, region 1 is always Non-secure Callable, and region 2 is always Non-secure. [Region 3](04-r-programming) is configurable as either Secure or Non-secure (`ESAUR3NS` in `SMU_ESAURTYPES0` register, default is secure after reset).
- Sizes of regions 0, 1, and 2 are adjusted in **4 kB increments** with the lower 12 bits of `ESAUMRB##` in `SMU_ESAURMBR##` ignored.  
  - The Secure region can be set to size 0 when mbr01 = base address of region 0.  
  - The Non-secure Callable regions can be set to size 0 when mbr01 = mbr12.
- The default value of mbr01 is equal to base address + `0x02000000`, so the size of region 0 is 32 MB. Out of reset, all flash is Secure since all Series 2 devices have less than 32 MB of flash.

<table>
    <thead>
        <tr>
            <th>Region</th>
            <th>Memory</th>
            <th>Base Address</th>
            <th>Limit Address</th>
            <th>Security Attribute</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>0</p>
            </td>
            <td>
                <p>Main flash</p>
            </td>
            <td>
                <p>0x00000000 or 0x08000000</p>
            </td>
            <td>
                <p>(0x00000000 or 0x08000000) | mbr01</p>
            </td>
            <td>
                <p>Secure</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>1</p>
            </td>
            <td>
                <p>Main flash</p>
            </td>
            <td>
                <p>(0x00000000 or 0x08000000) | mbr01</p>
            </td>
            <td>
                <p>(0x00000000 or 0x08000000) | mbr12</p>
            </td>
            <td>
                <p>Non-secure Callable</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>2</p>
            </td>
            <td>
                <p>Main flash</p>
            </td>
            <td>
                <p>(0x00000000 or 0x08000000) | mbr12</p>
            </td>
            <td>
                <p>0x0FE00000</p>
            </td>
            <td>
                <p>Non-secure</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>3</p>
            </td>
            <td>
                <p>Info flash</p>
            </td>
            <td>
                <p>0x0FE0000</p>
            </td>
            <td>
                <p>0x10000000</p>
            </td>
            <td>
                <p>Secure or Non-secure</p>
            </td>
        </tr>
    </tbody>
</table>

###### ARMv8-M RAM Regions (heading level 7)

- [Regions 4, 5, and 6](04-r-programming) cover the entire available RAM in the device.
- The mrb45 (`ESAUMRB45` in `SMU_ESAURMBR45` register) determines the end of region 4 and the start of region 5.
- The mrb56 (`ESAUMRB56` in `SMU_ESAURMBR56` register) determines the end of region 5 and the start of region 6.
- All three regions' security attributes are static. Region 4 is always Secure, region 5 is always Non-secure Callable, and region 6 is always Non-secure.
- Sizes of all three regions are adjusted in **4 kB increments** with the lower 12 bits of `ESAUMRB##` in `SMU_ESAURMBR##` ignored.  
  - The Secure region can be set to size 0 when mbr45 = base address of region 4.  
  - The Non-secure Callable region can be set to size 0 when mbr45 = mbr56.
- The default value of mbr45 is equal to `0x02000000`, so the size of region 4 is 32 MB. Out of reset, all RAM is Secure since all Series 2 devices have less than 32 MB of RAM.

<table>
    <thead>
        <tr>
            <th>Region</th>
            <th>Memory</th>
            <th>Base Address</th>
            <th>Limit Address</th>
            <th>Security Attribute</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>4</p>
            </td>
            <td>
                <p>SRAM</p>
            </td>
            <td>
                <p>0x20000000</p>
            </td>
            <td>
                <p>0x20000000 | mbr45</p>
            </td>
            <td>
                <p>Secure</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>5</p>
            </td>
            <td>
                <p>SRAM</p>
            </td>
            <td>
                <p>0x20000000 | mbr45</p>
            </td>
            <td>
                <p>0x20000000 | mbr56</p>
            </td>
            <td>
                <p>Non-secure Callable</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>6</p>
            </td>
            <td>
                <p>SRAM</p>
            </td>
            <td>
                <p>0x20000000 | mbr56</p>
            </td>
            <td>
                <p>0x30000000</p>
            </td>
            <td>
                <p>Non-secure</p>
            </td>
        </tr>
    </tbody>
</table>

###### ARMv8-M Peripheral Regions (heading level 7)

- These regions are aliases to the [chip peripherals and SE mailbox](04-r-programming) (a device with HSE).
- Both regions have a fixed size.
- Both regions' security attributes are static. Region 7 is always Secure, and region 8 is always Non-secure.

<table>
    <thead>
        <tr>
            <th>Region</th>
            <th>Memory</th>
            <th>Base Address</th>
            <th>Limit Address</th>
            <th>Security Attribute</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>7</p>
            </td>
            <td>
                <p>Chip Peripherals</p>
            </td>
            <td>
                <p>0x40000000</p>
            </td>
            <td>
                <p>0x50000000</p>
            </td>
            <td>
                <p>Secure</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>8</p>
            </td>
            <td>
                <p>Chip Peripherals</p>
            </td>
            <td>
                <p>0x50000000</p>
            </td>
            <td>
                <p>0x60000000</p>
            </td>
            <td>
                <p>Non-secure</p>
            </td>
        </tr>
    </tbody>
</table>

###### ARMv8-M Device Regions (heading level 7)

- These regions are aliases to all radio peripherals and radio RAM.
- Both regions have a fixed size.
- Both regions' security attributes are static. Region 9 is always Secure, and region 10 is always Non-secure.
- From the perspective of the device bus system, the [radio is one peripheral that is either Secure or Non-secure](04-r-programming). So any Bus Master accessing the radio needs to know the security attribute of the radio. From the perspective of the radio, all of its radio bus peripherals are accessible regardless of the security attribute. However, the radio needs to know the security attribute of chip bus peripherals to access them through the correct alias.

<table>
    <thead>
        <tr>
            <th>Region</th>
            <th>Memory</th>
            <th>Base Address</th>
            <th>Limit Address</th>
            <th>Security Attribute</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>9</p>
            </td>
            <td>
                <p>Radio Peripherals</p>
            </td>
            <td>
                <p>0xA0000000</p>
            </td>
            <td>
                <p>0xB0000000</p>
            </td>
            <td>
                <p>Secure</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>10</p>
            </td>
            <td>
                <p>Radio Peripherals</p>
            </td>
            <td>
                <p>0xB0000000</p>
            </td>
            <td>
                <p>0xC0000000</p>
            </td>
            <td>
                <p>Non-secure</p>
            </td>
        </tr>
    </tbody>
</table>

###### ARMv8-M System Private Peripheral Bus (PPB) Regions (heading level 7)

- Both regions have a fixed size.
- [Region 11](04-r-programming) is the Cortex-M33 EPPB memory region and is configurable as either Secure or Non-secure (`ESAUR11NS` in `SMU_ESAURTYPES1` register, default is secure after reset). It is important to note that the Cortex-M33 core is the only Bus Master that sees these memory regions. All other Bus Masters in the system do not have access to the System PPB, and it is an invalid region.
- Region 12 has a static security attribute of Exempted. It means that the Cortex-M33 core allows the transaction in all cases. It permits debuggers to read the system ROM Table regardless of the state of the Cortex-M33 core.

<table>
    <thead>
        <tr>
            <th>Region</th>
            <th>Memory</th>
            <th>Base Address</th>
            <th>Limit Address</th>
            <th>Security Attribute</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>11</p>
            </td>
            <td>
                <p>EPPB</p>
            </td>
            <td>
                <p>0xE0044000</p>
            </td>
            <td>
                <p>0xE00FE000</p>
            </td>
            <td>
                <p>Secure or Non-secure</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>12</p>
            </td>
            <td>
                <p>System ROM Table</p>
            </td>
            <td>
                <p>0xE00FE000</p>
            </td>
            <td>
                <p>0xE00FF000</p>
            </td>
            <td>
                <p>Exempted</p>
            </td>
        </tr>
    </tbody>
</table>

**Notes**:

- The regions in flash (0/1/2) and RAM (4/5/6) can only create in the order of Secure, Non-secure Callable, and Non-secure.
- The [ESAU and lite ESAUs](#bus-level-security-bls) handle the transactions of Bus Masters and must have consistent security attribute mapping. Therefore, configurations in the SMU registers apply to ESAU and lite ESAUs.
- Unlike other Bus Masters using BMPU and lite ESAU, merging the address lookup results from the [internal SAU and ESAU](02-r-basic) determines the [security attribute](#security-attribution-unit) of the Cortex-M33 transaction.

<table>
    <thead>
        <tr>
            <th>Bus Master</th>
            <th>Security Attribution</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Cortex-M33</p>
            </td>
            <td>
                <p>SAU and ESAU</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Other</p>
            </td>
            <td>
                <p>Lite ESAU</p>
            </td>
        </tr>
    </tbody>
</table>

###### Security Attribution Unit

In Series 2 devices, the combination of the integrated SAU in the Cortex-M33 processor and an ESAU determine the security attribute of a Cortex-M33 transaction.

The SAU consists of several [programmable registers](https://developer.arm.com/documentation/100690/0201/SAU-register-summary). These registers are placed in the [System Control Space (SCS)](02-r-basic) and are only accessible from the Secure privileged state.

- SAU Control Register (`SAU_CTRL`) — The SAU is disabled after RESET
- SAU Type Register (`SAU_TYPE`) — Indicates the number of [available regions](01-series-2-security-features) (read-only)
- SAU Region Number Register (`SMU_RNR`) — Assigns a region number
- SAU Region Base Address Register (`SAU->RBAR`) — Configures selected region base address
- SAU Region Limit Address Register (`SAU->RLAR`) — Configures selected region limit address and security attribute (NSC or NS), enable or disable the region

The following figure shows three different SAU configurations for determining the security attribute of a Cortex-M33 transaction.

![Configuration of SAU_CTRL Register](/series2-trustzone/0.2/images/sld717-configuration-of-sau-ctrl-register.png)

**Notes**:

- All address ranges after RESET in SAU are Secure by default.
- The SAU can configure a **32 bytes aligned** region as Non-secure or Non-secure Callable. Any address not defined in the SAU defaults to Secure.
- An [ESAU](#external-secure-attribution-unit-esau) can configure or hard-code a region as Secure, Non-secure Callable, Non-secure, or Exempted. An [Exempted](02-r-basic) region enables Non-secure debuggers to access debugging components and establish a debug connection to the processor before the SAU is configured.
- The processor determines the final attribute of the address based on the higher security attribute (Exempted > S > NSC > NS) from either the SAU or the ESAU.  
  ![state_hierarchy](/series2-trustzone/0.2/images/sld717-state-hierarchy.png)

###### All Secure Configuration (heading level 7)

Highlights:

- SAU is disabled.
- `ALLNS` bit in the SAU Control register is clear.
- The whole memory is in a Secure state (highest security attribute apart from Exempted).
- All Cortex-M33 transactions in this configuration are Secure or Exempted and give the Cortex-M33 access to all memory locations through either the Secure or Non-secure alias after RESET.  
  ![all_s](/series2-trustzone/0.2/images/sld717-all-s.png)
- It is up to the boot procedure in a Secure state to keep the current configuration or use other configurations once the boot process is complete.

###### All Non-secure Configuration (heading level 7)

Highlights:

- SAU is disabled.
- `ALLNS` bit in the SAU Control register is set.
- The whole memory is in a Non-secure state (lowest security attribute).
- Therefore the [ESAU configuration](#external-secure-attribution-unit-esau) determines the security attribute of all Cortex-M33 transactions.  
  ![all_ns](/series2-trustzone/0.2/images/sld717-all-ns.png)
- Except for the `SAU_CTRL` [register](04-r-programming), this configuration does not require programming on other SAU registers.

###### Configurable Configuration (heading level 7)

Highlights:

- SAU is enabled.
- `ALLNS` bit in the SAU Control register can be 0 or 1 (do not care).
- The `NSC` bit on the `SAU_RLAR` register determines the security attribute of an address as Non-secure or Non-secure Callable if an address matches an SAU region.
- The security attribute of an address is Secure by default if the address does not match any SAU region.
- This configuration [programs](04-r-programming)`SAU_RNR`, `SAU_RBAR`, and `SAU_RLAR` registers to correlate the [Non-secure regions](03-r-bls#configurable-configuration) in ESAU.
- The SAU or ESAU [overrides](02-r-basic) the attribute to a [higher security level](#security-attribution-unit) if any security attribute mismatch occurs in a memory region.  
  ![address_lookup](/series2-trustzone/0.2/images/sld717-address-lookup.png)
- The following figure is an example of a configurable configuration with the size of ESAU regions 0 and 5 are set to zero.  
  ![all_config](/series2-trustzone/0.2/images/sld717-all-config.png)

> **Note**: The Cortex-M33 has an internal SAU that defaults all undefined addresses to Secure if enabled. If the Secure regions do not align between the [Cortex-M33 (SAU + ESAU) and other Bus Masters (lite ESAU)](#bus-level-security-bls), the Cortex-M33 treats a memory region as Secure while other Bus Masters treat it as Non-secure. It can lead to the leaking of secure data if the Cortex-M33 stores secure data in what other Bus Masters think is a Non-secure area ([Main Flash Layout on page 34](05-r-implementation)).

###### Bus Master Protection Unit (BMPU)

The BMPU is a security wrapper used for assigning a Bus Master specific security and privileged states. Referring to [Figure 3.1 ARMv8-M TrustZone Implementation on page 16](#system-design), the BMPU generally lies between the Bus Master and the Advanced High-performance Bus (AHB) Matrix. BMPU interfaces with a [lite ESAU](#bus-level-security-bls) to determine the security attribute of all Bus Master transactions.

The registers below in SMU configure the [security](04-r-programming) and [privileged](04-r-programming) state of a Bus Master. The Bus Masters in group 0 are device-dependent. Out of reset, each Bus Master is Secure and privileged.

<table>
    <thead>
        <tr>
            <th>Register</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>SMU_BMPUPATD0</p>
            </td>
            <td>
                <p>Bitfields (privileged if set) for privileged attribute configuration on Bus Master group 0</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SMU_BMPUSATD0</p>
            </td>
            <td>
                <p>Bitfields (Secure if set) for security attribute configuration on Bus Master group 0</p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**: The Bus Master privileged attribute only applies to peripheral accesses. Flash and RAM accesses ignore the privileged attribute of the Bus Master.

The BMPU generates a security fault when the security attribute of the bus transaction is Secure, and the security attribute (`SMU_BMPUSATD0`) for the BMPU is configured as Non-secure.

Below is the security fault table that shows how the security attribute on the bus is driven based on the lite ESAU attribute and the BMPU security configuration. The interrupt is triggered if `BMPUSEC` in `SMU_IEN` is set and the `SMU_SECURE_IRQn` is enabled.

<table>
    <thead>
        <tr>
            <th>Lite ESAU Attribute</th>
            <th>Secure Bus Master</th>
            <th>Non-secure Bus Master</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Non-secure</p>
            </td>
            <td>
                <p>Non-secure</p>
            </td>
            <td>
                <p>Non-secure</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Secure</p>
            </td>
            <td>
                <p>Secure</p>
            </td>
            <td>
                <p>FAULT</p>
            </td>
        </tr>
    </tbody>
</table>

Upon a BMPU fault, the registers in SMU below notify that a BMPU security fault occurred and on which Bus Master. The registers also identify the offending fault address. If a fault is detected, the response is Read As Zero (RAZ) or Write Ignored (WI) and the corresponding interrupt flag is set in the `SMU_IF` register. The values in `SMU_BMPUFS` and `SMU_BMPUFSADDR` do not change until the BMPU fault (`BMPUSEC`) in the `SMU_IF` register is cleared by software.

<table>
    <thead>
        <tr>
            <th>Register</th>
            <th>Bitfield</th>
            <th>Fault</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>SMU_IF</p>
            </td>
            <td>
                <p>BMPUSEC</p>
            </td>
            <td>
                <p>Security Fault if set</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SMU_BMPUFS</p>
            </td>
            <td>
                <p>BMPUFSMASTERID</p>
            </td>
            <td>
                <p>ID of the Bus Master that triggered the fault</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SMU_BMPUFSADDR</p>
            </td>
            <td>
                <p>BMPUFSADDR</p>
            </td>
            <td>
                <p>Access address that triggered the fault</p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**: No privileged fault is generated because all the other Bus Masters in the system do not drive the privileged attribute.

###### Peripheral Protection Unit (PPU)

The PPU is a security wrapper used for assigning a Bus Slave peripheral specific security and privileged states. Referring to [Figure
3.1 ARMv8-M TrustZone Implementation on page 16](#system-design), the PPU comes in the form of a PPU in Advanced High-performance Bus (AHB) and a PPU in Advanced Peripheral Bus (APB).

- The PPU AHB generally lies between the Bus Matrix and an AHB Bus Slave peripheral.
- The PPU APB lies between the output of an AHB to APB bridge and all of the APB Slaves on that APB bus.

The registers below in SMU configure the [security](04-r-programming) and [privileged](04-r-programming) state of a peripheral. The peripherals in groups 0 and 1 are device-dependent. Out of reset, each peripheral is Secure and privileged. While each peripheral in address `0x40000000` (region 7) or `0x50000000` (region 8) can be configured independently, the radio subsystem in `0xA0000000` (region 9) or `0xB0000000` (region 10) is configured as a [unit](04-r-programming).

<table>
    <tbody>
        <tr>
            <th>Register</th>
            <th>Description</th>
        </tr>
        <tr>
            <td>SMU_PPUPATD0</td>
            <td>Bitfields (privileged if set) for privileged access configuration on peripheral group 0</td>
        </tr>
        <tr>
            <td>SMU_PPUPATD1</td>
            <td>Bitfields (privileged if set) for privileged access configuration on peripheral group 0</td>
        </tr>
        <tr>
            <td>SMU_PPUSATD0</td>
            <td>Bitfields (Secure if set) for security access configuration on peripheral group 0</td>
        </tr>
        <tr>
            <td>SMU_PPUSATD1</td>
            <td>Bitfields (Secure if set) for security access configuration on peripheral group 1</td>
        </tr>
    </tbody>
</table>

The PPU can generate three types of faults:

1. Privileged faults occur on unprivileged transactions to privileged peripherals. Below is the privileged fault table that shows when a privileged fault occurs based on the PPU peripheral privileged configuration and the bus transaction privileged attribute. The interrupt is triggered if `PPUPRIV` in `SMU_IEN` is set and the `SMU_PRIVILEGED_IRQn` is enabled.  
   <table>  
       <thead>  
           <tr>  
               <th>Bus Attribute</th>  
               <th>Privileged Peripheral</th>  
               <th>Unprivileged Peripheral</th>  
           </tr>  
       </thead>  
       <tbody>  
           <tr>  
               <td>  
                   <p>Privileged</p>  
               </td>  
               <td>  
                   <p>SUCCESS</p>  
               </td>  
               <td>  
                   <p>SUCCESS</p>  
               </td>  
           </tr>  
           <tr>  
               <td>  
                   <p>Unprivileged</p>  
               </td>  
               <td>  
                   <p>FAULT</p>  
               </td>  
               <td>  
                   <p>SUCCESS</p>  
               </td>  
           </tr>  
       </tbody>  
   </table>
2. Security faults occur on Secure transactions to Non-secure peripherals and Non-secure transactions to Secure peripherals. Below is the security fault table that shows when a security fault occurs based on the PPU Peripheral security configuration and the bus transaction security attribute. The interrupt is triggered if `PPUSEC` in `SMU_IEN` is set and the `SMU_SECURE_IRQn` is enabled.  
   <table>  
       <thead>  
           <tr>  
               <th>Bus Attribute</th>  
               <th>Secure Peripheral</th>  
               <th>Non-secure Peripheral</th>  
           </tr>  
       </thead>  
       <tbody>  
           <tr>  
               <td>  
                   <p>Secure</p>  
               </td>  
               <td>  
                   <p>SUCCESS</p>  
               </td>  
               <td>  
                   <p>FAULT</p>  
               </td>  
           </tr>  
           <tr>  
               <td>  
                   <p>Non-secure</p>  
               </td>  
               <td>  
                   <p>FAULT</p>  
               </td>  
               <td>  
                   <p>SUCCESS</p>  
               </td>  
           </tr>  
       </tbody>  
   </table>
3. Instruction faults occur on any transaction marked as an instruction fetch. Below is the instruction fault table that shows when a PPU instruction fault occurs based on the bus transaction instruction attribute. The interrupt is triggered if `PPUINST` in `SMU_IEN` is set and the `SMU_PRIVILEGED_IRQn` is enabled.  
   <table>  
       <thead>  
           <tr>  
               <th>Bus Attribute</th>  
               <th>Secure Peripheral</th>  
               <th>Non-secure Peripheral</th>  
           </tr>  
       </thead>  
       <tbody>  
           <tr>  
               <td>  
                   <p>Secure</p>  
               </td>  
               <td>  
                   <p>SUCCESS</p>  
               </td>  
               <td>  
                   <p>FAULT</p>  
               </td>  
           </tr>  
           <tr>  
               <td>  
                   <p>Non-secure</p>  
               </td>  
               <td>  
                   <p>FAULT</p>  
               </td>  
               <td>  
                   <p>SUCCESS</p>  
               </td>  
           </tr>  
       </tbody>  
   </table>

Upon a [PPU fault](04-r-programming), the registers below in [SMU](#security-management-unit-smu) notifies which PPU fault occurred and on which peripheral. If a fault is detected, the response is Read As Zero (RAZ) or Write Ignored (WI) and set the corresponding interrupt flag in the `SMU_IF` register. The values in `SMU_IF` and `SMU_PPUFS` do not change until all PPU faults in the `SMU_IF` register are cleared by software.

<table>
    <thead>
        <tr>
            <th>Register</th>
            <th>Bitfield</th>
            <th>Fault</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>SMU_IF</p>
            </td>
            <td>
                <p>PPUPRIV</p>
            </td>
            <td>
                <p>Privilege Fault if set</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SMU_IF</p>
            </td>
            <td>
                <p>PPUSEC</p>
            </td>
            <td>
                <p>Security Fault if set</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SMU_IF</p>
            </td>
            <td>
                <p>PPUINST</p>
            </td>
            <td>
                <p>Instruction Fault if set</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SMU_PPUFS</p>
            </td>
            <td>
                <p>PPUFSPERIPHID</p>
            </td>
            <td>
                <p>ID of the peripheral that caused the fault</p>
            </td>
        </tr>
    </tbody>
</table>

###### Compatibility

Secure software usually controls the [SYSCFG and SMU](05-r-implementation) peripherals to prevent Non-secure software from changing critical configurations in the Secure domain. It requires switching between Secure and Non-secure states when Non-secure software wants to update the registers in these peripherals. Therefore dedicated registers for Non-secure access are added to SYSCFG and SMU peripherals on newer Series 2 devices.

###### System Configuration (SYSCFG) (heading level 7)

Except for EFR32xG21 devices, the following tables apply to all Series 2 devices.

**Table**: Dedicated Bitfield to Configure Access for Non-secure SYSCFG Registers

<table>
    <thead>
        <tr>
            <th>Bitfield (Register)</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>SYSCFGCFGNS (SMU_PPUPATD0)</p>
            </td>
            <td>
                <p>Bitfields (privileged if set) for privileged access configuration on NS SYSCFG registers</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SYSCFGCFGNS (SMU_PPUSATD0)</p>
            </td>
            <td>
                <p>Bitfields (Secure if set) for security access configuration on NS SYSCFG registers</p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**: Reset `SYSCFGCFGNS` bit in `SMU_PPUSATD0` to allow Non-secure software to access NS SYSCFG registers.

**Table**: Dedicated SYSCFG Registers for Non-secure State

<table>
    <thead>
        <tr>
            <th>SYSCFG Non-secure Registers</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>SYSCFG_CFGNS_CFGNSTCALIB</p>
            </td>
            <td>
                <p>NS SysTick calibration value register</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SYSCFG_CFGNS_ROOTNSDATA0</p>
            </td>
            <td>
                <p>NS root data register 0</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SYSCFG_CFGNS_ROOTNSDATA0</p>
            </td>
            <td>
                <p>NS root data register 1</p>
            </td>
        </tr>
    </tbody>
</table>

###### Security Management Unit (SMU) (heading level 7)

Except for EFR32xG21 devices, the following tables apply to all Series 2 devices.

**Table**: Dedicated Bitfield to Configure Access for Non-secure SMU Registers

<table>
    <thead>
        <tr>
            <th>Bitfield (Register)</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>SMUCFGNS (SMU_PPUPATD1)</p>
            </td>
            <td>
                <p>Bitfields (privileged if set) for privileged access configuration on NS SMU registers</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SMUCFGNS (SMU_PPUSATD1)</p>
            </td>
            <td>
                <p>Bitfields (Secure if set) for security access configuration on NS registers</p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**: Reset `SMUCFGNS` bit in `SMU_PPUSATD1` to allow Non-secure software to access NS SMU registers.

The SMU_CFGNS register file is for the TrustZone Non-secure state and has its register lock (`NSLOCK`). It allows hardware to maintain the privileged assignments for the NS state. The privileged configuration within the NS state is the same as the Secure state, except it has an "NS" to differentiate the registers.

**Table**: Dedicated SMU Registers for Non-secure State

<table>
    <thead>
        <tr>
            <th>SMU Non-secure Registers</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>SMU_CFGNS_NSSTATUS</p>
            </td>
            <td>
                <p>Lock status of SMU_CFGNS registers</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SMU_CFGNS_NSLCOK</p>
            </td>
            <td>
                <p>Lock and unlock the SMU_CFGNS registers</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SMU_CFGNS_NSIF</p>
            </td>
            <td>
                <p>Interrupt flags for NS privilege (PPUNSPRIVIF) and instruction (PPUNSINSTIF) faults</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SMU_CFGNS_NSIEN</p>
            </td>
            <td>
                <p>Interrupt enable flags for NS privilege (PPUNSPRIVIEN) and instruction (PPUNSINSTIEN) faults</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SMU_CFGNS_PPUNSPATD0</p>
            </td>
            <td>
                <p>Bitfields (privileged if set) for NS privileged access configuration on peripheral group 0</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SMU_CFGNS_PPUNSPATD1</p>
            </td>
            <td>
                <p>Bitfields (privileged if set) for NS privileged access configuration on peripheral group 1</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SMU_CFGNS_PPUNSFS</p>
            </td>
            <td>
                <p>ID (PPUFSPERIPHID) of the NS peripheral that caused the fault</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SMU_CFGNS_BMPUNSPATD0</p>
            </td>
            <td>
                <p>Bitfields (privileged if set) for privileged attribute configuration on NS Bus Master group 0</p>
            </td>
        </tr>
    </tbody>
</table>

**Table**: Fault Statuses Only for Secure State

<table>
    <thead>
        <tr>
            <th>Bitfield (Register)</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>PPUPRIV (SMU_IF)</p>
            </td>
            <td>
                <p>Fault status now limited only to Secure state</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>PPUINST (SMU_IF)</p>
            </td>
            <td>
                <p>Fault status now limited only to Secure state</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>PPUPRIV (SMU_IEN)</p>
            </td>
            <td>
                <p>Fault status now limited only to Secure state</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>PPUINST (SMU_IEN)</p>
            </td>
            <td>
                <p>Fault status now limited only to Secure state</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>PPUFSPERIPHID (SMU_PPUFS)</p>
            </td>
            <td>
                <p>Fault status now limited only to Secure state</p>
            </td>
        </tr>
    </tbody>
</table>

**Table** Dedicated SMU Interrupt for Non-secure State

<table>
    <thead>
        <tr>
            <th>Interrupt</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>SMU_NS_PRIVILEGED_IRQHandler()</p>
            </td>
            <td>
                <p>An interrupt flag in the SMU_CFGNS_NSIF register can generate an NS privileged interrupt when its corresponding interrupt enable bit in the SMU_CFGNS_NSIEN register is set and SMU_NS_PRIVILEGED_IRQn is enabled, and in which the peripheral (ID) that triggers the fault is in the SMU_CFGNS_PPUNSFS register.</p>
            </td>
        </tr>
    </tbody>
</table>

##### Secure and Privileged Programming Model

The implementation of BLS on Series 2 devices, both flash and RAM, use a programmable watermark to delineate Secure, Non-secure Callable, and Non-secure regions. On the other hand, peripherals exist in both a Secure and Non-secure alias of memory.

###### BLS SMU Programming

###### Enabling SMU Clock (heading level 7)

Except for the EFR32xG21 devices, all Series 2 devices enable the SMU clock in CMU before programming the SMU registers.

```C

#if (_SILICON_LABS_32B_SERIES_2_CONFIG > 1)
  CMU->CLKEN1_SET = CMU_CLKEN1_SMU;
#endif

```

###### Cortex-M33 Lock Control (heading level 7)

The Cortex-M33 security and privileged configurations can be locked by programming the `SMU_M33CTRL` register.

```C

  // Lock Secure MPU configuration
  SMU->M33CTRL |= SMU_M33CTRL_LOCKSMPU;

```

###### Locking SMU Configuration (heading level 7)

Th entire SMU configuration can be locked down to avoid runaway code. Below is an example of how to lock and unlock the SMU.

```C

  uint32_t lock_status;
  // Lock Down SMU
  SMU->LOCK = ~SMU_LOCK_SMULOCKKEY_UNLOCK;
  // Grab Lock Status
  lock_status = (SMU->STATUS & _SMU_STATUS_SMULOCK_MASK) >> _SMU_STATUS_SMULOCK_SHIFT;
  // Unlock SMU
  SMU->LOCK = SMU_LOCK_SMULOCKKEY_UNLOCK;

```

###### Interrupt Control (heading level 7)

Each interrupt flag in `SMU_IF` can generate an interrupt when its corresponding interrupt enable flag in the `SMU_IEN` register is set. Each interrupt flag can be cleared by writing the clear alias of the `SMU_IF` register.

```C

  // Clear and enable the SMU PPUSEC and BMPUSEC interrupt
  NVIC_ClearPendingIRQ(SMU_SECURE_IRQn);
  SMU->IF_CLR = SMU_IF_PPUSEC | SMU_IF_BMPUSEC;
  NVIC_EnableIRQ(SMU_SECURE_IRQn);
  SMU->IEN = SMU_IEN_PPUSEC | SMU_IEN_BMPUSEC;

```

###### BLS ESAU Programming

###### Region Types (heading level 7)

The `SMU_ESAURTYESn` registers are used to configure memory regions with a specific security attribute. All configurable memory regions reset to Secure. Below is an example of programming regions 3 and 11 to Non-secure.

```C

  // Region 3 (Info flash) is Non-secure
  SMU->ESAURTYPES0 = SMU_ESAURTYPES0_ESAUR3NS;
  // Region 11 (EPPB) is Non-secure
  SMU->ESAURTYPES1 = SMU_ESAURTYPES1_ESAUR11NS;

```

###### Region Sizes (heading level 7)

The code and figure below highlight how to program the Movable Region Boundaries (MRBs) of ESAU.

```C

  // ESAU region 0/1/2 programming
  // Boundary01 at 252kB and  Boundary12 at 256kB
  SMU->ESAUMRB01 = 0x0003F000U & _SMU_ESAUMRB01_MASK;
  SMU->ESAUMRB12 = 0x00040000U & _SMU_ESAUMRB12_MASK;
  // ESAU region 4/5/6 programming
  // Boundary45 at 44kB and Boundary56 at 44kB (region 5 size = 0)
  SMU->ESAUMRB45 = 0x0000B000U & _SMU_ESAUMRB45_MASK;
  SMU->ESAUMRB56 = 0x0000B000U & _SMU_ESAUMRB56_MASK;

```

![esau_prog](/series2-trustzone/0.2/images/sld717-esau-prog.png)

**Notes**:

- The mrb12 (`ESAUMRB12` in `SMU_ESAURMBR12`) has to be greater than or equal to mrb01 (`ESAUMRB12` in `SMU_ESAURMBR12`).
- The mrb56 (`ESAUMRB56` in `SMU_ESAURMBR562`) has to be greater than or equal to mrb45 (`ESAUMRB45` in `SMU_ESAURMBR45`).
- If one of the rules above is violated, the `SMU_STATUS.SMUPRGERR` is asserted.
- When mrb01 and mrb12 are equal, region 1 (NSC) is a size of 0 and is not seen by the system.
- When mrb45 and mrb56 are equal, region 5 (NSC) is a size of 0 and is not seen by the system.

###### BLS SAU Programming

###### All Secure Configuration (heading level 7)

[All secure configuration](03-r-bls) is the default state after reset. It clears the `SAU_CTRL.ENABLE` and the `SAU_CTRL.ALLNS` bits in SAU, and the entire memory is in a Secure attribute.

###### All Non-secure Configuration (heading level 7)

[All Non-secure Configuration](03-r-bls) occurs when the `SAU_CTRL.ENABLE` bit is cleared, and the `SAU_CTRL.ALLNS` bit is set. The ESAU controls the security attribute of a Cortex-M33 transaction.

```C

  // Disable SAU (ALLNS = 1) and clear data and instruction pipe
  SAU->CTRL = SAU_CTRL_ALLNS_Msk;
  __DSB();
  __ISB();

```

###### Configurable Configuration (heading level 7)

[Configurable configuration](03-r-bls) occurs when the `SAU_CTRL.ENABLE` bit is set (`SAU_CTRL.ALLNS` is irrelevant). Both SAU and [ESAU](#region-sizes) determine the security attribute of a Cortex-M33 transaction. The code and figure below highlight how to program the SAU regions.

```C

  // Define all Non-secure (NS) and Non-secure Callable (NSC) Regions
  #define REGION0_BASE 0x0001E000UL
  #define REGION1_BASE 0x00020000UL
  #define REGION2_BASE 0x20004000UL
  #define REGION0_LIMIT 0x0001FFFFUL
  #define REGION1_LIMIT 0x000FFFFFUL
  #define REGION2_LIMIT 0x20017FFFUL
  // CMSIS calls to enable SAU Regions
  // SAU region 0 - Flash NSC at 120 kB to 128 kB (0x0001E000 - 0x0001FFFF)
  SAU->RNR = (0UL & SAU_RNR_REGION_Msk);
  SAU->RBAR = (REGION0_BASE & SAU_RBAR_BADDR_Msk);
  SAU->RLAR = (REGION0_LIMIT & SAU_RLAR_LADDR_Msk) | SAU_RLAR_NSC_Msk | SAU_RLAR_ENABLE_Msk;
  // SAU region 1 - Flash NS at 128 KB to 1024 kB (0x00020000 - 0x000FFFFF)
  SAU->RNR = (1UL & SAU_RNR_REGION_Msk);
  SAU->RBAR = (REGION1_BASE & SAU_RBAR_BADDR_Msk);
  SAU->RLAR = (REGION1_LIMIT & SAU_RLAR_LADDR_Msk) | SAU_RLAR_ENABLE_Msk;
  // SAU region 2 - RAM NS at 16 kB to 96 kB (0x20004000 - 0x20017FFF)
  SAU->RNR = (2UL & SAU_RNR_REGION_Msk);
  SAU->RBAR = (REGION2_BASE & SAU_RBAR_BADDR_Msk);
  SAU->RLAR = (REGION2_LIMIT & SAU_RLAR_LADDR_Msk) | SAU_RLAR_ENABLE_Msk;
  // CMSIS functions to enable SAU and clear data and instruction pipe
  TZ_SAU_Enable();
  __DSB();
  __ISB();

```

![sau-frog](/series2-trustzone/0.2/images/sld717-sau-prog.png)

###### BLS BMPU Programming

###### Bus Master Privileged Attribute (heading level 7)

A Bus Master can be configured as either privileged (default) or unprivileged by programming the corresponding index in the `SMU_BMPUPATDn` register.

```C

  // Configure all odd Bus Masters unprivileged
  for (i = 0; i < SMU_NUM_BMPUS; i++) {
    if (i & 0x01) {
      SMU->BMPUPATD0 &= ~(1 << i);
    }
  }

```

###### Bus Master Security Attribute (heading level 7)

A Bus Master can be configured as either Secure (default) or Non-secure by programming the corresponding index in the `SMU_BMPUPATDn` register. Configure a Bus Master as Non-secure results in the Bus Master only being able to access Non-secure addresses.

```C

  // Configure all odd Bus Masters Non-secure
  for (i = 0; i < SMU_NUM_BMPUS; i++) {
    if (i & 0x01) {
      SMU->BMPUSATD0 &= ~(1 << i);
    }
  }

```

###### Bus Master Fault Status (heading level 7)

The Bus Master ID and the address that triggered the fault can be read from the `SMU_BMPUFS` and `SMU_BMPUFSADDR` registers.

```C

  uint32_t fs_bmpu_id;
  uint32_t fs_bmpu_addr;
  uint32_t fs_bmpu_secfault;
  // Read Bus Master fault status
  fs_bmpu_id = SMU->BMPUFS;
  fs_bmpu_addr = SMU->BMPUFSADDR;
  fs_bmpu_secfault = (SMU->IF & _SMU_IF_BMPUSEC_MASK) >> _SMU_IF_BMPUSEC_SHIFT;
  // Clear the IF to capture a new fault
  SMU->IF_CLR = SMU_IF_BMPUSEC;

```

###### BLS PPU Programming

###### Peripheral Privileged Attributes (heading level 7)

A peripheral can be configured as either privileged (default) or unprivileged by programming the corresponding index in the `SMU_PPUPATDn` register.

```C

  // Configure all odd peripherals unprivileged
  for (i = 0; i < SMU_NUM_PPU_PERIPHS; i++) {
    if (i & 0x01) {
      if (i >= 32){
        SMU->PPUPATD1 &= ~(1 << (i-32));
      } else {
        SMU->PPUPATD0 &= ~(1 << i);
      }
    }
  }

```

**Notes**:

- The peripherals in `SMU_PPUPATD0` and `SMU_PPUPATD0` are device-dependent.
- The privileged attribute of the radio subsystem (`AHBRADIO` index) is configured as a unit.

###### Peripheral Security Attributes (heading level 7)

A peripheral can be configured as either Secure (default) or Non-secure by programming the corresponding index in the `SMU_PPUSATDn` register. The figure below shows the memory map when the ADC, I2C0, USART1, and RADIO are configured as Non-secure and other peripherals (e.g., SMU, RTCC, TIMER1, TIMER0, USART0...) as Secure.

```C

  // Configure all the Non-secure peripherals
  SMU->PPUSATD0 &= ~SMU_PPUSATD0_USART1;
  SMU->PPUSATD1 &= ~(SMU_PPUSATD1_I2C0 | SMU_PPUSATD1_IADC0 | SMU_PPUSATD1_AHBRADIO);

```

![ppu_prog](/series2-trustzone/0.2/images/sld717-ppu-prog.png)

**Notes**:

- The peripherals in `SMU_PPUSATD0` and `SMU->PPUSATD1` are device-dependent.
- The security attribute of the radio subsystem (`AHBRADIO` index) is configured as a unit.

###### Peripheral Fault Status (heading level 7)

The peripheral ID that triggered the fault can be read from the `SMU_PPUFS` register.

```C

  uint32_t fs_ppu_periph_id;
  uint32_t fs_sec_fault;
  uint32_t fs_priv_fault;
  uint32_t fs_inst_fault;
  // Read peripheral fault status
  fs_ppu_periph_id = SMU->PPUFS;
  fs_sec_fault = (SMU->IF & _SMU_IF_PPUSEC_MASK) >> _SMU_IF_PPUSEC_SHIFT;
  fs_priv_fault = (SMU->IF & _SMU_IF_PPUPRIV_MASK) >> _SMU_IF_PPUPRIV_SHIFT;
  fs_inst_fault = (SMU->IF & _SMU_IF_PPUINST_MASK) >> _SMU_IF_PPUINST_SHIFT;
  // Clear the IF to capture a new fault
  SMU->IF_CLR = SMU_IF_PPUSEC | SMU_IF_PPUPRIV | SMU_IF_PPUINST;

```

###### Floating Point Unit (FPU) Programming

If the Non-secure application enables the FPU at initialization, the Secure software needs to set up the `NSACR` register in [SCB](02-r-basic) to grant the FPU access for Non-secure software.

```C

  // Enable Non-secure access to the FPU
  SCB->NSACR |= SCB_NSACR_CP10_Msk + SCB_NSACR_CP11_Msk;

```

##### TrustZone Implementation

The goal of TrustZone implementation is to provide Secure Key Storage that can keep access to keys limited to Secure applications while at the same time allowing Non-secure applications to exercise the keys. It is an added feature for the SVM devices that do not have dedicated hardware for [Secure Key Storage](https://docs.silabs.com/iot-security/latest/efr32-secure-key-storage/) as in SVH devices.

The [PSA Crypto](https://docs.silabs.com/iot-security/latest/mbedtls-psa-crypto-porting-guide/) is placed in a Secure region to keep key material hidden from the Non-secure application. The exposed PSA Crypto APIs stay the same while the backend provides persistent key encryption and decryption similar to the key wrapping and unwrapping functionality of the SVH device.

The following items need to be considered when upgrading the existing system for Secure Key Storage with TrustZone.

- [System Configuration](#system-configuration)
- [Gecko Bootloader](#gecko-bootloader)
- [Secure Library](#secure-library)
- [TrustZone Secure Key Storage](#trustzone-secure-key-storage)
- [PSA Attestation](#psa-attestation)
- [SE Manager](#se-manager)
- [Common Vulnerabilities and Exposures (CVE)](#common-vulnerabilities-and-exposures-cve)

###### System Configuration

The system configuration includes the following items:

- Enable system exceptions in the Secure state.
- Set the security attributes of different regions in the SAU and ESAU.
- Place peripherals and associated interrupts in either Secure or Non-secure applications.
- Assign the Bus Masters' security attributes.
- The system has two Secure/Non-secure pairs for the bootloader and application. The Secure part of each pair is responsible for properly configuring the split in its Secure application before branching to the Non-secure application.

> **Note**: The secure application will issue a software reset at startup (fatal error) if the device's SE firmware version is lower than the [first version](06-r-migration) that supports TrustZone.

###### System Exceptions (heading level 7)

The following [system exceptions](02-r-basic#type-of-exceptions) are enabled in the Secure state for the bootloader and application.

- MemManage Fault
- BusFault
- UsageFault
- SecureFault

###### Main Flash Layout (heading level 7)

The following figure is an overview of the main flash layout that covers the isolation requirements for the Secure Key Storage solution. The SAU and ESAU configurations provide the required security to the Cortex-M33 and other Bus Masters during boot and normal operation.

![Main Flash Layout](/series2-trustzone/0.2/images/sld717-main-flash-layout.png)

1. Settings:  
   - The application is set to non-executable (XN) by [Secure MPU](02-r-basic#secure-attribution-unit-sau-implementation-defined-attribution-unit-idau-and-memory-protection-unit-mpu) to avoid any code execution in this area during boot.  
   - The bootloader is set to non-executable (XN) by Secure MPU to avoid any code execution in this area during normal operation.
2. The ESAU configuration only uses the NSC section by setting mrb01 to the [base address of region 0](04-r-programming). The reason is that lite ESAU in other Bus Masters treats both S and NSC as a Secure attribute. For the Cortex-M33, the SAU upgrades the NSC in the ESAU to Secure. The 32 bytes region alignment of SAU also relaxes the 4 kB alignment restriction on the start address of the NSC in ESAU.
3. The whole application area is set to Secure in SAU for Cortex-M33 during boot to hide details from the bootloader NS part.
4. The ESAU cannot mark any region that comes after a Non-secure section as Secure (must be in the order of S/NSC/NS). Therefore the Secure application area does not align between the Cortex-M33 (SAU + ESAU) and other Bus Masters (lite ESAU) during boot. The secrets stored in that Secure region expose as Non-secure for other Bus Masters during boot (no such issue in normal operations). So the application must not save any plaintext secrets in that Secure region to overcome this limitation during boot.
5. The NVM storage is in the Non-secure region, so the application must [encrypt](05-r-implementation#secure-library) the persistent keys before storing them in this area.

###### RAM Layout (heading level 7)

The following figure is an overview of the RAM layout used for the bootloader and application. The SAU and ESAU are used to split the RAM into a Secure and Non-secure region (Non-secure Callable is not required).

![RAM Layout](/series2-trustzone/0.2/images/sld717-ram-layout.png)

In practice, the Secure part (bootloader or application) takes ownership of the amount of RAM it needs from the beginning of RAM and leaves the rest (up to the ESAU 4 kB alignment requirement) configured as Non-secure.

The bootloader does not know how the application partitions the RAM between Secure and Non-secure. So bootloader removes any secrets from RAM before handing control to the application.

###### Info Flash and EPPB (heading level 7)

The following figure is an overview of the Info flash and EPPB layout for the application. The Cortex-M33 core is the only Bus Master that can access the EPPB region.

![Info Flash and EPPB Layout](/series2-trustzone/0.2/images/sld717-info-flash-and-eppb-layout.png)

###### Peripheral and Device (heading level 7)

The following figure is an overview of the peripheral and device layout for the bootloader and application. The SAU and ESAU are used to split the peripheral and device into a Secure and Non-secure region.

![Peripheral and Device Layout](/series2-trustzone/0.2/images/sld717-peripheral-and-device-layout.png)

The Secure software is responsible for moving all peripherals and associated interrupts to the Non-secure state at startup, except for the peripherals and interrupts that need to be Secure. The Non-secure software must not include code that attempts to directly access any peripheral that is used by the Secure software.

**Peripherals owned by the Secure software (application)**

1. Security Management Unit (SMU)  
   - It prevents Non-secure software from changing the configuration for the ESAUs, BMPUs, and PPUs.  
   - Except for EFR32xG21 devices, some features are also available in the dedicated [Non-secure version of SMU registers](03-r-bls) (`SMU_CFGNS`).
2. CRYPTOACC (VSE devices) or SEMAILBOX (HSE devices)  
   - The crypto engine is placed in the Secure domain for [Secure library](#secure-library).
3. System Configuration (SYSCFG)  
   - It prevents Non-secure software from changing system configurations for Secure software.  
   - Except for EFR32xG21 devices, some features are also available in the dedicated [Non-secure version of SYSCFG registers](03-r-bls) (`SYSCFG_CFGNS`).
4. Memory System Controller (MSC)  
   - It prevents Non-secure software from writing to Secure flash.

**Peripheral interrupts owned by the Secure software**:

**Table**: Secure Peripheral Interrupts (Application)

<table>
    <tbody>
        <tr>
            <td>VSE Device</td>
            <td>HSE Device</td>
        </tr>
        <tr>
            <td>SMU_SECURE_IRQn</td>
            <td>SMU_SECURE_IRQn</td>
        </tr>
        <tr>
            <td>SYSCFG_IRQn</td>
            <td>SYSCFG_IRQn</td>
        </tr>
        <tr>
            <td>MSC_IRQn</td>
            <td>MSC_IRQn</td>
        </tr>
        <tr>
            <td>CRYTOACC_IRQn</td>
            <td>SEMBRX_IRQn</td>
        </tr>
        <tr>
            <td>TRNG_IRQn</td>
            <td>SEMBTX_IRQn</td>
        </tr>
        <tr>
            <td>PKE_IRQn</td>
            <td></td>
        </tr>
    </tbody>
</table>

The `PRIS` bit in the `AIRCR` register is set to 1 to place all Non-secure exceptions/interrupts in [lower priority level space](02-r-basic). Therefore any Secure exceptions/interrupts can be programmed with higher priority than Non-secure ones.

The [`BMPUSEC`](03-r-bls) and [`PPUSEC`](03-r-bls) interrupt enable flags in the `SMU_IEN` register are set to enable the SMU security fault interrupts (`SMU_SECURE_IRQn`) on Bus Masters and peripherals.

**Floating Point Unit (FPU)**:

The Secure application does not use the FPU. But the Secure startup code also enables the [FPU](04-r-programming) for use by the Non-secure application.

###### Bus Masters (heading level 7)

To keep all secrets from the Non-secure world, only the Bus Masters in the table below can access data in the Secure world. For the Bus Masters living in the Secure world, the secure application must configure their corresponding control interfaces in the peripheral space to Secure. The Cortex-M33 core as a Bus Master is split to run in Secure and Non-secure contexts.

**Table**: Secure Bus Masters (Application)

<table>
    <thead>
        <tr>
            <th>Device</th>
            <th>Secure Bus Master</th>
            <th>Control Interface of Bus Master</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>VSE</p>
            </td>
            <td>
                <p>CRYPTOACC</p>
            </td>
            <td>
                <p>CRYTPOACC</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>HSE</p>
            </td>
            <td>
                <p>SEDMA or SEEXTDMA</p>
            </td>
            <td>
                <p>SEMAILBOX</p>
            </td>
        </tr>
    </tbody>
</table>

**Notes**:

- Use `SMU_BMPUSATD0` register to [configure](04-r-programming) the security attribute of a Bus Master.
- Use `SMU_PPUSATDn` register to [configure](04-r-programming) the control interface of Bus Master as a Secure peripheral.
- LDMA is set as a Non-secure Bus Master to make sure it cannot be used to copy out data from the Secure memory.

###### Application Transitions (heading level 7)

The system contains two Secure/Non-secure pairs.

1. The [bootloader pair](#bootloader pair) has a Secure bootloader and a Non-secure bootloader containing the communication interfaces.
2. The [application pair](#application pair) has a Secure application and a Non-secure application consisting of the wireless stacks (if applicable) and application layers.

As described in the preceding sections, the Secure part of these pairs is responsible for setting the security configurations of the system during startup. For the handover between Secure/Non-secure pairs, the software must restore the system so the Secure part of the other pair can execute and reconfigure the system.

The software must reconfigure the following items before transitioning to the next Secure/Non-secure pair:

- Restored all peripherals and interrupts to Secure
- Reset ESAU to default configuration (all configurable regions to Secure)
- Reset SAU to default configuration (Secure for everything)
- Reset MPU to default configuration (removes any XN)

###### Gecko Bootloader

The [Gecko bootloader](https://docs.silabs.com/mcu-bootloader/latest/bootloader-user-guide-gsdk-4/) ensures the Secure assets are protected during the boot flow and normal operation.

![Gecko Bootloader Flow](/series2-trustzone/0.2/images/sld717-gecko-bootloader-flow.png)

1. The SAU and [Secure MPU](02-r-basic) mark all the flash for application and NVM as Secure and non-executable (XN) during boot. It guards against bootloader NS code execution branching into the application code.
2. The bootloader needs to split into Secure and Non-secure software to protect secrets in the system. Secure code can access the entire flash to validate or upgrade the system.
3. For VSE devices, the [GBL Decryption Key (AES-128 key)](https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/) is moved from the NS memory (last page of the main flash) to the Secure part of the bootloader. The Simplicity Commander v1.13 or higher provides a feature to inject the AES-128 key to the bootloader binary file.  
   ```C  
   commander convert <BL image file> --aeskey <decryption key file> --outfile <BL image with decryption key>  
   ```
4. The bootloader communication interfaces are placed in the NS area to support various [communication components](https://docs.silabs.com/mcu-bootloader/latest/group-Communication) below for firmware upgrades.  
   - BGAPI UART  
   - EZSP-SPI  
   - UART XMODEM
5. The NS communication functions call into the [bootloader APIs](https://docs.silabs.com/mcu-bootloader/latest/group-Interface) placed in the bootloader NSC region. The Secure application [validates](02-r-basic) all input arguments before processing the request.
6. Before transiting from bootloader to normal operation, it resets the SAU to default configuration to make all the flash for bootloader as Secure.
7. The Non-secure application software can call [bootloader APIs](https://docs.silabs.com/mcu-bootloader/latest/group-Interface) through application NSC, and the corresponding Secure function releases the non-executable (XN) restriction on the bootloader during normal operation.

###### Secure Library

The goal of the Secure library is to keep the [PSA Crypto key](#trustzone-secure-key-storage) and [attestation token](#psa-attestation) protected from malicious code on the NSPE. The following figure overviews multiple components to support the Secure library.

![Secure Library Components](/series2-trustzone/0.2/images/sld717-secure-library-components.png)

1. The NS interfaces in NSPE are responsible for packing and passing all input arguments over the [NSC](02-r-basic) functions on wrappers in SPE.
2. The wrappers in SPE validate all input arguments before calling into the corresponding APIs in different drivers.
3. Because of the system memory layout limitation, the [flash](#main-flash-layout) for NVM3 storage is located in the NSPE. Therefore the updated PSA Internal Trusted Storage (ITS) driver needs to encrypt all crypto keys before storing them in Non-secure NVM.
4. Data stored directly using the NVM3 APIs are not encrypted.

The following table describes the new and updated components of the Secure library.

<table>
    <thead>
        <tr>
            <th>Component</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>SE Manager NS interface</p>
            </td>
            <td>
                <p>This component contains SE Manager API callable from the NSPE. It packages the list of input arguments in the appropriate format before calling into the SE Manager wrapper's NSC functions.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SE manager wrapper</p>
            </td>
            <td>
                <p>This component contains the interface into SE Manager exposed to the NSPE. These NSC functions grant access to the SE Manager utility API and validate all input arguments before calling into SE Manager.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>PSA Crypto &amp; Attestation NS interface</p>
            </td>
            <td>
                <p>This component contains PSA Crypto and attestation API callable from the NSPE. It packages the list of input arguments in the appropriate format before calling into the PSA Crypto and attestation wrapper's NSC functions.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>PSA Crypto &amp; Attestation wrapper</p>
            </td>
            <td>
                <p>This component contains the interface into PSA Crypto and attestation exposed to the NSPE. These NSC functions grant access to the entire PSA Crypto and attestation API and validate all input arguments before calling into PSA Crypto and attestation.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>PSA attestation</p>
            </td>
            <td>
                <p>This component in SPE provides the functionality required by the PSA attestation specification.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Encrypted PSA ITS</p>
            </td>
            <td>
                <p>The PSA ITS layer builds on top of NVM3. This component is updated to support encrypted storage to secure stored keys. The encryption is based on the device's TrustZone Root Key.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>NVM3 NS interface</p>
            </td>
            <td>
                <p>This component contains NVM3 API callable from the NSPE. It packages the list of input arguments in the appropriate format before calling into the NVM3 wrapper's NSC functions.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>NVM3 wrapper</p>
            </td>
            <td>
                <p>This component contains the interface into NVM3 exposed to the NSPE. These NSC functions grant access to the NVM3 API and validate all input arguments before calling into NVM3.</p>
            </td>
        </tr>
    </tbody>
</table>

**Notes**:

- The SE Manager NS interface, PSA Crypto NS interface, and NVM3 NS interface in the NSPE provide drop-in replacement on [SE Manager utility](#se-manager), [PSA Crypto](https://armmbed.github.io/mbed-crypto/html/about.html), and [NVM3](https://docs.silabs.com/gecko-platform/latest/driver/api/group-nvm3) APIs for existing wireless stacks and user applications.
- The NSC calls can only take a limited number of arguments, so all NSC functions take a pointer to a list of parameters to support a long list of arguments. All arguments must be validated using the [intrinsic functions](02-r-basic) from CMSIS.

###### TrustZone Secure Key Storage

The TrustZone Secure Key Storage provides a solution to store a user key in Secure RAM or an encrypted form in Non-secure flash.

The TrustZone Root Key stored in the SE NVM for Secure Key Storage encryption is generated or renewed by following operations.

- The TrustZone Root Key had already existed if the shipped Series 2 device with [SE firmware version](06-r-migration) supports this key.
- Generate a TrustZone Root Key when upgrading from a SE firmware version that did not support this key to the one that does.
- Renew a TrustZone Root Key after performing a [Device Erase](https://docs.silabs.com/iot-security/latest/series2-secure-debug/).

> **Note**: The TrustZone Root Key cannot be renewed if Device Erase is disabled.

The TrustZone Root Key is not exposable to the NSPE, and access to this key in SPE is different in HSE and VSE devices.

- HSE - The SPE can access the TrustZone Root Key as a built-in non-exportable key in HSE NVM.
- VSE - The SPE can access the TrustZone Root Key in Secure RAM, which is copied from VSE NVM during boot.

The TrustZone Root Key value after reset is identical to the value before reset. TrustZone Root Keys are unique on each device. The key allows a user to securely store a key in the Non-secure flash, limiting the number of keys that can be saved only by the amount of Non-secure storage. The following figure describes using the TrustZone Root Key to encrypt a plaintext key and store it in Non-secure NVM.

![root_encrypt](/series2-trustzone/0.2/images/sld717-root-encrypt.png)

1. After power-on, the device's TrustZone Root Key is available for the SPE.
2. A user key is generated and imported into the device's Non-secure memory. In this example, the key is imported into Non-secure RAM for easy deletion, and the key is lost if device power is removed.
3. Call PSA Crypto API (`psa_import_key()` or `psa_generate_key()`) through SG in NSC to generate a key for crypto operations.
4. The plaintext key is passed to the PSA Crypto in SPE, where it is encrypted (AES-GCM) with the TrustZone Root Key.
5. The encrypted key is stored to the NVM in NSPE through the PSA ITS and NVM3 drivers.
6. The plaintext key can now be deleted from the Non-secure RAM.
7. Only the PSA Crypto in SPE can retrieve the encrypted key from NVM in NSPE and decrypt it for crypto operations in SPE.

> **Note**: Ignore steps 2 and 6 if the plaintext key is randomly generated by the PSA Crypto.

The following tables describe the storage differences between SVM and SVH devices with and without TrustZone Secure Key Storage (SKS).

<table>
    <thead>
        <tr>
            <th>Key Type</th>
            <th>Storage on SVM Device</th>
            <th>Storage on SVH Device</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Volatile Plaintext (without TrustZone SKS)</p>
            </td>
            <td>
                <p>RAM</p>
            </td>
            <td>
                <p>RAM</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Persistent Plaintext (without TrustZone SKS)</p>
            </td>
            <td>
                <p>NVM</p>
            </td>
            <td>
                <p>NVM</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Volatile Wrapped (without TrustZone SKS)</p>
            </td>
            <td>
                <p>Not supported</p>
            </td>
            <td>
                <p>RAM (1)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Persistent Wrapped (without TrustZone SKS)</p>
            </td>
            <td>
                <p>Not supported</p>
            </td>
            <td>
                <p>NVM (1)</p>
            </td>
        </tr>
    </tbody>
</table>

<table>
    <thead>
        <tr>
            <th>Key Type</th>
            <th>Storage on SVM Device</th>
            <th>Storage on SVH Device</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Volatile Plaintext (with TrustZone SKS)</p>
            </td>
            <td>
                <p>Secure RAM (2)</p>
            </td>
            <td>
                <p>Secure RAM</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Persistent Plaintext (with TrustZone SKS)</p>
            </td>
            <td>
                <p>Encrypted plaintext key in NS NVM (2)</p>
            </td>
            <td>
                <p>Encrypted plaintext key in NS NVM</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Volatile Wrapped (with TrustZone SKS)</p>
            </td>
            <td>
                <p>Not supported</p>
            </td>
            <td>
                <p>Secure RAM</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Persistent Wrapped (with TrustZone SKS)</p>
            </td>
            <td>
                <p>Not supported</p>
            </td>
            <td>
                <p>Encrypted wrapped key in NS NVM</p>
            </td>
        </tr>
    </tbody>
</table>

**Notes**:

- The NVM or [NS NVM](#main-flash-layout) is at the last part of the main flash.
- It is possible to replace the [wrapped key](https://docs.silabs.com/iot-security/latest/efr32-secure-key-storage/) solution on the SVH device (1) with TrustZone Secure Key Storage on the SVM device (2), but this is a less secure approach.

###### PSA Attestation

The device attestation service creates a token that contains a fixed set of device-specific data when requested by the caller. Each device must have a unique Initial Attestation Key (IAK) pair. The device uses the private IAK to sign the token, and the caller uses the public IAK to verify the token's authenticity.

The generation of the private IAK is different in SVM and SVH devices.

- SVM - If the private IAK does not exist in NVM3, it is randomly generated when requested from the [PSA Attestation](#secure-library) driver and saved to NVM3 through the [TrustZone Secure Key Storage](#trustzone-secure-key-storage).
- SVH - The private IAK is generated and securely stored in the HSE during chip production.

An Entity Attestation Token (EAT) is a mini-report that is cryptographically signed. An EAT is encoded in either one of two standardized data formats: a Concise Binary Object Representation ([CBOR](https://www.rfc-editor.org/info/rfc7049)) or in the text-based format JSON. A digital signature is then used to protect its content. The technical specification defining the content of the EAT, which are claims about the hardware and the software running on a device, is specified by the Internet Engineering Task Force ([IETF](https://datatracker.ietf.org/doc/html/draft-ietf-rats-eat-11)).

The EAT is a crypto-signed report card with claims. A claim is a data item that is represented as a Key-Value pair. Claims can relate to the device's pedigree or anything one wants the device to attest. Collected data can originate from the Root of Trust (RoT), any protected area, or non-protected areas.

The EAT must be signed following the structure of the CBOR Object Signing and Encryption ([COSE](https://www.rfc-editor.org/info/rfc8152)) specification. For asymmetric key algorithms, the signature structure must be COSE-Sign1. A COSE-Sign1 is a CBOR encoded, self-secured data blob that contains headers, a payload, and a signature.

The primary need for EAT verification is to check correct formation and signing as for any token. In addition, though, the verifier can operate a policy where values of some of the claims in this profile can be compared to reference values that are registered with the verifier for a given deployment, to confirm that the device is endorsed by the manufacturer supply chain.

The [PSA attestation token](https://www.ietf.org/archive/id/draft-tschofenig-rats-psa-token-21.html) (aka Initial Attestation Token - IAT) is a profiled EAT. The Series 2 device will generate this token by (Nonce claim below) unless the SE OTP is uninitialized or the [`SECURE_BOOT_ENABLE`](https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/) option in SE OTP is disabled.

The following tables describe claims used in the PSA attestation token of the Series 2 device.

**Table**: Claims of PSA Attestation Token

<table>
    <thead>
        <tr>
            <th>Key</th>
            <th>Claim Name (Present)</th>
            <th>Claim Description</th>
            <th>Claim Value</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>265 (-75000)</p>
            </td>
            <td>
                <p>Profile Definition (Must)</p>
            </td>
            <td>
                <p>The Profile Definition claim encodes the unique identifier corresponds to the EAT profile.</p>
            </td>
            <td>
                <p>http://arm.com/psa/2.0.0</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>2394 (-75001)</p>
            </td>
            <td>
                <p>Client ID (Must)</p>
            </td>
            <td>
                <p>The Client ID claim represents the security domain of the caller.</p>
            </td>
            <td>
                <p>See note below (2 byes)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>2395 (-75002)</p>
            </td>
            <td>
                <p>Security Lifecycle (Must)</p>
            </td>
            <td>
                <p>The Security Lifecycle claim represents the current lifecycle state of the PSA RoT.</p>
            </td>
            <td>
                <p>Device dependent (2 bytes)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>2396 (-75003)</p>
            </td>
            <td>
                <p>Implementation ID (Must)</p>
            </td>
            <td>
                <p>The Implementation ID claim uniquely identifies the implementation of the immutable PSA RoT.</p>
            </td>
            <td>
                <p>Device dependent (32 bytes)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>2397 (-75004)</p>
            </td>
            <td>
                <p>Boot Seed (Optional)</p>
            </td>
            <td>
                <p>The Boot Seed claim represents a value created at system boot time that will allow differentiation of reports from different boot sessions.</p>
            </td>
            <td>
                <p>Device dependent (32 bytes)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>2399 (-75006)</p>
            </td>
            <td>
                <p>Software Components (Must)</p>
            </td>
            <td>
                <p>The Software Components claim is a list of software components that includes all the software loaded by the PSA RoT.</p>
            </td>
            <td>
                <p>See note below</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>10 (-75008)</p>
            </td>
            <td>
                <p>Nonce (Must)</p>
            </td>
            <td>
                <p>The Nonce claim is used to carry the challenge provided by the caller to demonstrate freshness of the generated token. The length must be either 32, 48, or 64 bytes.</p>
            </td>
            <td>
                <p>Random nonce (32/48/64 bytes)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>256 (-75009)</p>
            </td>
            <td>
                <p>Instance ID (Must)</p>
            </td>
            <td>
                <p>The Instance ID claim represents the unique identifier of the IAK. The length must be 33 bytes.</p>
            </td>
            <td>
                <p>SHA-256 hash of public IAK (32 bytes) with header 0x01</p>
            </td>
        </tr>
    </tbody>
</table>

**Notes**:

- Some claims MUST be present in a PSA attestation token.
- The keys `-7500x` were defined in a previous version of the PSA attestation token specification (`PSA_IOT_PROFILE_1` profile) that is still used in the HSE-SVH firmware.
- The actual claims returned from the tokens on the SVH device are HSE firmware version-dependent.
- Key 2394: In PSA, a security domain is represented by a signed integer where negative values represent callers from the NSPE and positive IDs represent callers from the SPE. The value 0 is not permitted.
- Key 2395 (For the definitions of these lifecycle states, refer to the ARM [PSA Security Model](https://developer.arm.com/-/media/Files/pdf/PlatformSecurityArchitecture/Architect/DEN0079_PSA_SM_ALPHA-03_RC01.pdf)):  
  - UNKNOWN (`0x0000 - 0x00ff`)  
  - ASSEMBLY_AND_TEST (`0x1000 - 0x10ff`)  
  - PSA_ROT_PROVISIONING (`0x2000 - 0x20ff`)  
  - SECURED (`0x3000 - 0x30ff`)  
  - NON_PSA_ROT_DEBUG (`0x4000 - 0x40ff`)  
  - RECOVERABLE_PSA_ROT_DEBUG (`0x5000 - 0x50ff`)  
  - DECOMMISSIONED (`0x6000 - 0x60ff`)
- Key 2396:  
  - Word[0]: Die revision  
  - Word[1]: SE OTP version (return 0 for VSE SE firmware < [v1.2.14](06-r-migration))  
  - Word[2]: Security capability (not applicable to HSE-SVH device, always returns 1 in this word)    
    - 0: Unknown security capability    
    - 1: Security capability not applicable    
    - 2: Basic security capability    
    - 3: Root of Trust security capability    
    - 4: HSE-SVM security capability    
    - 5: HSE-SVH security capability (run HSE-SVM binary on HSE-SVH device)  
  - Word[3]: Production version  
  - Word[4:7]: Reserved (zeros)
- Key 2399: Each software component uses the attributes described in the following table, and some MUST be present in a software component claim.

<table>
    <thead>
        <tr>
            <th>Key</th>
            <th>Attribute (Present)</th>
            <th>Description</th>
            <th>Value</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>1</p>
            </td>
            <td>
                <p>Measurement Type (Optional)</p>
            </td>
            <td>
                <p>The Measurement Type attribute is a short string representing the role of this software component.</p>
            </td>
            <td>
                <p>See note below</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>2</p>
            </td>
            <td>
                <p>Measurement Value (Must)</p>
            </td>
            <td>
                <p>The Measurement Value attribute represents a hash of the invariant software component in memory at startup time.</p>
            </td>
            <td>
                <p>SHA-256 hash (32 bytes) of the firmware</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>4</p>
            </td>
            <td>
                <p>Version (Optional)</p>
            </td>
            <td>
                <p>The Version attribute is the issued software version in the form of a text string.</p>
            </td>
            <td>
                <p>A string of 8 bytes</p>
            </td>
        </tr>
    </tbody>
</table>

The following measurement types may be used for Key 1:

- "BL": a Bootloader
- "PRoT": a component of the PSA Root of Trust
- "ARoT": a component of the Application Root of Trust
- "App": a component of the NSPE application
- "TS": a component of a Trusted Subsystem

The PSA Attestation API allows access to the PSA attestation token, so an external entity can cryptographically verify the identity and trust status of the device.

**Table**: PSA Attestation API

<table>
    <thead>
        <tr>
            <th>PSA Attestation API</th>
            <th>Usage</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>psa_initial_attest_get_token</p>
            </td>
            <td>
                <p>Retrieve the PSA attestation Token.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>psa_initial_attest_get_token_size</p>
            </td>
            <td>
                <p>Calculate the size of a PSA attestation Token.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_tz_attestation_get_public_key</p>
            </td>
            <td>
                <p>Get the public IAK key for PSA attestation token signature verification.</p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**: The `sl_tz_attestation_get_public_key` is a Silicon Labs custom API.

###### SE Manager

SE Manager is the foundation for the [Secure library](#secure-library) cryptographic operations on HSE devices. It means that SE Manager has to move into the SPE.

The following SE Manager [core](https://docs.silabs.com/gecko-platform/latest/service/api/group-sl-se-manager-core) APIs are always available in the NSPE.

<table>
    <thead>
        <tr>
            <th>SE Manager Core API</th>
            <th>VSE-SVM</th>
            <th>HSE-SVM</th>
            <th>HSE-SVH</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>sl_se_init</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_deinit</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_init_command_context</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_deinit_command_context</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_set_yield</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
    </tbody>
</table>

The following SE Manager [core](https://docs.silabs.com/gecko-platform/latest/service/api/group-sl-se-manager-core) APIs expose to the NSPE through the NSC interface for the VSE devices.

<table>
    <thead>
        <tr>
            <th>SE Manager Core API</th>
            <th>VSE-SVM</th>
            <th>HSE-SVM</th>
            <th>HSE-SVH</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>sl_se_read_executed_command</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>-</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_ack_command</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>-</p>
            </td>
        </tr>
    </tbody>
</table>

The following SE Manager [utility](https://docs.silabs.com/gecko-platform/latest/service/api/group-sl-se-manager-util) APIs expose to the NSPE through the NSC interface for configuring the security features of HSE or VSE devices.

<table>
    <thead>
        <tr>
            <th>SE Manager Utility API</th>
            <th>VSE-SVM</th>
            <th>HSE-SVM</th>
            <th>HSE-SVH</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>sl_se_check_se_image</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_apply_se_image</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_get_upgrade_status_se_image</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_check_host_image</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_apply_host_image</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_get_upgrade_status_host_image</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_init_otp_key</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_read_pubkey</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_init_otp</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_read_otp</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_get_se_version</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_get_debug_lock_status</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_apply_debug_lock</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_get_otp_version</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_write_user_data</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>Y (EFR32xG21 only)</p>
            </td>
            <td>
                <p>Y (EFR32xG21 only)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_erase_user_data</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>Y (EFR32xG21 only)</p>
            </td>
            <td>
                <p>Y (EFR32xG21 only)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_get_reset_cause</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>Y (EFR32xG21 only)</p>
            </td>
            <td>
                <p>Y (EFR32xG21 only)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_get_status</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_get_serialnumber</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_enable_secure_debug</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_disable_secure_debug</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
    </tbody>
</table>

<table>
    <thead>
        <tr>
            <th>SE Manager Utility API</th>
            <th>VSE-SVM</th>
            <th>HSE-SVM</th>
            <th>HSE-SVH</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>sl_se_set_debug_options</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_erase_device</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_disable_device_erase</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_get_challenge</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_roll_challenge</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_open_debug</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_disable_tamper</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_read_cert_size</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>sl_se_read_cert</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>Y</p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**: The NSPE cannot access the other [SE Manager APIs](https://docs.silabs.com/gecko-platform/latest/service/api/group-sl-se-manager) for cryptographic and attestation operations.

###### Common Vulnerabilities and Exposures (CVE)

At this writing, the following known TrustZone CVE had been fixed in the current implementation.

- [CVE-2020-16273](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-16273): Stack sealing
- [CVE-2021-36465](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-35465): VLLDM instruction/floating-point vulnerability

##### Upgrade Existing Application to TrustZone

The main concerns when upgrading existing deployment to the TrustZone solution are:

- The [Secure/Non-secure pair for the bootloader](05-r-implementation) (24 kB) does not fit inside the current allotted bootloader space (16 kB).
- The [Secure/Non-secure pair for the application](07-r-ex) does not fit inside the current allotted application space.
- The [PSA ITS](05-r-implementation) moves from a non-encrypted to an encrypted format, so the existing stored cryptographic keys in NVM3 cannot be reused after upgrading the current application to TrustZone.

The [Secure Library](05-r-implementation) is based on PSA Crypto, so the existing application cannot integrate with the TrustZone if one of the following conditions is valid.

- Use [SE Manager APIs](https://docs.silabs.com/gecko-platform/latest/service/api/group-sl-se-manager) for cryptographic and attestation operations.
- Use classic Mbed TLS APIs for cryptographic operations (except for X.509 certificate) and Transport Layer Security (TLS) protocol.

###### System Requirements

The following table lists the tools and software required for TrustZone development on Series 2 devices.

<table>
    <thead>
        <tr>
            <th>Tool/Software</th>
            <th>Required Version</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>GCC</p>
            </td>
            <td>
                <p>v10.3.1</p>
            </td>
            <td>
                <p>Fix a bug (ID 99271) on cmse_nonsecure_call attribute.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>IAR EWARM</p>
            </td>
            <td>
                <p>v9.20.4</p>
            </td>
            <td>
                <p>Fix a bug (EWARM-9484) on __cmse_nonsecure_call attribute.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Segger J-Link</p>
            </td>
            <td>
                <p>≥ v7.6.2c</p>
            </td>
            <td>
                <p>v7.6.2c is the first version to add basic TrustZone support on Series 2 devices.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Simplicity Studio</p>
            </td>
            <td>
                <p>≥ v5.6.3.0</p>
            </td>
            <td>
                <p>v5.6.3.0 is the first version to support TrustZone software development on Series 2 devices.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Simplicity Commander</p>
            </td>
            <td>
                <p>≥ v1.13.3</p>
            </td>
            <td>
                <p>v1.13.3 includes a TrustZone-aware flash loader and supports features required for TrustZone development.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>GSDK</p>
            </td>
            <td>
                <p>≥ v4.2.2</p>
            </td>
            <td>
                <p>GSDK v4.2.2 is the first version to support TrustZone software development on Series 2 devices.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SE Firmware</p>
            </td>
            <td>
                <p>≥ v1.2.14</p>
            </td>
            <td>
                <p>v1.2.14 is the first version to fully support TrustZone on xG21 (HSE) and xG22 (VSE) devices.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SE Firmware</p>
            </td>
            <td>
                <p>≥ v2.2.1</p>
            </td>
            <td>
                <p>v2.2.1 is the first version to fully support TrustZone on other Series 2 HSE and VSE devices.</p>
            </td>
        </tr>
    </tbody>
</table>

**Notes**:

- Required GCC and IAR EWARM versions are GSDK-dependent.
- [Bug list of GCC v10.3](https://gcc.gnu.org/bugzilla/buglist.cgi?bug_status=RESOLVED&resolution=FIXED&target_milestone=10.3)
- [IAR EWARM release note](https://updates.iar.com/?product=EWARM)
- [Segger J-Link release note](https://www.segger.com/downloads/jlink/ReleaseNotes_JLink.html)
- [Simplicity Studio user guide](https://docs.silabs.com/simplicity-studio-5-users-guide/latest/ss-5-users-guide-overview/)
- [Latest version of Simplicity Commander](https://www.silabs.com/developers/mcu-programming-options)
- [GSDK release note](https://github.com/SiliconLabs/gecko_sdk/releases)
- Silicon Labs strongly recommends installing the latest SE firmware on Series 2 devices to support the required TrustZone features. The latest SE firmware image and release notes after installing the GSDK (Windows folder):  
  C:\Users<PC USER NAME>\SimplicityStudio\SDKs\gecko_sdk\util\se_release\public

###### Peripheral Addresses in Device Header Files

The device header files (e.g., efr32mg21b020f1024im32.h) need to be configurable for different situations. The `SL_TRUSTZONE_SECURE` and `SL_TRUSTZONE_NONSECURE` definitions specify whether the compilation is for Secure or Non-secure applications. The `SL_TRUSTZONE_SECURE` and `SL_TRUSTZONE_NONSECURE` should be exclusive. If none of the definitions are true, the state should be similar to the Non-secure configuration, but the [startup code](#startup-code) (`SystemInit()` in system_*.c) will be responsible for reconfiguring the system.

<table>
    <thead>
        <tr>
            <th>Define (Software Component)</th>
            <th>Default Peripheral Pointer</th>
            <th>Startup Code</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>SL_TRUSTZONE_SECURE (TrustZone Secure)</p>
            </td>
            <td>
                <p>Point to Secure peripherals (*_BASE = *_S_BASE)</p>
            </td>
            <td>
                <p>No effect on SystemInit()</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SL_TRUSTZONE_NONSECURE (TrustZone Non-Secure)</p>
            </td>
            <td>
                <p>Point to Non-secure peripherals (*_BASE = *_NS_BASE)</p>
            </td>
            <td>
                <p>No effect on SystemInit()</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>None of the above (-)</p>
            </td>
            <td>
                <p>Point to Non-secure peripherals (*_BASE = *_NS_BASE)</p>
            </td>
            <td>
                <p>SystemInit() moves peripherals to Non-secure</p>
            </td>
        </tr>
    </tbody>
</table>

When building a Secure application (`SL_TRUSTZONE_SECURE` is true), all peripherals shall have their non-suffixed default address pointing to the Secure location of the peripheral (e.g., EMU). But the definitions in sl_trustzone_secure_config.h can force the addresses of specific peripherals pointing to the Non-secure location.

```C

#ifndef SL_TRUSTZONE_SECURE_CONFIG_H
#define SL_TRUSTZONE_SECURE_CONFIG_H
// Specify security configuration of peripherals. Peripherals that are not
// included here will automatically have their _BASE addresses point to their
// secure address. This might not be true, since most peripherals are configured
// to be non-secure -- but it's also not a problem if the peripheral is not
// accessed from the S app.
// Used in multiple places.
#define SL_TRUSTZONE_PERIPHERAL_CMU_S (0)
// Used by SE Manager service.
#define SL_TRUSTZONE_PERIPHERAL_AHBRADIO_S (0)
// Used by MSC service.
#define SL_TRUSTZONE_PERIPHERAL_LDMA_S (1)
// Used by MSC service.
#define SL_TRUSTZONE_PERIPHERAL_LDMAXBAR_S (1)
#endif // SL_TRUSTZONE_SECURE_CONFIG_H

```

```C

#if defined(SL_CATALOG_TRUSTZONE_SECURE_CONFIG_PRESENT)
#include "sl_trustzone_secure_config.h"
#endif
#if ((defined(SL_TRUSTZONE_SECURE) && !defined(SL_TRUSTZONE_PERIPHERAL_EMU_S))
    || (defined(SL_TRUSTZONE_PERIPHERAL_EMU_S) && (SL_TRUSTZONE_PERIPHERAL_EMU_S != 0)))
#define EMU_BASE               (EMU_S_BASE)                  /* EMU base address */
#else

```

In other cases (`SL_TRUSTZONE_NONSECURE` is true or both `SL_TRUSTZONE_SECURE` and `SL_TRUSTZONE_NONSECURE` are false), all peripherals shall have their non-suffixed default address pointing to the Non-secure location of the peripheral (e.g., EMU).

```C

#define EMU_BASE               (EMU_NS_BASE)                 /* EMU base address */

```

> **Note**: Do not install the **TrustZone Secure** or **TrustZone Non-Secure** software component to the [TrustZone-unaware](#startup-code) application.

###### Startup Code

The startup code moves peripherals from Secure to Non-secure to support the [default peripheral locations](#peripheral-addresses-in-device-header-files). In a TrustZone-aware application (either `SL_TRUSTZONE_SECURE` or `SL_TRUSTZONE_NONSECURE` is true), this is the application's responsibility (skip lines 168 to 194 in `SystemInit()`) and is done in the [Secure firmware](02-r-basic) of the system.

For the TrustZone-unaware application (both `SL_TRUSTZONE_SECURE` and `SL_TRUSTZONE_NONSECURE` are false), the `SystemInit()` in system_*.c (e.g., system_efr32mg21.c) moves peripherals to the Non-secure location.

- The `SystemInit()` sets the accesses of all peripherals to Non-secure except for the [SMU](03-r-bls) and HSE SEMAILBOX (lines 172 to 178).
- The `SystemInit()` sets the SAU in [All Non-secure](03-r-bls) configuration (lines 180 to 187).  
  - It ensures Non-secure access to Non-secure peripherals.  
  - The device component files (e.g., `efr32mg21b020f1024im32.slcc`) enable the [CMSE](https://developer.arm.com/documentation/ecm0359818/latest) compiler option (`-mcmse` for GCC and `--cmse` for IAR) to pass the condition in line 181 to program the SAU.  
  - To catch the missing CMSE compiler option, it will generate a preprocessor error (line 186) if the CMSE flag is not set when manually upgrading a project from GSDK v4.0.x to ≥v4.1.x for the TrustZone-unaware application.
- The `SystemInit()` does not program the [ESAU](03-r-bls) (default Secure flash is 32 MB), so the whole program is run in the **Secure** state.
- The `SystemInit()` also enables the [`BMPUSEC`](03-r-bls) and [`PPUSEC`](03-r-bls) interrupts in the SMU (lines 189 to 193). It ensures the TrustZone-unaware application catches any violations of Bus Master and peripheral security access permissions.

![system_init](/series2-trustzone/0.2/images/sld717-system-init.png)

The `SMU_BASE` and HSE `SEMAILBOX_HOST_BASE` in device header files must point to the Secure location regardless of the `SL_TRUSTZONE_SECURE` and `SL_TRUSTZONE_NONSECURE` settings to avoid security violations on peripherals in the TrustZone-unaware application (SMU and HSE SEMAILBOX are set to Secure peripherals).

```C

#if ((defined(SL_TRUSTZONE_SECURE) && !defined(SL_TRUSTZONE_PERIPHERAL_SMU_S))
    || (defined(SL_TRUSTZONE_PERIPHERAL_SMU_S) && (SL_TRUSTZONE_PERIPHERAL_SMU_S != 0)))
#define SMU_BASE               (SMU_S_BASE)                  /* SMU base address */
#else
#define SMU_BASE               (SMU_S_BASE)                  /* SMU base address */

```

```C

#if ((defined(SL_TRUSTZONE_SECURE) && !defined(SL_TRUSTZONE_PERIPHERAL_SEMAILBOX_HOST_S))
    || (defined(SL_TRUSTZONE_PERIPHERAL_SEMAILBOX_HOST_S) && (SL_TRUSTZONE_PERIPHERAL_SEMAILBOX_HOST_S != 0)))
#define SEMAILBOX_HOST_BASE    (SEMAILBOX_S_HOST_BASE)       /* SEMAILBOX_HOST base address */
#else
#define SEMAILBOX_HOST_BASE    (SEMAILBOX_S_HOST_BASE)       /* SEMAILBOX_HOST base address */

```

**Notes**:

- The CMSE compiler option of GCC is in the Other flagswindow under C/C++ Build → Settings → Tool Settings → GNU ARM C Compiler→ Miscellaneous.  
  ![gcc_cmse](/series2-trustzone/0.2/images/sld717-gcc-cmse.png)
- The CMSE compiler option of IAR is in the Command line options: (one per line) window under Options... → C/C++ Compiler → Extra Options.  
  ![iar_cmse](/series2-trustzone/0.2/images/sld717-iar-cmse.png)

###### Linker File

The [`template_contribution`](https://siliconlabs.github.io/slc-specification/latest/format/component/template_contribution/) defined in the [slcp](https://siliconlabs.github.io/slc-specification/latest/format/project/) files of [Secure and Non-secure projects](07-r-ex) will override the default memory settings defined in the device component files (e.g., `efr32mg21b020f1024im32.slcc`) to generate the linker files for [Secure](07-r-ex) and [Non-secure](07-r-ex) applications.

<table>
    <thead>
        <tr>
            <th>Memory Region</th>
            <th>Default Setting in Device Component File</th>
            <th>Override Setting in template_contribution</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Flash start address</p>
            </td>
            <td>
                <p>device_flash_addr</p>
            </td>
            <td>
                <p>memory_flash_start</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Flash size</p>
            </td>
            <td>
                <p>device_flash_size</p>
            </td>
            <td>
                <p>memory_flash_size</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>RAM start address</p>
            </td>
            <td>
                <p>device_ram_addr</p>
            </td>
            <td>
                <p>memory_ram_start</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>RAM size</p>
            </td>
            <td>
                <p>device_ram_size</p>
            </td>
            <td>
                <p>memory_ram_size</p>
            </td>
        </tr>
    </tbody>
</table>

![linker_file](/series2-trustzone/0.2/images/sld717-linker-file.png)

The [ESAU](03-r-bls) sets the flash and RAM start address, so these addresses should be alignment at **4 kB** (0x1000). The Secure project linker file needs to have a section for [NSC](02-r-basic) (Secure Gateway) at the end of the Secure flash section. The [SAU](03-r-bls) sets the start address of the NSC section, so this section only needs to be **32 bytes** aligned.

- GCC NSC: The `.gnu.sgstubs` region in the Secure application map file (.map)
- IAR NSC: The `Veneer$$CMSE` region in the Secure application map file (.map)

The Secure and Non-secure flash and RAM sizes are incremented or decremented in **4 kB**. The memory configurations in Secure and Non-secure applications are correlated, so the flash and RAM settings are in pairs.

![mem_config](/series2-trustzone/0.2/images/sld717-mem-config.png)

> **Note**: Users should not directly edit the `template_contribution` in the `slcp` file, but rather use the [Memory Editor](07-r-ex) in Simplicity Studio to update the memory configuration.

###### Debugger

Simplicity Studio supports two [debuggers](https://docs.silabs.com/simplicity-studio-5-users-guide/latest/ss-5-users-guide-testing-and-debugging/using-the-debugger):

- GNU Debugger (GDB) client and SEGGER's GDB server
- Simplicity Studio Debugger

The [TrustZone-unaware](#startup-code) and [TrustZone-aware](05-r-implementation) applications enable the [`PPUSEC`](03-r-bls) interrupts in the SMU. The debugger will trigger the `SMU_SECURE_IRQHandler` if the **[Registers]** or **[Peripherals]** view feature violates peripheral security access permission.

###### Simplicity Studio Debugger (heading level 7)

The **[Registers]** view of Simplicity Studio Debugger can only access the Secure location of a peripheral. The following figure demonstrates the `Default_Handler` (`SMU_SECURE_IRQHandler` not defined) is triggered (`PPUSEC` in `SMU->IF` = 1) when viewing the registers of GPIO peripheral (`PPUFSPERIPHID` = 13) that is set to Non-secure access in the SMU.

The debugger can access the registers of the SMU since this peripheral is set to Secure access in the SMU.

This limitation does not apply to **GSDK < v4.1.0** since no peripherals are configured for Non-secure access.

![ppusec_fault](/series2-trustzone/0.2/images/sld717-ppusec-fault.png)

The Simplicity Studio Debugger is not the preferred choice for TrustZone debugging since it has limitations on viewing Non-secure access peripherals.

###### GNU Debugger (GDB) (heading level 7)

The **[Peripherals]** view of GNU Debugger can access either the Secure or Non-secure location of the peripheral to avoid conflicts on security access permission. The following figure shows the registers of GPIO on Secure (`GPIO` at `0x4003C000`) and Non-secure (`GPIO_NS` at `0x5003C000`) addresses. The GPIO peripheral is set to Non-secure access in the SMU, so the registers in the Secure address are displayed as zero.

![register-view](/series2-trustzone/0.2/images/sld717-register-view.png)

The GNU Debugger is the preferred choice for TrustZone debugging and is the default debugger for Simplicity Studio ≥ v5.5.0.0.

##### TrustZone Platform Examples

The following TrustZone platform examples located in the C:\Users<PC USER NAME>\SimplicityStudio\SDKs\gecko_sdk\app\common\example folder (Windows) demonstrate the TrustZone implementation on Series 2 devices. All TrustZone platform examples do not include [Gecko Bootloader](05-r-implementation).

###### TrustZone PSA Attestation

![tz_attestation](/series2-trustzone/0.2/images/sld717-tz-attestation.png)

![tz_attestation_folder](/series2-trustzone/0.2/images/sld717-tz-attestation-folder.png)

<table>
    <thead>
        <tr>
            <th>Example Folder</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>tz_psa_attestation</p>
            </td>
            <td>
                <p>The workspace description file (tz_psa_attestation_ws.slcw) creates the TrustZone PSA Attestation example. The project description file (tz_psa_attestation_s.slcp) configures a Secure application that provides the Secure Library functionality required by the Non-secure application.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>tz_psa_attestation_ns</p>
            </td>
            <td>
                <p>The project description file (tz_psa_attestation_ns.slcp) configures a Non-secure application for the TrustZone PSA Attestation example.</p>
            </td>
        </tr>
    </tbody>
</table>

**Notes**:

- This example cannot run if the `SECURE_BOOT_ENABLE` (root of trust of the attestation) option in SE OTP is disabled.
- The combined image of Secure and Non-secure applications is signed by the `example_signing_key.pem` (private key) in C:\Users<PC USER NAME>\SimplicityStudio\SDKs\gecko_sdk\platform\commonfolder (Windows). The `example_signing_pubkey.pem` (public key) in the same folder is installed to the SE OTP to verify the image signature during [Secure Boot](https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/).

###### TrustZone PSA Crypto ECDH

![tz-ecdh](/series2-trustzone/0.2/images/sld717-tz-ecdh.png)

![tz-cdh-folder](/series2-trustzone/0.2/images/sld717-tz-ecdh-folder.png)

|Example Folder|Description|
|---|---|
|tz_psa_crypto_ecdh|The workspace description file (tz_psa_crypto_ecdh_ws.slcw) upgrades the existing Platform - PSA Crypto ECDH example to TrustZone-aware. The project description file (tz_psa_crypto_ecdh_s.slcp) configures a Secure application that provides the Secure Library functionality required by the Non-secure application.|
|tz_psa_crypto_ecdh_ns|The project description file (tz_psa_crypto_ecdh_ns.slcp) configures the existing Platform - PSA Crypto ECDH example as a Non-secure application. The source code can be reused without changes.|

The following sections use Simplicity Studio v5.6.3.0 and GSDK v4.2.2. The procedures and pictures may be different if using higher versions of Simplicity Studio 5 and GSDK.

###### Project Description File

The project description file ([`.slcp`](https://siliconlabs.github.io/slc-specification/1.0/format/project/)) contains references to the GSDK used and a list of components to use from these. The TrustZone-aware application requires separate `slcp` files for the Secure and Non-secure applications.

Users should not directly edit the `slcp` files, but rather use the [Memory Editor](#memory-editor) and Post Build Editor in Simplicity Studio to update the [memory configuration](#memory-configuration) and post-build actions.

###### Secure Application (heading level 7)

The following figure describes which TrustZone software components are installed for the TrustZone Secure library of the [TrustZone PSA Crypto ECDH](#trustzone-platform-examples) example.

![tz_secure_component](/series2-trustzone/0.2/images/sld717-tz-secure-component.png)

**Notes**:

- The services provided by the Secure library are standardized.
- The source files for the Secure library will be automatically added to the application when generating the Secure project from the `slcp` file. For the current TrustZone implementation, modifications of the source files of the Secure library are not recommended.

###### Non-secure Application (heading level 7)

The following figure describes which TrustZone software components are installed for the Non-secure application of the [TrustZone PSA Crypto ECDH](#trustzone-platform-examples) example.

![tz_nonsecure_component](/series2-trustzone/0.2/images/sld717-tz-nonsecure-component.png)

**Notes**:

- The following software components are automatically installed when [PSA Crypto and ITS](05-r-implementation) services are used on the Non-secure application.  
  - MSC Service for TrustZone Secure Key Library  
  - NVM3 Service for TrustZone Secure Key Library  
  - PSA Crypto Service for TrustZone Secure Key Library  
  - PSA ITS Service for TrustZone Secure Key Library  
  - SYSCFG Service for TrustZone Secure Key Library
- The following software components can be installed to the Non-secure application when those services are required.  
  - [PSA Attestation Service for TrustZone Secure Key Library](05-r-implementation#psa-attestation)  
  - [SE Manager Service for TrustZone Secure Key Library](05-r-implementation#se-manager)

###### Workspace

A workspace is a structure that can contain multiple projects. 'Workspace' is a generic term for this construct. In the context of Simplicity Studio, where workspace has a different, Eclipse-based, meaning, workspaces are referred to as [Solutions](https://docs.silabs.com/simplicity-studio-5-users-guide/latest/ss-5-users-guide-developing-with-project-configurator/project-solutions).

The workspace description file ([`.slcw`](https://siliconlabs.github.io/slc-specification/latest/format/workspace/)) contains references to projects ([`.slcp`](https://siliconlabs.github.io/slc-specification/latest/format/project/)) that make up the workspace. Users should not directly edit the `slcw` file, but rather use the Post Build Editor in Simplicity Studio to update the post-build actions.

###### Memory Configuration

The [memory configurations](06-r-migration#linker-file) in the TrustZone platform examples are based on the Series 2 radio board with minimum flash (512 kB) and RAM (32 kB), so these configurations can run on all Series 2 radio boards. Users can customize the settings when more flash and RAM are available on the selected device.

- Memory flash size (total) = `memory_flash_size` (S) + `memory_flash_size` (NS) = 512 kB
- Memory RAM size (total) = `memory_ram_size` (S) + `memory_ram_size` (NS) = 32 kB

###### Secure Application (heading level 7)

The project description file of the Secure application (`*_s.slcp`) uses the default memory setting below to generate the [Secure linker file](06-r-migration) (linkerfile.ld for GCC and linkerfile.icf for IAR in the project autogen folder).

The actual memory usage during software development is unknown, so it needs to reserve enough flash (`memory_flash_size`: 176 kB) and RAM (`memory_ram_size`: 12 kB) for the Secure part of all TrustZone platform examples. The bigger RAM size (including stack and heap) is mainly for the software fallback on cryptographic operations in PSA Crypto.

<table>
    <thead>
        <tr>
            <th>Default Memory Setting (Secure)</th>
            <th>xG21 and xG22 Devices</th>
            <th>Other Series 2 Devices</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>memory_flash_start</p>
            </td>
            <td>
                <p>0x00000000</p>
            </td>
            <td>
                <p>0x08000000</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>memory_flash_size</p>
            </td>
            <td>
                <p>0x0002C000 (176 kB)</p>
            </td>
            <td>
                <p>0x0002C000 (176 kB)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>memory_ram_start</p>
            </td>
            <td>
                <p>0x20000000</p>
            </td>
            <td>
                <p>0x20000000</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>memory_ram_size</p>
            </td>
            <td>
                <p>0x00003000 (12 kB)</p>
            </td>
            <td>
                <p>0x00003000 (12 kB)</p>
            </td>
        </tr>
    </tbody>
</table>

```C

 MEMORY
 {
   FLASH   (rx)  : ORIGIN = 0x0, LENGTH = 0x2c000
   RAM     (rwx) : ORIGIN = 0x20000000, LENGTH = 0x3000
 }

```

###### Non-secure Application (heading level 7)

The project description files of the Non-secure application (*_ns.slcp) use the default memory setting below to generate the [Non-secure linker file](06-r-migration) (linkerfile.ld for GCC and linkerfile.icf for IAR in the project autogen folder).

The actual memory usage during software development is unknown, so the remaining flash (`memory_flash_size`: 336 kB) and RAM (`memory_ram_size`: 20 kB) should be big enough for the Non-secure part of all TrustZone platform examples.

<table>
    <thead>
        <tr>
            <th>Default Memory Setting (Non-secure)</th>
            <th>xG21 and xG22 Devices</th>
            <th>Other Series 2 Devices</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>memory_flash_start</p>
            </td>
            <td>
                <p>0x0002C000 (176 kB)</p>
            </td>
            <td>
                <p>0x0802C000 (176 kB)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>memory_flash_size</p>
            </td>
            <td>
                <p>0x00054000 (336 kB)</p>
            </td>
            <td>
                <p>0x00054000 (336 kB)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>memory_ram_start</p>
            </td>
            <td>
                <p>0x20003000 (12 kB)</p>
            </td>
            <td>
                <p>0x20003000 (12 kB)</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>memory_ram_size</p>
            </td>
            <td>
                <p>0x00005000 (20 kB)</p>
            </td>
            <td>
                <p>0x00005000 (20 kB)</p>
            </td>
        </tr>
    </tbody>
</table>

```C

 MEMORY
 {
   FLASH   (rx)  : ORIGIN = 0x2c000, LENGTH = 0x54000
   RAM     (rwx) : ORIGIN = 0x20003000, LENGTH = 0x5000
 }

```

> **Note**: The usable flash for Non-secure code should be equal to `memory_flash_size` - `NVM size` (default is 40 kB) if NVM3 storage is required.

###### Memory Editor (heading level 7)

The default memory setting of [Secure](#secure-application) and [Non-secure](#non-secure-application) applications are good enough for software development and debugging. The final memory layouts of Secure and Non-secure projects are deduced by inspecting the flash and RAM usage in the Secure application memory map file (.map).

The [Memory Editor](https://docs.silabs.com/simplicity-studio-5-users-guide/latest/ss-5-users-guide-getting-started/start-a-project#memory-editor) in Simplicity Studio 5 is a graphical tool for editing the memory layout (flash and RAM) of the applications in the workspace. The Memory Editor will update the linker file in the project `autogen` folder with the custom settings. [Rebuild](#build) the projects to use the new memory configurations in the linker files.

The Memory Editor is located at the **Quick Links** and **CONFIGURATION TOOLS** of Secure or Non-secure `slcp` file.

![quick_links](/series2-trustzone/0.2/images/sld717-quick-links.png)

![conf_tools](/series2-trustzone/0.2/images/sld717-conf-tools.png)

The following items will be determined by the flash usage in the Secure application memory map file:

- memory_flash_size (S)
- memory_flash_start (NS)
- memory_flash_szie (NS)

![memory_edit_flash](/series2-trustzone/0.2/images/sld717-memory-edit-flash.png)

> **Note**: The Memory Editor in Simplicity Studio v5.6.3.0 can only adjust the flash size in **8 kB** (page size) alignment, which may not fit the [4kB alignment](06-r-migration) between the Secure and Non-secure flash boundary.

The following items will be determined by the RAM usage in the Secure application memory map file:

- memory_ram_size (S)
- memory_ram_start (NS)
- memory_ram_szie (NS)

![memory_edit_ram](/series2-trustzone/0.2/images/sld717-memory-edit-ram.png)

###### Build

The Secure project must be built first to create the Secure object library (trustzone_secure_library.o) with function entries for the Non-secure project. Both projects need to be rebuilt if any changes in the Secure project. Users can use Simplicity IDE in Simplicity Studio 5 or IAR EWARM v9.20.4 to build the TrustZone platform examples.

###### Simplicity IDE (heading level 7)

The following procedures are based on the **TrustZone PSA Crypto ECDH** example on BRD4182A Radio Board (EFR32MG22C224F512IM40).

1. Use the `tz_psa_crypto` keyword to search in **EXAMPLE PROJECTS & DEMOS** tab. Select the **`tz_psa_crypto_ecdh_ws`** example.

![tz_gcc_search_ws](/series2-trustzone/0.2/images/sld717-tz-gcc-search-ws.png)
2. Click **[CREATE]** to generate the [solution](https://docs.silabs.com/simplicity-studio-5-users-guide/latest/ss-5-users-guide-developing-with-project-configurator/project-solutions).

![./resources/sld717-tz_ecdh](/series2-trustzone/0.2/images/sld717-tz-ecdh.png)
3. The **Project Configuration** dialog shows the Secure and Non-secure projects in the target solution. Click **[FINISH]** to start the creation process.

![tz_gcc_proj_conf](/series2-trustzone/0.2/images/sld717-tz-gcc-proj-conf.png)
4. The Simplicity IDE perspective opens after finishing the solution creation. Click **Build** on the Simplicity IDE perspective toolbar to build the projects of a selected solution in order (Secure then Non-secure).

![tz_gcc_build](/series2-trustzone/0.2/images/sld717-tz-gcc-build.png)
5. The post-build actions (`.slpb` files) of the Secure project, Non-secure project, and workspace will be processed in sequence if the solution is successfully built. The combined image (tz_psa_crypto_ecdh_ws-combined.s37) in the Secure project artifact folder can be used for programming the device or debugging.

![tz_gcc_combine](/series2-trustzone/0.2/images/sld717-tz-gcc-combine.png)
6. Use [Memory Editor](#memory-editor) to finalize the memory layouts of Secure and Non-secure applications and rebuild the solution to update the memory configurations.

> **Note**: The Simplicity IDE can only apply the post-build action to a particular project if multiple Secure or Non-secure projects exist in the solution.

###### IAR EWARM (heading level 7)

The following procedures are based on the **TrustZone PSA Crypto ECDH** example on BRD4181A Radio Board (EFR32MG21A010F1024IM32).

1. Follow steps 1 to 3 in [TrustZone PSA Crypto ECDH](#simplicity-ide) to generate the solution for the `tz_psa_crypto_ws`. Select the `tz_psa_crypto_ecdh_s.slcp` file.
2. The **Overview** tab shows the **Target and Tool Settings** card on the left side. Scroll down if necessary and click **[ChangeTarget/SDK/Generators]**.

![tz_iar_s_change](/series2-trustzone/0.2/images/sld717-tz-iar-s-change.png)
3. Drop down the **CHANGE PROJECT GENERATORS** list and select **IAR Embedded Workbench Project**.

![tz_iar_s_select](/series2-trustzone/0.2/images/sld717-tz-iar-s-select.png)
4. Click **[Save]** to generate an IAR **Secure** project (tz_psa_crypto_ecdh_s.ewp).

![tz_iar_s_save](/series2-trustzone/0.2/images/sld717-tz-iar-s-save.png)
5. Select the `tz_psa_crypto_ecdh_ns.slcp` file. Repeat steps 2 to 4 to generate an IAR **Non-secure*** project (tz_psa_crypto_ecdh_ns.ewp).
6. Use a text editor to create an IAR tz_psa_crypto_ecdh_ws.ewwfile (shown below) to house the projects (tz_psa_crypto_ecdh_s.ewpand tz_psa_crypto_ecdh_ns.ewp) generated in steps 4 and 5. The location of the tz_psa_crypto_ecdh_ws.eww is the directory for WS_DIR.

```C

<?xml version ="1.0" encoding="iso-8859-1"?>
<workspace>
  <project>
    <path>$WS_DIR$\tz_psa_crypto_ecdh_s\tz_psa_crypto_ecdh_s.ewp</path>
  </project>
  <project>
    <path>$WS_DIR$\tz_psa_crypto_ecdh_ns\tz_psa_crypto_ecdh_ns.ewp</path>
  </project>
  <batchBuild/>
</workspace>

```

![./resources/sld717-tz_iar_folder](/series2-trustzone/0.2/images/sld717-tz-iar-folder.png)
7. Double-click the tz_psa_crypto_ecdh_ws.ewwfile to open the workspace that includes Secure and Non-secure projects.
![./resources/sld717-tz_iar_ws](/series2-trustzone/0.2/images/sld717-tz-iar-ws.png)
8. Click the tz_psa_crypto_ecdh_s tab to open the Secure project. Click ![tz_iar_make_icon](/series2-trustzone/0.2/images/sld717-tz-iar-make-icon.png) (Make) to build. It exports the Secure object library (trustzone_secure_library.o) for function entries that will be used by the Non-secure project.

![tz_iar_s](/series2-trustzone/0.2/images/sld717-tz-iar-s.png)
9. Click the tz_psa_crypto_ecdh_ns tab to open the Non-secure project.

![./resources/sld717-tz_iar_ns](/series2-trustzone/0.2/images/sld717-tz-iar-ns.png)
10. The [`SL_TRUSTZONE_NONSECURE`](06-r-migration#peripheral-addresses-in-device-header-files) defined in the Non-secure project disables the [CMSE compiler option](06-r-migration#startup-code) (`--cmse`) regardless of whether the **Project → Options... → General Options → 32-bit → TrustZone → Mode:** setting is Secure or Non-secure. So changing this configuration from Secure to Non-secure is optional. Click **[OK]** to exit.

![tz_iar_select_ns](/series2-trustzone/0.2/images/sld717-tz-iar-select-ns.png)
11. Click ![tz_iar_make_icon](/series2-trustzone/0.2/images/sld717-tz-iar-make-icon.png) (Make) to build the Non-secure project. The post-build actions of the workspace (tz_psa_crypto_ecdh_ws.slpb) will be triggered in IAR to combine the Secure and Non-secure images (tz_psa_crypto_ecdh_ws-combined.s37) to the artifact folder of `tz_psa_crypto_ecdh_s` for programming the device.

![tz_iar_combine](/series2-trustzone/0.2/images/sld717-tz-iar-combine.png)
12. Use [Memory Editor](#memory-editor) to finalize the memory layouts of Secure and Non-secure applications and rebuild the Secure and Non-secure projects to update the memory configurations.

> **Note**: The IAR EWARM can only apply the workspace post-build action to a particular project if multiple Secure or Non-secure projects exist in the workspace.

###### Debugging

Users can use Simplicity IDE in Simplicity Studio 5 or IAR EWARM v9.20.4 to debug the TrustZone platform examples. Building the projects with Optimization Level None (-O0) is recommended for debugging.

###### Simplicity IDE (heading level 7)

The TrustZone debugging process on Simplicity IDE is similar to the existing sample projects in Simplicity Studio.

1. [GNU Debugger (GDB)](06-r-migration#debugger) is recommended to debug TrustZone applications.
2. Flash the combined image (tz_psa_crypto_ecdh_ws-combined.s37) generated in [Simplicity IDE](#simplicity-ide) to the device.
3. Select the Secure or Non-secure project and use the **Debug** icon to launch a debug session.

![gcc_debug_launch](/series2-trustzone/0.2/images/sld717-gcc-debug-launch.png)
4. Follow the instructions in the **[Using the Debugger](https://docs.silabs.com/simplicity-studio-5-users-guide/latest/ss-5-users-guide-testing-and-debugging/using-the-debugger)** section in Simplicity Studio 5 User's Guide to debug the Secure or Non-secure application.
5. The debugger cannot step into the function in a Non-secure application when debugging the Secure application and vice versa. Use the **Program Counter** (PC in Secure or Non-secure address) in the **Registers** window to determine the program status.

![gcc_debug_state](/series2-trustzone/0.2/images/sld717-gcc-debug-state.png)

###### IAR EWARM (heading level 7)

Use the tz_psa_crypto_ecdh_ws.eww workspace created in [IAR EWARM](#iar-ewarm) for the debugger settings. Except for a minor difference in step 3, the following steps are the same as those to set up the Secure (tz_psa_crypto_ecdh_s) and Non-secure (tz_psa_crypto_ecdh_ns) projects for debugging.

1. Select **Options...** in the ![tools_menu](/series2-trustzone/0.2/images/sld717-tools-menu.png) context menu of the Secure or Non-secure project and open the **IDE Options → Stack** dialog. Uncheck the **Stack pointer(s) not valid until program reaches*** checkbox. Click **[OK]** to exit.

![uncheck_stack](/series2-trustzone/0.2/images/sld717-uncheck-stack.png)
2. Select **Options...** in the ![project_menu](/series2-trustzone/0.2/images/sld717-project-menu.png) context menu of the Secure or Non-secure project and open the window for **Debugger** options. Click the **Setup** tab to open a dialog, and uncheck the **Run to → main** checkbox. Click the **Images** tab to set up another option.

![uncheck_main](/series2-trustzone/0.2/images/sld717-uncheck-main.png)
3. Check the **ownload extra image** option. Enter the location of the .out file to **Path:** with **Offset:** set to 0. All project relative paths are resolved from the directory location of the tz_psa_crypto_ecdh_ws.eww workspace file.

Location of Non-secure .out file for Secure project: tz_psa_crypto_ecdh_ns\ewarm-iar\exe\tz_psa_crypto_ecdh_ns.out

![set_image_s](/series2-trustzone/0.2/images/sld717-set-image-s.png)

Location of Secure .out file for Non-secure project: tz_psa_crypto_ecdh_s\ewarm-iar\exe\tz_psa_crypto_ecdh_s.out

![set_image_ns](/series2-trustzone/0.2/images/sld717-set-image-ns.png)
4. Click the **Extra Options** tab to set up another option.
5. Check the **Use command line options**. Enter --drv_vector_table_base=0x00000000 to **Command line options: (one per line)** window. Click **[OK]** to exit.

![set_extra](/series2-trustzone/0.2/images/sld717-set-extra.png)
6. Finish the debug settings in Secure and Non-secure projects, and click ![debug_icon](/series2-trustzone/0.2/images/sld717-debug-icon.png) (Download and Debug) in the Secure or Non-secure project to download the Secure and Non-secure images for debugging (assume both projects had successfully [built](#iar-ewarm) before). Click ![go_icon](/series2-trustzone/0.2/images/sld717-go-icon.png) (Go) to start running the code in a Secure or Non-secure project.
7. The debugger will automatically switch between Secure and Non-secure projects when stepping into a function or hitting a breakpoint in a Secure or Non-secure project. Use the **Program Counter** (PC in Secure or Non-secure address) or **SECURE** (0 or 1) in the **Registers** window to determine the program status.

![debug_state](/series2-trustzone/0.2/images/sld717-debug-state.png)
8. Click ![stop_icon](/series2-trustzone/0.2/images/sld717-stop-icon.png) (Stop Debugging) to end the debug session.

###### Benchmark

The TrustZone implementation will affect the memory footprint and performance of cryptographic operations. The following comparisons are based on the **TrustZone PSA Crypto ECDH** example on BRD4182A Radio Board (EFR32MG22C224F512IM40) with SE firmware v1.2.14.

###### Memory Footprint (heading level 7)

The memory footprint of a TrustZone project depends on which services (software components in the figure below) provided by the [Secure Library](05-r-implementation) are used in the Non-secure application (`tz_psa_crypto_ecdh_ns` project).

![tz_nonsecure_component](/series2-trustzone/0.2/images/sld717-tz-nonsecure-component.png)

The following tables compare the memory footprint of the [TrustZone-unaware](06-r-migration#startup-code) (`Platform - PSA Crypto ECDH`) and [TrustZone-aware](07-r-ex#memory-configuration) projects (`tz_psa_crypto_ecdh_ws`) based on the following conditions.

- The `tz_psa_crypto_ecdh_ns` reuses the source code from the `Platform - PSA Crypto ECDH` example without any changes.
- The total size in `tz_psa_crypto_ecdh_ns` does not consider the [4 kB alignment](06-r-migration) on the Secure and Non-secure flash and RAM. The 4 kB alignment requirement will increase the actual usage of flash and RAM.
- All source code is compiled with Optimize for size (-Os) in Simplicity IDE (GNU ARM v10.3.1) of Simplicity Studio 5.

**Table**: Flash Size Comparison

<table>
    <thead>
        <tr>
            <th>Platform Example</th>
            <th>Secure</th>
            <th>NSC</th>
            <th>Non-secure</th>
            <th>Total</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Platform - PSA Crypto ECDH</p>
            </td>
            <td>
                <p>64688 B</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>64688 B</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>tz_psa_crypto_ecdh_ws</p>
            </td>
            <td>
                <p>79172 B</p>
            </td>
            <td>
                <p>288 B</p>
            </td>
            <td>
                <p>29264 B</p>
            </td>
            <td>
                <p>108724 B</p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**: The NSC is part of the Secure code, and the total size does not include the flash for NVM3 storage.

**Table**: RAM Size Comparison

<table>
    <thead>
        <tr>
            <th>Platform Example</th>
            <th>Secure</th>
            <th>NSC</th>
            <th>Non-secure</th>
            <th>Total</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Platform - PSA Crypto ECDH</p>
            </td>
            <td>
                <p>3784 B</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>3764 B</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>tz_psa_crypto_ecdh_ws</p>
            </td>
            <td>
                <p>2156 B</p>
            </td>
            <td>
                <p>-</p>
            </td>
            <td>
                <p>1200 B</p>
            </td>
            <td>
                <p>3356 B</p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**: The total size does not include the RAM for the stack and heap. The Secure and Non-secure applications have their independent stack and heap.

###### PSA Crypto Performance (heading level 7)

The following sections compare the PSA Crypto performance of the [TrustZone-unaware](06-r-migration) (`Platform - PSA Crypto ECDH`) and [TrustZone-aware](#memory-configuration) projects (`tz_psa_crypto_ecdh_ws`) based on the following conditions.

- The `tz_psa_crypto_ecdh_ns` reuses the source code from the `Platform - PSA Crypto ECDH` example without any changes.
- All source code is compiled with Optimize most (-O3) in Simplicity IDE (GNU ARM v10.3.1) of Simplicity Studio 5.
- Use ECC curve `SECP256R1` on volatile and persistent keys.
- The EFR32MG22C224 runs at 38 MHz HFRCODPLL.

###### Volatile key ECDH operation on `Platform - PSA Crypto ECDH` (heading level 8)

```C

  . ECDH Client
  + Creating a SECP256R1 (256-bit) VOLATILE PLAIN client key... PSA_SUCCESS (cycles: 2928 time: 77 us)
  + Creating a SECP256R1 (256-bit) VOLATILE PLAIN server key... PSA_SUCCESS (cycles: 2960 time: 77 us)
  + Exporting a public key of a SECP256R1 (256-bit) VOLATILE PLAIN server key... PSA_SUCCESS (cycles: 332134 time: 8740 us)
  + Computing client shared secret with a SECP256R1 (256-bit) server public key... PSA_SUCCESS (cycles: 336860 time: 8864 us)

```

###### Volatile key ECDH operation on `tz_psa_crypto_ecdh_ws` (heading level 8)

```C

  . ECDH Client
  + Creating a SECP256R1 (256-bit) VOLATILE PLAIN client key... PSA_SUCCESS (cycles: 5047 time: 132 us)
  + Creating a SECP256R1 (256-bit) VOLATILE PLAIN server key... PSA_SUCCESS (cycles: 5067 time: 133 us)
  + Exporting a public key of a SECP256R1 (256-bit) VOLATILE PLAIN server key... PSA_SUCCESS (cycles: 333956 time: 8788 us)
  + Computing client shared secret with a SECP256R1 (256-bit) server public key... PSA_SUCCESS (cycles: 338470 time: 8907 us)

```

###### Persistent key ECDH operation on `Platform - PSA Crypto ECDH` (heading level 8)

```C

  . ECDH Client
  + Creating a SECP256R1 (256-bit) PERSISTENT PLAIN client key... PSA_SUCCESS (cycles: 27489 time: 723 us)
  + Creating a SECP256R1 (256-bit) PERSISTENT PLAIN server key... PSA_SUCCESS (cycles: 27587 time: 725 us)
  + Exporting a public key of a SECP256R1 (256-bit) PERSISTENT PLAIN server key... PSA_SUCCESS (cycles: 332949 time: 8761 us)
  + Computing client shared secret with a SECP256R1 (256-bit) server public key... PSA_SUCCESS (cycles: 337803 time: 8889 us)

```

###### Persistent key ECDH operation on `tz_psa_crypto_ecdh_ws` (heading level 8)

```C

  . ECDH Client
  + Creating a SECP256R1 (256-bit) PERSISTENT PLAIN client key... PSA_SUCCESS (cycles: 46998 time: 1236 us)
  + Creating a SECP256R1 (256-bit) PERSISTENT PLAIN server key... PSA_SUCCESS (cycles: 45962 time: 1209 us)
  + Exporting a public key of a SECP256R1 (256-bit) PERSISTENT PLAIN server key... PSA_SUCCESS (cycles: 334127 time: 8792 us)
  + Computing client shared secret with a SECP256R1 (256-bit) server public key... PSA_SUCCESS (cycles: 338321 time: 8903 us)

```

The overheads on the TrustZone-aware project (`tz_psa_crypto_ecdh_ws`) are due to the following operations of [Secure Library](05-r-implementation) implementation.

- Packages the list of input arguments in the appropriate format before calling into the NSC function.
- Switches from a Non-secure to a Secure state.
- Validates all input arguments before calling into the function in SPE.
- Encrypts PSA ITS if using a persistent key.
- Returns to a Non-secure state.

#### Integrating Crypto Functionality Using PSA Crypto Compared to Mbed TLS

##### Integrating Crypto Functionality Using PSA Crypto Compared to Mbed TLS

> **Note: This section replaces _AN1311: Integrating Crypto Functionality Using PSA Crypto Compared to Mbed TLS_. Further updates to this application note will be provided here**.

This application note describes how to integrate crypto functionality into applications using PSA Crypto compared to Mbed TLS. It includes a guide to migrating existing Mbed TLS implementations to PSA Crypto.

This document focuses on the Silicon Labs PSA Crypto implementations that support the RNG, symmetric and asymmetric keys, message digests, MAC, unauthenticated ciphers, AEAD, KDF, DSA, and ECDH.

This document assumes familiarity with the crypto algorithms discussed.

###### Key Points

- Overview of Mbed TLS and PSA Crypto
- Key management in PSA Crypto
- Migration guide
- PSA Crypto platform examples

##### Series 2 Device Security Features

Protecting IoT devices against security threats is central to a quality product. Silicon Labs offers several security options to help developers build secure devices, secure application software, and secure paths of communication to manage those devices. Silicon Labs’ security offerings were significantly enhanced by the introduction of the Series 2 products that included a Secure Engine. The Secure Engine is a tamper-resistant component used to securely store sensitive data and keys and to execute cryptographic functions and secure services.

On Series 1 devices, the security features are implemented by the TRNG (if available) and CRYPTO peripherals.

On Series 2 devices, the security features are implemented by the Secure Engine and CRYPTOACC (if available). The Secure Engine may be hardware-based, or virtual (software-based). Throughout this document, the following abbreviations are used:

- HSE - Hardware Secure Engine
- VSE - Virtual Secure Engine
- SE - Secure Engine (either HSE or VSE)

Additional security features are provided by Secure Vault. Three levels of Secure Vault feature support are available, depending on the part and SE implementation, as reflected in the following table:

|**Level (1)**|**SE Support**|**Part (2)**|
|---|---|---|
|Secure Vault High (SVH)|HSE only (HSE-SVH)|Refer to _IoT Endpoint Security Fundamentals_ for details on supporting devices.|
|Secure Vault Mid (SVM)|HSE (HSE-SVM)|"|
|"|VSE (VSE-SVM)|"|
|Secure Vault Base (SVB)|N/A|"|

> **Notes**:
> 
> 1. The features of different Secure Vault levels can be found in [https://www.silabs.com/security](https://www.silabs.com/security).
> 2. [IoT Endpoint Security Fundamentals](https://docs.silabs.com/iot-security/latest/iot-endpoint-security-fundamentals/).

Secure Vault Mid consists of two core security functions:

- **Secure Boot**: Process where the initial boot phase is executed from an immutable memory (such as ROM) and where code is authenticated before being authorized for execution.
- **Secure Debug access control**: The ability to lock access to the debug ports for operational security, and to securely unlock them when access is required by an authorized entity.

Secure Vault High offers additional security options:

- **Secure Key Storage**: Protects cryptographic keys by “wrapping” or encrypting the keys using a root key known only to the HSE-SVH.
- **Anti-Tamper protection**: A configurable module to protect the device against tamper attacks.
- **Device authentication**: Functionality that uses a secure device identity certificate along with digital signatures to verify the source or target of device communications.

A Secure Engine Manager and other tools allow users to configure and control their devices both in-house during testing and manufacturing, and after the device is in the field.

###### User Assistance

In support of these products Silicon Labs offers whitepapers, webinars, and documentation. The following table summarizes the key security documents:

<table>
    <thead>
        <tr>
            <th><strong>Document</strong></th>
            <th><strong>Summary</strong></th>
            <th><strong>Applicability</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/series2-secure-debug/">Series 2 and Series 3 Secure Debug</a></p>
            </td>
            <td>
                <p>How to lock and unlock Series 2 and Series 3 debug access, including background information about the SE</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/mcu-bootloader/latest/series2-secure-boot-with-rtsl/">Series 2 and Series 3 Secure Boot with RTSL</a></p>
            </td>
            <td>
                <p>Describes the secure boot process on Series 2 and Series 3 devices using SE</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/efr32-secure-vault-tamper/">Anti-Tamper Protection Configuration and Use</a></p>
            </td>
            <td>
                <p>How to program, provision, and configure the anti-tamper module</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/authenticating-devices-using-device-certificates/">Authenticating Silicon Labs Devices using Device Certificates</a></p>
            </td>
            <td>
                <p>How to authenticate a device using secure device certificates and signatures, at any time during the life of the product</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/efr32-secure-key-storage/">Secure Key Storage</a></p>
            </td>
            <td>
                <p>How to securely "wrap" keys so they can be stored in non-volatile storage.</p>
            </td>
            <td>
                <p>Secure Vault High, and Series 3 devices</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><a href="https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/">Production Programming of Series 2 and Series 3 Devices</a></p>
            </td>
            <td>
                <p>How to program, provision, and configure security information using SE during device production</p>
            </td>
            <td>
                <p>Secure Vault Mid, High, and Series 3 devices</p>
            </td>
        </tr>
    </tbody>
</table>

###### Key Reference

Public/Private keypairs along with other keys are used throughout Silicon Labs security implementations. Because terminology can sometimes be confusing, the following table lists the key names, their applicability, and the documentation where they are used.

|**Key Name**|**Customer Programmed**|**Purpose**|
|---|---|---|
|Public Sign key (Sign Key Public)|Yes|Secure Boot binary authentication and/or OTA upgrade payload authentication|
|Public Command key (Command Key Public)|Yes|Secure Debug Unlock or Disable Tamper command authentication|
|OTA Decryption key (GBL Decryption key) aka AES-128 Key|Yes|Decrypting GBL payloads used for firmware upgrades|
|Attestation key aka Private Device Key|No|Device authentication for secure identity|

###### SE Firmware

Silicon Labs strongly recommends installing the latest SE firmware on Series 2 devices to support the required security features. Refer to [Production Programming of Series 2 and Series 3 Devices](https://docs.silabs.com/iot-security/latest/prod-programming-series2-and-series3/) for the procedure to upgrade the SE firmware and [IoT Endpoint Security Fundamentals](https://docs.silabs.com/iot-security/latest/iot-endpoint-security-fundamentals/) for the latest SE Firmware shipped with Series 2 devices and modules.

##### Device Capability

The following table lists the hardware related to cryptography hardware acceleration features on Series 1 and Series 2 devices (MCU and Wireless SoC).

|**Feature**|**Series 1**|**Series 2 - VSE**|**Series 2 - HSE**|
|---|---|---|---|
|TRNG|TRNG peripheral (1)|CRYPTOACC peripheral|HSE|
|Crypto Engine (2)|CRYPTO peripheral|CRYPTOACC peripheral|HSE|
|Advanced Crypto (3)|—|—|HSE-SVH|
|Secure Key Storage (4)|—|—|HSE-SVH|

> **Notes**:
> 
> 1. See [Table Entropy Source on Series 1 and Series 2 Devices](06-migration-guide#initialization-and-random-number-generation-rng) for details of TRNG (True Random Number Generator) on Series 1 devices.
> 2. Crypto engine supports up to 256-bit ciphers and elliptic curves.
> 3. Advanced crypto supports up to 512-bit ciphers and 521-bit elliptic curves.
> 4. See [Table PSA Crypto Key Lifetime Support on Series 1 and Series 2 Devices](04-key-management-in-psa-crypto#key-lifetimes) for details of Secure Key Storage support on HSE-SVH devices.

##### Overview

###### Mbed TLS

Mbed TLS is a C library that implements cryptographic primitives, X.509 certificate manipulation, and the SSL/TLS and DTLS protocols.

ARM developed Mbed TLS, which was formerly known as PolarSSL. Mbed TLS has been handed over to [Trusted Firmware](https://www.trustedfirmware.org/projects/mbed-tls/) under open governance since March 2020.

For the time being, Trusted Firmware Mbed TLS is the project containing a reference implementation of the PSA Crypto API and the TLS portion of Mbed TLS. The following table lists different Mbed TLS versions supported in Simplicity Studio and Gecko SDK (GSDK) suites.

|**Mbed TLS**|**Gecko SDK Suite**|**PSA Crypto**|**Location in Windows**|
|---|---|---|---|
|v3.2.1|v4.3.x (Simplicity Studio 5)|Y|C:\Users\<PC USER NAME>\SimplicityStudio\SDKs\gecko_sdk\util\third_party\mbedtls|
|v3.2.1|v4.2.x (Simplicity Studio 5)|Y|C:\Users\<PC USER NAME>\SimplicityStudio\SDKs\gecko_sdk\util\third_party\mbedtls|
|v3.1.0|v4.1.x (Simplicity Studio 5)|Y|C:\Users\<PC USER NAME>\SimplicityStudio\SDKs\gecko_sdk\util\third_party\crypto\mbedtls|
|v3.0.0|v4.0.x (Simplicity Studio 5)|Y|C:\Users\<PC USER NAME>\SimplicityStudio\SDKs\gecko_sdk\util\third_party\crypto\mbedtls|
|v2.26.0|v3.2.x (Simplicity Studio 5)|Y|C:\SiliconLabs\SimplicityStudio\v5\developer\sdks\gecko_sdk_suite\v3.2\util\third_party\crypto\mbedtls|
|v2.24.0|v3.1.x (Simplicity Studio 5)|Y|C:\SiliconLabs\SimplicityStudio\v5\developer\sdks\gecko_sdk_suite\v3.1\util\third_party\crypto\mbedtls|
|v2.16.6|v3.0.x (Simplicity Studio 5)|—|C:\SiliconLabs\SimplicityStudio\v5\developer\sdks\gecko_sdk_suite\v3.0\util\third_party\mbedtls|
|v2.7.12|v2.7.8 (Simplicity Studio 4)|—|C:\SiliconLabs\SimplicityStudio\v4\developer\sdks\gecko_sdk_suite\v2.7\util\third_party\mbedtls|

###### PSA Crypto

**Platform Security Architecture (PSA)**

The [Platform Security Architecture](https://developer.arm.com/architectures/security-architectures/platform-security-architecture) (PSA) is made up of four key stages:

- Threat modeling
- Predefined architectural choices
- Standardized implementation
- Certification

The PSA Crypto API is one of the standardized implementation features and is discussed in the following sections.

**PSA Root of Trust (PSA-RoT)**

For an IoT product to achieve its security goals, it must meet the requirements of one of the pillars known as Root of Trust. The following figure shows the four PSA-Certified key elements that make up the Root of Trust.

![Key Requirements of PSA-RoT](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image8.png)

The PSA Root of Trust (PSA-RoT) is a source of confidentiality (for example, crypto keys) and integrity. The PSA-RoT defines what it takes for a hardware or software system to be trusted.

The [Trusted Firmware-M](https://www.trustedfirmware.org/projects/tf-m/) (TF-M) offers an open-source firmware reference implementation and APIs. These resources provide developers with a trusted codebase that complies with PSA specifications and APIs that create a consistent interface to underlying Root of Trust hardware.

**PSA Functional APIs**

[PSA Certified](https://www.psacertified.org/) defines a set of PSA Functional APIs (which are implemented as part of TF-M) to access the Root of Trust features. The PSA Functional APIs provide a standardized set of vetted APIs to ensure portability and promote adherence to best practices.

- PSA Crypto APIs
- PSA Attestation APIs
- PSA Secure Storage (Internal Trusted Storage and Protected Storage) APIs

Since the Trusted boot (aka Secure boot) shown in [Figure Key Requirements of PSA-RoT](#psa-crypto) is used when booting up the device and is not used after the system is up and running, there is no need for a Trusted boot API.

The three APIs provide software developers with access to security functions to ensure interoperability across different hardware implementations of the Root of Trust. It means another hardware platform can reuse the applications in the following figure, because these APIs are standardized across various security hardware.

![PSA Functional APIs](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image9.png)

[PSA Functional API Certification](https://www.psacertified.org/getting-certified/functional-api-certification/) is part of [PSA Certified](https://www.psacertified.org/), and demonstrates that software is compatible with the PSA Functional API specification. PSA Functional API Certified does not imply that a device has a security capability or is robust. Only PSA Certified Levels 1–3 can achieve this.

This application note only focuses on the PSA Crypto API.

**PSA Crypto API**

The PSA Crypto API is a low-level cryptographic API optimized for MCU and Wireless SoC. It provides APIs related to [Random Number Generation](06-migration-guide#initialization-and-random-number-generation-rng) (RNG), cryptographic algorithm usage, and [key handling](06-migration-guide#key-handling) (symmetric and asymmetric).

The PSA Crypto API provides developers with an easy-to-use and easy-to-learn interface to crypto primitives. It is designed for usability and flexibility and is based on the idea of a key store. The store can isolate the keys from the rest of the applications, which means keys remain opaque in [storage](04-key-management-in-psa-crypto#key-lifetimes) and only accessible for usage through crypto primitives.

##### Key Management in PSA Crypto

Key attributes are managed in a `psa_key_attributes_t` object. These are used when a key is created, after which the key attributes are impossible to change.

The actual key material is not considered an attribute of a key. Key attributes do not contain information that is generally considered highly confidential. The individual attributes ([Key Types](#key-types), [Key Lifetimes](#key-lifetimes), [Key Identifiers](#key-identifiers), and [Key Policies](#key-policies)) are described in the following sections.

The `crypto_values.h` in the Windows folder below includes the defines for macros of key attributes.

For GSDK v3.1.x and v3.2.x: `C:\SiliconLabs\SimplicityStudio\v5\developer\sdks\gecko_sdk_suite\<GSDK VERSION\>\util\third_party\crypto\mbedtls\include\psa`

For GSDK v4.0.0 and higher: `C:\Users\\<PC USER NAME\>\SimplicityStudio\SDKs\gecko_sdk\util\third_party\crypto\mbedtls\include\psa`

###### Key Types

This attribute consists of information about the key: the type, and the size used by this type. The key type and size are encoded in

`psa_key_type_t` and `psa_key_bits_t` objects. The following table describes the type and size in symmetric and asymmetric keys.

<table>
    <thead>
        <tr>
            <th><strong>Category</strong></th>
            <th><strong>Key Type</strong></th>
            <th><strong>Size in Bits</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td rowspan="4">Symmetric Keys</td>
            <td>HMAC key<br>• <code>PSA_KEY_TYPE_HMAC</code></td>
            <td>Non-zero multiple of 8</td>
        </tr>
        <tr>
            <td>Key derivation<br>• <code>PSA_KEY_TYPE_DERIVE</code></td>
            <td>Non-zero multiple of 8</td>
        </tr>
        <tr>
            <td>Cipher/AEAD/MAC key<br>• <code>PSA_KEY_TYPE_AES</code></td>
            <td>
                <ul>
                    <li>128 (16-byte)</li>
                    <li>192 (24-byte)</li>
                    <li>256 (32-byte)</li>
                </ul>
            </td>
        </tr>
        <tr>
            <td>ChaCha20/ChaCha20-Poly1305 AEAD key<br>• <code>PSA_KEY_TYPE_CHACHA20</code></td>
            <td>256 (32-byte)</td>
        </tr>
        <tr>
            <td rowspan="4">Elliptic Curve Cryptography (ECC) Keys</td>
            <td>SEC random curves over prime fields<br>• <code>PSA_ECC_FAMILY_SECP_R1</code></td>
            <td>
                <ul>
                    <li>secp192r1 : 192</li>
                    <li>secp224r1 : 224</li>
                    <li>secp256r1 : 256</li>
                    <li>secp384r1 : 384</li>
                    <li>secp521r1 : 521</li>
                </ul>
            </td>
        </tr>
        <tr>
            <td>SEC Koblitz curve over prime fields<br>• <code>PSA_ECC_FAMILY_SECP_K1</code></td>
            <td>secp256k1 : 256</td>
        </tr>
        <tr>
            <td>Montgomery curves<br>• <code>PSA_ECC_FAMILY_MONTGOMERY</code></td>
            <td>
                <ul>
                    <li>Curve25519 : 255</li>
                    <li>Curve448 : 448</li>
                </ul>
            </td>
        </tr>
        <tr>
            <td>Twisted Edwards curve<br>• <code>PSA_ECC_FAMILY_TWISTED_EDWARDS</code></td>
            <td>Edwards25519 : 255</td>
        </tr>
    </tbody>
</table>

###### Key Lifetimes

The lifetime is encoded in the `psa_key_lifetime_t` object (`[31:0]`). This object consists of a persistence level (`psa_key_persistence_t`) and a location indicator (`psa_key_location_t`). The persistent level indicates whether the key is volatile, persistent, or read-only. The location indicator indicates where the key is stored and where operations on the key are performed.

|**Type**|**Persistence Level [7:0]**|**Location Indicator [31:8]**|**Storage**|
|---|---|---|---|
|Volatile Plain Key|PSA_KEY_PERSISTENCE_VOLATILE|Local (0x0)|RAM|
|Persistent Plain Key|PSA_KEY_PERSISTENCE_DEFAULT|Local (0x0)|Flash (2)|
|Volatile Wrapped Key|PSA_KEY_PERSISTENCE_VOLATILE|Secure (0x1) (1)|RAM|
|Persistent Wrapped Key|PSA_KEY_PERSISTENCE_DEFAULT|Secure (0x1) (1)|Flash (2)|
|Public Sign Key|PSA_KEY_PERSISTENCE_READ_ONLY|Secure (0x1)|SE OTP|
|Public Command Key|PSA_KEY_PERSISTENCE_READ_ONLY|Secure (0x1)|SE OTP|
|AES-128 Key|PSA_KEY_PERSISTENCE_READ_ONLY|Secure (0x1)|SE OTP|
|Private Device Key|PSA_KEY_PERSISTENCE_READ_ONLY|Secure (0x1)|SE OTP|
|PUF-derived Hardware Unique Key|PSA_KEY_PERSISTENCE_READ_ONLY|Secure (0x1)|PUF Module|

> **Notes**:
> 
> 1. If the key cannot be stored persistently inside the SE, it must be stored in a [wrapped form](https://docs.silabs.com/iot-security/latest/efr32-secure-key-storage/) in RAM or flash such that only the SE can access the key material in plaintext.
> 2. Persistent storage in flash memory is implemented by the [NVM3 driver](https://docs.silabs.com/gecko-platform/latest/driver/api/group-nvm3).

|**Type**|**Series 1**|**Series 2 - VSE**|**Series 2 - HSE**|
|---|---|---|---|
|Volatile Plain Key|Y|Y|Y|
|Persistent Plain Key|Y|Y|Y|
|Volatile Wrapped Key|—|—|HSE-SVH|
|Persistent Wrapped Key|—|—|HSE-SVH|
|Public Sign Key|—|— (1)|Y (2)|
|Public Command Key|—|— (1)|Y (2)|
|AES-128 Key|—|—|Y (3)|
|Private Device Key|—|—|HSE-SVH (2)|
|PUF-derived Hardware Unique Key|—|Y (4)|—|

> **Notes**:
> 
> 1. The PSA Crypto cannot access the Public Sign Key and Public Command Key in the VSE-SVM OTP.
> 2. These keys can only be used for ECDSA (SECP256R1) precomputed hash operations.
> 3. This key can only be used for AES cipher operations. The `SL_SE_BUILTIN_KEY_AES128_ALG` in `sli_se_opaque_types.h` or `sl_psa_values.h` in the Windows folder below defines the default AES cipher algorithm (AES CTR) for this key.
> 
> For GSDK v3.1.x and v3.2.x (`sli_se_opaque_types.h`): `C:\SiliconLabs\SimplicityStudio\v5\developer\sdks\gecko_sdk_suite\<GSDK VERSION>\util\third_party\crypto\sl_component\sl_psa_driver\inc`
> 
> For GSDK v4.0.x (`sli_se_opaque_types.h`): `C:\Users\\<PC USER NAME\>\SimplicityStudio\SDKs\gecko_sdk\util\third_party\crypto\sl_component\sl_psa_driver\inc`
> 
> For GSDK v4.1.x (`sl_psa_values.h`): `C:\Users\\<PC USER NAME\>\SimplicityStudio\SDKs\gecko_sdk\util\third_party\crypto\sl_component\sl_psa_driver\inc\public`
> 
> For GSDK v4.2.x (`sl_psa_values.h`): `C:\Users\\<PC USER NAME\>\SimplicityStudio\SDKs\gecko_sdk\platform\security\sl_component\sl_psa_driver\inc\public`
> 
> For GSDK v4.3.x (`sl_psa_values.h`): `C:\Users\\<PC USER NAME\>\SimplicityStudio\SDKs\gecko_sdk\platform\security\sl_component\sl_mbedtls_support\inc`
> 
> 1. The PUF-derived Hardware Unique Key is only available on VSE-SVM devices with a built-in PUF module (e.g., EFR32xG27C). This key can only be used by the [CRYPTOACC](02-device-capability#device-capability) peripheral for [Message Authentication Codes (MAC)](06-migration-guide#message-authentication-codes-mac) and [Key Derivation](06-migration-guide#key-derivation).
> 
> The `SL_CRYPTOACC_BUILTIN_KEY_PUF_ALG` in `sl_psa_values.h` in the Windows folder below defines the default algorithm (PBKDF2 CMAC) for this key.
> 
> For GSDK v4.3.x and higher: `C:\Users\\<PC USER NAME\>\SimplicityStudio\SDKs\gecko_sdk\platform\security\sl_compon ent\sl_mbedtls_support\inc`

###### Key Identifiers

A key identifier can be a permanent name for a persistent key, or a transient reference to a volatile key. Key identifiers are encoded in a

`psa_key_id_t` object. The identifier and [lifetime](#key-lifetimes) of a key indicate the location of the key in storage.

The `sli_se_opaque_types.h` or `sl_psa_values.h` in the Windows folder below includes the defines for SE key identifier macros.

For GSDK v3.1.x and v3.2.x (`sli_se_opaque_types.h`): `C:\SiliconLabs\SimplicityStudio\v5\developer\sdks\gecko_sdk_suite\\<GSDK VERSION\>\util\third_party\crypto\sl_component\sl_psa_driver\inc`

For GSDK v4.0.x (`sli_se_opaque_types.h`): `C:\Users\\<PC USER NAME\>\SimplicityStudio\SDKs\gecko_sdk\util\third_party\ crypto\sl_component\sl_psa_driver\inc`

For GSDK v4.1.x (`sl_psa_values.h`): `C:\Users\\<PC USER NAME\>\SimplicityStudio\SDKs\gecko_sdk\util\third_party\crypto\sl_component\sl_psa_driver\inc\public`

For GSDK v4.2.x (`sl_psa_values.h`): `C:\Users\\<PC USER NAME\>\SimplicityStudio\SDKs\gecko_sdk\platform\security\sl_component\sl_psa_driver\inc\public`

For GSDK v4.3.x (`sl_psa_values.h`): `C:\Users\\<PC USER NAME\>\SimplicityStudio\SDKs\gecko_sdk\platform\security\sl_component\sl_mbedtls_support\inc`

|**Type**|**Key Identifier (Key ID)**|**SE Key Identifier**|
|---|---|---|
|Volatile Plain Key|0 (Assigned by the PSA Crypto)|—|
|Persistent Plain Key|PSA_KEY_ID_USER_MIN to PSA_KEY_ID_USER_MAX|—|
|Volatile Wrapped Key|0 (Assigned by the PSA Crypto)|—|
|Persistent Wrapped Key|PSA_KEY_ID_USER_MIN to PSA_KEY_ID_USER_MAX|—|
|Public Sign Key|PSA_KEY_ID_VENDOR_MIN to PSA_KEY_ID_VENDOR_MAX|SL_SE_BUILTIN_KEY_SECUREBOOT_ID|
|Public Command Key|PSA_KEY_ID_VENDOR_MIN to PSA_KEY_ID_VENDOR_MAX|SL_SE_BUILTIN_KEY_SECUREDEBUG_ID|
|AES-128 Key|PSA_KEY_ID_VENDOR_MIN to PSA_KEY_ID_VENDOR_MAX|SL_SE_BUILTIN_KEY_AES128_ID|
|Private Device Key|PSA_KEY_ID_VENDOR_MIN to PSA_KEY_ID_VENDOR_MAX|SL_SE_BUILTIN_KEY_APPLICATION_ATTESTATION_ID|
|PUF-derived Hardware Unique Key|PSA_KEY_ID_VENDOR_MIN to PSA_KEY_ID_VENDOR_MAX|SL_CRYPTOACC_BUILTIN_KEY_PUF_ID (GSDK ≥ v4.3.0)|

If users are about to use the PSA Crypto for persistent key storage in their application, adhere to the identifier (Key ID) allocation below. The value 0 is reserved as an invalid key identifier.

![image-10](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image10.png)

###### Key Policies

This attribute consists of usage flags and a specification of the permitted algorithm. The `psa_key_usage_t` encodes the usage flags in a bit-mask. The following table describes three kinds of usage flag in the PSA Crypto.

<table>
    <thead>
        <tr>
            <th><strong>Flag</strong></th>
            <th><strong>Bit-mask</strong></th>
            <th><strong>Description</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Extractable</td>
            <td>PSA_KEY_USAGE_EXPORT</td>
            <td>Permission to export the key.</td>
        </tr>
        <tr>
            <td>Copyable</td>
            <td>PSA_KEY_USAGE_COPY</td>
            <td>Permission to copy the key.</td>
        </tr>
        <tr>
            <td>Other usage</td>
            <td>PSA_KEY_USAGE_ENCRYPT</td>
            <td>Permission for a symmetric encryption operation, for an AEAD encryption-and-authentication operation, or for an asymmetric encryption operation.</td>
        </tr>
        <tr>
            <td>#</td>
            <td>PSA_KEY_USAGE_DECRYPT</td>
            <td>Permission for a symmetric decryption operation, for an AEAD decryption-and-verification operation, or for an asymmetric decryption operation.</td>
        </tr>
        <tr>
            <td>#</td>
            <td>PSA_KEY_USAGE_SIGN_MESSAGE</td>
            <td>Permission for a MAC calculation operation or for an asymmetric message signature operation.</td>
        </tr>
        <tr>
            <td>#</td>
            <td>PSA_KEY_USAGE_VERIFY_MESSAGE</td>
            <td>Permission for a MAC verification operation or for an asymmetric message signature verification operation.</td>
        </tr>
        <tr>
            <td>#</td>
            <td>PSA_KEY_USAGE_SIGN_HASH</td>
            <td>Permission to sign a message hash as part of an asymmetric signature operation.</td>
        </tr>
        <tr>
            <td>#</td>
            <td>PSA_KEY_USAGE_VERIFY_HASH</td>
            <td>Permission to verify a message hash as part of an asymmetric signature verification operation.</td>
        </tr>
        <tr>
            <td>#</td>
            <td>PSA_KEY_USAGE_DERIVE</td>
            <td>Permission to derive other keys from this key.</td>
        </tr>
    </tbody>
</table>

> **Note**: Users can always export a public key or the public part of a key pair regardless of the value of the `PSA_KEY_USAGE_EXPORT` ﬂag.

The `psa_algorithm_t` encodes the permitted algorithm with the key. The [Symmetric Cryptographic Operation](06-migration-guide#symmetric-cryptographic-operation) and [Asymmetric Cryptographic Operation](06-migration-guide#asymmetric-cryptographic-operation) describe which algorithms can apply to the corresponding cryptographic operations.

The application must supply the algorithm to use for the operation. This algorithm is checked against the permitted algorithm policy of the key.

###### Summary

The `psa_key_attributes_t` object specifies the attributes for the new key during the creation process. The attributes are immutable once the key has been created.

The key identifier and lifetime in the attributes determine the location of the key in storage. The application must set the key type and size, key algorithm policy, and the appropriate key usage flags in the attributes for the key to be used in any cryptographic operations.

The key material can be copied into a new key, which can have a different lifetime or a more restrictive usage policy.

If the key creation succeeds, the PSA Crypto will return an identifier for the newly created key. The PSA Crypto can destroy a key from volatile memory and non-volatile storage [NVM3 object](https://docs.silabs.com/gecko-platform/latest/driver/api/group-nvm3). The destroying process makes the key identifier invalid, and the key identifier must not be used again by the application until it is allocated to a newly created key.

If not necessary, the extractable usage flag (`PSA_KEY_USAGE_EXPORT`) should not be set to allow the key to export in binary format.

##### Key Attributes API

The following table lists the PSA Crypto API for the [key attributes](04-key-management-in-psa-crypto#key-management-in-psa-crypto).

<table>
    <thead>
        <tr>
            <th><strong>API</strong></th>
            <th><strong>Description</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p><code>psa_key_attributes_init(…)</code></p>
            </td>
            <td>
                <p>Initialize the key attributes (<code>psa_key_attributes_t</code>) before calling any function.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><code>psa_get_key_attributes(…)</code></p>
            </td>
            <td>
                <p>Retrieve the key attributes (<code>psa_key_attributes_t</code>) of a key if successful.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><code>psa_reset_key_attributes(…)</code></p>
            </td>
            <td>
                <p>Reset the key attributes (<code>psa_key_attributes_t</code>) to an initialized state.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><code>psa_set_key_type(…)</code></p>
            </td>
            <td>
                <p>Declare the key type (<code>psa_key_type_t</code>) of a key.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><code>psa_get_key_type(…)</code></p>
            </td>
            <td>
                <p>Retrieve the key type (<code>psa_key_type_t</code>) from key attributes.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><code>psa_set_key_bits(…)</code></p>
            </td>
            <td>
                <p>Declare the key size (<code>psa_key_bits_t</code>) of a key.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><code>psa_get_key_bits(…)</code></p>
            </td>
            <td>
                <p>Retrieve the key size (<code>size_t</code>) from key attributes.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><code>psa_set_key_usage_flags(…)</code></p>
            </td>
            <td>
                <p>Declare the usage flags (<code>psa_key_usage_t</code>) for a key.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><code>psa_get_key_usage_flags(…)</code></p>
            </td>
            <td>
                <p>Retrieve the usage flags (<code>psa_key_usage_t</code>) from key attributes.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><code>psa_set_key_algorithm(…)</code></p>
            </td>
            <td>
                <p>Declare the permitted algorithm policy (<code>psa_algorithm_t</code>) for a key.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><code>psa_get_key_algorithm(…)</code></p>
            </td>
            <td>
                <p>Retrieve the algorithm policy (<code>psa_algorithm_t</code>) from key attributes.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><code>psa_set_key_id(…)</code></p>
            </td>
            <td>
                <p>Declare a key as persistent and set its key identifier (<code>psa_key_id_t</code>).</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><code>psa_get_key_id(…)</code></p>
            </td>
            <td>
                <p>Retrieve the key identifier (<code>psa_key_id_t</code>) from key attributes.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><code>psa_set_key_lifetime(…)</code></p>
            </td>
            <td>
                <p>Set the location (<code>psa_key_lifetime_t</code>) of a persistent key.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p><code>psa_get_key_lifetime(…)</code></p>
            </td>
            <td>
                <p>Retrieve the lifetime (<code>psa_key_lifetime_t</code>) from key attributes.</p>
            </td>
        </tr>
    </tbody>
</table>

The following sections describe how to use the key attributes API to set up the [storage](04-key-management-in-psa-crypto) for a key. Refer to the quick reference examples in [Symmetric Key](06-migration-guide#symmetric-key) and [Asymmetric Key](06-migration-guide#asymmetric-key) for more details.

**Volatile Plain Key**

<table>
    <thead>
        <tr>
            <th><strong>Key ID</strong></th>
            <th><strong>Persistence Level</strong></th>
            <th><strong>Location Indicator</strong></th>
            <th><strong>API Flow</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>= 0</td>
            <td><code>PSA_KEY_PERSISTENCE_VOLATILE</code></td>
            <td>Local (0x0)</td>
            <td>
                It is the default setting after calling <code>psa_key_attributes_init()</code>.
                No need to call <code>psa_set_key_id()</code> and <code>psa_set_key_lifetime()</code>.
            </td>
        </tr>
    </tbody>
</table>

**Example**:

```sh
psa_key_attributes_t key_attr;
key_attr = psa_key_attributes_init();
psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_bits(&key_attr, 256);
psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH);
psa_set_key_algorithm(&key_attr, PSA_ALG_ECDSA_ANY);
```

**Persistent Plain Key**

<table>
    <thead>
        <tr>
            <th><strong>Key ID</strong></th>
            <th><strong>Persistence Level</strong></th>
            <th><strong>Location Indicator</strong></th>
            <th><strong>API Flow</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>&gt; 0</td>
            <td><code>PSA_KEY_PERSISTENCE_DEFAULT</code></td>
            <td>Local (0x0)</td>
            <td>
                A non-zero key ID in <code>psa_set_key_id()</code> will change the persistence level from <code>PSA_KEY_PERSISTENCE_VOLATILE</code> to <code>PSA_KEY_PERSISTENCE_DEFAULT</code>.
            </td>
        </tr>
    </tbody>
</table>

**Example**:

```sh
psa_key_attributes_t key_attr;
key_attr = psa_key_attributes_init();
psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_bits(&key_attr, 256);
psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH);
psa_set_key_algorithm(&key_attr, PSA_ALG_ECDSA_ANY);
psa_set_key_id(&key_attr, 0x02);
```

**Volatile Wrapped Key**

<table>
    <thead>
        <tr>
            <th><strong>Key ID</strong></th>
            <th><strong>Persistence Level</strong></th>
            <th><strong>Location Indicator</strong></th>
            <th><strong>API Flow</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>= 0</p>
            </td>
            <td>
                <p><code>PSA_KEY_PERSISTENCE_VOLATILE</code></p>
            </td>
            <td>
                <p>Secure (0x1)</p>
            </td>
            <td>
                <p>Use the <code>psa_set_key_lifetime()</code> to change the location indicator from Local to Secure (0x01).</p>
            </td>
        </tr>
    </tbody>
</table>

**Example**:

```sh
psa_key_attributes_t key_attr;
key_attr = psa_key_attributes_init();
psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_bits(&key_attr, 256);
psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH);
psa_set_key_algorithm(&key_attr, PSA_ALG_ECDSA_ANY);
psa_set_key_lifetime(&key_attr, PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_PERSISTENCE_VOLATILE, 0x01));
```

**Persistent Wrapped Key**

<table>
    <thead>
        <tr>
            <th><strong>Key ID</strong></th>
            <th><strong>Persistence Level</strong></th>
            <th><strong>Location Indicator</strong></th>
            <th><strong>API Flow</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>&gt; 0</p>
            </td>
            <td>
                <p><code>PSA_KEY_PERSISTENCE_DEFAULT</code></p>
            </td>
            <td>
                <p>Local (0x0)</p>
            </td>
            <td>
                <p>A non-zero key ID in <code>psa_set_key_id()</code> will change the persistence level from <code>PSA_KEY_PERSISTENCE_VOLATILE</code> to <code>PSA_KEY_PERSISTENCE_DEFAULT</code>.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>&gt; 0</p>
            </td>
            <td>
                <p><code>PSA_KEY_PERSISTENCE_DEFAULT</code></p>
            </td>
            <td>
                <p>Secure (0x1)</p>
            </td>
            <td>
                <p>Use the <code>psa_set_key_lifetime()</code> to change the location indicator from Local to Secure (0x01).</p>
            </td>
        </tr>
    </tbody>
</table>

**Example**:

```sh
psa_key_attributes_t key_attr;
key_attr = psa_key_attributes_init();
psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_bits(&key_attr, 256);
psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH);
psa_set_key_algorithm(&key_attr, PSA_ALG_ECDSA_ANY);
psa_set_key_id(&key_attr, 0x02);
psa_set_key_lifetime(&key_attr, PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_PERSISTENCE_DEFAULT, 0x01));
```

> **Note**:
> 
> - The `PSA_KEY_PERSISTENCE_DEFAULT` is equal to `PSA_KEY_LIFETIME_PERSISTENT`.
> - Refer to [Key Identifiers](04-key-management-in-psa-crypto#key-identifiers) for details about the Key ID.

##### Migration Guide

###### System Requirements and Document

1. Simplicity Studio 5
2. GSDK v3.1.1 (Mbed TLS v2.24.0) or later
3. The latest SE Firmware image and release note can be found in the Windows folder below.  
   - For GSDK v3.1.x and v3.2.x: `C:\SiliconLabs\SimplicityStudio\v5\developer\sdks\gecko_sdk_suite\<GSDK VERSION\>\util\se_release\public`  
   - For GSDK v4.0.0 and higher: `C:\Users\<PC USER NAME\>\SimplicityStudio\SDKs\gecko_sdk\util\se_release\public`
4. PSA Crypto API (aka PSA Cryptography API) document: [ARM](https://arm-software.github.io/psa-api/crypto/)

###### Mbed TLS Versus PSA Crypto API

<table>
    <thead>
        <tr>
            <th><strong>Item</strong></th>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto API</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Key input</p>
            </td>
            <td>
                <p>APIs take key input directly.</p>
            </td>
            <td>
                <p>
                    • APIs do not take key input directly.<br>
                    • Key (identifier) needs to be created or imported before use.<br>
                    • APIs take an identifier if a key is required.
                </p>
            </td>
        </tr>
        <tr>
            <td rowspan="3">
                <p>Symmetric cryptographic operation</p>
            </td>
            <td>
                <p>Individual API (one-shot and streaming) for algorithm-specific functions.</p>
            </td>
            <td>
                <p>
                    • APIs are grouped by algorithm category for one-shot and streaming modes.<br>
                    • The exact algorithm is a parameter (<code>psa_algorithm_t</code>) to the function, not an individual API.
                </p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Except for AEAD (encrypt and decrypt), a one-shot function is not in a pair.</p>
            </td>
            <td>
                <p>Single-part (one-shot) functions are in a pair. For example, compute and verify, or encrypt and decrypt.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Initialization and free a context are required.</p>
            </td>
            <td>
                <p>Initialization and abort an operation are only required in multi-part (streaming) operations.</p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Error code</p>
            </td>
            <td>
                <p>APIs always return an integer.</p>
            </td>
            <td>
                <p>APIs always return <code>psa_status_t</code>.</p>
            </td>
        </tr>
    </tbody>
</table>

###### Migration

In [Key Handling](#key-handling), [Symmetric Cryptographic Operation](#symmetric-cryptographic-operation), [Asymmetric Cryptographic Operation](#asymmetric-cryptographic-operation) the following items will be considered when migrating from Mbed TLS to PSA Crypto.

1. The algorithms that can be used in a cryptographic operation.
2. The [key attributes](04-key-management-in-psa-crypto#key-management-in-psa-crypto) [type](04-key-management-in-psa-crypto#key-types) and [usage flags](04-key-management-in-psa-crypto#key-policies) for specific [algorithms](04-key-management-in-psa-crypto#key-policies) in the PSA Crypto.
3. Security Software Components.
4. The functions (APIs) for the Mbed TLS and PSA Crypto. For each type of [symmetric cryptographic operation](#symmetric-cryptographic-operation), the functions include:  
   - A pair of single-part (one-shot) functions  
   - A series of functions that implement multi-part (streaming) operations
5. Quick Reference Examples. These examples do not have error checking, but the user should always check the return code ([psa_status_t](https://docs.silabs.com/gecko-platform/latest/service/api/group-error) = `PSA_SUCCESS` or `PSA_ERROR_XXX`) from PSA Crypto to determine whether to use the output parameters in the application.

###### Platform Examples

Simplicity Studio 5 includes the PSA Crypto platform examples to evaluate the performance on [key handling](#key-handling), [symmetric](#symmetric-cryptographic-operation) and [asymmetric cryptographic operations](#asymmetric-cryptographic-operation), and [X.509 certificate](#x509-certificate).

- Refer to the corresponding `readme` file for details about each PSA Crypto platform example. This file also includes the procedures to create the project and run the example.
- Unless specified in the example, the PSA Crypto platform examples will use the software fallback feature in Mbed TLS if the [cryptography hardware accelerator](02-device-capability#device-capability) of the selected device does not support the corresponding ECC key or algorithm.

The figures in the following sections are based on GSDK v4.1.0. These figures may be different on other versions of the GSDK.

###### Security Software Components

The `slcp` file for each PSA Crypto platform example defines the software components installed in the project. The following figure shows the installed security software components (under the **Platform → Security** category) in the PSA Crypto ECDH example (`psa_crypto_ecdh.slcp`) on an HSE-SVH device.

![Installed Security Software Components](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image12.png)

The Simplicity IDE uses the installed security software components to automatically generate the configuration files for Mbed TLS (`mbedtls_config_autogen.h`) and PSA Crypto (`psa_crypto_config_autogen.h`) in the `autogen` folder when creating the project.

![Mbed TLS and PSA Crypto Configuration Files](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image13.png)

The user can browse the available security software components (under the **Platform → Security** category) on the target MCU or Wireless SoC if the [**Installed Components**] checkbox is unchecked.

![Available Security Software Components on the Target Device](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image14.png)

The Mbed TLS and PSA Crypto configuration files automatically regenerates when the user installs or uninstalls a security software component in the project.

![Install a Security Software Component](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image15.png)

![Uninstall a Security Software Component](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image16.png)

For a new project (like `empty.slcp`), the required security software components will be automatically added to the project after installing any cryptographic operation in PSA Crypto (like `GCM`) from the user. The `SE Manager` component is only for the HSE devices.

![Security Software Components for PSA Crypt](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image17.png)

If users are about to use the PSA Crypto for persistent key storage (either plain or wrapped) in their application, make sure to add the `PSA Persistent Storage Support (ITS)` component to the project.

![Security Software Component for Persistent Key Storage](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image18.png)

###### PSA Crypto Configuration

Click **Configuration** in **Installed Components**. Click [**Configure**] to open the Configuration Wizard in Context Menu.

![image19](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image19.png)

Enter the desired values in **PSA User Maximum Open Keys Count** (`SL_PSA_KEY_USER_SLOT_COUNT`) and **PSA Maximum User Persistent Keys Count** (`SL_PSA_ITS_USER_MAX_FILES`) to replace the default values. Click [**X**] to exit.

The default value of **PSA User Maximum Open Keys Count** is equal to 0 if the project installed any Wireless Stack before.

![image20](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image20.png)

The PSA Crypto configuration file (`psa_crypto_config_autogen.h`) in the `autogen` folder [Figure Mbed TLS and PSA Crypto Configuration Files](#security-software-components) includes the definitions for PSA key slot count and maximum PSA ITS files.

```sh
#define MBEDTLS_PSA_KEY_SLOT_COUNT (2 + 1 + SL_PSA_KEY_USER_SLOT_COUNT)
#define SL_PSA_ITS_MAX_FILES (1 + SL_PSA_ITS_USER_MAX_FILES)
```

The first digit in `MBEDTLS_PSA_KEY_SLOT_COUNT` is Wireless Stack (if installed) dependent. The second digit should be 1 for the PSA Crypto.

The first digit in `SL_PSA_ITS_MAX_FILES` is equal to 1 if the project installed the `PSA persistent storage support (ITS)` component [Figure Security Software Component for Persistent Key Storage](#security-software-components) before.

###### Initialization and Random Number Generation (RNG)

In PSA Crypto, applications must call `psa_crypto_init()` to initialize the library before using any other function. The PSA Crypto initialization includes seeding the pseudo-random generator (CTR-DRBG) with a hardware entropy source during the execution of `psa_crypto_init()`.

<table>
    <thead>
        <tr>
            <th><strong>Item</strong></th>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Initialization</p>
            </td>
            <td>
                <p>
                    Initialize CTR-DRBG<br>
                    • <code>void mbedtls_entropy_init(…)</code><br>
                    • <code>void mbedtls_ctr_drbg_init(…)</code><br>
                    • <code>int mbedtls_entropy_add_source(…)</code><br>
                    Seed and set up the CTR-DRBG entropy<br>
                    • <code>int mbedtls_ctr_drbg_seed(…)</code>
                </p>
            </td>
            <td>
                <p>
                    Initialize PSA Crypto<br>
                    • <code>psa_status_t psa_crypto_init(void)</code>
                </p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Generate random bytes</p>
            </td>
            <td>
                <p><code>int mbedtls_ctr_drbg_random(…)</code></p>
            </td>
            <td>
                <p><code>psa_status_t psa_generate_random(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Free resources</p>
            </td>
            <td>
                <p>
                    • <code>void mbedtls_ctr_drbg_free(…)</code><br>
                    • <code>void mbedtls_entropy_free(…)</code>
                </p>
            </td>
            <td>
                <p><code>void mbedtls_psa_crypto_free(void)</code></p>
            </td>
        </tr>
    </tbody>
</table>

If a device includes a True Random Number Generator (TRNG) hardware module, the example will use the TRNG as an entropy source to seed the CTR-DRBG. If the device does not incorporate a TRNG, the example will use [RAIL](https://docs.silabs.com/rail/latest/), Non-volatile (NV) seed (requires [NVM3 driver](https://docs.silabs.com/gecko-platform/latest/driver/api/group-nvm3)), or ADC as the entropy source.

|**Device**|**Entropy Source**|
|---|---|
|MCU Series 1 - EFM32JG1, EFM32PG1|NV seed (default) or ADC|
|MCU Series 1 - EFM32JG12, EFM32PG12, EFM32GG11, EFM32GG12, EFM32TG11|TRNG|
|Wireless SoC Series 1 - EFR32xG1, EFR32xG14|RAIL|
|Wireless SoC Series 1 - EFR32xG12, EFR32xG13 (Revision D or later)|TRNG|
|All MCU Series 2 and Wireless SoC Series 2 devices|TRNG|

**Quick Reference Examples**

**PSA Crypto Initialization and Random Number Generation**

```c++
#include "psa/crypto.h"
void app_process_action(void)
{
  uint8_t rand_buf[32];
  psa_status_t ret;

  // Initialize the PSA Crypto and generate random numbers
  ret = psa_crypto_init();
  ret = psa_generate_random(rand_buf, sizeof(rand_buf));
}
```

###### Key Handling

The following table describes the main differences in key handling between Mbed TLS and PSA Crypto.

|**Item**|**Mbed TLS**|**PSA Crypto**|
|---|---|---|
|Random Number Generation (RNG)|It requires application code to keep track of RNG.|The core keeps track of RNG.|
|Buffer|It requires dedicated key buffers.|The core manages the key.|
|Key export|The key is exportable.|The usage flag manages this option.|
|Lifetime|It is volatile.|It can be volatile or persistent.|
|Location|Local|Local or Secure|

###### Symmetric Key

A symmetric key can be used with a block cipher or a stream cipher.

**Algorithms**

Refer to the [Symmetric Cryptographic Operation](#symmetric-cryptographic-operation) section.

**Key Attributes in PSA Crypto**

Refer to the [Symmetric Cryptographic Operation](#symmetric-cryptographic-operation) section.

**Security Software Components**

Refer to the [Symmetric Cryptographic Operation](#symmetric-cryptographic-operation) section.

**Functions**

<table>
    <thead>
        <tr>
            <th><strong>Item</strong></th>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Create a random key</p>
            </td>
            <td>
                <p>
                    Generate random numbers to a buffer<br>
                    • <code>int mbedtls_ctr_drbg_random(…)</code><br>
                    Set up a key from a buffer (API is algorithm dependent)<br>
                    • <code>int mbedtls_aes_setkey_enc(…)</code><br>
                    • <code>int mbedtls_aes_setkey_dec(…)</code><br>
                    • <code>int mbedtls_cipher_setkey(…)</code><br>
                    • <code>int mbedtls_ccm_setkey(…)</code><br>
                    • <code>int mbedtls_gcm_setkey(…)</code><br>
                    • <code>int mbedtls_chacha20_setkey(…)</code><br>
                    • <code>int mbedtls_chachapoly_setkey(…)</code>
                </p>
            </td>
            <td>
                <p>
                    Create a key from randomly generated data<br>
                    • <code>psa_status_t psa_generate_key(…)</code>
                </p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Import a key from a buffer</p>
            </td>
            <td>
                <p>
                    API is algorithm dependent<br>
                    • <code>int mbedtls_aes_setkey_enc(…)</code><br>
                    • <code>int mbedtls_aes_setkey_dec(…)</code><br>
                    • <code>int mbedtls_cipher_setkey(…)</code><br>
                    • <code>int mbedtls_ccm_setkey(…)</code><br>
                    • <code>int mbedtls_gcm_setkey(…)</code><br>
                    • <code>int mbedtls_chacha20_setkey(…)</code><br>
                    • <code>int mbedtls_chachapoly_setkey(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_import_key(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Copy a key</p>
            </td>
            <td>
                <p>—</p>
            </td>
            <td>
                <p><code>psa_status_t psa_copy_key(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Export a key to a buffer</p>
            </td>
            <td>
                <p>The key is always in a buffer.</p>
            </td>
            <td>
                <p><code>psa_status_t psa_export_key(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Destroy a key</p>
            </td>
            <td>
                <p>Zero the key buffer.</p>
            </td>
            <td>
                <p><code>psa_status_t psa_destroy_key(…)</code></p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**: The `psa_export_key()` can export a symmetric key in plaintext if the [PSA_KEY_USAGE_EXPORT](04-key-management-in-psa-crypto#key-policies) usage flag is set.

**Quick Reference Examples**

**Symmetric Plain Key Creation and Import**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t aes_ecb_key[16] = {0};

  psa_status_t ret;
  psa_key_id_t generate_key_id;
  psa_key_id_t import_key_id;
  psa_key_attributes_t key_attr;

  ret = psa_crypto_init();

  // Set up attributes for a AES ECB key
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_AES);
  psa_set_key_bits(&key_attr, 128);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
  psa_set_key_algorithm(&key_attr, PSA_ALG_ECB_NO_PADDING);

  // Generate a random volatile plain key for AES ECB
  ret = psa_generate_key(&key_attr, &generate_key_id);

  // Import a volatile plain key for AES ECB
  ret = psa_import_key(&key_attr, aes_ecb_key, sizeof(aes_ecb_key), &import_key_id);

  // Destroy the volatile plain keys for AES ECB
  ret = psa_destroy_key(generate_key_id);
  ret = psa_destroy_key(import_key_id);

  // Generate a random persistent plain key for AES ECB (ID = 0x02)
  psa_set_key_id(&key_attr, 0x02);
  ret = psa_generate_key(&key_attr, &generate_key_id);

  // Import a persistent plain key for AES ECB (ID = 0x03)
  psa_set_key_id(&key_attr, 0x03);
  ret = psa_import_key(&key_attr, aes_ecb_key, sizeof(aes_ecb_key), &import_key_id);

  // Destroy the persistent plain keys for AES ECB
  ret = psa_destroy_key(generate_key_id);
  ret = psa_destroy_key(import_key_id);
}
```

**Symmetric Wrapped Key Creation and Import (HSE-SVH only)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t aes_ecb_key[16] = {0};

  psa_status_t ret;
  psa_key_id_t generate_key_id;
  psa_key_id_t import_key_id;
  psa_key_attributes_t key_attr;
  ret = psa_crypto_init();

  // Set up attributes for a AES ECB key
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_AES);
  psa_set_key_bits(&key_attr, 128);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
  psa_set_key_algorithm(&key_attr, PSA_ALG_ECB_NO_PADDING);
  psa_set_key_lifetime(&key_attr, PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_PERSISTENCE_VOLATILE, 0x01));

  // Generate a random volatile wrapped key for AES ECB
  ret = psa_generate_key(&key_attr, &generate_key_id);

  // Import a volatile wrapped key for AES ECB
  ret = psa_import_key(&key_attr, aes_ecb_key, sizeof(aes_ecb_key), &import_key_id);

  // Destroy the volatile wrapped keys for AES ECB
  ret = psa_destroy_key(generate_key_id);
  ret = psa_destroy_key(import_key_id);

  // Generate a random persistent wrapped key for AES ECB (ID = 0x02)
  psa_set_key_id(&key_attr, 0x02);
  psa_set_key_lifetime(&key_attr, PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_PERSISTENCE_DEFAULT, 0x01));
  ret = psa_generate_key(&key_attr, &generate_key_id);

  // Import a persistent wrapped key for AES ECB (ID = 0x03)
  psa_set_key_id(&key_attr, 0x03);
  psa_set_key_lifetime(&key_attr, PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_PERSISTENCE_DEFAULT, 0x01));
  ret = psa_import_key(&key_attr, aes_ecb_key, sizeof(aes_ecb_key), &import_key_id);

  // Destroy the persistent wrapped keys for AES ECB
  ret = psa_destroy_key(generate_key_id);
  ret = psa_destroy_key(import_key_id);
}
```

**PSA Crypto Platform Example**

Click the `View Project Documentation` link to open the `readme` file.

![image21](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image21.png)

The following table describes the implementation status of the PSA Crypto symmetric key platform example.

|**Key**|**Series 1**|**Series 2 - VSE**|**Series 2 - HSE**|**Remark**|
|---|---|---|---|---|
|Extractable|Y|Y|Y|—|
|Copyable|Y|Y|Y|The PSA_KEY_USAGE_COPY usage flag does not apply to the wrapped key.|
|Wrapped|—|—|Y|Only on HSE-SVH devices.|
|128-bit|Y|Y|Y|—|
|192-bit|Y|Y|Y|—|
|256-bit|Y|Y|Y|—|

###### Asymmetric Key

An asymmetric key pair consists of a (secret) private key and a public key (not secret). A public key cryptographic algorithm can be used for key distribution and digital signatures.

**Algorithms**

Refer to the [Asymmetric Cryptographic Operation](#asymmetric-cryptographic-operation) section.

**Key Attributes in PSA Crypto**

Refer to the [Asymmetric Cryptographic Operation](#asymmetric-cryptographic-operation) section.

**Security Software Components**

Refer to the [Asymmetric Cryptographic Operation](#asymmetric-cryptographic-operation) section.

**Functions**

<table>
    <thead>
        <tr>
            <th><strong>Item</strong></th>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Create a random key</p>
            </td>
            <td>
                <p>
                    ECDH<br>
                    • <code>void mbedtls_ecdh_init(…)</code><br>
                    • <code>int mbedtls_ecp_group_load(…)</code><br>
                    • <code>int mbedtls_ecdh_gen_public(…)</code><br>
                    ECDSA<br>
                    • <code>void mbedtls_ecdsa_init(…)</code><br>
                    • <code>int mbedtls_ecdsa_genkey(…)</code>
                </p>
            </td>
            <td>
                <p>
                    Create a key from randomly generated data<br>
                    • <code>psa_status_t psa_generate_key(…)</code>
                </p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Import a private or public key from a buffer</p>
            </td>
            <td>
                <p>
                    • <code>int mbedtls_ecp_point_read_binary(…)</code><br>
                    • <code>int mbedtls_mpi_read_binary(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_import_key(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Copy a key</p>
            </td>
            <td>
                <p>—</p>
            </td>
            <td>
                <p><code>psa_status_t psa_copy_key(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Export a private key to a buffer</p>
            </td>
            <td>
                <p>
                    • <code>int mbedtls_ecp_point_write_binary(…)</code><br>
                    • <code>int mbedtls_mpi_write_binary(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_export_key(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Export a public key to a buffer</p>
            </td>
            <td>
                <p>
                    • <code>int mbedtls_ecp_point_write_binary(…)</code><br>
                    • <code>int mbedtls_mpi_write_binary(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_export_public_key(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Destroy a key</p>
            </td>
            <td>
                <p>
                    ECDH<br>
                    • <code>void mbedtls_ecdh_free(…)</code><br>
                    ECDSA<br>
                    • <code>void mbedtls_ecdsa_free(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_destroy_key(…)</code></p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**:
> 
> - The `psa_import_key()` cannot store a public key in [wrapped](04-key-management-in-psa-crypto) form.
> - The `psa_export_key()` can export a private key in plaintext if the [PSA_KEY_USAGE_EXPORT](04-key-management-in-psa-crypto) usage flag is set.

|**ECC Key**|**Private Key Size (Import and Export)**|**Public Key Size (Import and Export)**|
|---|---|---|
|secp192r1|24-byte|49-byte|
|secp224r1|28-byte|57-byte|
|secp256r1|32-byte|65-byte|
|secp384r1|48-byte|97-byte|
|secp521r1|66-byte|133-byte|
|secp256k1|32-byte|65-byte|
|Curve25519|32-byte|32-byte|
|Curve448|56-byte|56-byte|
|Edwards25519|32-byte|32-byte|

> **Note**:
> 
> - The public key of the `secpxxx` curve is stored in an uncompressed format (prefix `0x04` with the X and Y coordinates).
> - EFR32xG21A/B devices do not support hardware acceleration on the secp224r1 curve.
> - Only the VSE-SVM devices support hardware acceleration on the secp256k1 curve.
> - The secp224r1 and secp256k1 with wrapped keys are not supported yet.

**Quick Reference**

**Examples Asymmetric Key Creation and Import**

```c++
#include "psa/crypto.h"
void app_process_action(void)
{
  uint8_t public_key[65];         // Uncompressed point format
  size_t pubkey_len;

  psa_status_t ret;
  psa_key_id_t key_id;
  psa_key_attributes_t key_attr;

  ret = psa_crypto_init();

  // Check if there is already a persistent key with the given identifier (ID = 0x02)
  key_attr = psa_key_attributes_init();
  ret = psa_get_key_attributes(0x02, &key_attr);

  if (ret == PSA_ERROR_INVALID_HANDLE) {
    // Key identifier does not exist, set up attributes for a persistent private wrapped key (secp256r1)
    key_attr = psa_key_attributes_init();
    psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
    psa_set_key_bits(&key_attr, 256);
    psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH);
    psa_set_key_algorithm(&key_attr, PSA_ALG_ECDSA_ANY);
    psa_set_key_id(&key_attr, 0x02);
    psa_set_key_lifetime(&key_attr, PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_PERSISTENCE_DEFAULT, 0x01));

    // Generate a random persistent private wrapped key (ID = 0x02)
    ret = psa_generate_key(&key_attr, &key_id);

    // Export a public key from a persistent private wrapped key (ID = 0x02)
    ret = psa_export_public_key(0x02, public_key, sizeof(public_key), &pubkey_len);

    // Set up attributes for a public key (secp256r1)
    key_attr = psa_key_attributes_init();
    psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1));
    psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_VERIFY_HASH);
    psa_set_key_algorithm(&key_attr, PSA_ALG_ECDSA_ANY);

    // Import a public key
    ret = psa_import_key(&key_attr, public_key, sizeof(public_key), &key_id);

    // Destroy a persistent private wrapped key (ID = 0x02) and public key
    ret = psa_destroy_key(0x02);
    ret = psa_destroy_key(key_id);
  } else if (ret == PSA_SUCCESS) {
    // Key identifier already exists
    return;
  } else {
      // Unexpected error
    return;
  }
}
```

> **Note**: Remove the code for the `psa_set_key_lifetime()` function to generate a random persistent private plain key on non-HSE-SVH devices.

**PSA Crypto Platform Example**

Click the `View Project Documentation` link to open the `readme` file.

![image22](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image22.png)

The following table describes the implementation status of the PSA Crypto asymmetric key platform example.

|**ECC Key**|**Series 1**|**Series 2 - VSE**|**Series 2 - HSE**|**Remark**|
|---|---|---|---|---|
|Extractable|Y|Y|Y|—|
|Copyable|Y|Y|Y|The PSA_KEY_USAGE_COPY usage flag does not apply to the wrapped key.|
|Wrapped|—|—|Y|Only on HSE-SVH devices.|
|secp192r1|Y|Y|Y|—|
|secp256r1|Y|Y|Y|—|
|secp384r1|Y|Y|Y|Hardware acceleration only on HSE-SVH devices.|
|secp521r1|Y|Y|Y|Hardware acceleration only on HSE-SVH devices.|
|Curve25519|Y|Y|Y|Hardware acceleration only on HSE devices.|
|Curve448|—|—|Y|Only on HSE-SVH devices with hardware acceleration.|
|Edwards25519|—|—|Y|Only on HSE devices with hardware acceleration.|

> **Note**:
> 
> - This example does not include secp224r1 and secp256k1.
> - The PSA Crypto does not yet support software fallback on the Curve448 and Edwards25519.
> - The HSE-SVM devices require SE firmware v1.2.11 or higher (EFR32xG21) and v2.1.7 or higher (other HSE devices) to support hardware acceleration on Curve25519 and Edwards25519. This feature also requires GSDK v4.0.1 or higher.

###### Symmetric Cryptographic Operation

###### Message Digests (heading level 7)

Message digests are designed to protect the integrity of a piece of data or media to detect changes to any part of a message. They are a type of cryptography utilizing hash values that can warn the receiver of any modifications applied to a message transmitted over an insecure channel.

**Algorithms**

<table>
    <thead>
        <tr>
            <th><strong>Algorithm</strong></th>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>SHA-1</p>
            </td>
            <td>
                <p><code>MBEDTLS_MD_SHA1</code></p>
            </td>
            <td>
                <p><code>PSA_ALG_SHA_1</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>SHA-2</p>
            </td>
            <td>
                <p>
                    • <code>MBEDTLS_MD_SHA224</code><br>
                    • <code>MBEDTLS_MD_SHA256</code><br>
                    • <code>MBEDTLS_MD_SHA384</code><br>
                    • <code>MBEDTLS_MD_SHA512</code>
                </p>
            </td>
            <td>
                <p>
                    • <code>PSA_ALG_SHA_224</code><br>
                    • <code>PSA_ALG_SHA_256</code><br>
                    • <code>PSA_ALG_SHA_384</code><br>
                    • <code>PSA_ALG_SHA_512</code>
                </p>
            </td>
        </tr>
    </tbody>
</table>

**Security Software Components**

|**Algorithm**|**Security Software Components**|
|---|---|
|PSA_ALG_SHA_1|SHA-1|
|PSA_ALG_SHA_224|SHA-224|
|PSA_ALG_SHA_256|SHA-256|
|PSA_ALG_SHA_384|SHA-384|
|PSA_ALG_SHA_512|SHA-512|

**Single-Part Functions**

<table>
    <thead>
        <tr>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_md(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_sha1_ret(…)</code><br>
                    • <code>int mbedtls_sha256_ret(…)</code><br>
                    • <code>int mbedtls_sha512_ret(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_hash_compute(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>—</p>
            </td>
            <td>
                <p><code>psa_status_t psa_hash_compare(…)</code></p>
            </td>
        </tr>
    </tbody>
</table>

**Multi-Part Operations**

<table>
    <thead>
        <tr>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>void mbedtls_md_init(…)</code><br>
                    Algorithm specific<br>
                    • <code>void mbedtls_sha1_init(…)</code><br>
                    • <code>void mbedtls_sha256_init(…)</code><br>
                    • <code>void mbedtls_sha512_init(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_hash_operation_t psa_hash_operation_init(void)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_md_setup(…)</code><br>
                    • <code>int mbedtls_md_starts(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_sha1_starts_ret(…)</code><br>
                    • <code>int mbedtls_sha256_starts_ret(…)</code><br>
                    • <code>int mbedtls_sha512_starts_ret(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_hash_setup(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_md_update(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_sha1_update_ret(…)</code><br>
                    • <code>int mbedtls_sha256_update_ret(…)</code><br>
                    • <code>int mbedtls_sha512_update_ret(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_hash_update(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_md_finish(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_sha1_finish_ret(…)</code><br>
                    • <code>int mbedtls_sha256_finish_ret(…)</code><br>
                    • <code>int mbedtls_sha512_finish_ret(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_hash_finish(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>—</p>
            </td>
            <td>
                <p><code>psa_status_t psa_hash_verify(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>void mbedtls_md_free(…)</code><br>
                    Algorithm specific<br>
                    • <code>void mbedtls_sha1_free(…)</code><br>
                    • <code>void mbedtls_sha256_free(…)</code><br>
                    • <code>void mbedtls_sha512_free(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_hash_abort(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_md_clone(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_sha1_clone(…)</code><br>
                    • <code>int mbedtls_sha256_clone(…)</code><br>
                    • <code>int mbedtls_sha512_clone(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_hash_clone(…)</code></p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**: The multi-part operation allows the data to be processed for message digest in fragments instead of all at once.

**Quick Reference Examples**

**SHA-256 (One-shot)**

```c++
#include "psa/crypto.h"
void app_process_action(void)
{
  char test_msg[] = {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"};
  uint8_t expect_sha256_hash[] = {
    0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39,
    0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1
  };

  uint8_t hash_buf[32];
  size_t hash_len;
  psa_status_t ret;

  ret = psa_crypto_init();

  // Calculate the hash of a message
  ret = psa_hash_compute(PSA_ALG_SHA_256,
                        (uint8_t *)test_msg,
                        sizeof(test_msg) - 1,
                        hash_buf,
                        sizeof(hash_buf),
                        &hash_len);

  // Calculate the hash of a message and compare it with a reference value
  ret = psa_hash_compare(PSA_ALG_SHA_256,
                        (uint8_t *)test_msg,
                        sizeof(test_msg) - 1,
                        expect_sha256_hash,
                        sizeof(expect_sha256_hash));
}
```

**SHA-256 (Streaming)**

```c++
#include "psa/crypto.h"
void app_process_action(void)
{
  char test_msg[] = {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"};
  uint8_t hash_buf[32];
  size_t hash_len;
  uint32_t hash_total;
  uint32_t stream_block_size = 16;            // Block size for streaming

  psa_status_t ret;
  psa_hash_operation_t hash_op;
  psa_hash_operation_t verify_op;

  ret = psa_crypto_init();

  hash_op = psa_hash_operation_init();
  ret = psa_hash_setup(&hash_op, PSA_ALG_SHA_256);

  // Streaming block
  hash_total = 0;
  while ((sizeof(test_msg) - 1 - hash_total) > stream_block_size) {
  ret = psa_hash_update(&hash_op, (uint8_t *)(test_msg + hash_total), stream_block_size);
  hash_total += stream_block_size;
  }
  ret = psa_hash_update(&hash_op, (uint8_t *)(test_msg + hash_total), sizeof(test_msg) -1 - hash_total);

  // Generate hash and verify
  // Expected hash:
  // 24 8d 6a 61 d2 06 38 b8 e5 c0 26 93 0c 3e 60 39 a3 3c e4 59 64 ff 21 67 f6 ec ed d4 19 db 06 c1
  verify_op = psa_hash_operation_init();
  ret = psa_hash_clone(&hash_op, &verify_op);
  ret = psa_hash_finish(&hash_op, hash_buf, sizeof(hash_buf), &hash_len);
  ret = psa_hash_verify(&verify_op, hash_buf, hash_len);
}
```

**PSA Crypto Platform Example**

Click the `View Project Documentation` link to open the `readme` file.

![image23](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image23.png)

The following table describes the implementation status of the PSA Crypto hash platform example.

|**Algorithm**|**Series 1**|**Series 2 - VSE**|**Series 2 - HSE**|**Remark**|
|---|---|---|---|---|
|SHA-1|Y|Y|Y|—|
|SHA-224|Y|Y|Y|—|
|SHA-256|Y|Y|Y|—|
|SHA-384|Y|Y|Y|Hardware acceleration only on HSE-SVH devices.|
|SHA-512|Y|Y|Y|Hardware acceleration only on HSE-SVH devices.|

###### Message Authentication Codes (MAC)

A Message Authentication Code (MAC), sometimes known as a tag, is a short piece of information used to confirm that the message came from the stated sender (its authenticity) and has not been changed.

**Algorithms**

<table>
    <thead>
        <tr>
            <th><strong>Algorithm</strong></th>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>HMAC</p>
            </td>
            <td>
                <p><code>• MBEDTLS_MD_SHA1</code><br><code>• MBEDTLS_MD_SHA224</code><br><code>• MBEDTLS_MD_SHA256</code><br><code>• MBEDTLS_MD_SHA384</code><br><code>• MBEDTLS_MD_SHA512</code></p>
            </td>
            <td>
                <p>
                    • <code>PSA_ALG_HMAC(PSA_ALG_SHA_1)</code><br>
                    • <code>PSA_ALG_HMAC(PSA_ALG_SHA_224)</code><br>
                    • <code>PSA_ALG_HMAC(PSA_ALG_SHA_256)</code><br>
                    • <code>PSA_ALG_HMAC(PSA_ALG_SHA_384)</code><br>
                    • <code>PSA_ALG_HMAC(PSA_ALG_SHA_512)</code><br>
                    • <code>PSA_ALG_TRUNCATED_MAC(PSA_ALG_HMAC(hash_alg), mac_length)</code><br>
                    • <code>PSA_ALG_FULL_LENGTH_MAC(PSA_ALG_HMAC(hash_alg))</code><br>
                    • <code>PSA_ALG_AT_LEAST_THIS_LENGTH_MAC(PSA_ALG_HMAC(hash_alg), min_mac_length)</code>
                </p>
            </td>
        </tr>
        <tr>
            <td>
                <p>CMAC</p>
            </td>
            <td>
                <p>
                    • <code>MBEDTLS_CIPHER_AES_128_ECB</code><br>
                    • <code>MBEDTLS_CIPHER_AES_192_ECB</code><br>
                    • <code>MBEDTLS_CIPHER_AES_256_ECB</code>
                </p>
            </td>
            <td>
                <p>
                    • <code>PSA_ALG_CMAC</code><br>
                    • <code>PSA_ALG_TRUNCATED_MAC(PSA_ALG_CMAC, mac_length)</code><br>
                    • <code>PSA_ALG_FULL_LENGTH_MAC(PSA_ALG_CMAC)</code><br>
                    • <code>PSA_ALG_AT_LEAST_THIS_LENGTH_MAC(PSA_ALG_CMAC, min_mac_length)</code>
                </p>
            </td>
        </tr>
    </tbody>
</table>

**Key Attributes in PSA Crypto**

<table>
    <thead>
        <tr>
            <th><strong>Algorithm</strong></th>
            <th><strong>Key Type</strong></th>
            <th><strong>Key Size in Bits</strong></th>
            <th><strong>Key Usage Flag</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>•PSA_ALG_HMAC(PSA_ALG_SHA_1)<br><br>
                •PSA_ALG_HMAC(PSA_ALG_SHA_224)<br><br>
                •PSA_ALG_HMAC(PSA_ALG_SHA_256)<br><br>
                •PSA_ALG_HMAC(PSA_ALG_SHA_384)<br><br>
                •PSA_ALG_HMAC(PSA_ALG_SHA_512)<br><br>
                •PSA_ALG_TRUNCATED_MAC(PSA_ALG_HMAC(hash_alg), mac_length)<br><br>
                •PSA_ALG_FULL_LENGTH_MAC(PSA_ALG_HMAC(hash_alg))<br><br>
                •PSA_ALG_AT_LEAST_THIS_LENGTH_MAC(PSA_ALG_HMAC(hash_alg), min_mac_length)
            </td>
            <td>PSA_KEY_TYPE_HMAC</td>
            <td>Multiple of 8</td>
            <td rowspan="2">•PSA_KEY_USAGE_SIGN_MESSAGE<br><br>
                •PSA_KEY_USAGE_VERIFY_MESSAGE
            </td>
        </tr>
        <tr>
            <td>•PSA_ALG_CMAC<br><br>
                •PSA_ALG_TRUNCATED_MAC(PSA_ALG_CMAC, mac_length)<br><br>
                •PSA_ALG_FULL_LENGTH_MAC(PSA_ALG_CMAC)<br><br>
                •PSA_ALG_AT_LEAST_THIS_LENGTH_MAC(PSA_ALG_CMAC, min_mac_length)
            </td>
            <td>PSA_KEY_TYPE_AES</td>
            <td>•128 (16-byte)<br><br>
                •192 (24-byte)<br><br>
                •256 (32-byte)
            </td>
        </tr>
    </tbody>
</table>

> **Note**: For GSDK lower than v4.1.0, use usage flag `PSA_KEY_USAGE_SIGN_HASH` and `PSA_KEY_USAGE_VERIFY_HASH` instead of `PSA_KEY_USAGE_SIGN_MESSAGE` and `PSA_KEY_USAGE_VERIFY_MESSAGE`.

**Security Software Components**

|**Algorithm**|**Security Software Components**|
|---|---|
|PSA_ALG_HMAC(PSA_ALG_SHA_1)|HMAC and SHA-1|
|PSA_ALG_HMAC(PSA_ALG_SHA_224)|HMAC and SHA-224|
|PSA_ALG_HMAC(PSA_ALG_SHA_256)|HMAC and SHA-256|
|PSA_ALG_HMAC(PSA_ALG_SHA_384)|HMAC and SHA-384|
|PSA_ALG_HMAC(PSA_ALG_SHA_512)|HMAC and SHA-512|
|PSA_ALG_TRUNCATED_MAC(PSA_ALG_HMAC(hash_alg), mac_length)|HMAC and SHA-X|
|PSA_ALG_FULL_LENGTH_MAC(PSA_ALG_HMAC(hash_alg))|HMAC and SHA-X|
|PSA_ALG_AT_LEAST_THIS_LENGTH_MAC(PSA_ALG_HMAC(hash_alg), min_mac_length)|HMAC and SHA-X|
|PSA_ALG_CMAC|CMAC|
|PSA_ALG_TRUNCATED_MAC(PSA_ALG_CMAC, mac_length)|CMAC|
|PSA_ALG_FULL_LENGTH_MAC(PSA_ALG_CMAC)|CMAC|
|PSA_ALG_AT_LEAST_THIS_LENGTH_MAC(PSA_ALG_CMAC, min_mac_length)|CMAC|

**Single-Part Functions**

<table>
    <thead>
        <tr>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>
                    HMAC<br>
                    • <code>int mbedtls_md_hmac(…)</code><br>
                    CMAC<br>
                    • <code>int mbedtls_cipher_cmac(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_mac_compute(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>—</p>
            </td>
            <td>
                <p><code>psa_status_t psa_mac_verify(…)</code></p>
            </td>
        </tr>
    </tbody>
</table>

**Multi-Part Operations**

<table>
    <thead>
        <tr>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>
                    HMAC<br>
                    • <code>void mbedtls_md_init(…)</code><br>
                    • <code>int mbedtls_md_setup(…)</code><br>
                    CMAC<br>
                    • <code>void mbedtls_cipher_init(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_mac_operation_t psa_mac_operation_init(void)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    HMAC<br>
                    • <code>int mbedtls_md_hmac_starts(…)</code><br>
                    CMAC<br>
                    • <code>int mbedtls_cipher_cmac_starts(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_mac_sign_setup(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>—</p>
            </td>
            <td>
                <p><code>psa_status_t psa_mac_verify_setup(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    HMAC<br>
                    • <code>int mbedtls_md_hmac_update(…)</code><br>
                    CMAC<br>
                    • <code>int mbedtls_cipher_cmac_update(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_mac_update(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    HMAC<br>
                    • <code>int mbedtls_md_hmac_finish(…)</code><br>
                    CMAC<br>
                    • <code>int mbedtls_cipher_cmac_finish(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_mac_sign_finish(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>—</p>
            </td>
            <td>
                <p><code>psa_status_t psa_mac_verify_finish(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    HMAC<br>
                    • <code>void mbedtls_md_free(…)</code><br>
                    CMAC<br>
                    • <code>void mbedtls_cipher_free(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_mac_abort(…)</code></p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**: The multi-part operation allows the data to be processed for MAC in fragments instead of all at once.

**Quick Reference Examples**

**CMAC (One-shot)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t cmac_key[] = {
    0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
    0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4
  };
  uint8_t cmac_msg[] = {
    0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
    0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
    0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11
  };
  uint8_t mac_buf[16];
  size_t mac_len;

  psa_status_t ret;
  psa_key_id_t key_id;
  psa_key_attributes_t key_attr;

  ret = psa_crypto_init();

  // Set up attributes for a CMAC key
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_AES);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_VERIFY_MESSAGE);
  psa_set_key_algorithm(&key_attr, PSA_ALG_CMAC);

  // Import a volatile plain key for CMAC
  ret = psa_import_key(&key_attr, cmac_key, sizeof(cmac_key), &key_id);

  // Calculate the CMAC MAC of a message
  // Expected CMAC MAC: aa f3 d8 f1 de 56 40 c2 32 f5 b1 69 b9 c9 11 e6
  ret = psa_mac_compute(key_id, PSA_ALG_CMAC,
                        cmac_msg, sizeof(cmac_msg),
                        mac_buf, sizeof(mac_buf), &mac_len);

  // Verify the CMAC MAC of a message
  ret = psa_mac_verify(key_id, PSA_ALG_CMAC,
                        cmac_msg, sizeof(cmac_msg),
                        mac_buf, mac_len);

  // Destroy a volatile plain key for CMACss
  ret = psa_destroy_key(key_id);
}
```

> **Note**: There are two ways to change the CMAC MAC length (default 16 bytes).
> 
> 1. Replace all MAC algorithm `PSA_ALG_CMAC` with `PSA_ALG_TRUNCATED_MAC``(PSA_ALG_CMAC, mac_length)` for the desired size (≥ 4) of the MAC in bytes.
> 2. Replace the MAC algorithm `PSA_ALG_CMAC` in the `psa_set_key_algorithm(&key_attr, PSA_ALG_CMAC)` function with `PSA_ALG_AT_LEAST_THIS_LENGTH_MAC(PSA_ALG_CMAC, min_mac_length)` to set the minimum MAC length (≥ 4) in bytes. Replace the MAC algorithm `PSA_ALG_CMAC` in `psa_mac_compute()` and `psa_mac_verify()` with `PSA_ALG_TRUNCATED_MAC``(PSA_ALG_CMAC, mac_length)` to set the desired MAC length (≥ `min_mac_length` and ≤ `PSA_MAC_LENGTH``(PSA_KEY_TYPE_AES, 256, PSA_ALG_CMAC))` in bytes.

**HMAC SHA-256 (One-shot)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t hmac_key[] = {
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66
  };
  uint8_t hmac_msg[] = {
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66
  };
  uint8_t mac_buf[32];
  size_t mac_len;

  psa_status_t ret;
  psa_key_id_t key_id;
  psa_key_attributes_t key_attr;

  ret = psa_crypto_init();

  // Set up attributes for a HMAC key
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_HMAC);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_VERIFY_MESSAGE);
  psa_set_key_algorithm(&key_attr, PSA_ALG_HMAC(PSA_ALG_SHA_256));

  // Import a volatile plain key for HMAC
  ret = psa_import_key(&key_attr, hmac_key, sizeof(hmac_key), &key_id);

  // Calculate the HMAC MAC of a message
  // Expected HMAC MAC:
  // fb 5b 26 22 9c 20 b7 ed 86 67 06 a2 fb fa e6 7e 3f 40 4b b6 ab e7 7f f4 50 63 a4 59 a4 29 24 a4
  ret = psa_mac_compute(key_id, PSA_ALG_HMAC(PSA_ALG_SHA_256),
                        hmac_msg, sizeof(hmac_msg),
                        mac_buf, sizeof(mac_buf), &mac_len);
  // Verify the HMAC MAC of a message
  ret = psa_mac_verify(key_id, PSA_ALG_HMAC(PSA_ALG_SHA_256),
                        hmac_msg, sizeof(hmac_msg),
                        mac_buf, mac_len);

  // Destroy a volatile plain key for HMAC
  ret = psa_destroy_key(key_id);
}
```

> **Notes**: There are two ways to change the HMAC MAC length (default is `hash_alg` dependent).
> 
> 1. Replace all MAC algorithm `PSA_ALG_HMAC(hash_alg)` with `PSA_ALG_TRUNCATED_MAC(PSA_ALG_HMAC(hash_alg)`, `mac_length`) for the desired size (≥ 4) of the MAC in bytes.
> 2. Replace the MAC algorithm `PSA_ALG_HMAC(hash_alg)` in the `psa_set_key_algorithm(&key_attr, PSA_ALG_HMAC(hash_alg))` function with `PSA_ALG_AT_LEAST_THIS_LENGTH_MAC(PSA_ALG_HMAC(hash_alg), min_mac_length)` to set the minimum MAC length (≥ 4) in bytes.
> 
> Replace the MAC algorithm `PSA_ALG_HMAC(hash_alg)` in `psa_mac_compute()` and `psa_mac_verify()` with `PSA_ALG_TRUNCATED_MAC(PSA_ALG_HMAC(hash_alg), mac_length)` to set the desired MAC length `(≥ min_mac_length` and `≤ PSA_MAC_LENGTH(PSA_KEY_TYPE_HMAC, 256, PSA_ALG_HMAC(hash_alg)))` in bytes.

**CMAC (Streaming)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t cmac_key[] = {
    0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
    0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4
  };
  uint8_t cmac_msg[] = {
    0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
    0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
    0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11
  };
  uint8_t mac_buf[16];
  size_t mac_len;
  uint32_t mac_total;
  uint32_t stream_block_size = 8;

  psa_status_t ret;
  psa_key_id_t key_id;
  psa_key_attributes_t key_attr;
  psa_mac_operation_t mac_op;

  ret = psa_crypto_init();

  // Set up attributes for a CMAC key
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_AES);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_VERIFY_MESSAGE);
  psa_set_key_algorithm(&key_attr, PSA_ALG_CMAC);

  // Import a volatile plain key for CMAC
  ret = psa_import_key(&key_attr, cmac_key, sizeof(cmac_key), &key_id);

  // Stream message and calculate the CMAC MAC
  // Expected CMAC MAC: aa f3 d8 f1 de 56 40 c2 32 f5 b1 69 b9 c9 11 e6
  mac_op = psa_mac_operation_init();
  ret = psa_mac_sign_setup(&mac_op, key_id, PSA_ALG_CMAC);
  mac_total = 0;        // Streaming block
  while ((sizeof(cmac_msg) - mac_total) > stream_block_size) {
    ret = psa_mac_update(&mac_op, cmac_msg + mac_total, stream_block_size);
    mac_total += stream_block_size;
  }
  ret = psa_mac_update(&mac_op, cmac_msg + mac_total, sizeof(cmac_msg) - mac_total);
  ret = psa_mac_sign_finish(&mac_op, mac_buf, sizeof(mac_buf), &mac_len);

  // Stream message and verify the CMAC MAC
  mac_op = psa_mac_operation_init();
  ret = psa_mac_verify_setup(&mac_op, key_id, PSA_ALG_CMAC);
  mac_total = 0;        // Streaming block
  while ((sizeof(cmac_msg) - mac_total) > stream_block_size) {
    ret = psa_mac_update(&mac_op, cmac_msg + mac_total, stream_block_size);
    mac_total += stream_block_size;
  }
  ret = psa_mac_update(&mac_op, cmac_msg + mac_total, sizeof(cmac_msg) - mac_total);
  ret = psa_mac_verify_finish(&mac_op, mac_buf, mac_len);

  // Destroy a volatile plain key for CMAC
  ret = psa_destroy_key(key_id);
}
```

> **Notes**: There are two ways to change the CMAC MAC length (default 16 bytes).
> 
> 1. Replace all MAC algorithm `PSA_ALG_CMAC` with `PSA_ALG_TRUNCATED_MAC(PSA_ALG_CMAC, mac_length)` for the desired size (≥ 4) of the MAC in bytes.
> 2. Replace the MAC algorithm `PSA_ALG_CMAC` in the `psa_set_key_algorithm(&key_attr, PSA_ALG_CMAC)` function with `PSA_ALG_AT_LEAST_THIS_LENGTH_MAC(PSA_ALG_CMAC, min_mac_length)` to set the minimum MAC length (≥ 4) in bytes.
> 
> Replace the MAC algorithm `PSA_ALG_CMAC` in `psa_mac_sign_setup()` and `psa_mac_verify_setup()` with `PSA_ALG_TRUNCATED_MAC(PSA_ALG_CMAC, mac_length)` to set the desired MAC length `(≥ min_mac_length and ≤ PSA_MAC_LENGTH(PSA_KEY_TYPE_AES, 256, PSA_ALG_CMAC))` in bytes.

**HMAC SHA-256 (Streaming)**

```c++
#include "psa/crypto.h"
void app_process_action(void)
{
  uint8_t hmac_key[] = {
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66
  };
  uint8_t hmac_msg[] = {
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66
  };
  uint8_t mac_buf[32];
  size_t mac_len;
  uint32_t mac_total;
  uint32_t stream_block_size = 8;

  psa_status_t ret;
  psa_key_id_t key_id;
  psa_key_attributes_t key_attr;
  psa_mac_operation_t mac_op;

  ret = psa_crypto_init();

  // Set up attributes for a HMAC key
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_HMAC);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_VERIFY_MESSAGE);
  psa_set_key_algorithm(&key_attr, PSA_ALG_HMAC(PSA_ALG_SHA_256));

  // Import a volatile plain key for HMAC
  ret = psa_import_key(&key_attr, hmac_key, sizeof(hmac_key), &key_id);

  // Stream message and calculate the HMAC MAC
  // Expected HMAC MAC:
  // fb 5b 26 22 9c 20 b7 ed 86 67 06 a2 fb fa e6 7e 3f 40 4b b6 ab e7 7f f4 50 63 a4 59 a4 29 24 a4
  mac_op = psa_mac_operation_init();
  ret = psa_mac_sign_setup(&mac_op, key_id, PSA_ALG_HMAC(PSA_ALG_SHA_256));
  mac_total = 0; // Streaming block
  while ((sizeof(hmac_msg) - mac_total) > stream_block_size) {
    ret = psa_mac_update(&mac_op, hmac_msg + mac_total, stream_block_size);
    mac_total += stream_block_size;
  }
  ret = psa_mac_update(&mac_op, hmac_msg + mac_total, sizeof(hmac_msg) - mac_total);
  ret = psa_mac_sign_finish(&mac_op, mac_buf, sizeof(mac_buf), &mac_len);

  // Stream message and verify the HMAC MAC
  mac_op = psa_mac_operation_init();
  ret = psa_mac_verify_setup(&mac_op, key_id, PSA_ALG_HMAC(PSA_ALG_SHA_256));
  mac_total = 0; // Streaming block
  while ((sizeof(hmac_msg) - mac_total) > stream_block_size) {
    ret = psa_mac_update(&mac_op, hmac_msg + mac_total, stream_block_size);
    mac_total += stream_block_size;
  }
  ret = psa_mac_update(&mac_op, hmac_msg + mac_total, sizeof(hmac_msg) - mac_total);
  ret = psa_mac_verify_finish(&mac_op, mac_buf, mac_len);

  // Destroy a volatile plain key for HMAC
  ret = psa_destroy_key(key_id);
}
```

> **Notes**: There are two ways to change the HMAC MAC length (default is `hash_alg` dependent).
> 
> 1. Replace all MAC algorithm `PSA_ALG_HMAC(hash_alg)` with `PSA_ALG_TRUNCATED_MAC(PSA_ALG_HMAC(hash_alg), mac_length)` for the desired size (≥ 4) of the MAC in bytes.
> 2. Replace the MAC algorithm `PSA_ALG_HMAC(hash_alg)` in the `psa_set_key_algorithm(&key_attr, PSA_ALG_HMAC(hash_alg))` function with `PSA_ALG_AT_LEAST_THIS_LENGTH_MAC(PSA_ALG_HMAC(hash_alg), min_mac_length)` to set the minimum MAC length (≥ 4) in bytes.
> 
> Replace the MAC algorithm `PSA_ALG_HMAC(hash_alg)` in `psa_mac_sign_setup()` and `psa_mac_verify_setup()` with `PSA_ALG_TRUNCATED_MAC(PSA_ALG_HMAC(hash_alg), mac_length)` to set the desired MAC length `(≥ min_mac_length and ≤ PSA_MAC_LENGTH(PSA_KEY_TYPE_HMAC, 256, PSA_ALG_HMAC(hash_alg)))` in bytes.

**PSA Crypto Platform Example**

Click the View Project Documentation link to open the readme file.

![image24](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image24.png)

The following table describes the implementation status of the PSA Crypto MAC platform example.

<table>
    <thead>
        <tr>
            <th><strong>Algorithm</strong></th>
            <th><strong>Series 1</strong></th>
            <th><strong>Series 2 - VSE</strong></th>
            <th><strong>Series 2 - HSE</strong></th>
            <th>Remark</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>HMAC</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>
                    - Hardware acceleration only on Series 2 devices.<br>
                    - HMAC streaming with wrapped key is not supported yet.
                </p>
            </td>
        </tr>
        <tr>
            <td>
                <p>CMAC</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Y</p>
            </td>
            <td>
                <p>Series 1 devices do not support a 192-bit key.</p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**:
> 
> - The MAC platform example uses the default MAC length.
> - The single-part MAC functions are only available on GSDK v4.0.0 and higher.

###### Unauthenticated Ciphers (heading level 7)

The unauthenticated cipher API is for use cases where the data integrity and authenticity are guaranteed by non-cryptographic means.

**Algorithms**

<table>
    <thead>
        <tr>
            <th><strong>Algorithm</strong></th>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>AES ECB</p>
            </td>
            <td>
                <p>
                    • <code>MBEDTLS_CIPHER_AES_128_ECB</code><br>
                    • <code>MBEDTLS_CIPHER_AES_192_ECB</code><br>
                    • <code>MBEDTLS_CIPHER_AES_256_ECB</code>
                </p>
            </td>
            <td>
                <p><code>PSA_ALG_ECB_NO_PADDING</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>AES CBC</p>
            </td>
            <td>
                <p>
                    • <code>MBEDTLS_CIPHER_AES_128_CBC</code><br>
                    • <code>MBEDTLS_CIPHER_AES_192_CBC</code><br>
                    • <code>MBEDTLS_CIPHER_AES_256_CBC</code>
                </p>
            </td>
            <td>
                <p><code>PSA_ALG_CBC_NO_PADDING</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>AES CFB</p>
            </td>
            <td>
                <p>
                    • <code>MBEDTLS_CIPHER_AES_128_CFB128</code><br>
                    • <code>MBEDTLS_CIPHER_AES_192_CFB128</code><br>
                    • <code>MBEDTLS_CIPHER_AES_256_CFB128</code>
                </p>
            </td>
            <td>
                <p><code>PSA_ALG_CFB</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>AES CTR</p>
            </td>
            <td>
                <p>
                    • <code>MBEDTLS_CIPHER_AES_128_CTR</code><br>
                    • <code>MBEDTLS_CIPHER_AES_192_CTR</code><br>
                    • <code>MBEDTLS_CIPHER_AES_256_CTR</code>
                </p>
            </td>
            <td>
                <p><code>PSA_ALG_CTR</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>CHACHA20</p>
            </td>
            <td>
                <p><code>MBEDTLS_CIPHER_CHACHA20</code></p>
            </td>
            <td>
                <p><code>PSA_ALG_STREAM_CIPHER</code></p>
            </td>
        </tr>
    </tbody>
</table>

**Key Attributes in PSA Crypto**

<table>
    <thead>
        <tr>
            <th><strong>Algorithm</strong></th>
            <th><strong>Key Type</strong></th>
            <th><strong>Key Size in Bits</strong></th>
            <th><strong>Key Usage Flag</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>PSA_ALG_ECB_NO_PADDING</code></td>
            <td rowspan="4"><code>PSA_KEY_TYPE_AES</code></td>
            <td rowspan="4">128 (16-byte)<br>192 (24-byte)<br>256 (32-byte)</td>
            <td rowspan="5"><code>PSA_KEY_USAGE_ENCRYPT</code><br><code>PSA_KEY_USAGE_DECRYPT</code></td>
        </tr>
        <tr>
            <td><code>PSA_ALG_CBC_NO_PADDING</code></td>
        </tr>
        <tr>
            <td><code>PSA_ALG_CFB</code></td>
        </tr>
        <tr>
            <td><code>PSA_ALG_CTR</code></td>
        </tr>
        <tr>
            <td><code>PSA_ALG_STREAM_CIPHER</code></td>
            <td><code>PSA_KEY_TYPE_CHACHA20</code></td>
            <td>256 (32-byte)</td>
        </tr>
    </tbody>
</table>

**Security Software Components**

|**Algorithm and Built-in Key**|**Security Software Components**|
|---|---|
|PSA_ALG_ECB_NO_PADDING|ECB Mode|
|PSA_ALG_CBC_NO_PADDING|CBC Mode|
|PSA_ALG_CFB|CFB Mode|
|PSA_ALG_CTR|CTR Mode|
|PSA_ALG_STREAM_CIPHER|Chacha20 Stream Cipher|

|**Built-in Key**|**Security Software Components**|
|---|---|
|secp256r1 keys in SE OTP|Built-In Keys|

**Single-Part Functions**

<table>
    <thead>
        <tr>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_cipher_crypt(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_aes_crypt_ecb(…)</code><br>
                    • <code>int mbedtls_aes_crypt_cbc(…)</code><br>
                    • <code>int mbedtls_aes_crypt_cfb128(…)</code><br>
                    • <code>int mbedtls_aes_crypt_ctr(…)</code><br>
                    • <code>int mbedtls_chacha20_crypt(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_cipher_encrypt(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_cipher_crypt(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_aes_crypt_ecb(…)</code><br>
                    • <code>int mbedtls_aes_crypt_cbc(…)</code><br>
                    • <code>int mbedtls_aes_crypt_cfb128(…)</code><br>
                    • <code>int mbedtls_aes_crypt_ctr(…)</code><br>
                    • <code>int mbedtls_chacha20_crypt(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_cipher_decrypt(…)</code></p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**:
> 
> - The `psa_cipher_encrypt()` encrypts a message with a random initialization vector (IV). The output of this function is the IV followed by the ciphertext. Use the multi-part operations to manage the IV and ciphertext separately.
> - The input to `psa_cipher_decrypt()` must contain the IV followed by the ciphertext, as output by` psa_cipher_encrypt()`. Use the multi-part operations to decrypt data that is not in the expected input format.

**Multi-Part Operations**

<table>
    <thead>
        <tr>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>void mbedtls_cipher_init(…)</code><br>
                    Algorithm specific<br>
                    • <code>void mbedtls_chacha20_init(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_cipher_operation_t psa_cipher_operation_init(void)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_cipher_setup(…)</code><br>
                    • <code>int mbedtls_cipher_setkey(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_chacha20_setkey(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_cipher_encrypt_setup(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_cipher_set_iv(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_chacha20_starts(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_cipher_generate_iv(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_cipher_setup(…)</code><br>
                    • <code>int mbedtls_cipher_setkey(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_chacha20_setkey(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_cipher_decrypt_setup(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_cipher_set_iv(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_chacha20_starts(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_cipher_set_iv(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_cipher_update(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_chacha20_update(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_cipher_update(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p><code>int mbedtls_cipher_finish(…)</code></p>
            </td>
            <td>
                <p><code>psa_status_t psa_cipher_finish(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>void mbedtls_cipher_free(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_chacha20_free(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_cipher_abort(…)</code></p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**: The following situations require the use of a multi-part operation:
> 
> - Processing messages that cannot be assembled in memory.
> - Using a deterministic initialization vector (IV) for unauthenticated encryption.
> - Providing the IV separately for unauthenticated encryption or decryption.

**Quick Reference Examples**

**AES ECB (One-shot)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t aes_ecb_key[] = {
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
  };
  uint8_t plain_msg_buf[] = {
    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
  };
  uint8_t cipher_buf[16];
  size_t out_len;

  psa_status_t ret;
  psa_key_id_t key_id;
  psa_key_attributes_t key_attr;
  psa_cipher_operation_t cipher_op;

  ret = psa_crypto_init();

  // Set up attributes for a AES ECB key
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_AES);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
  psa_set_key_algorithm(&key_attr, PSA_ALG_ECB_NO_PADDING);

  // Import a volatile plain key for AES ECB
  ret = psa_import_key(&key_attr, aes_ecb_key, sizeof(aes_ecb_key), &key_id);
  
  // AES ECB encryption and decryption
  // Expected ciphertext: 69 c4 e0 d8 6a 7b 04 30 d8 cd b7 80 70 b4 c5 5a
  // Single-part
  ret = psa_cipher_encrypt(key_id, PSA_ALG_ECB_NO_PADDING,
                          plain_msg_buf, sizeof(plain_msg_buf),
                          cipher_buf, sizeof(cipher_buf), &out_len);

  ret = psa_cipher_decrypt(key_id, PSA_ALG_ECB_NO_PADDING,
                          cipher_buf, out_len,
                          plain_msg_buf, sizeof(plain_msg_buf), &out_len);

  // Multi-part
  cipher_op = psa_cipher_operation_init();
  ret = psa_cipher_encrypt_setup(&cipher_op, key_id, PSA_ALG_ECB_NO_PADDING);
  ret = psa_cipher_update(&cipher_op, plain_msg_buf, sizeof(plain_msg_buf),
                          cipher_buf, sizeof(cipher_buf), &out_len);

  ret = psa_cipher_finish(&cipher_op,
                          cipher_buf + out_len,
                          sizeof(cipher_buf) - out_len,
                          &out_len);

  cipher_op = psa_cipher_operation_init();
  ret = psa_cipher_decrypt_setup(&cipher_op, key_id, PSA_ALG_ECB_NO_PADDING);
  ret = psa_cipher_update(&cipher_op, cipher_buf, sizeof(cipher_buf),
                          plain_msg_buf, sizeof(plain_msg_buf), &out_len);

  ret = psa_cipher_finish(&cipher_op,
                          plain_msg_buf + out_len,
                          sizeof(plain_msg_buf) - out_len,
                          &out_len);

  // Destroy a volatile plain key for AES ECB
  ret = psa_destroy_key(key_id);
}
```

**AES CFB (One-shot)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t aes_cbc_key[] = {
    0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
  };
  uint8_t iv_buf[] = {
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
  };
  uint8_t plain_msg_buf[] = {
    0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a
  };
  uint8_t cipher_buf[32]; // Random IV + Ciphertext for single-part
  size_t out_len;

  psa_status_t ret;
  psa_key_id_t key_id;
  psa_key_attributes_t key_attr;
  psa_cipher_operation_t cipher_op;

  ret = psa_crypto_init();

  // Set up attributes for a AES CBC key
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_AES);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
  psa_set_key_algorithm(&key_attr, PSA_ALG_CBC_NO_PADDING);

  // Import a volatile plain key for AES CBC
  ret = psa_import_key(&key_attr, aes_cbc_key, sizeof(aes_cbc_key), &key_id);

  // AES CBC encryption and decryption
  // Single-part - Random IV generated during encryption is embedded in the ciphertext buffer
  ret = psa_cipher_encrypt(key_id, PSA_ALG_CBC_NO_PADDING,
                          plain_msg_buf, sizeof(plain_msg_buf),
                          cipher_buf, sizeof(cipher_buf), &out_len);

  ret = psa_cipher_decrypt(key_id, PSA_ALG_CBC_NO_PADDING,
                          cipher_buf, out_len,
                          plain_msg_buf, sizeof(plain_msg_buf), &out_len);

  // Multi-part
  // Expected ciphertext: 76 49 ab ac 81 19 b2 46 ce e9 8e 9b 12 e9 19 7d
  cipher_op = psa_cipher_operation_init();
  ret = psa_cipher_encrypt_setup(&cipher_op, key_id, PSA_ALG_CBC_NO_PADDING);
  ret = psa_cipher_set_iv(&cipher_op, iv_buf, sizeof(iv_buf));
  ret = psa_cipher_update(&cipher_op,
                          plain_msg_buf, sizeof(plain_msg_buf),
                          cipher_buf, sizeof(cipher_buf),
                          &out_len);
  ret = psa_cipher_finish(&cipher_op,
                          cipher_buf + out_len,
                          sizeof(cipher_buf) - out_len,
                          &out_len);

  cipher_op = psa_cipher_operation_init();
  ret = psa_cipher_decrypt_setup(&cipher_op, key_id, PSA_ALG_CBC_NO_PADDING);
  ret = psa_cipher_set_iv(&cipher_op, iv_buf, sizeof(iv_buf));
  ret = psa_cipher_update(&cipher_op,
                          cipher_buf, sizeof(plain_msg_buf),
                          plain_msg_buf, sizeof(plain_msg_buf),
                          &out_len);
  ret = psa_cipher_finish(&cipher_op,
                          plain_msg_buf + out_len,
                          sizeof(plain_msg_buf) - out_len,
                          &out_len);

  // Destroy a volatile plain key for AES CBC
  ret = psa_destroy_key(key_id);
}
```

> **Note**: The multi-part operations provide the IV separately for AES CFB encryption or decryption.

**AES CTR (One-shot)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t aes_ctr_key[] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  };
  uint8_t iv_buf[] = {
    0x22, 0x22, 0x1a, 0x70, 0x22, 0x22, 0x1a, 0x70, 0x22, 0x22, 0x1a, 0x70, 0x22, 0x22, 0x1a, 0x70
  };
  uint8_t plain_msg_buf[] = {
    0xd8, 0x65, 0xc9, 0xcd, 0xea, 0x33, 0x56, 0xc5, 0x48, 0x8e, 0x7b, 0xa1, 0x5e, 0x84, 0xf4, 0xeb,
    0xa3, 0xb8, 0x25, 0x9c, 0x05, 0x3f, 0x24, 0xce, 0x29, 0x67, 0x22, 0x1c, 0x00, 0x38, 0x84, 0xd7,
    0x9d, 0x4c, 0xa4, 0x87, 0x7f, 0xfa, 0x4b, 0xc6, 0x87, 0xc6, 0x67, 0xe5, 0x49, 0x5b, 0xcf, 0xec,
    0x12, 0xf4, 0x87, 0x17, 0x32, 0xaa, 0xe4, 0x5a, 0x11, 0x06, 0x76, 0x11, 0x3d, 0xf9, 0xe7, 0xda
  };
  uint8_t cipher_buf[80]; // Random IV + Ciphertext for single-part
  size_t out_len;

  psa_status_t ret;
  psa_key_id_t key_id;
  psa_key_attributes_t key_attr;
  psa_cipher_operation_t cipher_op;

  ret = psa_crypto_init();

  // Set up attributes for a AES CTR key
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_AES);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
  psa_set_key_algorithm(&key_attr, PSA_ALG_CTR);

  // Import a volatile plain key for AES CTR
  ret = psa_import_key(&key_attr, aes_ctr_key, sizeof(aes_ctr_key), &key_id);

  // AES CTR encryption and decryption
  // Single-part - Random IV generated during encryption is embedded in the ciphertext buffer
  ret = psa_cipher_encrypt(key_id, PSA_ALG_CTR,
                          plain_msg_buf, sizeof(plain_msg_buf),
                          cipher_buf, sizeof(cipher_buf), &out_len);

  ret = psa_cipher_decrypt(key_id, PSA_ALG_CTR,
                          cipher_buf, out_len,
                          plain_msg_buf, sizeof(plain_msg_buf), &out_len);
  // Multi-part
  // Expected ciphertext:
  // b6 72 f2 af 6a cc 20 ae ee 1a d8 14 12 8c 31 8b 95 5b be 80 5b 38 92 49 89 76 00 f5 20 74 54 32
  // 7d 6d 0f b4 ac 0a 94 f3 7c a0 9e 45 05 33 98 fe a8 9c 20 0a d3 58 12 6d 9e 89 a4 05 26 5c 96 e7
  cipher_op = psa_cipher_operation_init();
  ret = psa_cipher_encrypt_setup(&cipher_op, key_id, PSA_ALG_CTR);
  ret = psa_cipher_set_iv(&cipher_op, iv_buf, sizeof(iv_buf));
  ret = psa_cipher_update(&cipher_op,
                          plain_msg_buf, sizeof(plain_msg_buf),
                          cipher_buf, sizeof(cipher_buf),
                          &out_len);
  ret = psa_cipher_finish(&cipher_op,
                          cipher_buf + out_len,
                          sizeof(cipher_buf) - out_len,
                          &out_len);

  cipher_op = psa_cipher_operation_init();
  ret = psa_cipher_decrypt_setup(&cipher_op, key_id, PSA_ALG_CTR);
  ret = psa_cipher_set_iv(&cipher_op, iv_buf, sizeof(iv_buf));
  ret = psa_cipher_update(&cipher_op,
                          cipher_buf, sizeof(plain_msg_buf),
                          plain_msg_buf, sizeof(plain_msg_buf),
                          &out_len);
  ret = psa_cipher_finish(&cipher_op,
                          plain_msg_buf + out_len,
                          sizeof(plain_msg_buf) - out_len,
                          &out_len);
  // Destroy a volatile plain key for AES CTR
  ret = psa_destroy_key(key_id);
}
```

> **Note**: The multi-part operations provide the IV separately for AES CTR encryption or decryption.

**CHACHA20 (One-shot)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t chacha20_key[32] = {0};
  uint8_t iv_buf[12] = {0};
  uint8_t plain_msg_buf[64] = {0};
  uint8_t cipher_buf[76];         // Random IV + Ciphertext for single-part
  size_t out_len;

  psa_status_t ret;
  psa_key_id_t key_id;
  psa_key_attributes_t key_attr;
  psa_cipher_operation_t cipher_op;

  ret = psa_crypto_init();

  // Set up attributes for a CHACHA20 key
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_CHACHA20);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
  psa_set_key_algorithm(&key_attr, PSA_ALG_STREAM_CIPHER);

  // Import a volatile plain key for CHACHA20
  ret = psa_import_key(&key_attr, chacha20_key, sizeof(chacha20_key), &key_id);

  // CHACHA20 encryption and decryption
  // Single-part - Random IV generated during encryption is embedded in the ciphertext buffer
  ret = psa_cipher_encrypt(key_id, PSA_ALG_STREAM_CIPHER,
                          plain_msg_buf, sizeof(plain_msg_buf),
                          cipher_buf, sizeof(cipher_buf), &out_len);

  ret = psa_cipher_decrypt(key_id, PSA_ALG_STREAM_CIPHER,
                          cipher_buf, out_len,
                          plain_msg_buf, sizeof(plain_msg_buf), &out_len);

  // Multi-part
  // Expected ciphertext:
  // 76 b8 e0 ad a0 f1 3d 90 40 5d 6a e5 53 86 bd 28 bd d2 19 b8 a0 8d ed 1a a8 36 ef cc 8b 77 0d c7
  // da 41 59 7c 51 57 48 8d 77 24 e0 3f b8 d8 4a 37 6a 43 b8 f4 15 18 a1 1c c3 87 b6 69 b2 ee 65 86
  cipher_op = psa_cipher_operation_init();
  ret = psa_cipher_encrypt_setup(&cipher_op, key_id, PSA_ALG_STREAM_CIPHER);
  ret = psa_cipher_set_iv(&cipher_op, iv_buf, sizeof(iv_buf));
  ret = psa_cipher_update(&cipher_op,
                          plain_msg_buf, sizeof(plain_msg_buf),
                          cipher_buf, sizeof(cipher_buf),
                          &out_len);
  ret = psa_cipher_finish(&cipher_op,
                          cipher_buf + out_len,
                          sizeof(cipher_buf) - out_len,
                          &out_len);

  cipher_op = psa_cipher_operation_init();
  ret = psa_cipher_decrypt_setup(&cipher_op, key_id, PSA_ALG_STREAM_CIPHER);
  ret = psa_cipher_set_iv(&cipher_op, iv_buf, sizeof(iv_buf));
  ret = psa_cipher_update(&cipher_op,
                          cipher_buf, sizeof(plain_msg_buf),
                          plain_msg_buf, sizeof(plain_msg_buf),
                          &out_len);
  ret = psa_cipher_finish(&cipher_op,
                          plain_msg_buf + out_len,
                          sizeof(plain_msg_buf) - out_len,
                          &out_len);

  // Destroy a volatile plain key for CHACHA20
  ret = psa_destroy_key(key_id);
}
```

> **Note**: The multi-part operations provide the IV separately for CHACHA20 encryption or decryption.

**AES CTR (Streaming)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
 uint8_t aes_ctr_key[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 };
 uint8_t iv_buf[] = {
   0x22, 0x22, 0x1a, 0x70, 0x22, 0x22, 0x1a, 0x70, 0x22, 0x22, 0x1a, 0x70, 0x22, 0x22, 0x1a, 0x70
 };
 uint8_t plain_msg_buf[] = {
  0xd8, 0x65, 0xc9, 0xcd, 0xea, 0x33, 0x56, 0xc5, 0x48, 0x8e, 0x7b, 0xa1, 0x5e, 0x84, 0xf4, 0xeb,
  0xa3, 0xb8, 0x25, 0x9c, 0x05, 0x3f, 0x24, 0xce, 0x29, 0x67, 0x22, 0x1c, 0x00, 0x38, 0x84, 0xd7,
  0x9d, 0x4c, 0xa4, 0x87, 0x7f, 0xfa, 0x4b, 0xc6, 0x87, 0xc6, 0x67, 0xe5, 0x49, 0x5b, 0xcf, 0xec,
  0x12, 0xf4, 0x87, 0x17, 0x32, 0xaa, 0xe4, 0x5a, 0x11, 0x06, 0x76, 0x11, 0x3d, 0xf9, 0xe7, 0xda
 };
 uint8_t cipher_buf[64];
 size_t out_len;
 uint32_t out_total;
 uint32_t stream_block_size = 14;                 // Block size for streaming

 psa_status_t ret;
 psa_key_id_t key_id;
 psa_key_attributes_t key_attr;
 psa_cipher_operation_t cipher_op;

 ret = psa_crypto_init();

 // Set up attributes for a AES CTR key
 key_attr = psa_key_attributes_init();
 psa_set_key_type(&key_attr, PSA_KEY_TYPE_AES);
 psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
 psa_set_key_algorithm(&key_attr, PSA_ALG_CTR);

 // Import a volatile plain key for AES CTR
 ret = psa_import_key(&key_attr, aes_ctr_key, sizeof(aes_ctr_key), &key_id);

 // AES CTR stream encryption and decryption
 // Expected ciphertext:
 // b6 72 f2 af 6a cc 20 ae ee 1a d8 14 12 8c 31 8b 95 5b be 80 5b 38 92 49 89 76 00 f5 20 74 54 32
 // 7d 6d 0f b4 ac 0a 94 f3 7c a0 9e 45 05 33 98 fe a8 9c 20 0a d3 58 12 6d 9e 89 a4 05 26 5c 96 e7
 cipher_op = psa_cipher_operation_init();
 ret = psa_cipher_encrypt_setup(&cipher_op, key_id, PSA_ALG_CTR);
 ret = psa_cipher_set_iv(&cipher_op, iv_buf, sizeof(iv_buf));

 out_total = 0; // Streaming block
 while ((sizeof(plain_msg_buf) - out_total) > stream_block_size) {
   ret = psa_cipher_update(&cipher_op,
                            plain_msg_buf + out_total, stream_block_size,
                            cipher_buf + out_total, sizeof(cipher_buf) - out_total,
                            &out_len);
  out_total += out_len;
 }
  ret = psa_cipher_update(&cipher_op, // Last block
                          plain_msg_buf + out_total, sizeof(plain_msg_buf) - out_total,
                          cipher_buf + out_total, sizeof(cipher_buf) - out_total,
                          &out_len);
  out_total += out_len;

 ret = psa_cipher_finish(&cipher_op,
                        cipher_buf + out_total,
                        sizeof(cipher_buf) - out_total,
                        &out_len);

 cipher_op = psa_cipher_operation_init();
 ret = psa_cipher_decrypt_setup(&cipher_op, key_id, PSA_ALG_CTR);
 ret = psa_cipher_set_iv(&cipher_op, iv_buf, sizeof(iv_buf));

 out_total = 0; // Streaming block
 while ((sizeof(cipher_buf) - out_total) > stream_block_size) {
  ret = psa_cipher_update(&cipher_op,
                          cipher_buf + out_total, stream_block_size,
                          plain_msg_buf + out_total, sizeof(plain_msg_buf) - out_total,
                          &out_len);
  out_total += out_len;
 }

  ret = psa_cipher_update(&cipher_op, // Last block
                          cipher_buf + out_total, sizeof(cipher_buf) - out_total,
                          plain_msg_buf + out_total, sizeof(plain_msg_buf) - out_total,
                          &out_len);
  out_total += out_len;

  ret = psa_cipher_finish(&cipher_op,
                          plain_msg_buf + out_total,
                          sizeof(plain_msg_buf) - out_total,
                          &out_len);

 // Destroy a volatile plain key for AES CTR
 ret = psa_destroy_key(key_id);
}
```

**AES CTR with Built-in AES-128 Key (HSE only)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t iv_buf[16] = {0};
  uint8_t plain_msg_buf[16] = {0};
  uint8_t cipher_buf[32]; // Random IV + Ciphertext for single-part
  size_t out_len;

  psa_status_t ret;
  psa_cipher_operation_t cipher_op;

  ret = psa_crypto_init();

  // AES CTR encryption and decryption with built-in AES-128 key
  // ret = -140 (PSA_ERROR_DOES_NOT_EXIST) if the AES-128 key has not been provisioned
  // Single-part - Random IV generated during encryption is embedded in the ciphertext buffer
  ret = psa_cipher_encrypt(SL_SE_BUILTIN_KEY_AES128_ID, PSA_ALG_CTR,
                          plain_msg_buf, sizeof(plain_msg_buf),
                          cipher_buf, sizeof(cipher_buf), &out_len);

  ret = psa_cipher_decrypt(SL_SE_BUILTIN_KEY_AES128_ID, PSA_ALG_CTR,
                          cipher_buf, out_len,
                          plain_msg_buf, sizeof(plain_msg_buf), &out_len);

  // Multi-part
  // Built-in AES-128 key: 81 a5 e2 1f a1 52 86 f1 df 44 5c 2c c1 20 fa 3f
  // Expected ciphertext: 66 d2 0f 99 65 3e a8 d0 83 05 a6 39 d4 4e 98 a6
  cipher_op = psa_cipher_operation_init();
  ret = psa_cipher_encrypt_setup(&cipher_op, SL_SE_BUILTIN_KEY_AES128_ID, PSA_ALG_CTR);
  ret = psa_cipher_set_iv(&cipher_op, iv_buf, sizeof(iv_buf));
  ret = psa_cipher_update(&cipher_op,
                          plain_msg_buf, sizeof(plain_msg_buf),
                          cipher_buf, sizeof(cipher_buf),
                          &out_len);
  ret = psa_cipher_finish(&cipher_op,
                          cipher_buf + out_len,
                          sizeof(cipher_buf) - out_len,
                          &out_len);

  cipher_op = psa_cipher_operation_init();
  ret = psa_cipher_decrypt_setup(&cipher_op, SL_SE_BUILTIN_KEY_AES128_ID, PSA_ALG_CTR);
  ret = psa_cipher_set_iv(&cipher_op, iv_buf, sizeof(iv_buf));
  ret = psa_cipher_update(&cipher_op,
                          cipher_buf, sizeof(plain_msg_buf),
                          plain_msg_buf, sizeof(plain_msg_buf),
                          &out_len);
  ret = psa_cipher_finish(&cipher_op,
                          plain_msg_buf + out_len,
                          sizeof(plain_msg_buf) - out_len,
                          &out_len);
}
```

**PSA Crypto Platform Example**

Click the `View Project Documentation` link to open the `readme` file.

![image25](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image25.png)

The following table describes the implementation status of the PSA Crypto cipher platform example.

|**Algorithm/Key**|**Series 1**|**Series 2 - VSE**|**Series 2 - HSE**|**Remark**|
|---|---|---|---|---|
|AES ECB|Y|Y|Y|Series 1 devices do not support a 192-bit key.|
|AES CBC|Y|Y|Y|Series 1 devices do not support a 192-bit key.|
|AES CFB|Y|Y|Y|Series 1 devices do not support a 192-bit key.|
|AES CTR|Y|Y|Y|Series 1 devices do not support a 192-bit key.|
|CHACHA20|Y|Y|Y|Hardware acceleration only on HSE-SVH devices.|
|AES-128 Key|—|—|Y|—|

> **Note**: The single-part unauthenticated cipher functions are only available on GSDK v4.0.0 and higher.

###### Authenticated Encryption with Associated Data (AEAD) (heading level 7)

The authenticated encryption with associated data (AEAD) is a form of encryption that simultaneously assures the confidentiality and authenticity of data.

**Algorithms**

<table>
    <thead>
        <tr>
            <th><strong>Algorithm</strong></th>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>AES GCM</p>
            </td>
            <td>
                <p>
                    • MBEDTLS_CIPHER_AES_128_GCM<br>
                    • MBEDTLS_CIPHER_AES_192_GCM<br>
                    • MBEDTLS_CIPHER_AES_256_GCM
                </p>
            </td>
            <td>
                <p>
                    • PSA_ALG_GCM<br>
                    • PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, tag_length)<br>
                    • PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(PSA_ALG_GCM)<br>
                    • PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_GCM, min_tag_length)
                </p>
            </td>
        </tr>
        <tr>
            <td>
                <p>AES CCM</p>
            </td>
            <td>
                <p>
                    • MBEDTLS_CIPHER_AES_128_CCM<br>
                    • MBEDTLS_CIPHER_AES_192_CCM<br>
                    • MBEDTLS_CIPHER_AES_256_CCM
                </p>
            </td>
            <td>
                <p>
                    • PSA_ALG_CCM<br>
                    • PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, tag_length)<br>
                    • PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(PSA_ALG_CCM)<br>
                    • PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_CCM, min_tag_length)
                </p>
            </td>
        </tr>
        <tr>
            <td>
                <p>CHACHA20_POLY1305</p>
            </td>
            <td>
                <p>MBEDTLS_CIPHER_CHACHA20_POLY1305</p>
            </td>
            <td>
                <p>PSA_ALG_CHACHA20_POLY1305</p>
            </td>
        </tr>
    </tbody>
</table>

|**Algorithm**|**Nonce Length (Bytes)**|**Authentication Tag Length (Bytes)**|
|---|---|---|
|AES GCM|1 - 16 (Default 12)|4, 8, 12, 13, 14, 15, and 16 (Default)|
|AES CCM|7 - 13|4, 6, 8, 10, 12, 14, and 16 (Default)|
|CHACHA20_POLY1305|12|16|

**Key Attributes in PSA Crypto**

<table>
    <thead>
        <tr>
            <th><strong>Algorithm</strong></th>
            <th><strong>Key Type</strong></th>
            <th><strong>Key Size in Bits</strong></th>
            <th><strong>Key Usage Flag</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <code>PSA_ALG_GCM</code> or<br>
                <code>PSA_ALG_AEAD_WITH_*(PSA_ALG_GCM…)</code>
            </td>
            <td rowspan="2"><code>PSA_KEY_TYPE_AES</code></td>
            <td>128 (16-byte)<br>192 (24-byte)<br>256 (32-byte)</td>
            <td rowspan="3">
                <code>PSA_KEY_USAGE_ENCRYPT</code><br>
                <code>PSA_KEY_USAGE_DECRYPT</code>
            </td>
        </tr>
        <tr>
            <td>
                <code>PSA_ALG_CCM</code> or<br>
                <code>PSA_ALG_AEAD_WITH_*(PSA_ALG_CCM…)</code>
            </td>
        </tr>
        <tr>
            <td><code>PSA_ALG_CHACHA20_POLY1305</code></td>
            <td><code>PSA_KEY_TYPE_CHACHA20</code></td>
            <td>256 (32-byte)</td>
        </tr>
    </tbody>
</table>

**Security Software Components**

|**Algorithm**|**Security Software Components**|
|---|---|
|PSA_ALG_GCM or PSA_ALG_AEAD_WITH_*(PSA_ALG_GCM…)|GCM (12-byte IV) or GCM with Non-Recommended IV Lengths|
|PSA_ALG_CCM or PSA_ALG_AEAD_WITH_*(PSA_ALG_CCM…)|CCM Mode|
|PSA_ALG_CHACHA20_POLY1305|ChachaPoly|

**Single-Part Functions**

<table>
    <thead>
        <tr>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_cipher_auth_encrypt(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_ccm_encrypt_and_tag(…)</code><br>
                    • <code>int mbedtls_gcm_crypt_and_tag(…)</code><br>
                    • <code>int mbedtls_chachapoly_encrypt_and_tag(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_aead_encrypt(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_cipher_auth_decrypt(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_ccm_auth_decrypt(…)</code><br>
                    • <code>int mbedtls_gcm_auth_decrypt(…)</code><br>
                    • <code>int mbedtls_chachapoly_auth_decrypt(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_aead_decrypt(…)</code></p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**: The single-part functions use one buffer for the ciphertext and AEAD authentication tag.

**Multi-Part Operations**

<table>
    <thead>
        <tr>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>void mbedtls_cipher_init(…)</code><br>
                    Algorithm specific<br>
                    • <code>void mbedtls_gcm_init(…)</code><br>
                    • <code>void mbedtls_chachapoly_init(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_aead_operation_t psa_aead_operation_init(void)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_cipher_setup(…)</code><br>
                    • <code>int mbedtls_cipher_setkey(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_gcm_setkey(…)</code><br>
                    • <code>int mbedtls_chachapoly_setkey(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_aead_encrypt_setup(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_cipher_set_iv(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_gcm_starts(…)</code><br>
                    • <code>int mbedtls_chachapoly_starts(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_aead_generate_nonce(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>—</p>
            </td>
            <td>
                <p><code>psa_status_t psa_aead_set_lengths(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_cipher_setup(…)</code><br>
                    • <code>int mbedtls_cipher_setkey(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_gcm_setkey(…)</code><br>
                    • <code>int mbedtls_chachapoly_setkey(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_aead_decrypt_setup(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_cipher_set_iv(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_gcm_starts(…)</code><br>
                    • <code>int mbedtls_chachapoly_starts(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_aead_set_nonce(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_cipher_update_ad(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_gcm_starts(…)</code><br>
                    • <code>int mbedtls_chachapoly_update_aad(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_aead_update_ad(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_cipher_update(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_gcm_update(…)</code><br>
                    • <code>int mbedtls_chachapoly_update(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_aead_update(…)</code></p>
            </td>
        </tr>
    </tbody>
</table>

<table>
    <thead>
        <tr>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>int mbedtls_cipher_finish(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_gcm_finish(…)</code><br>
                    • <code>int mbedtls_chachapoly_finish(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_aead_finish(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>—</p>
            </td>
            <td>
                <p><code>psa_status_t psa_aead_verify(…)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>
                    Generic<br>
                    • <code>void mbedtls_cipher_free(…)</code><br>
                    Algorithm specific<br>
                    • <code>int mbedtls_gcm_free(…)</code><br>
                    • <code>int mbedtls_chachapoly_free(…)</code>
                </p>
            </td>
            <td>
                <p><code>psa_status_t psa_aead_abort(…)</code></p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**:
> 
> - For `PSA_ALG_CCM`, calling` psa_aead_set_lengths()` is required.
> - For the other AEAD algorithms, calling `psa_aead_set_lengths()` is not required.
> - The following situations require the use of a multi-part operation:
> - Processing messages that cannot be assembled in memory.
> - Separating the AEAD authentication tag from the ciphertext.

**Quick Reference Examples**

**AES CCM (One-shot)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t key_buf[] = {
    0xea, 0x4f, 0x6f, 0x3c, 0x2f, 0xed, 0x2b, 0x9d, 0xd9, 0x70, 0x8c, 0x2e, 0x72, 0x1a, 0xe0, 0x0f
  };
  uint8_t nonce_buf[] = {0xf9, 0x75, 0x80, 0x9d, 0xdb, 0x51, 0x72, 0x38, 0x27, 0x45, 0x63, 0x4f};
  uint8_t ad_buf[] = {0x5c, 0x65, 0xd4, 0xf2, 0x61, 0xd2, 0xc5, 0x4f, 0xfe, 0x6a};
  uint8_t plain_msg_buf[] = {0x8d, 0x6c, 0x08, 0x44, 0x6c, 0xb1, 0x0d, 0x9a, 0x20, 0x75};
  uint8_t cipher_tag_buf[32];       // Ciphertext + Tag
  size_t out_len;

  psa_status_t ret;
  psa_key_id_t key_id;
  psa_key_attributes_t key_attr;

  ret = psa_crypto_init();

  // Set up attributes for a AES CCM key
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_AES);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
  psa_set_key_algorithm(&key_attr, PSA_ALG_CCM);

  // Import a volatile plain key for AES CCM
  ret = psa_import_key(&key_attr, key_buf, sizeof(key_buf), &key_id);

  // AES CCM encryption and descryption
  // Expected ciphertext: e2 2f 37 3b eb f6 4a 3e 9b 87
  // Expected tag: 75 2b f9 db 34 dc 4d 43 3f 00 f5 5c 3f 53 0c 89
  ret = psa_aead_encrypt(key_id, PSA_ALG_CCM,
                        nonce_buf, sizeof(nonce_buf),
                        ad_buf, sizeof(ad_buf),
                        plain_msg_buf, sizeof(plain_msg_buf),
                        cipher_tag_buf, sizeof(cipher_tag_buf),
                        &out_len);
  ret = psa_aead_decrypt(key_id, PSA_ALG_CCM,
                        nonce_buf, sizeof(nonce_buf),
                        ad_buf, sizeof(ad_buf),
                        cipher_tag_buf, out_len,
                        plain_msg_buf, sizeof(plain_msg_buf),
                        &out_len);

  // Destroy a volatile plain key for AES CCM
  ret = psa_destroy_key(key_id);
}
```

> **Notes**:
> 
> - There are two ways to change the CCM authentication tag length (default 16 bytes).
>   1. Replace all AEAD algorithm `PSA_ALG_CCM` with `PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, tag_length)` for the desired size of the authentication tag in bytes.
>   2. Replace the AEAD algorithm `PSA_ALG_CCM` in the `psa_set_key_algorithm(&key_attr, PSA_ALG_CCM)` function with `PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_CCM, min_tag_length)` to set the minimum authentication tag length in bytes. Replace the AEAD algorithm `PSA_ALG_CCM in psa_aead_encrypt()` and `psa_aead_decrypt()` with `PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, tag_length)` to set the desired tag length `(≥ min_tag_length and ≤ PSA_AEAD_TAG_LENGTH(PSA_KEY_TYPE_AES, 256, PSA_ALG_CCM))` in bytes.

**AES GCM (One-shot)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t key_buf[] = {
    0xea, 0x4f, 0x6f, 0x3c, 0x2f, 0xed, 0x2b, 0x9d, 0xd9, 0x70, 0x8c, 0x2e, 0x72, 0x1a, 0xe0, 0x0f
  };
  uint8_t nonce_buf[] = {0xf9, 0x75, 0x80, 0x9d, 0xdb, 0x51, 0x72, 0x38, 0x27, 0x45, 0x63, 0x4f};
  uint8_t ad_buf[] = {0x5c, 0x65, 0xd4, 0xf2, 0x61, 0xd2, 0xc5, 0x4f, 0xfe, 0x6a};
  uint8_t plain_msg_buf[] = {0x8d, 0x6c, 0x08, 0x44, 0x6c, 0xb1, 0x0d, 0x9a, 0x20, 0x75};
  uint8_t cipher_tag_buf[32]; // Ciphertext + Tag
  size_t out_len;

  psa_status_t ret;
  psa_key_id_t key_id;
  psa_key_attributes_t key_attr;

  ret = psa_crypto_init();

  // Set up attributes for a AES GCM key
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_AES);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
  psa_set_key_algorithm(&key_attr, PSA_ALG_GCM);

  // Import a volatile plain key for AES GCM
  ret = psa_import_key(&key_attr, key_buf, sizeof(key_buf), &key_id);

  // AES GCM encryption and decryption
  // Expected ciphertext: 0f 51 f7 a8 3c 5b 5a a7 96 b9
  // Expected tag: 70 25 9c dd fe 8f 9a 15 a5 c5 eb 48 5a f5 78 fb
  ret = psa_aead_encrypt(key_id, PSA_ALG_GCM,
                          nonce_buf, sizeof(nonce_buf),
                          ad_buf, sizeof(ad_buf),
                          plain_msg_buf, sizeof(plain_msg_buf),
                          cipher_tag_buf, sizeof(cipher_tag_buf),
                          &out_len);

  ret = psa_aead_decrypt(key_id, PSA_ALG_GCM,
                          nonce_buf, sizeof(nonce_buf),
                          ad_buf, sizeof(ad_buf),
                          cipher_tag_buf, out_len,
                          plain_msg_buf, sizeof(plain_msg_buf),
                          &out_len);
                          
  // Destroy a volatile plain key for AES GCM
  ret = psa_destroy_key(key_id);
}
```

> **Notes**:
> 
> - There are two ways to change the GCM authentication tag length (default 16 bytes).
>   1. Replace all AEAD algorithm `PSA_ALG_GCM` with `PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, tag_length)` for the desired size of the authentication tag in bytes.
>   2. Replace the AEAD algorithm `PSA_ALG_GCM in the psa_set_key_algorithm(&key_attr, PSA_ALG_GCM)` function with `PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_GCM, min_tag_length)` to set the minimum authentication tag length in bytes. Replace the AEAD algorithm `PSA_ALG_GCM in psa_aead_encrypt()` and `psa_aead_decrypt()` with `PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, tag_length)` to set the desired tag length `(≥ min_tag_length and ≤ PSA_AEAD_TAG_LENGTH(PSA_KEY_TYPE_AES, 256, PSA_ALG_GCM))` in bytes.

**CHACHA20_POLY1305 (One-shot)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t key_buf[] = {
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f
  };
  uint8_t nonce_buf[] = {0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47};
  uint8_t ad_buf[] = {0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7};
  uint8_t plain_msg_buf[] = {
    0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c,
    0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73,
    0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39, 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63,
    0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x66, 0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f,
    0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x74, 0x69, 0x70, 0x20, 0x66, 0x6f, 0x72, 0x20,
    0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x2c, 0x20, 0x73, 0x75, 0x6e, 0x73,
    0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69,
    0x74, 0x2e
  };
  uint8_t cipher_tag_buf[130]; // Ciphertext + Tag
  size_t out_len;

  psa_status_t ret;
  psa_key_id_t key_id;
  psa_key_attributes_t key_attr;
  
  ret = psa_crypto_init();

  // Set up attributes for a CHACHA20_POLY1305 key
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_CHACHA20);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
  psa_set_key_algorithm(&key_attr, PSA_ALG_CHACHA20_POLY1305);

  // Import a volatile plain key for CHACHA20_POLY1305
  ret = psa_import_key(&key_attr, key_buf, sizeof(key_buf), &key_id);

  // CHACHA20_POLY1305 encryption
  // Expected ciphertext:
  // d3 1a 8d 34 64 8e 60 db 7b 86 af bc 53 ef 7e c2 a4 ad ed 51 29 6e 08 fe a9 e2 b5 a7 36 ee 62 d6
  // 3d be a4 5e 8c a9 67 12 82 fa fb 69 da 92 72 8b 1a 71 de 0a 9e 06 0b 29 05 d6 a5 b6 7e cd 3b 36
  // 92 dd bd 7f 2d 77 8b 8c 98 03 ae e3 28 09 1b 58 fa b3 24 e4 fa d6 75 94 55 85 80 8b 48 31 d7 bc
  // 3f f4 de f0 8e 4b 7a 9d e5 76 d2 65 86 ce c6 4b 61 16
  // Expected tag: 1a e1 0b 59 4f 09 e2 6a 7e 90 2e cb d0 60 06 91
  ret = psa_aead_encrypt(key_id,
                        PSA_ALG_CHACHA20_POLY1305,
                        nonce_buf,
                        sizeof(nonce_buf),
                        ad_buf,
                        sizeof(ad_buf),
                        plain_msg_buf,
                        sizeof(plain_msg_buf),
                        cipher_tag_buf,
                        sizeof(cipher_tag_buf),
                        &out_len);

  // CHACHA20_POLY1305 decryption
  ret = psa_aead_decrypt(key_id,
                        PSA_ALG_CHACHA20_POLY1305,
                        nonce_buf,
                        sizeof(nonce_buf),
                        ad_buf,
                        sizeof(ad_buf),
                        cipher_tag_buf,
                        out_len,
                        plain_msg_buf,
                        sizeof(plain_msg_buf),
                        &out_len);

  // Destroy a volatile plain key for CHACHA20_POLY1305
  ret = psa_destroy_key(key_id);
}
```

**AES CCM (Streaming)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t key_buf[] = {
    0x9c, 0xde, 0xba, 0xee, 0xe8, 0x69, 0x0b, 0x68, 0x75, 0x10, 0x70, 0x69, 0x1f, 0x49, 0x59, 0x36,
    0x68, 0xa6, 0xde, 0x12, 0xd3, 0xa9, 0x48, 0xb3, 0x8d, 0xdb, 0xd3, 0xf7, 0x52, 0x18, 0xb2, 0xd4
  };
  uint8_t nonce_buf[] = {0xaf, 0x1a, 0x97, 0xd4, 0x31, 0x51, 0xf5, 0xea, 0x9c, 0x48, 0xad, 0x36, 0xa3};
  uint8_t ad_buf[] = {
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
    0x10, 0x11, 0x12, 0x13
  };
  uint8_t plain_msg_buf[] = {
    0x3c, 0xbb, 0x08, 0xf1, 0x33, 0x27, 0x0e, 0x44, 0x54, 0xbc, 0xaa, 0xa0, 0xf2, 0x0f, 0x6d, 0x63,
    0xc3, 0x8b, 0x65, 0x72, 0xe7, 0x66
  };
  uint8_t cipher_buf[22];
  uint8_t tag_buf[16];
  size_t tag_len;
  size_t out_len;
  uint32_t out_total;
  uint32_t stream_cnt;
  uint32_t stream_block_size = 14;                      // Block size for streaming

  psa_status_t ret;
  psa_key_id_t key_id;
  psa_key_attributes_t key_attr;
  psa_aead_operation_t aead_op;

  ret = psa_crypto_init();

  // Set up attributes for a AES CCM key
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_AES);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
  psa_set_key_algorithm(&key_attr, PSA_ALG_CCM);

  // Import a volatile plain key for AES CCM
  ret = psa_import_key(&key_attr, key_buf, sizeof(key_buf), &key_id);

  // AES CCM stream encryption and decryption
  // Expected ciphertext:
  // 39 66 93 0a 2a e8 fd d8 f4 0e 70 07 f3 fd e0 bd 6e b4 8a 46 e6 d2
  // Expected tag: 7c 0c 1b a4 bf d2 bd 21 5b 0c d9 21 f0 6a 8f 3b
  aead_op = psa_aead_operation_init();
  ret = psa_aead_encrypt_setup(&aead_op, key_id, PSA_ALG_CCM);
  ret = psa_aead_set_lengths(&aead_op, sizeof(ad_buf), sizeof(plain_msg_buf));
  ret = psa_aead_set_nonce(&aead_op, nonce_buf, sizeof(nonce_buf));
  ret = psa_aead_update_ad(&aead_op, ad_buf, sizeof(ad_buf));

  stream_cnt = 0;                                             // Streaming block
  out_total = 0;
  while ((sizeof(plain_msg_buf) - (stream_cnt * stream_block_size)) > stream_block_size) {
    ret = psa_aead_update(&aead_op,
                          plain_msg_buf + (stream_cnt * stream_block_size), stream_block_size,
                          cipher_buf + out_total, sizeof(cipher_buf) - out_total,
                          &out_len);
                          stream_cnt++;
    out_total += out_len;
  }
    ret = psa_aead_update(&aead_op,                         // Last block
                          plain_msg_buf + (stream_cnt * stream_block_size),
                          sizeof(plain_msg_buf) - (stream_cnt * stream_block_size),
                          cipher_buf + out_total, sizeof(cipher_buf) - out_total,
                          &out_len);
    out_total += out_len;

    ret = psa_aead_finish(&aead_op,                         // Generate tag
                          cipher_buf + out_total, sizeof(cipher_buf) - out_total,
                          &out_len,
                          tag_buf, sizeof(tag_buf),
                          &tag_len);
  out_total += out_len;

  aead_op = psa_aead_operation_init();
  ret = psa_aead_decrypt_setup(&aead_op, key_id, PSA_ALG_CCM);
  ret = psa_aead_set_lengths(&aead_op, sizeof(ad_buf), sizeof(plain_msg_buf));
  ret = psa_aead_set_nonce(&aead_op, nonce_buf, sizeof(nonce_buf));
  ret = psa_aead_update_ad(&aead_op, ad_buf, sizeof(ad_buf));

  stream_cnt = 0;                                           // Streaming block
  out_total = 0;
  while ((sizeof(cipher_buf) - (stream_cnt * stream_block_size)) > stream_block_size) {
    ret = psa_aead_update(&aead_op,
                          cipher_buf + (stream_cnt * stream_block_size), stream_block_size,
                          plain_msg_buf + out_total, sizeof(plain_msg_buf) - out_total,
                          &out_len);
                          stream_cnt++;
    out_total += out_len;
  }

    ret = psa_aead_update(&aead_op,                           // Last block
                        cipher_buf + (stream_cnt * stream_block_size),
                        sizeof(cipher_buf) - (stream_cnt * stream_block_size),
                        plain_msg_buf + out_total, sizeof(plain_msg_buf) - out_total,
                        &out_len);
    out_total += out_len;
    ret = psa_aead_verify(&aead_op,                           // Verify tag
                          plain_msg_buf + out_total, sizeof(plain_msg_buf) - out_total,
                          &out_len,
                          tag_buf, sizeof(tag_buf));
    out_total += out_len;

    // Destroy a volatile plain key for AES CCM
    ret = psa_destroy_key(key_id);
}
```

**AES GCM (Streaming)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t key_buf[] = {
    0x5f, 0xe0, 0x1c, 0x4b, 0xaf, 0x01, 0xcb, 0xe0, 0x77, 0x96, 0xd5, 0xaa, 0xef, 0x6e, 0xc1, 0xf4,
    0x51, 0x93, 0xa9, 0x8a, 0x22, 0x35, 0x94, 0xae, 0x4f, 0x0e, 0xf4, 0x95, 0x2e, 0x82, 0xe3, 0x30
  };
  uint8_t nonce_buf[] = {0xbd, 0x58, 0x73, 0x21, 0x56, 0x6c, 0x7f, 0x1a, 0x5d, 0xd8, 0x65, 0x2d};
  uint8_t ad_buf[] = {
    0x90, 0x13, 0x61, 0x78, 0x17, 0xdd, 0xa9, 0x47, 0xe1, 0x35, 0xee, 0x6d, 0xd3, 0x65, 0x33, 0x82
  };
  uint8_t plain_msg_buf[] = {
    0x88, 0x1d, 0xc6, 0xc7, 0xa5, 0xd4, 0x50, 0x9f, 0x3c, 0x4b, 0xd2, 0xda, 0xab, 0x08, 0xf1, 0x65,
    0xdd, 0xc2, 0x04, 0x48, 0x9a, 0xa8, 0x13, 0x45, 0x62, 0xa4, 0xea, 0xc3, 0xd0, 0xbc, 0xad, 0x79,
    0x65, 0x84, 0x7b, 0x10, 0x27, 0x33, 0xbb, 0x63, 0xd1, 0xe5, 0xc5, 0x98, 0xec, 0xe0, 0xc3, 0xe5,
    0xda, 0xdd, 0xdd
  };
  uint8_t cipher_buf[51];
  uint8_t tag_buf[16];
  size_t tag_len;
  size_t out_len;
  uint32_t out_total;
  uint32_t stream_cnt;
  uint32_t stream_block_size = 14;                              // Block size for streaming

  psa_status_t ret;
  psa_key_id_t key_id;
  psa_key_attributes_t key_attr;
  psa_aead_operation_t aead_op;

  ret = psa_crypto_init();

  // Set up attributes for a AES GCM key
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_AES);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
  psa_set_key_algorithm(&key_attr, PSA_ALG_GCM);

  // Import a volatile plain key for AES GCM
  ret = psa_import_key(&key_attr, key_buf, sizeof(key_buf), &key_id);

  // AES GCM stream encryption and decryption
  // Expected ciphertext:
  // 16 e3 75 b4 97 3b 33 9d 3f 74 6c 1c 5a 56 8b c7 52 6e 90 9d df f1 e1 9c 95 c9 4a 6c cf f2 10 c9
  // a4 a4 06 79 de 57 60 c3 96 ac 0e 2c eb 12 34 f9 f5 fe 26
  // Expected tag: ab d3 d2 6d 65 a6 27 5f 7a 4f 56 b4 22 ac ab 49
  aead_op = psa_aead_operation_init();
  ret = psa_aead_encrypt_setup(&aead_op, key_id, PSA_ALG_GCM);
  ret = psa_aead_set_nonce(&aead_op, nonce_buf, sizeof(nonce_buf));
  ret = psa_aead_update_ad(&aead_op, ad_buf, sizeof(ad_buf));

  stream_cnt = 0; // Streaming block
  out_total = 0;
  while ((sizeof(plain_msg_buf) - (stream_cnt * stream_block_size)) > stream_block_size) {
    ret = psa_aead_update(&aead_op,
                          plain_msg_buf + (stream_cnt * stream_block_size), stream_block_size,
                          cipher_buf + out_total, sizeof(cipher_buf) - out_total,
                          &out_len);
                          stream_cnt++;
    out_total += out_len;
    }

    ret = psa_aead_update(&aead_op,                                     // Last block
                        plain_msg_buf + (stream_cnt * stream_block_size),
                        sizeof(plain_msg_buf) - (stream_cnt * stream_block_size),
                        cipher_buf + out_total, sizeof(cipher_buf) - out_total,
                        &out_len);
    out_total += out_len;

    ret = psa_aead_finish(&aead_op,                                       // Generate tag
                        cipher_buf + out_total, sizeof(cipher_buf) - out_total,
                        &out_len,
                        tag_buf, sizeof(tag_buf),
                        &tag_len);
    out_total += out_len;

  aead_op = psa_aead_operation_init();
  ret = psa_aead_decrypt_setup(&aead_op, key_id, PSA_ALG_GCM);
  ret = psa_aead_set_nonce(&aead_op, nonce_buf, sizeof(nonce_buf));
  ret = psa_aead_update_ad(&aead_op, ad_buf, sizeof(ad_buf));

  stream_cnt = 0;                                                       // Streaming block
  out_total = 0;
  while ((sizeof(cipher_buf) - (stream_cnt * stream_block_size)) > stream_block_size) {
    ret = psa_aead_update(&aead_op,
                          cipher_buf + (stream_cnt * stream_block_size), stream_block_size,
                          plain_msg_buf + out_total, sizeof(plain_msg_buf) - out_total,
                          &out_len);
                          stream_cnt++;
    out_total += out_len;
  }

    ret = psa_aead_update(&aead_op,                                         // Last block
                          cipher_buf + (stream_cnt * stream_block_size),
                          sizeof(cipher_buf) - (stream_cnt * stream_block_size),
                          plain_msg_buf + out_total, sizeof(plain_msg_buf) - out_total,
                          &out_len);
    out_total += out_len;

    ret = psa_aead_verify(&aead_op,                                         // Verify tag
                          plain_msg_buf + out_total, sizeof(plain_msg_buf) - out_total,
                          &out_len,
                          tag_buf, sizeof(tag_buf));
    out_total += out_len;

    // Destroy a volatile plain key for AES GCM
    ret = psa_destroy_key(key_id);
}
```

**PSA Crypto Platform Example**

Click the `View Project Documentation` link to open the `readme` file.

![image26](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image26.png)

The following table describes the implementation status of the PSA Crypto AEAD platform example.

|**Algorithm**|**Series 1**|**Series 2 - VSE**|**Series 2 - HSE**|**Remark**|
|---|---|---|---|---|
|AES CCM|Y|Y|Y|Series 1 devices do not support a 192-bit key.|
|AES GCM|Y|Y|Y|Series 1 devices do not support a 192-bit key.|
|CHACHA20_POLY1305|Y|Y|Y|Hardware acceleration only on HSE-SVH devices.|

> **Notes**:
> 
> - The AEAD platform example uses default nonce (12-byte for GCM) and tag length (16-byte for CCM and GCM).
> - The multi-part AEAD operations are only available on GSDK v4.2.0 and higher.
> - The multi-part AEAD operations for CHACHA20_POLY1305 are not yet implemented.
> - The multi-part AEAD operations for a shortened tag length (AES CCM and GCM) are not yet implemented.
> - The multi-part GCM operations do not support non-12-byte nonce (GCM with Non-Recommended IV Lengths).
> - The AEAD platform example for multi-part AEAD operations is pending for fully-featured multi-part AEAD drivers.

###### Key Derivation

A Key Derivation Function (KDF) derives one or many secret keys from a secret value such as a master key, a password, or a pass-phrase using a pseudo-random function. The typical usage of a key derivation function is to use a secret, such as a password or an ECDH shared secret, and a salt to produce a symmetric key and initialization vector (IV) for use with AES.

**Algorithms**

<table>
    <thead>
        <tr>
            <th><strong>Algorithm</strong></th>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>HKDF (SHA-1)</p>
            </td>
            <td>
                <p><code>MBEDTLS_MD_SHA1</code></p>
            </td>
            <td>
                <p><code>PSA_ALG_HKDF(PSA_ALG_SHA_1)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>HKDF (SHA-2)</p>
            </td>
            <td>
                <p>
                    • <code>MBEDTLS_MD_SHA224</code><br>
                    • <code>MBEDTLS_MD_SHA256</code><br>
                    • <code>MBEDTLS_MD_SHA384</code><br>
                    • <code>MBEDTLS_MD_SHA512</code>
                </p>
            </td>
            <td>
                <p>
                    • <code>PSA_ALG_HKDF(PSA_ALG_SHA_224)</code><br>
                    • <code>PSA_ALG_HKDF(PSA_ALG_SHA_256)</code><br>
                    • <code>PSA_ALG_HKDF(PSA_ALG_SHA_384)</code><br>
                    • <code>PSA_ALG_HKDF(PSA_ALG_SHA_512)</code>
                </p>
            </td>
        </tr>
        <tr>
            <td>
                <p>PBKDF2 (SHA-1)</p>
            </td>
            <td>
                <p><code>MBEDTLS_MD_SHA1</code></p>
            </td>
            <td>
                <p><code>PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_1)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>PBKDF2 (SHA-2)</p>
            </td>
            <td>
                <p>
                    • <code>MBEDTLS_MD_SHA224</code><br>
                    • <code>MBEDTLS_MD_SHA256</code><br>
                    • <code>MBEDTLS_MD_SHA384</code><br>
                    • <code>MBEDTLS_MD_SHA512</code>
                </p>
            </td>
            <td>
                <p>
                    • <code>PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_224)</code><br>
                    • <code>PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)</code><br>
                    • <code>PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_384)</code><br>
                    • <code>PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_512)</code>
                </p>
            </td>
        </tr>
        <tr>
            <td>
                <p>PBKDF2 CMAC</p>
            </td>
            <td>
                <p>—</p>
            </td>
            <td>
                <p><code>PSA_ALG_PBKDF2_AES_CMAC_PRF_128</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>ECDH + HKDF</p>
            </td>
            <td>
                <p>—</p>
            </td>
            <td>
                <p><code>PSA_ALG_KEY_AGREEMENT(PSA_ALG_ECDH, PSA_ALG_HKDF(hash_alg))</code></p>
            </td>
        </tr>
    </tbody>
</table>

**Key Attributes in PSA Crypto**

|**Algorithm**|**Key Type**|**Key Size in Bits**|**Key Usage Flag**|
|---|---|---|---|
|- PSA_ALG_HKDF(PSA_ALG_SHA_1)|PSA_KEY_TYPE_DERIVE|Multiple of 8|PSA_KEY_USAGE_DERIVE|
|- PSA_ALG_HKDF(PSA_ALG_SHA_224)| | | |
|- PSA_ALG_HKDF(PSA_ALG_SHA_256)| | | |
|- PSA_ALG_HKDF(PSA_ALG_SHA_384)| | | |
|- PSA_ALG_HKDF(PSA_ALG_SHA_512)| | | |
|- PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_1)| | | |
|- PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_224)| | | |
|- PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)| | | |
|- PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_384)| | | |
|- PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_512)| | | |
|PSA_ALG_PBKDF2_AES_CMAC_PRF_128| | | |

**Security Software Components**

|**Algorithm**|**Security Software Components**|
|---|---|
|PSA_ALG_HKDF(PSA_ALG_SHA_1)|HKDF and SHA-1|
|PSA_ALG_HKDF(PSA_ALG_SHA_224)|HKDF and SHA-224|
|PSA_ALG_HKDF(PSA_ALG_SHA_256)|HKDF and SHA-256|
|PSA_ALG_HKDF(PSA_ALG_SHA_384)|HKDF and SHA-384|
|PSA_ALG_HKDF(PSA_ALG_SHA_512)|HKDF and SHA-512|
|PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_1)|PBKDF2 and SHA-1|
|PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_224)|PBKDF2 and SHA-224|
|PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)|PBKDF2 and SHA-256|
|PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_384)|PBKDF2 and SHA-384|
|PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_512)|PBKDF2 and SHA-512|
|PSA_ALG_PBKDF2_AES_CMAC_PRF_128|PBKDF2|
|PSA_ALG_KEY_AGREEMENT(PSA_ALG_ECDH, PSA_ALG_HKDF(hash_alg))|ECDH and HKDF and SHA-X|

> **Note**:
> 
> - It should add the components for the derived key algorithm to implement the HKDF. For example, it requires the `CTR Mode` component if the derived key algorithm is `PSA_ALG_CTR`.
> - Refer to [Key Agreement (ECDH)](#key-agreement-ecdh) to add the ECDH components to derive the shared secret for the ECDH + HKDF algorithm (`PSA_ALG_KEY_AGREEMENT(PSA_ALG_ECDH, PSA_ALG_HKDF(hash_alg))`).

**Single-Part Functions**

|**Mbed TLS**|**PSA Crypto**|
|---|---|
|int mbedtls_hkdf(…)|psa_status_t sl_psa_key_derivation_single_shot(…)|
|int mbedtls_pkcs5_pbkdf2_hmac(…)|psa_status_t sl_psa_key_derivation_single_shot(…)|

> **Note**: The `sl_psa_key_derivation_single_shot(…)` is a **Silicon Labs custom API**. It can only use on HSE-SVH devices.

**Multi-Part Operations**

|**Mbed TLS**|**PSA Crypto**|
|---|---|
|—|psa_key_derivation_operation_t psa_key_derivation_operation_init(void)|
|—|psa_status_t psa_key_derivation_setup(…)|
|—|psa_status_t psa_key_derivation_get_capacity(…)|
|—|psa_status_t psa_key_derivation_set_capacity(…)|
|—|psa_status_t psa_key_derivation_input_bytes(…)|
|—|psa_status_t psa_key_derivation_input_key(…)|
|—|psa_status_t psa_key_derivation_output_bytes(…)|
|—|psa_status_t psa_key_derivation_output_key(…)|
|—|psa_status_t psa_key_derivation_abort(…)|

> **Note**: The multi-part operation allows the data to be processed for KDF in fragments instead of all at once.

**Quick Reference Examples HKDF (SHA-256)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t hkdf_ikm[] = {
    0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
    0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
  };
  uint8_t hkdf_salt[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c};
  uint8_t hkdf_info[] = {0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9};
  uint8_t key_buf[32];
  size_t key_len;
  psa_status_t ret;
  psa_key_id_t master_key_id;
  psa_key_id_t hkdf_key_id;
  psa_key_attributes_t key_attr;
  psa_key_derivation_operation_t kdf_op;

  ret = psa_crypto_init();

  // Set up attributes for a volatile master plain key
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_DERIVE);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_DERIVE);
  psa_set_key_algorithm(&key_attr, PSA_ALG_HKDF(PSA_ALG_SHA_256));

  // Import a volatile master plain key
  ret = psa_import_key(&key_attr, hkdf_ikm, sizeof(hkdf_ikm), &master_key_id);

  // Set up attributes for a volatile derived plain key (exportable for verification)
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_AES);
  psa_set_key_bits(&key_attr, sizeof(key_buf) * 8);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
  psa_set_key_algorithm(&key_attr, PSA_ALG_CTR);

  // Derive (HKDF SHA256) a volatile plain key for AES CTR
  #if defined(SEMAILBOX_PRESENT) && (_SILICON_LABS_SECURITY_FEATURE == _SILICON_LABS_SECURITY_FEATURE_VAULT)

  // Silicon Labs custom API for Secure Vault High devices
  ret = sl_psa_key_derivation_single_shot(PSA_ALG_HKDF(PSA_ALG_SHA_256), master_key_id,
                                          hkdf_info, sizeof(hkdf_info),
                                          hkdf_salt, sizeof(hkdf_salt),
                                          0, &key_attr, &hkdf_key_id);
  #else
  kdf_op = psa_key_derivation_operation_init();
  ret = psa_key_derivation_setup(&kdf_op, PSA_ALG_HKDF(PSA_ALG_SHA_256));
  ret = psa_key_derivation_set_capacity(&kdf_op, sizeof(key_buf));
  ret = psa_key_derivation_input_bytes(&kdf_op, PSA_KEY_DERIVATION_INPUT_SALT, hkdf_salt, sizeof(hkdf_salt));
  ret = psa_key_derivation_input_bytes(&kdf_op, PSA_KEY_DERIVATION_INPUT_INFO, hkdf_info, sizeof(hkdf_info));
  ret = psa_key_derivation_input_key(&kdf_op, PSA_KEY_DERIVATION_INPUT_SECRET, master_key_id);
  ret = psa_key_derivation_output_key(&key_attr, &kdf_op, &hkdf_key_id);
  ret = psa_key_derivation_abort(&kdf_op);
  #endif

  // Export derived volatile plain key (expected value of HKDF SHA256):
  // 3c b2 5f 25 fa ac d5 7a 90 43 4f 64 d0 36 2f 2a 2d 2d 0a 90 cf 1a 5a 4c 5d b0 2d 56 ec c4 c5 bf
  ret = psa_export_key(hkdf_key_id, key_buf, sizeof(key_buf), &key_len);

  // Destroy the master and derived keys
  ret = psa_destroy_key(master_key_id);
  ret = psa_destroy_key(hkdf_key_id);
}
```

**PBKDF2-HMAC-SHA256 (HSE-SVH only)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t pbkdf2_ikm[] = {0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64};
  uint8_t pbkdf2_salt[] = {0x73, 0x61, 0x6c, 0x74};
  uint8_t key_buf[32];
  size_t key_len;
  psa_status_t ret;
  psa_key_id_t master_key_id;
  psa_key_id_t pbkdf2_key_id;
  psa_key_attributes_t key_attr;

  ret = psa_crypto_init();
  // Set up attributes for a volatile master plain key

  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_DERIVE);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_DERIVE);
  psa_set_key_algorithm(&key_attr, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256));

  // Import a volatile master plain key
  ret = psa_import_key(&key_attr, pbkdf2_ikm, sizeof(pbkdf2_ikm), &master_key_id);

  // Set up attributes for a volatile derived plain key (exportable for verification)
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_AES);
  psa_set_key_bits(&key_attr, sizeof(key_buf) * 8);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
  psa_set_key_algorithm(&key_attr, PSA_ALG_CTR);

  // Derive (PBKDF2 SHA256 with 4096 iterations) a volatile plain key for AES CTR
  // Silicon Labs custom API for Secure Vault High devices
  ret = sl_psa_key_derivation_single_shot(PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256), master_key_id,
                                          NULL, 0,
                                          pbkdf2_salt, sizeof(pbkdf2_salt),
                                          4096, &key_attr, &pbkdf2_key_id);

  // Export derived volatile plain key (expected value of PBKDF2 SHA256 with 4096 iterations):
  // c5 e4 78 d5 92 88 c8 41 aa 53 0d b6 84 5c 4c 8d 96 28 93 a0 01 ce 4e 11 a4 96 38 73 aa 98 13 4a
  ret = psa_export_key(pbkdf2_key_id, key_buf, sizeof(key_buf), &key_len);

  // Destroy the master and derived keys
  ret = psa_destroy_key(master_key_id);
  ret = psa_destroy_key(pbkdf2_key_id);
}
```

> **Note**: The PBKDF2-HMAC implementation of PSA Crypto is not yet available.

**PBKDF2-AES-CMAC-PRF-128 (HSE-SVH only)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t pbkdf2_ikm[] = {0x4a, 0x30, 0x31, 0x4e, 0x4d, 0x45};
  uint8_t pbkdf2_salt[] = {
    0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x37, 0x33, 0x35, 0x63, 0x38, 0x37, 0x62, 0x34, 0x4f, 0x70,
    0x65, 0x6e, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x44, 0x65, 0x6d, 0x6f
  };
  uint8_t key_buf[16];
  size_t key_len;
  psa_status_t ret;
  psa_key_id_t master_key_id;
  psa_key_id_t pbkdf2_key_id;
  psa_key_attributes_t key_attr;

  ret = psa_crypto_init();

  // Set up attributes for a volatile master plain key
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_DERIVE);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_DERIVE);
  psa_set_key_algorithm(&key_attr, PSA_ALG_PBKDF2_AES_CMAC_PRF_128);

  // Import a volatile master plain key
  ret = psa_import_key(&key_attr, pbkdf2_ikm, sizeof(pbkdf2_ikm), &master_key_id);

  // Set up attributes for a volatile derived plain key (exportable for verification)
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_AES);
  psa_set_key_bits(&key_attr, sizeof(key_buf) * 8);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
  psa_set_key_algorithm(&key_attr, PSA_ALG_CTR);

  // Derive (PBKDF2 AES_CMAC_PRF_128 with 16384 iterations) a volatile plain key for AES CTR
  // Silicon Labs custom API for Secure Vault High devices
  ret = sl_psa_key_derivation_single_shot(PSA_ALG_PBKDF2_AES_CMAC_PRF_128, master_key_id,
                                          NULL, 0,
                                          pbkdf2_salt, sizeof(pbkdf2_salt),
                                          16384, &key_attr, &pbkdf2_key_id);

  // Export derived volatile plain key (expected value of PBKDF2 AES_CMAC_PRF_128 with 16384 iterations):
  // 8b 27 be ed 7e 7a 4d d6 c5 31 38 c8 79 a8 e3 3c
  
  ret = psa_export_key(pbkdf2_key_id, key_buf, sizeof(key_buf), &key_len);

  // Destroy the master and derived keys
  ret = psa_destroy_key(master_key_id);
  ret = psa_destroy_key(pbkdf2_key_id);
}
```

> **Note**:
> 
> - The PBKDF2-AES-CMAC-PRF-128 implementation of PSA Crypto is not yet available.
> - EFR32xG21B (HSE-SVH) devices do not support PBKDF2-AES-CMAC-PRF-128.

**ECDH and HKDF**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t client_private_key[] = {
    0xc8, 0x8f, 0x01, 0xf5, 0x10, 0xd9, 0xac, 0x3f, 0x70, 0xa2, 0x92, 0xda, 0xa2, 0x31, 0x6d, 0xe5,
    0x44, 0xe9, 0xaa, 0xb8, 0xaf, 0xe8, 0x40, 0x49, 0xc6, 0x2a, 0x9c, 0x57, 0x86, 0x2d, 0x14, 0x33
  };
  uint8_t client_public_key[] = { // Uncompressed point format
    0x04, 0xda, 0xd0, 0xb6, 0x53, 0x94, 0x22, 0x1c, 0xf9, 0xb0, 0x51, 0xe1, 0xfe, 0xca, 0x57, 0x87, 0xd0,
    0x98, 0xdf, 0xe6, 0x37, 0xfc, 0x90, 0xb9, 0xef, 0x94, 0x5d, 0x0c, 0x37, 0x72, 0x58, 0x11, 0x80,
    0x52, 0x71, 0xa0, 0x46, 0x1c, 0xdb, 0x82, 0x52, 0xd6, 0x1f, 0x1c, 0x45, 0x6f, 0xa3, 0xe5, 0x9a,
    0xb1, 0xf4, 0x5b, 0x33, 0xac, 0xcf, 0x5f, 0x58, 0x38, 0x9e, 0x05, 0x77, 0xb8, 0x99, 0x0b, 0xb3
  };
  uint8_t server_private_key[] = {
    0xc6, 0xef, 0x9c, 0x5d, 0x78, 0xae, 0x01, 0x2a, 0x01, 0x11, 0x64, 0xac, 0xb3, 0x97, 0xce, 0x20,
    0x88, 0x68, 0x5d, 0x8f, 0x06, 0xbf, 0x9b, 0xe0, 0xb2, 0x83, 0xab, 0x46, 0x47, 0x6b, 0xee, 0x53
  };
  uint8_t server_public_key[] = { // Uncompressed point format
    0x04, 0xd1, 0x2d, 0xfb, 0x52, 0x89, 0xc8, 0xd4, 0xf8, 0x12, 0x08, 0xb7, 0x02, 0x70, 0x39, 0x8c, 0x34,
    0x22, 0x96, 0x97, 0x0a, 0x0b, 0xcc, 0xb7, 0x4c, 0x73, 0x6f, 0xc7, 0x55, 0x44, 0x94, 0xbf, 0x63,
    0x56, 0xfb, 0xf3, 0xca, 0x36, 0x6c, 0xc2, 0x3e, 0x81, 0x57, 0x85, 0x4c, 0x13, 0xc5, 0x8d, 0x6a,
    0xac, 0x23, 0xf0, 0x46, 0xad, 0xa3, 0x0f, 0x83, 0x53, 0xe7, 0x4f, 0x33, 0x03, 0x98, 0x72, 0xab
  };
  uint8_t hkdf_salt[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c};
  uint8_t hkdf_info[] = {0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9};
  uint8_t key_buf[32];
  size_t key_len;

  psa_status_t ret;
  psa_key_id_t master_key_id;
  psa_key_id_t hkdf_key_id;
  psa_key_attributes_t key_attr;
  psa_key_derivation_operation_t kdf_op;

  ret = psa_crypto_init();

  // Set up attributes for a volatile master plain key (algorithm for ECDH and HKDF)
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_DERIVE);
  psa_set_key_algorithm(&key_attr, PSA_ALG_KEY_AGREEMENT(PSA_ALG_ECDH, PSA_ALG_HKDF(PSA_ALG_SHA_256)));

  // Import a volatile master plain key (client private key)
  ret = psa_import_key(&key_attr, client_private_key, sizeof(client_private_key),
  &master_key_id);

  // Set up attributes for a volatile derived plain key (exportable for verification)
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_AES);
  psa_set_key_bits(&key_attr, sizeof(key_buf) * 8);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
  psa_set_key_algorithm(&key_attr, PSA_ALG_CTR);

  // Derive (HKDF SHA256) a volatile plain key from ECDH shared secret (server public key) for AES CTR
  kdf_op = psa_key_derivation_operation_init();
  ret = psa_key_derivation_setup(&kdf_op, PSA_ALG_KEY_AGREEMENT(PSA_ALG_ECDH, PSA_ALG_HKDF(PSA_ALG_SHA_256)));
  ret = psa_key_derivation_set_capacity(&kdf_op, sizeof(key_buf));
  ret = psa_key_derivation_input_bytes(&kdf_op, PSA_KEY_DERIVATION_INPUT_SALT, hkdf_salt, sizeof(hkdf_salt));
  ret = psa_key_derivation_input_bytes(&kdf_op, PSA_KEY_DERIVATION_INPUT_INFO, hkdf_info, sizeof(hkdf_info));
  ret = psa_key_derivation_key_agreement(&kdf_op, PSA_KEY_DERIVATION_INPUT_SECRET, master_key_id, server_public_key,
  sizeof(server_public_key));
  ret = psa_key_derivation_output_key(&key_attr, &kdf_op, &hkdf_key_id);
  ret = psa_key_derivation_abort(&kdf_op);

  // Export derived volatile plain key (client shared secret):
  // B7 CA BD A7 42 60 DE D5 4C 4C 11 FA BC A3 56 4B 77 35 CC 9F 89 E9 BF E8 08 24 8A F3 54 99 B0 55
  ret = psa_export_key(hkdf_key_id, key_buf, sizeof(key_buf), &key_len);

  // Destroy the master and derived keys
  ret = psa_destroy_key(master_key_id);
  ret = psa_destroy_key(hkdf_key_id);

  // Set up attributes for a volatile master plain key (algorithm for ECDH and HKDF)
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_DERIVE);
  psa_set_key_algorithm(&key_attr, PSA_ALG_KEY_AGREEMENT(PSA_ALG_ECDH, PSA_ALG_HKDF(PSA_ALG_SHA_256)));

  // Import a volatile master plain key (server private key)
  ret = psa_import_key(&key_attr, server_private_key, sizeof(server_private_key),
                      &master_key_id);

  // Set up attributes for a volatile derived plain key (exportable for verification)
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_AES);
  psa_set_key_bits(&key_attr, sizeof(key_buf) * 8);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
  psa_set_key_algorithm(&key_attr, PSA_ALG_CTR);

  // Derive (HKDF SHA256) a volatile plain key from ECDH shared secret (client public key) for AES CTR
  kdf_op = psa_key_derivation_operation_init();
  ret = psa_key_derivation_setup(&kdf_op, PSA_ALG_KEY_AGREEMENT(PSA_ALG_ECDH, PSA_ALG_HKDF(PSA_ALG_SHA_256)));
  ret = psa_key_derivation_set_capacity(&kdf_op, sizeof(key_buf));
  ret = psa_key_derivation_input_bytes(&kdf_op, PSA_KEY_DERIVATION_INPUT_SALT, hkdf_salt, sizeof(hkdf_salt));
  ret = psa_key_derivation_input_bytes(&kdf_op, PSA_KEY_DERIVATION_INPUT_INFO, hkdf_info, sizeof(hkdf_info));
  ret = psa_key_derivation_key_agreement(&kdf_op, PSA_KEY_DERIVATION_INPUT_SECRET, master_key_id, client_public_key,
                                        sizeof(client_public_key));
  ret = psa_key_derivation_output_key(&key_attr, &kdf_op, &hkdf_key_id);
  ret = psa_key_derivation_abort(&kdf_op);

  // Export derived volatile plain key (server shared secret):
  // B7 CA BD A7 42 60 DE D5 4C 4C 11 FA BC A3 56 4B 77 35 CC 9F 89 E9 BF E8 08 24 8A F3 54 99 B0 55
  ret = psa_export_key(hkdf_key_id, key_buf, sizeof(key_buf), &key_len);

  // Destroy the master and derived keys
  ret = psa_destroy_key(master_key_id);
  ret = psa_destroy_key(hkdf_key_id);
}
```

**PSA Crypto Platform Example**

Click the `View Project Documentation` link to open the `readme` file.

![image27](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image27.png)

The following table describes the implementation status of the PSA Crypto KDF platform example.

|**Algorithm**|**Series 1**|**Series 2 - VSE**|**Series 2 - HSE**|**Remark**|
|---|---|---|---|---|
|HKDF|Y|Y|Y|Hardware acceleration only on HSE-SVH devices with Silicon Labs custom API.|
|ECDH + HKDF|Y|Y|Y|Hardware acceleration (HKDF) only on HSE-SVH devices with Silicon Labs custom API.|

> **Note**:
> 
> - The PBKDF2 implementation of PSA Crypto is not yet available.
> - The ECDH + HKDF algorithm does not apply to the [wrapped key](04-key-management-in-psa-crypto#key-lifetimes).

###### Asymmetric Cryptographic Operation

###### Asymmetric Signature (ECDSA and EdDSA) (heading level 7)

In modern cryptography, the Elliptic-Curve-based signatures (like ECDSA and EdDSA) are widely used because of shorter key lengths, shorter signature lengths, higher security levels (for the same key length), and better performance.

The Elliptic Curve Digital Signature Algorithm (ECDSA) is a cryptographically secure digital signature scheme based on the Elliptic Curve Cryptography (ECC). The Edwards-curve Digital Signature Algorithm (EdDSA) is a fast digital signature algorithm, using elliptic curves in Edwards form (like Ed25519 and Ed448).

**Algorithms**

<table>
    <thead>
        <tr>
            <th><strong>Algorithm</strong></th>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>ECDSA (SHA-1)</p>
            </td>
            <td>
                <p>—</p>
            </td>
            <td>
                <p><code>PSA_ALG_ECDSA(PSA_ALG_SHA_1)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>ECDSA (SHA-2)</p>
            </td>
            <td>
                <p>—</p>
            </td>
            <td>
                <p>
                    • <code>PSA_ALG_ECDSA(PSA_ALG_SHA_224)</code><br>
                    • <code>PSA_ALG_ECDSA(PSA_ALG_SHA_256)</code><br>
                    • <code>PSA_ALG_ECDSA(PSA_ALG_SHA_384)</code><br>
                    • <code>PSA_ALG_ECDSA(PSA_ALG_SHA_512)</code>
                </p>
            </td>
        </tr>
        <tr>
            <td>
                <p>ECDSA (Any hash algorithm)</p>
            </td>
            <td>
                <p>—</p>
            </td>
            <td>
                <p><code>PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>ECDSA (No hashing)</p>
            </td>
            <td>
                <p>—</p>
            </td>
            <td>
                <p><code>PSA_ALG_ECDSA_ANY</code></p>
            </td>
        </tr>
        <tr>
            <td>
                <p>EdDSA</p>
            </td>
            <td>
                <p>—</p>
            </td>
            <td>
                <p><code>PSA_ALG_PURE_EDDSA</code></p>
            </td>
        </tr>
    </tbody>
</table>

> **Note**: The hash-and-sign algorithms `(PSA_ALG_ECDSA(hash_alg)` and `PSA_ALG_ECDSA(PSA_ALG_ANY_HASH))` include the hashing step for ECDSA.

**Key Attributes in PSA Crypto**

<table>
    <thead>
        <tr>
            <th><strong>Algorithm</strong></th>
            <th><strong>Key Type</strong></th>
            <th><strong>Key Size in Bits</strong></th>
            <th><strong>Key Usage Flag</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>PSA_ALG_ECDSA_ANY</code></td>
            <td><code>PSA_ECC_FAMILY_SECP_R1</code></td>
            <td>
                secp192r1 : 192<br>
                secp224r1 : 224<br>
                secp256r1 : 256<br>
                secp384r1 : 384<br>
                secp521r1 : 521
            </td>
            <td>
                <code>PSA_KEY_USAGE_SIGN_HASH</code><br>
                <code>PSA_KEY_USAGE_VERIFY_HASH</code>
            </td>
        </tr>
        <tr>
            <td></td>
            <td><code>PSA_ECC_FAMILY_SECP_K1</code></td>
            <td>secp256k1 : 256</td>
            <td></td>
        </tr>
        <tr>
            <td>
                <code>PSA_ALG_ECDSA(PSA_ALG_SHA_1)</code><br>
                <code>PSA_ALG_ECDSA(PSA_ALG_SHA_224)</code><br>
                <code>PSA_ALG_ECDSA(PSA_ALG_SHA_256)</code><br>
                <code>PSA_ALG_ECDSA(PSA_ALG_SHA_384)</code><br>
                <code>PSA_ALG_ECDSA(PSA_ALG_SHA_512)</code><br>
                <code>PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)</code>
            </td>
            <td><code>PSA_ECC_FAMILY_SECP_R1</code></td>
            <td>
                secp192r1 : 192<br>
                secp224r1 : 224<br>
                secp256r1 : 256<br>
                secp384r1 : 384<br>
                secp521r1 : 521
            </td>
            <td>
                <code>PSA_KEY_USAGE_SIGN_MESSAGE</code><br>
                <code>PSA_KEY_USAGE_VERIFY_MESSAGE</code>
            </td>
        </tr>
        <tr>
            <td></td>
            <td><code>PSA_ECC_FAMILY_SECP_K1</code></td>
            <td>secp256k1 : 256</td>
            <td></td>
        </tr>
        <tr>
            <td><code>PSA_ALG_PURE_EDDSA</code></td>
            <td><code>PSA_ECC_FAMILY_TWISTED_EDWARDS</code></td>
            <td>Edwards25519 : 255</td>
            <td>
                <code>PSA_KEY_USAGE_SIGN_MESSAGE</code><br>
                <code>PSA_KEY_USAGE_VERIFY_MESSAGE</code>
            </td>
        </tr>
    </tbody>
</table>

**Security Software Components**

|**Algorithm**|**Security Software Components**|
|---|---|
|PSA_ALG_ECDSA_ANY|ECDSA|
|PSA_ALG_ECDSA(PSA_ALG_SHA_1)|ECDSA and SHA-1|
|PSA_ALG_ECDSA(PSA_ALG_SHA_224)|ECDSA and SHA-224|
|PSA_ALG_ECDSA(PSA_ALG_SHA_256)|ECDSA and SHA-256|
|PSA_ALG_ECDSA(PSA_ALG_SHA_384)|ECDSA and SHA-384|
|PSA_ALG_ECDSA(PSA_ALG_SHA_512)|ECDSA and SHA-512|
|PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)|ECDSA and SHA-X|

|**Algorithm**|**Security Software Components**|
|---|---|
|PSA_ALG_PURE_EDDSA|EdDSA|

|**PSA_ECC_FAMILY_SECP_R1**|**Security Software Components**|
|---|---|
|secp192r1|secp192r1|
|secp224r1|secp224r1|
|secp256r1|secp256r1|
|secp384r1|secp384r1|
|secp521r1|secp521r1|

|**PSA_ECC_FAMILY_SECP_K1**|**Security Software Components**|
|---|---|
|secp256k1|secp256k1|

|**PSA_ECC_FAMILY_TWISTED_EDWARDS**|**Security Software Components**|
|---|---|
|Edwards25519|edwards25519|

|**Built-in Key**|**Security Software Components**|
|---|---|
|secp256r1 keys in SE OTP|Built-In Keys|

**Functions**

<table>
    <thead>
        <tr>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>Hash-and-sign<br>—</p>
            </td>
            <td>
                <p>Hash-and-sign<br>
                    <code>psa_status_t psa_sign_message(…)</code><br>
                    <code>psa_status_t psa_verify_message(…)</code>
                </p>
            </td>
        </tr>
        <tr>
            <td>
                <p>Precomputed hash<br>
                    <code>int mbedtls_ecdsa_write_signature(…)</code><br>
                    <code>int mbedtls_ecdsa_read_signature(…)</code>
                </p>
            </td>
            <td>
                <p>Precomputed hash<br>
                    <code>psa_status_t psa_sign_hash(…)</code><br>
                    <code>psa_status_t psa_verify_hash(…)</code>
                </p>
            </td>
        </tr>
    </tbody>
</table>

**Quick Reference Examples**

**ECDSA on secp256r1 (Precomputed Hash)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t hash_data[] = {
    0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39,
    0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1
  };
  uint8_t public_key[65]; // Uncompressed point format
  size_t pubkey_len;
  uint8_t signature_buf[64];
  size_t signature_len;

  psa_status_t ret;
  psa_key_id_t key_id;
  psa_key_attributes_t key_attr;

  ret = psa_crypto_init();

  // Set up attributes for a volatile private plain key (secp256r1)
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
  psa_set_key_bits(&key_attr, 256);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH);
  psa_set_key_algorithm(&key_attr, PSA_ALG_ECDSA_ANY);

  // Generate a random volatile private plain key
  ret = psa_generate_key(&key_attr, &key_id);

  // Sign a hash with a volatile private plain key
  ret = psa_sign_hash(key_id,
                      PSA_ALG_ECDSA_ANY,
                      hash_data,
                      sizeof(hash_data),
                      signature_buf,
                      sizeof(signature_buf),
                      &signature_len);

  // Verify a signature with a volatile private plain key
  ret = psa_verify_hash(key_id,
                        PSA_ALG_ECDSA_ANY,
                        hash_data,
                        sizeof(hash_data),
                        signature_buf,
                        signature_len);

  // Export a public key from a volatile private plain key and then destroy the private key
  ret = psa_export_public_key(key_id,
                              public_key,
                              sizeof(public_key),
                              &pubkey_len);

  ret = psa_destroy_key(key_id);

  // Set up attributes for a public key (secp256r1)
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1));
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_VERIFY_HASH);
  psa_set_key_algorithm(&key_attr, PSA_ALG_ECDSA_ANY);

  // Import a public key
  ret = psa_import_key(&key_attr, public_key, sizeof(public_key), &key_id);

  // Verify a signature with a public key and then destroy the public key
  ret = psa_verify_hash(key_id,
                        PSA_ALG_ECDSA_ANY,
                        hash_data,
                        sizeof(hash_data),
                        signature_buf,
                        signature_len);

  ret = psa_destroy_key(key_id);
}
```

**ECDSA on secp256r1 (Hash-and-Sign)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t ecdsa_msg[] = {
    0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31,
    0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a,
    0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
    0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f
  };
  uint8_t public_key[65];                                     // Uncompressed point format
  size_t pubkey_len;
  uint8_t signature_buf[64];
  size_t signature_len;

  psa_status_t ret;
  psa_key_id_t key_id;
  psa_key_attributes_t key_attr;

  ret = psa_crypto_init();

  // Set up attributes for a volatile private plain key (secp256r1)
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
  psa_set_key_bits(&key_attr, 256);
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_VERIFY_MESSAGE);
  psa_set_key_algorithm(&key_attr, PSA_ALG_ECDSA(PSA_ALG_ANY_HASH));

  // Generate a random volatile private plain key
  ret = psa_generate_key(&key_attr, &key_id);

  // Hash-and-Sign (SHA-256) a message with a volatile private plain key
  ret = psa_sign_message(key_id,
                        PSA_ALG_ECDSA(PSA_ALG_SHA_256),
                        ecdsa_msg,
                        sizeof(ecdsa_msg),
                        signature_buf,
                        sizeof(signature_buf),
                        &signature_len);

  // Hash (SHA-256) a message and verify a signature with a volatile private plain key
  ret = psa_verify_message(key_id,
                          PSA_ALG_ECDSA(PSA_ALG_SHA_256),
                          ecdsa_msg,
                          sizeof(ecdsa_msg),
                          signature_buf,
                          signature_len);

  // Export a public key from a volatile private plain key and then destroy the private key
  ret = psa_export_public_key(key_id,
                              public_key,
                              sizeof(public_key),
                              &pubkey_len);

  ret = psa_destroy_key(key_id);

  // Set up attributes for a public key (secp256r1)
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1));
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_VERIFY_MESSAGE);
  psa_set_key_algorithm(&key_attr, PSA_ALG_ECDSA(PSA_ALG_ANY_HASH));

  // Import a public key
  ret = psa_import_key(&key_attr, public_key, sizeof(public_key), &key_id);

  // Hash (SHA-256) and verify a signature with a public key and then destroy the public key
  ret = psa_verify_message(key_id,
                          PSA_ALG_ECDSA(PSA_ALG_SHA_256),
                          ecdsa_msg,
                          sizeof(ecdsa_msg),
                          signature_buf,
                          signature_len);

  ret = psa_destroy_key(key_id);
}
```

**ECDSA with Built-in Private Device Key (HSE-SVH only)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
#if (_SILICON_LABS_SECURITY_FEATURE == _SILICON_LABS_SECURITY_FEATURE_VAULT)
 uint8_t hash_data[] = {
  0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39,
  0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1
 };
 uint8_t public_key[65];                                  // Uncompressed point format
 size_t pubkey_len;
 uint8_t signature_buf[64];
 size_t signature_len;

 psa_status_t ret;
 psa_key_id_t key_id;
 psa_key_attributes_t key_attr;

 ret = psa_crypto_init();

 // Sign a hash with a built-in private device key
 ret = psa_sign_hash(SL_SE_BUILTIN_KEY_APPLICATION_ATTESTATION_ID,
                    PSA_ALG_ECDSA_ANY,
                    hash_data,
                    sizeof(hash_data),
                    signature_buf,
                    sizeof(signature_buf),
                    &signature_len);

 // Verify a signature with a built-in private device key
 ret = psa_verify_hash(SL_SE_BUILTIN_KEY_APPLICATION_ATTESTATION_ID,
                      PSA_ALG_ECDSA_ANY,
                      hash_data,
                      sizeof(hash_data),
                      signature_buf,
                      signature_len);

 // Export a built-in public device key
 ret = psa_export_public_key(SL_SE_BUILTIN_KEY_APPLICATION_ATTESTATION_ID,
                            public_key,
                            sizeof(public_key),
                            &pubkey_len);

 // Set up attributes for a public device key
 key_attr = psa_key_attributes_init();
 psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1));
 psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_VERIFY_HASH);
 psa_set_key_algorithm(&key_attr, PSA_ALG_ECDSA_ANY);

 // Import a public device key
 ret = psa_import_key(&key_attr, public_key, sizeof(public_key), &key_id);

 // Verify a signature with a public device key
 ret = psa_verify_hash(key_id,
                      PSA_ALG_ECDSA_ANY,
                      hash_data,
                      sizeof(hash_data),
                      signature_buf,
                      signature_len);

 // Destroy a public device key
 ret = psa_destroy_key(key_id);
#endif
}
```

**EdDSA on Ed25519 (HSE only)**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
#if defined(SEMAILBOX_PRESENT)
  uint8_t eddsa_msg[] = {
    0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31,
    0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a,
    0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
    0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f
  };
  uint8_t ed25519_private[] = {
    0x83, 0x3f, 0xe6, 0x24, 0x09, 0x23, 0x7b, 0x9d, 0x62, 0xec, 0x77, 0x58, 0x75, 0x20, 0x91, 0x1e,
    0x9a, 0x75, 0x9c, 0xec, 0x1d, 0x19, 0x75, 0x5b, 0x7d, 0xa9, 0x01, 0xb9, 0x6d, 0xca, 0x3d, 0x42
  };
  uint8_t ed25519_public[] = {
    0xec, 0x17, 0x2b, 0x93, 0xad, 0x5e, 0x56, 0x3b, 0xf4, 0x93, 0x2c, 0x70, 0xe1, 0x24, 0x50, 0x34,
    0xc3, 0x54, 0x67, 0xef, 0x2e, 0xfd, 0x4d, 0x64, 0xeb, 0xf8, 0x19, 0x68, 0x34, 0x67, 0xe2, 0xbf
  };
  uint8_t signature_buf[64];
  size_t signature_len;

  psa_status_t ret;
  psa_key_id_t key_id;
  psa_key_attributes_t key_attr;

  ret = psa_crypto_init();

  // Set up attributes for a volatile private plain key (Ed25519)
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS));
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_VERIFY_MESSAGE);
  psa_set_key_algorithm(&key_attr, PSA_ALG_PURE_EDDSA);

  // Import a volatile private plain key
  ret = psa_import_key(&key_attr, ed25519_private, sizeof(ed25519_private), &key_id);

  // Hash-and-Sign a message with a volatile private plain key (expected EdDSA signature):
  // dc 2a 44 59 e7 36 96 33 a5 2b 1b f2 77 83 9a 00 20 10 09 a3 ef bf 3e cb 69 be a2 18 6c 26 b5 89
  // 09 35 1f c9 ac 90 b3 ec fd fb c7 c6 64 31 e0 30 3d ca 17 9c 13 8a c1 7a d9 be f1 17 73 31 a7 04
  ret = psa_sign_message(key_id,
                        PSA_ALG_PURE_EDDSA,
                        eddsa_msg,
                        sizeof(eddsa_msg),
                        signature_buf,
                        sizeof(signature_buf),
                        &signature_len);

  // Destroy a volatile private plain key
  ret = psa_destroy_key(key_id);

  // Set up attributes for a public key (Ed25519)
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_TWISTED_EDWARDS));
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_VERIFY_MESSAGE);
  psa_set_key_algorithm(&key_attr, PSA_ALG_PURE_EDDSA);

  // Import a public key
  ret = psa_import_key(&key_attr, ed25519_public, sizeof(ed25519_public), &key_id);

  // Hash a message and verify the signature with a public key
  ret = psa_verify_message(key_id,
                          PSA_ALG_PURE_EDDSA,
                          eddsa_msg,
                          sizeof(eddsa_msg),
                          signature_buf,
                          signature_len);

  // Destroy a public key
  ret = psa_destroy_key(key_id);
#endif
}
```

**PSA Crypto Platform Example**

Click the `View Project Documentation` link to open the `readme` file.

![image28](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image28.png)

The following table describes the implementation status of the PSA Crypto DSA platform example.

|**ECC Key**|**Series 1**|**Series 2 - VSE**|**Series 2 - HSE**|**Remark**|
|---|---|---|---|---|
|secp192r1|Y|Y|Y|—|
|secp256r1|Y|Y|Y|—|
|secp384r1|Y|Y|Y|Hardware acceleration only on HSE-SVH devices.|
|secp521r1|Y|Y|Y|Hardware acceleration only on HSE-SVH devices.|
|Edwards25519|—|—|Y|Only on HSE devices with hardware acceleration.|
|Public Sign Key|—|—|Y|—|
|Public Command Key|—|—|Y|—|
|Private Device Key|—|—|Y|Only on HSE-SVH devices.|

> **Note**:
> 
> - This example does not include secp224r1 and secp256k1. The secp256k1 ECDSA on HSE devices is not yet implemented.
> - The PSA Crypto does not yet support software fallback on the Edwards25519.
> - The HSE-SVM devices require SE firmware v1.2.11 or higher (EFR32xG21) and v2.1.7 or higher (other HSE devices) to support hardware acceleration on Edwards25519. This feature also requires GSDK v4.0.1 or higher.

###### Key Agreement (ECDH)

The Elliptic Curve Diffie-Hellman (ECDH) is an anonymous key agreement protocol that allows two parties, each having an elliptic-curve private-public key pair, to establish a shared secret over an insecure channel.

**Algorithms**

|**Algorithm**|**Mbed TLS**|**PSA Crypto**|
|---|---|---|
|ECDH|—|PSA_ALG_ECDH|
|ECDH and HKDF|—|PSA_ALG_KEY_AGREEMENT(PSA_ALG_ECDH, PSA_ALG_HKDF(hash_alg))|

**Key Attributes in PSA Crypto**

<table>
    <thead>
        <tr>
            <th><strong>Algorithm</strong></th>
            <th><strong>Key Type</strong></th>
            <th><strong>Key Size in Bits</strong></th>
            <th><strong>Key Usage Flag</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>PSA_ALG_ECDH</p>
            </td>
            <td>
                <p>PSA_ECC_FAMILY_SECP_R1</p>
            </td>
            <td>
                <p>
                    • secp192r1 : 192<br>
                    • secp224r1 : 224<br>
                    • secp256r1 : 256<br>
                    • secp384r1 : 384<br>
                    • secp521r1 : 521
                </p>
            </td>
            <td>
                <p>PSA_KEY_USAGE_DERIVE</p>
            </td>
        </tr>
        <tr>
            <td></td>
            <td>
                <p>PSA_ECC_FAMILY_SECP_K1</p>
            </td>
            <td>
                <p>secp256k1 : 256</p>
            </td>
            <td></td>
        </tr>
        <tr>
            <td></td>
            <td>
                <p>PSA_ECC_FAMILY_MONTGOMERY</p>
            </td>
            <td>
                <p>
                    • Curve25519 : 255<br>
                    • Curve448 : 448
                </p>
            </td>
            <td></td>
        </tr>
    </tbody>
</table>

**Security Software Components**

|**Algorithm**|**Security Software Components**|
|---|---|
|PSA_ALG_ECDH|ECDH|

|**PSA_ECC_FAMILY_SECP_R1**|**Security Software Components**|
|---|---|
|secp192r1|secp192r1|
|secp224r1|secp224r1|
|secp256r1|secp256r1|
|secp384r1|secp384r1|
|secp521r1|secp521r1|

|**PSA_ECC_FAMILY_SECP_K1**|**Security Software Components**|
|---|---|
|secp256k1|secp256k1|

|**PSA_ECC_FAMILY_MONTGOMERY**|**Security Software Components**|
|---|---|
|Curve25519|Curve25519|
|Curve448|Curve448|

**Functions**

|**Mbed TLS**|**PSA Crypto**|
|---|---|
|int mbedtls_ecdh_compute_shared(…)|psa_status_t psa_raw_key_agreement(…)|
|—|psa_status_t psa_key_derivation_key_agreement(…)|

> **Note**: For the `psa_key_derivation_key_agreement(…)` function, refer to the PSA Crypto [KDF](#key-derivation) quick reference (ECDH and HKDF) and platform examples for details.

**Quick Reference Examples ECDH on secp256r1**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t client_private_key[] = {
    0xc8, 0x8f, 0x01, 0xf5, 0x10, 0xd9, 0xac, 0x3f, 0x70, 0xa2, 0x92, 0xda, 0xa2, 0x31, 0x6d, 0xe5,
    0x44, 0xe9, 0xaa, 0xb8, 0xaf, 0xe8, 0x40, 0x49, 0xc6, 0x2a, 0x9c, 0x57, 0x86, 0x2d, 0x14, 0x33
  };

  uint8_t client_public_key[] = { // Uncompressed point format
    0x04, 0xda, 0xd0, 0xb6, 0x53, 0x94, 0x22, 0x1c, 0xf9, 0xb0, 0x51, 0xe1, 0xfe, 0xca, 0x57, 0x87, 0xd0,
    0x98, 0xdf, 0xe6, 0x37, 0xfc, 0x90, 0xb9, 0xef, 0x94, 0x5d, 0x0c, 0x37, 0x72, 0x58, 0x11, 0x80,
    0x52, 0x71, 0xa0, 0x46, 0x1c, 0xdb, 0x82, 0x52, 0xd6, 0x1f, 0x1c, 0x45, 0x6f, 0xa3, 0xe5, 0x9a,
    0xb1, 0xf4, 0x5b, 0x33, 0xac, 0xcf, 0x5f, 0x58, 0x38, 0x9e, 0x05, 0x77, 0xb8, 0x99, 0x0b, 0xb3
  };

  uint8_t server_private_key[] = {
    0xc6, 0xef, 0x9c, 0x5d, 0x78, 0xae, 0x01, 0x2a, 0x01, 0x11, 0x64, 0xac, 0xb3, 0x97, 0xce, 0x20,
    0x88, 0x68, 0x5d, 0x8f, 0x06, 0xbf, 0x9b, 0xe0, 0xb2, 0x83, 0xab, 0x46, 0x47, 0x6b, 0xee, 0x53
  };

  uint8_t server_public_key[] = { // Uncompressed point format
    0x04, 0xd1, 0x2d, 0xfb, 0x52, 0x89, 0xc8, 0xd4, 0xf8, 0x12, 0x08, 0xb7, 0x02, 0x70, 0x39, 0x8c, 0x34,
    0x22, 0x96, 0x97, 0x0a, 0x0b, 0xcc, 0xb7, 0x4c, 0x73, 0x6f, 0xc7, 0x55, 0x44, 0x94, 0xbf, 0x63,
    0x56, 0xfb, 0xf3, 0xca, 0x36, 0x6c, 0xc2, 0x3e, 0x81, 0x57, 0x85, 0x4c, 0x13, 0xc5, 0x8d, 0x6a,
    0xac, 0x23, 0xf0, 0x46, 0xad, 0xa3, 0x0f, 0x83, 0x53, 0xe7, 0x4f, 0x33, 0x03, 0x98, 0x72, 0xab
  };

  // Expected shared secret:
  // d6 84 0f 6b 42 f6 ed af d1 31 16 e0 e1 25 65 20 2f ef 8e 9e ce 7d ce 03 81 24 64 d0 4b 94 42 de
  uint8_t shared_secret_buf[32];
  size_t shared_secret_len;
  psa_status_t ret;
  psa_key_id_t key_id;
  psa_key_attributes_t key_attr;

  ret = psa_crypto_init();

  // Set up attributes for a volatile client private plain key (secp256r1)
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_DERIVE);
  psa_set_key_algorithm(&key_attr, PSA_ALG_ECDH);

  // Import a volatile client private plain key
  ret = psa_import_key(&key_attr, client_private_key, sizeof(client_private_key), &key_id);

  // Perform a key agreement with the server public key and then destroy the client private key
  ret = psa_raw_key_agreement(PSA_ALG_ECDH,
                              key_id,
                              server_public_key,
                              sizeof(server_public_key),
                              shared_secret_buf,
                              sizeof(shared_secret_buf),
                              &shared_secret_len);

  ret = psa_destroy_key(key_id);

  // Set up attributes for a volatile server private plain key (secp256r1)
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_DERIVE);
  psa_set_key_algorithm(&key_attr, PSA_ALG_ECDH);

  // Import a volatile server private plain key
  ret = psa_import_key(&key_attr, server_private_key, sizeof(server_private_key), &key_id);

  // Perform a key agreement with the client public key and then destroy the server private key
  ret = psa_raw_key_agreement(PSA_ALG_ECDH,
                              key_id,
                              client_public_key,
                              sizeof(client_public_key),
                              shared_secret_buf,
                              sizeof(shared_secret_buf),
                              &shared_secret_len);

  ret = psa_destroy_key(key_id);
}
```

**ECDH on Curve25519**

```c++
#include "psa/crypto.h"

void app_process_action(void)
{
  uint8_t client_private_key[] = {
    0xB0, 0x76, 0x51, 0xEA, 0x20, 0xF0, 0x28, 0xA8,0x16, 0xEE, 0x01, 0xB0, 0xD1, 0x06, 0x2A, 0x7C,
    0x81, 0x58, 0xE8, 0x84, 0xE9, 0xBC, 0xC6, 0x1C, 0x5D, 0xAB, 0xDB, 0x4E, 0x38, 0x2F, 0x96, 0x69,
  };

  uint8_t client_public_key[] = {
    0x87, 0xD8, 0x6B, 0xDA, 0xAC, 0x38, 0x3C, 0x85, 0xA6, 0xBC, 0xF8, 0xFC, 0xC6, 0x26, 0xD6, 0x14,
    0x36, 0xE4, 0x8F, 0xDB, 0xFA, 0x5A, 0x45, 0xFE, 0x0C, 0x9E, 0xA8, 0x4B, 0x35, 0x3E, 0xF1, 0x37,
  };

  uint8_t server_private_key[] = {
    0x98, 0x2E, 0xB6, 0x7D, 0x0A, 0x01, 0x57, 0x90, 0xE1, 0x45, 0xF3, 0x67, 0xF6, 0xDA, 0xA6, 0x44,
    0x2C, 0x87, 0xC0, 0xED, 0x3C, 0x36, 0x71, 0xA6, 0x89, 0xC7, 0x49, 0xAC, 0x0D, 0xFE, 0x43, 0x6E,
  };

  uint8_t server_public_key[] = {
    0x0C, 0x04, 0x10, 0x5B, 0xE8, 0x7C, 0xAB, 0x37, 0x21, 0x15, 0x7A, 0x8D, 0x49, 0x85, 0x8C, 0x7A,
    0x9F, 0xC1, 0x46, 0xDA, 0xCC, 0x96, 0xEF, 0x6E, 0xD4, 0xDA, 0x71, 0xBF, 0xED, 0x32, 0x0D, 0x76,
  };

  // Expected shared secret:
  // F2 E6 0E 1C B7 64 BC 48 F2 9D BB 12 FB 12 17 31 32 1D 79 AF 0A 9F AB AD 34 05 A2 07 39 9C 5F 15
  uint8_t shared_secret_buf[32];
  size_t shared_secret_len;
  psa_status_t ret;
  psa_key_id_t key_id;
  psa_key_attributes_t key_attr;

  ret = psa_crypto_init();

  // Set up attributes for a volatile client private plain key (Curve25519)
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_MONTGOMERY));
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_DERIVE);
  psa_set_key_algorithm(&key_attr, PSA_ALG_ECDH);

  // Import a volatile client private plain key
  ret = psa_import_key(&key_attr, client_private_key, sizeof(client_private_key), &key_id);

  // Perform a key agreement with server public key
  ret = psa_raw_key_agreement(PSA_ALG_ECDH,
                              key_id,
                              server_public_key,
                              sizeof(server_public_key),
                              shared_secret_buf,
                              sizeof(shared_secret_buf),
                              &shared_secret_len);

  // Destroy the client private key
  ret = psa_destroy_key(key_id);

  // Set up attributes for a volatile server private plain key (Curve25519)
  key_attr = psa_key_attributes_init();
  psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_MONTGOMERY));
  psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_DERIVE);
  psa_set_key_algorithm(&key_attr, PSA_ALG_ECDH);

  // Import a volatile server private plain key
  ret = psa_import_key(&key_attr, server_private_key, sizeof(server_private_key), &key_id);

  // Perform a key agreement with client public key
  ret = psa_raw_key_agreement(PSA_ALG_ECDH,
                              key_id,
                              client_public_key,
                              sizeof(client_public_key),
                              shared_secret_buf,
                              sizeof(shared_secret_buf),
                              &shared_secret_len);

  // Destroy the server private key
  ret = psa_destroy_key(key_id);
}
```

**PSA Crypto Platform Example**

Click the `View Project Documentation` link to open the `readme` file.

![image29](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image29.png)

The following table describes the implementation status of the PSA Crypto ECDH platform example.

|**ECC Key**|**Series 1**|**Series 2 - VSE**|**Series 2 - HSE**|**Remark**|
|---|---|---|---|---|
|secp192r1|Y|Y|Y|—|
|secp256r1|Y|Y|Y|—|
|secp384r1|Y|Y|Y|Hardware acceleration only on HSE-SVH devices.|
|secp521r1|Y|Y|Y|Hardware acceleration only on HSE-SVH devices.|
|Curve25519|Y|Y|Y|Hardware acceleration only on HSE devices.|
|Curve448|—|—|Y|Only on HSE-SVH devices with hardware acceleration.|

> **Note**:
> 
> - This example does not include secp224r1 and secp256k1. The secp256k1 ECDH on HSE devices is not yet implemented.
> - The PSA Crypto does not yet support software fallback on the Curve448.
> - The HSE-SVM devices require SE firmware v1.2.11 or higher (EFR32xG21) and v2.1.7 or higher (other HSE devices) to support hardware acceleration on Curve25519. This feature also requires GSDK v4.0.1 or higher.

###### X.509 Certificate

An X.509 certificate is a digital certificate that uses the widely accepted international X.509 public key infrastructure (PKI) standard to verify that a public key belongs to the user, computer, or service identity contained within the certificate.

An X.509 certificate contains a public key and an identity (a hostname, an organization, an individual). It is either signed by a certificate authority or self-signed. When a certificate is signed by a trusted certificate authority or validated by other means, someone holding that certificate can rely on the public key it contains to establish secure communications with another party or validate documents digitally signed by the corresponding private key.

**Algorithms**

<table>
    <thead>
        <tr>
            <th><strong>Algorithm</strong></th>
            <th><strong>Mbed TLS</strong></th>
            <th><strong>PSA Crypto</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <p>ECDSA (SHA-2)</p>
            </td>
            <td>
                <p>—</p>
            </td>
            <td>
                <p>
                    PSA_ALG_ECDSA(PSA_ALG_SHA_224)<br>
                    PSA_ALG_ECDSA(PSA_ALG_SHA_256)<br>
                    PSA_ALG_ECDSA(PSA_ALG_SHA_384)<br>
                    PSA_ALG_ECDSA(PSA_ALG_SHA_512)
                </p>
            </td>
        </tr>
    </tbody>
</table>

**Key Attributes in PSA Crypto**

|**Algorithm**|**Key Type**|**Key Size in Bits**|**Key Usage Flag**|
|---|---|---|---|
|• PSA_ALG_ECDSA(PSA_ALG_SHA_224)|PSA_ECC_FAMILY_SECP_R1|• secp192r1 : 192|• PSA_KEY_USAGE_SIGN_HASH|
|• PSA_ALG_ECDSA(PSA_ALG_SHA_256)| |• secp256r1 : 256|• PSA_KEY_USAGE_VERIFY_HASH|
|• PSA_ALG_ECDSA(PSA_ALG_SHA_384)| |• secp384r1 : 384| |
|• PSA_ALG_ECDSA(PSA_ALG_SHA_512)| |• secp521r1 : 521| |

> **Note**: The key usage flag must use PSA_KEY_USAGE_SIGN_HASH and PSA_KEY_USAGE_VERIFY_HASH.

**Security Software Components**

|**Algorithm**|**Security Software Components**|
|---|---|
|PSA_ALG_ECDSA(PSA_ALG_SHA_224)|ECDSA and SHA-224|
|PSA_ALG_ECDSA(PSA_ALG_SHA_256)|ECDSA amd SHA-256|
|PSA_ALG_ECDSA(PSA_ALG_SHA_384)|ECDSA and SHA-384|
|PSA_ALG_ECDSA(PSA_ALG_SHA_512)|ECDSA and SHA-512|

|**PSA_ECC_FAMILY_SECP_R1**|**Security Software Components**|
|---|---|
|secp192r1|secp192r1|
|secp256r1|secp256r1|
|secp384r1|secp384r1|
|secp521r1|secp521r1|

|**Built-in Key**|**Security Software Components**|
|---|---|
|secp256r1 keys in SE OTP|Built-In Keys|

|**Item**|**Security Software Components**|
|---|---|
|SHA-1 for X.509 certificate|SHA-1|

|**Item**|**Security Software Components**|
|---|---|
|X.509 certificate support|X.509|

**Using Opaque ECDSA Key to Generate Certificate Signing Request (CSR)**

1. Declare (and allocate) an object of type `mbedtls_pk_context` (PK context) and an object of type `psa_key_id_t` (key identifier).
2. Use the key identifier to generate an ECDSA key or load the built-in ECDSA key. Refer to the [Asymmetric Key](#asymmetric-key) for details.
3. Set up the PK context to wrap that PSA key by calling `mbedtls_pk_setup_opaque(mbedtls_pk_context \*ctx, const psa_key_id_t key)`.
4. Configure the pending CSR object to use that key by calling `mbedtls_x509write_csr_set_key(mbedtls_x509write_csr \*ctx, mbedtls_pk_context \*key)` on that PK context.
5. Call any other function that needs to configure and generate the CSR.
6. After generating the CSR, free the PK context using `mbedtls_pk_free(mbedtls_pk_context \*ctx)`. It only frees the PK context itself and leaves the key identifier untouched.
7. Either keep using the key identifier or call `psa_destroy_key()` on it, depending on the application flow.

**PSA Crypto Platform Example**

Click the `View Project Documentation` link to open the `readme` file.

![image30](/mbedtls-psa-crypto-porting-guide/0.1/images/sld817-image30.png)

The following table describes the implementation status of the PSA Crypto X.509 platform example.

|**ECDSA Key**|**Series 1**|**Series 2 - VSE**|**Series 2 - HSE**|**Remark**|
|---|---|---|---|---|
|secp192r1|Y|Y|Y|—|
|secp256r1|Y|Y|Y|—|
|secp384r1|Y|Y|Y|Hardware acceleration only on HSE-SVH devices.|
|secp521r1|Y|Y|Y|Hardware acceleration only on HSE-SVH devices.|
|Private Device Key|—|—|Y|Only on HSE-SVH devices.|

> **Note**: This example can select the Private Device Key (secp256r1) to generate the root certificate CSR.

### Performance

#### Performance

This section describes tools to help improve network performance.

- [**Manufacturing Test Overview**](/openthread/3.1.0/mfg-test-overview): Provides a high-level description of the different options for integrating RF testing and characterization into standard test flows. It is intended for customers who are moving from the early prototype development stage to the manufacturing production environment and need assistance with manufacturing test.
- [**Manufacturing Test Guidelines for the EFR32**](/openthread/3.1.0/mfg-test-guidelines-efr32): Details the different options for integrating RF testing and characterization into standard test flows.
- [**Performance Results for Multi-PAN RCP for OpenThread and Zigbee**](/openthread/3.1.0/multi-pan-rcp-performance-for-openthread-and-zigbee): Summarizes the performance test effort and results for some testing scenarios of the CPCd interface using multi-PAN for both OpenThread and Zigbee protocols.
- [**Mesh Network Performance Comparison**](/openthread/3.1.0/mesh-network-performance-comparison): Reviews the Zigbee, Thread, and Bluetooth mesh networks to evaluate their differences in performance and behavior.
- [**OpenThread Mesh Network Performance**](https://www.silabs.com/documents/public/application-notes/an1408-openthread-mesh-network-performance.pdf): Details methods for testing Thread mesh network performance; results are intended to provide guidance on design practices and principles as well as expected field performance results.

#### OpenThread Mesh Network Performance (PDF)

- [OpenThread Mesh Network Performance (PDF)](https://www.silabs.com/documents/public/application-notes/an1408-openthread-mesh-network-performance.pdf)
#### Manufacturing Test Overview

##### Manufacturing Test Overview

> **Note: This section replaces _AN718: Manufacturing Test Overview_. Further updates to this application note will be provided here**.

Most customers have standard product manufacturing test flows, but some do not incorporate RF testing. This document describes the different options for integrating RF testing and  characterization into your standard test flows. This application note is intended for Silicon Labs customers who are moving from the early prototype development stage to the manufacturing production environment and need assistance with manufacturing test. The specific target audience is senior manufacturing engineers and manufacturing managers who are investigating test processes for their Silicon Labs-enabled products.

This document summarizes approaches to manufacturing tests for all Silicon Labs protocols and platforms. Additional detail is provided in [Manufacturing Test Guidelines for the EFR32 Family](https://docs.silabs.com/zigbee/latest/mfg-test-guidelines-efr32/).

###### Key Points

- Manufacturing test flow
- Test definitions
- Test recommendations
- Embedded Software tools
- Regulatory and Compliance Testing

##### Manufacturing Test Flow

PCB manufacturing testing has two primary objectives: verifying components are placed properly on the boards and verifying functionality of the boards. The overall goal is to maximize test coverage while minimizing test costs.

Manufacturing testing parallels the product lifecycle in that each phase is unique and builds on the previous phase. The following figure shows the traditional product lifecycle and how the different phases of the manufacturing process align with it.

![Product Lifecycle](/mfg-test-overview/0.1/images/sld869-image1.jpg)

Silicon Labs recommends four phases of testing products according to the product lifecycle. Each phase has a purpose and builds off the previous stages.

1. Prototype: The objective of this phase is to fully verify the design on a small number of devices.
2. Characterization: This phase verifies the functionality of the product.

Once the product has been fully characterized, volume testing is next. The two phases of volume manufacturing testing are low-volume and high-volume.

1. Low-volume: This phase contains a subset of the tests run during the characterization phase but with reduced test time and coverage.
2. High-volume: This phase is a much faster test, which allows for ease of test and scalability.

The objective of these manufacturing test phases is to verify that components are placed properly on the boards. The tradeoffs for these phases discussed the next sections are the type of testing, test coverage, data collection, test application, test time, and test cost.

###### Phase 1 - Prototype Testing

The first phase of manufacturing test involves initial prototype testing, also known as design verification/validation. This involves product that has gone through its “first build” on a new product introduction (NPI) assembly line. This phase incorporates bench tests with test equipment and usually is not automated. Prototype testing usually involves the engineering design team. Therefore it is time consuming and expensive, but it is very important in verifying the product functionality.

As part of design verification, the product should be tested over the full environmental product requirements including temperature range and voltage supply range. The product should also be subjected to certification and compliance testing, which may include FCC, ETSI, CE, and any applicable protocol-specific compliance testing. Finally, this phase includes general qualification testing, according to JEDEC standards.

Phase I tradeoffs are as follows:

- Volume: First 5-50 boards
- Type of Testing: Bench test with test equipment, not necessarily automated
- Test Coverage: Full design verification
- Data Collection: Not necessarily automated but very detailed
- Test Application: Standalone application
- Test Time: Hours per device (or months for some qualification tests)
- Test Cost: Expensive

###### Phase II - Characterization Testing

The second phase of testing in the product lifecycle is characterization testing. The objective of this phase is to verify functionality and repeatability. During this phase, the hardware is manufactured in higher volume (on an NPI line or production assembly line). The assembled product is fully characterized with automated test programs to determine design performance and manufacturability, as well as to collect valuable test data to be used to help with setting test limits in later phases. This phase also provides yield expectations and provides valuable design for manufacturability (DFM) and design for test (DFT) feedback to the engineering design team.

Phase II tradeoffs are as follows:

- Volume: Next 500 to 1,000 boards, depending on the customer
- Type of Testing: Automated with test equipment
- Test Coverage: Full design verification
- Data Collection: Automated, datalogs
- Test Application: Standalone application or application test mode
- Test Time: 10–30 minutes per device
- Test Cost: Expensive

###### Phase III - Low Volume Manufacturing Testing

The third phase is low-volume manufacturing. The objective of the volume manufacturing test phases (Phases III and IV) involves the verification of component placement. During this phase, a subset of the characterization testing may be performed. Test data from the characterization stage is used to help determine which tests may be reduced or eliminated. Test time during this stage is more important than the characterization stage because volumes are increased, but is still not crucial. In addition, yield analysis should be done on a continual basis with appropriate feedback provided to the engineering design team.

Phase III tradeoffs are as follows:

- Volume: Next 1,000 boards
- Type of Testing: Automated with subset of test equipment
- Test Coverage: Subset of characterization tests
- Data Collection: Automated, datalogs
- Test Application: Standalone application or application test mode
- Test Time: 2–5 minutes per device
- Test Cost: Moderate

###### Phase IV - High Volume Manufacturing Testing

The fourth and final phase is high-volume manufacturing. During this phase, test time is crucial and only minimal testing may be required depending on the customer and the application. A Golden Node application (a known good device that can be used in test for repeatable measurements) can be developed to transmit and receive packets to and from a device under test (DUT) to verify basic functionality. To further reduce test time, a manufacturing library can be used to allow for a test mode within the application itself, thus avoiding multiple programming steps.

Phase IV tradeoffs are as follows:

- Volume: After 1,000 to 2,000 boards
- Type of Testing: Automated with subset of test equipment
- Test Coverage: Minimal, basic functionality
- Data Collection: Minimal data, still automated
- Test Application: Application test mode
- Test Time: Less than 1 minute per device
- Test Cost: Minimal

##### Test Definitions

Automated test is defined as a test method where test equipment and DUT are controlled by a PC. A test program on the PC controls the test equipment and DUT. The DUT is loaded with embedded software that allows the radio to be configured for particular tests.

The tests can be divided into different types of tests— RF testing, DC testing, and peripheral testing.

- RF testing is any test specific to the operation and functionality of the radio (for example, transmitting and receiving Zigbee packets).
- DC testing is any test related to the voltage and current characteristics of the device or board (for example, active and sleep currents).
- Peripheral testing is any test not specific to RF or DC, like a sensor or an external crystal.

For more details on the specific recommended tests, refer to [Manufacturing Test Guidelines for the EFR32](https://docs.silabs.com/zigbee/latest/mfg-test-guidelines-efr32/).

##### Test Recommendations

This section summarizes the functionality that should be tested on the hardware product, depending on the phase. These recommendations are for SoC (System-on-Chip) products. Silicon Labs also offers EFR-based modules that are pre-certified or fully certified. In such cases, the customer’s end product will require limited RF testing depending on the region and its compliance to the regulatory standards. See _AN1048: Regulatory RF Module Certifications_ for more information on module certifications and the required testing.

###### Prototype Testing

Prototype testing is necessary for all product designs. In this phase of testing, the design is verified across the full environmental product requirements, including temperature range and voltage supply range, as well as humidity range in some cases. This level of environmental testing may occur over several months, depending on the qualification requirements of the product. Certification and compliance testing may also be necessary in this phase of testing. External test houses are likely to be involved to support this testing. This phase fully verifies the design of the hardware and product that is being developed, and helps identify any design issues that need to be corrected before proceeding to the characterization testing phase.

###### Characterization Testing

Characterization testing is recommended for early production stages. In this phase of testing, the RF functionality (transmit and receive) should be characterized on all applicable channels or a subset of these channels, as well as at various transmit output power levels or receiver input power levels. This phase fully characterizes the hardware that is being developed, determines the tests to be executed in manufacturing test, determines the test limits of these tests, and flushes out any manufacturing or process issues that might be present.

###### Low-Volume Manufacturing Testing

Low-volume manufacturing testing is usually a subset of the characterization testing. A subset of the applicable channels or transmit output power levels can be tested to reduce the test time without compromising test coverage. For example, one channel/power level combination (likely mid-band at max power) can be measured for transmit and receive functionality.

The results from the characterization phase of testing help determine not only what should be tested in the manufacturing phase but also the test limits to be applied to certain tests. For example, if a particular test does not fail at all during the characterization phase, it can be omitted from the manufacturing phase altogether. Also, if it is determined that a particular test will fail all channels if it fails at all, testing can be reduced from all channels to a single channel, most likely mid-band.

###### High-Volume Manufacturing Testing

High-volume manufacturing testing is much simpler than characterization testing or low-volume manufacturing testing. The hardware design and manufacturing process have already been proven, so the product now just requires a quick “go/no go” transmit and receive functional test to verify operation.

##### Embedded Software Tools

The embedded software application could be a standalone test application or the customer’s own application with a test mode included. Silicon Labs provides different test applications depending on the protocol being used. These applications are discussed in detail in [Manufacturing Test Guidelines for the EFR32](https://docs.silabs.com/zigbee/latest/mfg-test-guidelines-efr32/).

The following table summarizes the test tools provided by Silicon Labs.

|**Software Tool**|**Family**|**Protocol**|
|---|---|---|
|NodeTest|EFR32|Zigbee|
|Manufacturing Library|EFR32|Zigbee|
|RAILtest|EFR32|Zigbee / Bluetooth LE / OpenThread / Proprietary/Z-Wave|
|Direct Test Mode (DTM)|EFR32|Bluetooth LE|

**NodeTest**

The NodeTest standalone application is provided with the EmberZNet SDK (Software Development Kit). Silicon Labs recommends NodeTest for any test stage in which the customer’s Zigbee application is not yet mature enough to include a test mode. NodeTest provides a serial command line interface to the Silicon Labs device. Instructions for using NodeTest are provided in _AN1019: Using the NodeTest Application_.

**Manufacturing Library**

Silicon Labs recommends that customers use the manufacturing library in mature applications, regardless of the testing phase. Customers without mature applications can build a simple Zigbee sample application with the manufacturing library enabled to access this functionality. The manufacturing library provides access to a test mode within the application and removes the need for multiple application bootloads or multiple programming steps within the manufacturing process. The manufacturing library is available as a configurable plugin in the EmberZNet SDK. The guidelines for enabling the manufacturing library plugin and using the manufacturing library CLI commands for manufacturing tests are provided in [Using the Manufacturing Library for EmberZNet](https://docs.silabs.com/zigbee/latest/using-manufacturing-library-for-emberznet/).

**RAILtest**

The RAILtest standalone application is provided with the Flex SDK. It provides customers with a simple tool for testing the radio and the functionality of the RAIL library. For any advanced usage customers should write their own software with a custom radio configuration. RAILtest is documented in [RAILtest User's Guide](https://docs.silabs.com/rail/latest/railtest-users-guide/), [EFR32 RF Evaluation Guide](https://docs.silabs.com/rail/latest/efr32-rf-eval-guide/), and [Bring-up/Test HW Development](https://docs.silabs.com/z-wave/latest/z-wave-bring-up-test-hardware-development/).

**Direct Test Mode protocol (DTM)**

The DTM protocol is defined in the Bluetooth specification as a means for testing the radio performance of Bluetooth low energy products Bluetooth-enabled Silicon Labs EFR32xG SoCs and the BGM/MGM modules support both the DTM upper and lower testers. DTM testing is described in _AN1046: Radio Frequency Physical Layer Evaluation in the Bluetooth® SDK v2.x_ and [Radio Frequency Physical Layer Evaluation in Bluetooth® SDK v3.x](https://docs.silabs.com/bluetooth/latest/bt-rf-phy-evaluation-using-dtm-sdk-v3x/).

##### Radio Frequency Regulatory and Compliance Testing

Customers need to perform RF measurements to validate and certify the product’s compliance with the regulatory rules. The level of testing depends on the Silicon Labs product. For example, the SoC/IC-level wireless components cannot be pre-certified because they do not have a fixed RF path and antenna. Customers therefore must conduct the full set of radio tests to certify their product. The test tools referenced in section [Embedded Software Tools](04-embedded-software-tools) should be used to validate the RF performance of the device. For more information on RF Regulatory requirements for various regions, refer to _AN1048: Regulatory RF Module Certifications_.

##### Range Test

A crucial aspect of designing an RF wireless product is maximizing its operational range when communicating with another device. To achieve this, perform a range test, which could be the final but important step for characterizing the product. The test setup consists of a transmitter and a receiver with their known RF parameters (transmit power, receiver sensitivity) and their spatial arrangement. Wireless communication is then initiated (frequency, data rate, and modulation parameters chosen according to the desired operation), and the receiver performance is measured.

The accounting of the measurable parameters and environmental effects is called the link budget, according to the following equation:

![link budget](/mfg-test-overview/0.1/images/sld869-image2.png)

where:

- PRX: received power (dBm)
- PTX: transmitter output power (dBm)
- GTX: transmitter antenna gain (dBi)
- LTX: transmitter losses (cables, connectors) (dB)
- LFS: path loss (free space and multipath propagation) (dB)
- LM: miscellaneous losses (fading, polarization, mismatch, interference, absorption, weather) (dB)
- GRX: receiver antenna gain (dBi)
- LRX: receiver losses (cables, connectors) (dB)

However, other variables play a crucial role in the quality of the RF link, such as:

- Antenna radiation pattern: Silicon Labs recommends placing the transmitter and receiver devices facing each another with their maximas of radiation.
- Frequency offset: Any occurrent frequency offset between the transmitter and receiver can degrade the range.
- Distance from the ground: The proximity of ground degrades performance.

See KBA [RF range factors](https://community.silabs.com/s/article/rf-range-factors?language=en_US) for a list of the most prominent effecting variables.

Prior to the range test, ensure that the product is finalized in terms of overall RF performance (TX power, harmonics, RX sensitivity, antenna matching) and that the test procedure best represents a use case scenario with the inclusion of the plastic case or having a human hand in the proximity of the device (if there is going to be one).

Sensitivity is the minimum received power at the antenna port of the device at which the data transfer is still adequately successful. A common way of determining sensitivity is by counting the number of sent, and successfully received packets, and calculating the Packet Error Rate percentage (PER%). A similar method is calculating the Bit Error Rate percentage (BER%). Refer to the datasheet of the Silicon Labs device for the ideal sensitivity values depending on data rate, frequency, and modulation.

Silicon Labs recommends first choosing an open field with as few environmental factors as possible to determine the maximum range. Then, measure the BER or PER and modify the range accordingly, so that the measured value is equal to or less than the predefined value in the datasheet of the device. If equal, the received signal strength at the antenna port equals the sensitivity of the device by definition.

It is good practice to choose an operating range that is smaller than the determined maximum range so that there is some margin (link margin) between the received signal and the sensitivity of the device to allow for the detrimental effects of the degrading variables.

After the range of the communication has been determined, any introduced additional link budget to the system (e.g., by increasing the transmitter power) also yields an improvement in the range of the RF link, which can be approximated by the following formula:

![RF link](/mfg-test-overview/0.1/images/sld869-image3.png)

where:

- ΔLB is the extra link budget introduced to the RF link
- n is the propagation factor, which is usually between 2.8 and 4 (3 for line of sight) outdoors
- Roriginal is the range prior to the introduced link budget
- Rnew is the improved range after the introduced link budget
- ΔR is the ratio of the new range to the original range

As an example, this means that if the link budget introduced to the RF link is e.g., 3 dB and n = 3 is assumed, the calculated range increase is ΔR = 1.25 (25% improvement).

Follow [Flex SDK v3.x Range Test Demo User's Guide](https://docs.silabs.com/rail/latest/flex-v3x-range-test-demo/) for the range test evaluation of the Gecko EFR32 devices.

#### Manufacturing Test Guidelines For The EFR32

##### Manufacturing Test Guidelines for the EFR32

> **Note: This section replaces _AN700.1: Manufacturing Test Guidelines for the EFR32_. Further updates to this application note will be provided here**.

Most customers have standard product manufacturing test flows, but some do not incorporate RF testing. [Manufacturing Test Overview](https://docs.silabs.com/zigbee/latest/mfg-test-overview/) provides a high-level overview of the product tests flow and phases – prototype testing, characterization testing, low-volume manufacturing, and high-volume manufacturing. This document’s goal is to provide the finer details of these test phases and Silicon Labs’ recommended best practices for manufacturing test. This document is intended for Silicon Labs customers developing for the EFR32 product family and who are moving from the early prototype development stage to the manufacturing production environment.

###### Key Points

- Test flow
- Test definitions
- Test recommendations
- Test architecture and equipment
- Embedded software tools

##### Introduction

Most customers have standard product manufacturing test flows, but some do not incorporate RF testing. This document details the different options for integrating RF testing and characterization into your standard test flows. [Manufacturing Test Overview](https://docs.silabs.com/zigbee/latest/mfg-test-overview/) provides a high-level overview of the product tests flow and phases – prototype testing, characterization testing, low-volume manufacturing, and high-volume manufacturing. This document’s goal is to provide the finer details of these test phases, introduction to the test tools available, and Silicon Labs’ recommended best practices for manufacturing test, focusing on the EFR32 family of products. Due to the various options available during prototype testing, this document focuses on the characterization and manufacturing test phases.

Device programming is not discussed in this application note. For information on the programming options available for EFR32 devices, see [https://www.silabs.com/products/mcu/programming-options](https://www.silabs.com/products/mcu/programming-options).

If after reading this document you have questions or require assistance with the procedures described, contact a support representative at [https://www.silabs.com/support](https://www.silabs.com/support).

##### Test Definitions

To communicate with the device under test (DUT) and control various device test modes by automated test software, Silicon Labs provides embedded software applications to allow for both serial communication as well as Over the Air (OTA) communication to automated test software. Both of these interfaces enable configuring a device for receive or transmit modes, turning on peripherals (if applicable), reading Analog to Digital Conversion (ADC) pins on the micro (if applicable), putting the radio and/or microprocessor to sleep, and similar control functions. See [Embedded Software Tools](03-embedded-software-tools#embedded-software-tools) for a description of these applications.

The tests can be divided into different types of tests: RF testing, DC testing, and peripheral testing.

- RF testing is any test specific to the operation and functionality of the radio (for example, transmitting and receiving packets).
- DC testing is any test related to the voltage and current characteristics of the device or board (for example, active and sleep currents).
- Peripheral testing is any test not specific to RF or DC, like a sensor or an external crystal.

The following sections describe the tests that make up the potential suite of DUT testing.

###### Serial Communication Test

The Serial Communication Test verifies valid serial communication with the DUT before testing. This is a basic check that the device has been programmed correctly. If no communication is present, the DUT fails this test and does not proceed with further testing. This test is important because, if there is no serial communication with the DUT, there is no way to interface with the DUT to put the device into test mode or send commands in a standalone test application.

###### Supply Current Test

The Supply Current Test verifies that current consumption is valid for each mode of operation for the DUT. The modes of operation are set through the serial interface and include transmit mode, receive mode, and sleep modes (radio sleep and deep sleep). If there is excessive current draw, the DUT fails this test and does not proceed with further testing. This test is especially important for devices that will be used in battery-operated applications, as these measurements are an effective predictor of battery life.

Guidelines for measuring current consumption are provided in _AN969: Measuring Power Consumption on Wireless Gecko Devices_.

_AN1082: EFR32 Transmit and Receive Current Measurements_ provides some more insight into the current measurement procedure and configurations.

###### Quick Verify of Transmit and Receive Test

The Quick Verify of Transmit and Receive Test quickly verifies that the DUT transmits valid packets to the Reference Node and receives valid packets from the signal generator. This can be tested on any single channel in the available frequency band. If either of these checks fails, the DUT fails this test and does not proceed with further testing. This test identifies hardware that does not require full characterization testing due to a major manufacturing defect.

###### Transmitter Tests

###### Transmit Power Test (heading level 7)

The Transmit Power Test verifies that the power level of the transmitter is at the appropriate level and within a specified range. The power output is measured with the power meter at multiple power levels to confirm power output accuracy at various coded settings. The serial command interface can include a function that enables continuous waveform (CW) or unmodulated tone to be transmitted for ease of measuring these power levels. Silicon Labs recommends that this test be performed over a subset of the frequency band to record trends in power output versus frequency.

The transmit power output can be measured with a spectrum analyzer or a power meter.

###### Transmit Frequency Offset Test (heading level 7)

The Transmit Frequency Offset Test verifies the crystal accuracy and valid transmission frequency offset of the DUT. The CW tone is again used for this transmission and the crystal capacitor value is set using software.

The EFR32 38.4MHz crystal does not require external loading caps as there is a tunable capacitor bank in the EFR32 that can be used instead. Different capacitor values can be written to registers to observe the corresponding frequency offset, and the CTUNE manufacturing token is used to store the value for use by the application. The optimal CTUNE value for the DUT can be determined in the design characterization stage by sweeping all CTUNE values at the transmit frequency and measuring the frequency offset with a spectrum analyzer. This value can then be programmed as the CTUNE manufacturing token in volume manufacturing, or each board can be tested for optimal CTUNE value, using the value from the design verification stage as a starting point. Note that the measured frequency offset will differ depending on the frequency band, so Silicon Labs recommends determining CTUNE for each frequency band needed for the device.

Additionally, the test can be performed over a subset of the frequency band to record trends versus frequency.

For information on how to set the CTUNE token, please refer to the applicable software tool application note described in section [Embedded Software Tools](03-embedded-software-tools).

###### Transmit Error Vector Magnitude Test (heading level 7)

The Transmit Error Vector Magnitude (EVM) test verifies that the device’s EVM is within specified limits. The EVM is measured with a spectrum analyzer. Either a transmit packet or transmit stream command is used for this transmission, as the spectrum itself is analyzed. Silicon Labs recommends that this test be performed over a subset of the frequency band to record trends versus frequency.

Some spectrum analyzers are able to measure EVM as a standalone instrument, while other spectrum analyzers require a PC software tool to provide EVM measurement details.

###### Transmit Sweep Test (heading level 7)

The Transmit Sweep Test verifies transmission of valid packets from the DUT to the Reference Node at all channels or a subset of channels across the frequency band. The Reference Node is put into receive mode while the DUT transmits 100 packets to the Reference Node for each channel, with an attenuation between nodes that translates to a strong signal, approximately 60 dB attenuation between devices. Please refer to the specific radio chip data sheet for more information.

Packet success rate is measured at each channel. The packet success rate percentage is defined as the number of packets received divided by the number of packets transmitted and then multiplied by 100. For the Transmit Sweep Test, anything below 100% packet success rate is flagged as a failure, as this test is conducted at a signal level where all packets should be received. This test confirms that there are no frequency-dependent issues with transmit mode.

###### Spurious Emissions Test (heading level 7)

The Spurious Emissions test verifies that the transmitter’s unwanted emissions outside the channel bandwidth that result from the modulation process and non-linearity are within the applicable limits set by regulatory bodies. A transmit tone from the DUT can be used to measure the out-of-band emission levels in a spectrum analyzer.

###### Receiver Tests

###### Receive Sweep Test (heading level 7)

The Receive Sweep Test is similar to the Transmit Sweep Test. It verifies reception of valid packets at the DUT from the signal generator at all channels or a subset of channels across the frequency band and at two receiver input power levels (a strong signal level, approximately -50 dBm, and a level closer to the edge of sensitivity performance, approximately -90 dBm).

The DUT is put into receive mode while the signal generator transmits 100 packets for each channel. Packet success rate is measured at each channel/receive input level. Any packets missed at the strong signal level are considered a failure, while the failure threshold at the lower input level can be at a lower percentage, depending on the expected sensitivity of the radio. Please refer to the data sheet of the radio chip for details related to receive sensitivity. This test should be performed over the full operating band to record trends versus frequency.

Silicon Labs recommends using a signal generator to transmit packets to a DUT, as the signal generator allows for easily configuring the receive input power level at the DUT.

###### Receive Sensitivity Test (heading level 7)

The Receive Sensitivity Test determines the receiver sensitivity of the DUT by measuring the input power level at 0.1% BER (Bit Error Rate) or 1% PER (Packet Error Rate) depending on the radio chip configuration. Please refer to the data sheet for the specific radio chip information on the sensitivity requirements. To measure PER, the DUT is placed in receive mode with 1,000 valid packets being sent from the signal generator for each channel. The power level should begin at some level before the 1% Packet Error Rate (PER) threshold. The PER is measured until the receiver input power level corresponding to 1% PER is determined. This test should be performed over a subset of the operating band to record trends versus frequency. During the characterization testing phase Silicon Labs recommends that the actual sensitivity level be determined, while at high volumes the receive sensitivity specification can be set as the low limit and be used as a single power level for ease of testing.

Similarly, to measure BER, the DUT is placed in receive mode and the signal generator sends valid bits to the DUT for each channel. The BER is measured until the receiver input power level corresponds to 0.1 % BER.

Silicon Labs recommends using a signal generator to transmit packets to a DUT, as the signal generator allows for easily configuring the receive input power level at the DUT.

###### Receive Waterfall Test (heading level 7)

The Receive Waterfall Test determines the receiver sensitivity of the DUT by collecting data to determine the receiver roll-off curve. The DUT is placed in receive mode with 100 valid packets being sent from the signal generator for each channel. The power level should begin at some level before the 1% PER or 0.1% BER threshold. Please refer to the data sheet for the specific radio chip for more information on the sensitivity. The packet success rate is measured for all input powers selected for testing. Silicon Labs recommends that enough input power levels are selected to ensure that the data collected includes both 100% and 0% packets received. This allows for a complete roll-off curve to be observed. This test should be performed over a subset of the operating band to record trends versus frequency. Silicon Labs recommends this test for characterization testing so that the roll-off is quantified and understood but can be omitted at higher volumes.

Silicon Labs recommends using a signal generator to transmit packets to a DUT, as the signal generator allows for easily configuring the receive input power level at the DUT.

###### Receive Signal Strength Indicator (RSSI) Test (heading level 7)

The RSSI Test measures the RSSI value for a single channel and known receiver input power level. The RSSI is determined by receiving a valid packet from the signal generator and reading the RSSI value through the serial command interface. The DUT is placed into receive mode while the signal generator transmits a single packet and the RSSI measurement is averaged to determine RSSI value. This single data point is measured to verify that the RSSI pin for the radio chip is connected and that RSSI is reporting a valid level. The RSSI operation of the chip itself is validated at the chip testing level and is not tested here.

Silicon Labs recommends using a signal generator to transmit packets to a DUT, as the signal generator allows for easily configuring the receive input power level at the DUT.

###### External 32kHz Test

The operation of the external 32 kHz crystal (if applicable) should be verified through the LFXO tune manufacturing token.

###### Peripherals Test

Various peripherals, if applicable, can be tested through the serial port. These include anything that may be accessed through the ADC or GPIO (general purpose I/O) on the micro. For example, an LED is often tied to a GPIO pin for some status to be alerted. The state of the LED can be modified by changing the level of the GPIO pin. Another example is reading an ADC pin for a particular voltage level that corresponds to the status of a peripheral such as a temperature sensor or an accelerometer. Any peripheral accessible through GPIO or the ADC should be tested to ensure valid functionality.

##### Embedded Software Tools

Silicon Labs supports multiple embedded software tools for manufacturing test for the EFR32. This supports testing with a standalone test application or a test mode within the customer application.

**NodeTest**

The NodeTest is a pre-built standalone application is provided with the EmberZNet SDK (Software Development Kit). Silicon Labs recommends that NodeTest be used only for radio boards in the kit for evaluation purposes. NodeTest provides a serial command line interface to the Silicon Labs device. Instructions for using NodeTest are provided in _AN1019: Using the NodeTest Application_. Note that NodeTest has been deprecated in EmberZNet SDK 7.0.

**Manufacturing Library**

Silicon Labs recommends that customers use the manufacturing library in mature applications, regardless of the testing phase. Customers without mature applications can build a simple Zigbee sample application with the manufacturing library enabled to access this functionality. The manufacturing library provides access to a test mode within the application and removes the need for multiple application bootloads or multiple programming steps within the manufacturing process. The manufacturing library is available as a configurable component/plugin in the EmberZNet SDK. The guidelines for enabling the manufacturing library component/plugin and using the manufacturing library CLI commands for manufacturing tests are provided in [Using the Manufacturing Library for EmberZNet](https://docs.silabs.com/zigbee/latest/using-manufacturing-library-for-emberznet/).

**RAILtest**

The RAILtest standalone application is provided with the Flex SDK. It provides customers with a simple tool for testing the radio and the functionality of the RAIL library. For any advanced usage customers should write their own software with a custom radio configuration. RAILtest is documented in [RAILtest User's Guide](https://docs.silabs.com/rail/latest/railtest-users-guide/), [EFR32 RF Evaluation Guide](https://docs.silabs.com/rail/latest/efr32-rf-eval-guide/), and [Bring-up/Test HW Development](https://docs.silabs.com/z-wave/latest/z-wave-bring-up-test-hardware-development/).

**Direct Test Mode protocol (DTM)**

The DTM protocol is defined in the Bluetooth specification as a means for testing the radio performance of Bluetooth low energy products. Bluetooth-enabled Silicon Labs EFR32xG SoCs and the xGM modules support two approaches for RF PHY testing offered by DTM. One approach is where the RF PHY tester controls the DUT over the standardized HCI (host control interface) of the DUT. In another approach, the tester had direct access to the DUT through a dedicated 2-wire connection to control the radio tests on the DUT. More information on DTM testing is described in _AN1046: Bluetooth Radio Frequency Physical Layer Evaluation_.

##### Test Recommendations

This section outlines the various tests that can be run on the hardware product, which of those tests Silicon Labs recommends running, and the channel selection for each test in each phase.

###### SoC Test Recommendations

###### Characterization Testing (heading level 7)

Characterization testing is recommended for early production stages. In this phase of testing, the hardware is characterized on all 16 Zigbee channels or a subset of these channels, as well as at various transmit output power levels or receiver input power levels. This phase fully characterizes the hardware that is being developed, determines the tests to be executed in manufacturing test, determines the test limits of these tests, and flushes out any manufacturing or process issues that might be present.

Silicon Labs recommends that the tests outlined in the following table be conducted in the characterization phase of testing. This table, and the similar tables that follow in subsequent sections of this document, list the various tests that could be run on these devices, which of those tests Silicon Labs recommends running, and the channel selection for each test in each phase.

> **Note**: An X in these tables represents a test that is recommended for this phase of testing.

<table>
    <thead>
        <tr>
            <th rowspan="2"><strong>Test</strong></th>
            <th rowspan="2"><strong>Run?</strong></th>
            <th colspan="4"><strong>Channel</strong></th>
        </tr>
        <tr>
            <th><strong>Mid</strong></th>
            <th><strong>Low<br>Mid<br>High</strong></th>
            <th><strong>Subset</strong></th>
            <th><strong>All</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Serial Communication</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Supply Current</td>
            <td>X</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Transmit/Receive Verify</td>
            <td>X</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Transmit Power</td>
            <td>X</td>
            <td></td>
            <td>X</td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Transmit Frequency Offset</td>
            <td>X</td>
            <td></td>
            <td>X</td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Transmit EVM</td>
            <td>X</td>
            <td></td>
            <td>X</td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Transmit Sweep</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
            <td>X</td>
        </tr>
        <tr>
            <td>Spurious Emissions</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Receive Sweep</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
            <td>X</td>
        </tr>
        <tr>
            <td>Receive Sensitivity</td>
            <td>X</td>
            <td></td>
            <td>X</td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Receive Waterfall</td>
            <td>X</td>
            <td></td>
            <td>X</td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>RSSI</td>
            <td>X</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>External 32 kHz Crystal</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Peripherals</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
    </tbody>
</table>

###### Low-Volume Manufacturing Test (heading level 7)

Low-volume manufacturing test is usually a subset of the characterization testing. A subset of the 16 Zigbee channels or transmit output power levels can be tested to reduce the test time without compromising test coverage. For example, one channel/power level combination (likely mid-band at max power) can be measured for transmit power and frequency. Also, receive waterfall can be omitted and receive sensitivity can be run in its place, where a certain packet-success rate is expected at mid-band for a given input power level.

The results from the characterization phase of testing help determine not only what should be tested in the manufacturing phase but also the test limits to be applied to certain tests. For example, if a particular test does not fail at all during the characterization phase, it can be omitted from the manufacturing phase altogether. Also, if it is determined that a particular test will fail all channels if it fails at all, testing can be reduced from all channels to a single channel, most likely mid-band.

###### Test Recommendations (heading level 8)

The following table lists the tests Silicon Labs recommends be conducted in the low-volume manufacturing phase of testing.

<table>
    <thead>
        <tr>
            <th rowspan="2"><strong>Test</strong></th>
            <th rowspan="2"><strong>Run?</strong></th>
            <th colspan="4"><strong>Channel</strong></th>
        </tr>
        <tr>
            <th><strong>Mid</strong></th>
            <th><strong>Low<br>Mid<br>High</strong></th>
            <th><strong>Subset</strong></th>
            <th><strong>All</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Serial Communication</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Supply Current</td>
            <td>X</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Transmit/Receive Verify</td>
            <td>X</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Transmit Power</td>
            <td>X</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Transmit Frequency Offset</td>
            <td>X</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Transmit EVM</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Transmit Sweep</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td>X</td>
            <td></td>
        </tr>
        <tr>
            <td>Spurious Emissions</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Receive Sweep</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td>X</td>
            <td></td>
        </tr>
        <tr>
            <td>Receive Sensitivity</td>
            <td>X</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Receive Waterfall</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>RSSI</td>
            <td>X</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>External 32kHz Crystal</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Peripherals</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
    </tbody>
</table>

###### Test Times (heading level 8)

The typical test time that can be achieved in the low-volume manufacturing test phase is three minutes per board. If the devices are preprogrammed, the overall test time can be reduced to less than three minutes. Program times vary depending on the flash memory size of the microprocessor. Note that programming may be included at both the front end (test application) and back end (final application) of the process.

###### Setting Test Limits (heading level 8)

The results of the characterization phase of testing help determine how the limits are set for low-volume manufacturing test. Other factors in setting limits are customer application and manufacturing variation. For example, if an application specifies only a certain amount of dynamic range, perhaps limits will be relaxed to allow for this. Manufacturing variation can also be a factor in setting limits. For example, if the performance of the board is sensitive to particular components, it is important to account for any performance variation that may be seen with these particular components.

###### Full Characterization Sampling (heading level 8)

It is important to continue to fully characterize samples from each production run to ensure that nothing in the process has shifted, causing a difference in the overall performance of a production run compared to a previous run. The size of this sample can be determined by the manufacturer, but Silicon Labs recommends this full characterization sampling for additional test coverage and process control at volume testing.

###### High-Volume Manufacturing Test (heading level 7)

High-volume manufacturing testing is much simpler than characterization testing or low volume manufacturing testing. The hardware design and manufacturing process have already been proven, so the product now just requires a quick “go/no go” functional test to verify operation.

###### Test Recommendations (heading level 8)

Silicon Labs recommends that the tests in the following table be conducted in the high-volume phase of testing.

<table>
    <thead>
        <tr>
            <th rowspan="2"><strong>Test</strong></th>
            <th rowspan="2"><strong>Run?</strong></th>
            <th colspan="4"><strong>Channel</strong></th>
        </tr>
        <tr>
            <th><strong>Mid</strong></th>
            <th><strong>Low<br>Mid<br>High</strong></th>
            <th><strong>Subset</strong></th>
            <th><strong>All</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Serial Communication</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Supply Current</td>
            <td>X</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Transmit/Receive Verify</td>
            <td>X</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Transmit Power</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Transmit Frequency Offset</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Transmit EVM</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Transmit Sweep</td>
            <td></td>
            <td></td>
            <td></td>
            <td>X</td>
            <td></td>
        </tr>
        <tr>
            <td>Spurious Emissions</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Receive Sweep</td>
            <td></td>
            <td></td>
            <td></td>
            <td>X</td>
            <td></td>
        </tr>
        <tr>
            <td>Receive Sensitivity</td>
            <td>X</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Receive Waterfall</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>RSSI</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>External 32 kHz Crystal</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Peripherals</td>
            <td>X</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
    </tbody>
</table>

###### Test Times (heading level 8)

The typical test time that can be achieved in the high-volume manufacturing test phase is less than one minute per board. This assumes that devices are preprogrammed with the customer application and that the customer application uses the appropriate test tool for invoking test modes.

###### Setting Test Limits (heading level 8)

Since the test environment in the high-volume manufacturing phase is different from the test environment in the low-volume phase, setting the limits is also done differently. The test limits for the basic transmit and receive tests are dependent on the fixed attenuation between the Golden Node and the DUT, as well as the variation in over-the-air results. Silicon Labs recommends that customers run a sample size of boards through testing to determine these test limits.

###### Full Characterization Sampling (heading level 8)

Even in high-volume testing, it makes sense to fully characterize samples from each production run to ensure that the process has not shifted in any way. The size of this sample can be determined by the manufacturer, but this full characterization sampling is recommended for additional test coverage at high volumes.

###### PCB and SiP Module Testing

Silicon Labs offers EFR-based modules that are pre-certified or fully certified. When customers use PCB or SiP modules, the end product will require only limited RF testing depending on the module variant, market region and its compliance to the regulatory standards. Therefore, modules need only a subset of the tests described in Section [Test Definitions](02-test-definitions). For example, modules with an integrated antenna may only need a quick go/no-go test to verify functionality and modules with an external antenna path will need to be evaluated for its radiated RF performance. Refer to _AN1048: Regulatory RF Module Certifications_ for more information on module certifications and the required testing.

##### Test Architecture and Equipment

The following sections detail the recommended test architecture and equipment for each phase of testing, discuss test results and their dependence on test setup, and describe the typical manufacturing faults detected in manufacturing test.

###### Characterization Testing

The architecture and test equipment used in the characterization stage of testing is more comprehensive than that of the volume manufacturing stages.

###### Test Architecture (heading level 7)

The following figure shows an example of the interfaces of the test equipment to the DUT. The test equipment can be controlled by test software through the General Purpose Interface Bus (GPIB) or Recommended Standard 232 (RS-232). The DUT may be controlled by utilizing the Wireless Starter Kit Mainboard which contains an on-board J-Link debugger and a Virtual COM port over USB or Ethernet, enabling application development and debugging/testing of custom hardware. For more information on the debug interfaces available, please refer to _AN958: Debugging and Programming Interfaces for Custom Designs_. Any number of DUTs may be tested at once, but this number is dependent on serial ports available and will affect the selection of the power splitter and matrix switch hardware described in the following figure.

![High level Architecture Example of the Characterization Test](/mfg-test-guidelines-efr32/0.1/images/sld856-image1.png)

###### Recommended Equipment (heading level 7)

The recommended characterization test setup uses Agilent test equipment for the basic radio frequency (RF) measurements and current measurements, as well as for supplying power to the DUT and switching the RF connections from the DUT to various types of measurement equipment. A Reference Node, as mentioned in the characterization stage of testing, is any Ember radio communication module (RCM) configured with the same radio chip as the DUT that can be used to verify transmission of packets from the DUT.

The following table lists the basic set of test equipment Silicon Labs has used for characterization testing. Please consult the equipment manufacturer for the appropriate models for these particular instruments.

|**Description**|**Purpose**|
|---|---|
|Power Meter|Used with power sensor to measure transmit power of the DUT|
|Power Sensor|Used with power meter to measure transmit power of the DUT|
|Universal Counter|Measures the transmission frequency offset of the DUT|
|Spectrum Analyzer|Used to verify transmit power, Transmit Frequency Offset accuracy, and EVM of the DUT|
|Signal Generator|Verifies reception of valid packets at the DUT|
|Dual Output Power Supply|Powers the DUT and Reference Node|
|Digital Multimeter|Verifies current consumption of the DUT in various modes of operation|
|Data Acquisition Switch Unit|Supplies power to the DUT and switches RF connection|
|3.5 GHz 1:N RF Mux Module|Switches RF connection from the DUT to the Reference Node/Spectrum Analyzer, frequency counter, power meter, or signal generator|
|1:N Multiplexer Splitter|Splits power from test equipment to N DUTs; alternative parts can be used for more or fewer DUTs|
|Shielded RF Enclosure|Provides RF isolation for the DUT during testing|
|Reference Node|Known good radio module that is used as a receiver reference node in DUT transmit tests; manufacturer can be Silicon Labs, a Silicon Labs customer, or a Silicon Labs module partner|

> **Note**: A power meter and frequency counter may be used in place of a spectrum analyzer in characterization testing to measure transmit power and transmit frequency offset. In volume manufacturing test, the power meter and frequency counter are a much more cost-effective method of measuring transmit power and transmit frequency offset than the spectrum analyzer.

###### Configuring Equipment (heading level 7)

Some test equipment needs to be configured specifically for 802.15.4/BLE radio communications. For the signal generator, for instance, the specific packet needs to be configured. Silicon Labs recommends contacting the specific test equipment manufacturer for information on configuring the signal generator for valid 802.15.4/BLE packets.

###### Low-Cost Alternative to Signal Generator (Golden Node) (heading level 7)

For some customers, adding a signal generator to manufacturing test is not desired or not possible due to cost concerns. In this case, Silicon Labs recommends using a Golden Node (a known good device that can be used in test for repeatable measurements) with TCXO (temperature controlled/compensated crystal oscillator). The TCXO allows for frequency accuracy when using the Golden Node as a known good transmitter source for DUT receive tests. Using a Golden Node without TCXO would present issues with test accuracy, as the crystal would drift over time and temperature. The Golden Node will have known transmitter performance (0 dBm +/0.5 dBm) across voltage and temperature, allowing for test repeatability.

###### RF Test Interface Examples (heading level 7)

The type of interface to the DUT and the RF shielded test enclosure selected for testing can determine the accuracy and repeatability of the measurements.

It is important to pay special attention to the RF test interface to the DUT because of the sensitivity of these signals. For example, a product with a 50-Ohm terminated Subminiature Type A (SMA) connector populated on the board can be connected directly to a coaxial cable with a known loss. Repeatability in this scenario is very good.

Another example is a product with an embedded antenna that was designed with test points for RF and ground can be connected with a pogo-pin style RF probe. The path loss from the RF test point to the cabled connections of the setup can be calibrated to determine accurate performance. The repeatability of this setup is dependent on the board layout, in the sense that the RF and ground signals should have test points in close proximity to one another. The repeatability is also more dependent on the shielded enclosure in this case, because the RF signal is exposed at the test point rather than enclosed within an SMA connector as in the first example.

As a final example, a product with an embedded antenna can be tested over the air within an enclosure. A reference antenna would be used within the enclosure to feed back the RF signal to the RF Mux. The path loss over the air from the reference antenna to the DUT antenna can be calibrated to determine accurate performance. Fixed position of the DUT and position of the DUT and reference antennas are crucial to getting repeatable results.

###### Low-Volume Manufacturing Testing

The architecture and test equipment used in the characterization stage of testing is more comprehensive than that of the volume manufacturing stages.

###### Test Architecture (heading level 7)

The test architecture in the low-volume phase of testing is very similar to that of the characterization phase.

###### Recommended Equipment (heading level 7)

The equipment Silicon Labs recommends for the low-volume manufacturing phase of testing is very similar to that for the characterization phase. Some of the equipment may be removed depending on the tests selected for this phase.

###### Interference (heading level 7)

There are many RF devices in a test environment, such as wireless networks, microwave ovens, and mobile phones. For this reason, it is important to maintain RF isolation of the DUT from these sources of interference. It is also important to maintain RF isolation between multiple stations. For example, the equipment for test station A should be able to communicate only with a DUT from test station A and not with a DUT from test station B. Likewise, the equipment for test station B should be able to communicate only with a DUT from test station B and not a DUT from test station A. If these stations are not isolated from each other, the DUT will not be uniquely configured and multiple boards could share unique configuration information.

###### High-Volume Manufacturing Testing

The architecture and test equipment used in the characterization stage of testing is more comprehensive than that of the volume manufacturing phases.

###### Test Architecture (heading level 7)

The test architecture in the high-volume phase of testing can be structured any number of ways depending on customer preference. One approach is to develop this test with a Golden Node programmed with an application that allows the DUT to obtain unique configuration information from the Golden Node through packets transmitted and received. This test may be limited to configuring one DUT at a time. The Golden Node initiates communication with the unconfigured DUT and sends unique configuration information to the DUT. The Golden Node can then interface with the PC application to configure the DUT uniquely. Then the DUT can reboot itself and be ready for testing or application operation. A basic transmit/receive test can then be run. The test architecture for this phase of testing is shown in the following figure.

![Architecture Example of a High-Volume Manufacturing Test](/mfg-test-guidelines-efr32/0.1/images/sld856-image2.jpg)

Another approach is to develop a test with a serial interface to both the Golden Node and the DUT. This may be more straightforward to develop but requires a more extensive set up and use of a UART.

###### Recommended Equipment (heading level 7)

The equipment is based on the tests selected for this phase of testing, as well as the preference of the customer. No matter the approach, it is important that, at the very least, the Golden Node (and in some cases the DUT) have an interface to a PC or server so that it can log test information like EUI, test results, and so on.

###### Interference (heading level 7)

A facility running multiple test stations maintains RF isolation among the stations, as previously detailed. In this test phase, the only interface to the DUT is power, as all communication between the Golden Node and the DUT is done through the radio. All of the test equipment used in the characterization testing can be removed from the process, unless a customer decides to continue to test certain functionality on the board that requires test equipment.

###### Manufacturing Coverage

Manufacturing testing not only determines the tests that should be included in the manufacturing test phase, but it also helps detect manufacturing failures. The following sections provide some examples of typical manufacturing faults and how they are detected in testing.

###### IC Manufacturing (heading level 7)

###### Insufficient Solder on Ember IC (heading level 8)

An insufficient amount of solder will result in any number of failures in test. Insufficient solder will prevent some pins on the device from making contact with the pads on the board. This will result in programming failures, serial communication failures, and RF performance problems, all of which are detected in manufacturing test. Insufficient solder on the ground pad underneath the device will result in degraded RF performance, most likely in the receiver. This type of issue will be detected in either the Receive Waterfall Test or Receive Sensitivity Test. In some drastic cases where there is no solder on the ground pad and thus no ground connection from the device to the board, the device will not function and would either fail to program or fail to calibrate properly.

###### Wrong Component (heading level 8)

An incorrect component or component value will alter the performance of the board. For example, if a crystal tuning cap is the incorrect value, the Transmit Frequency Offset Test will detect this failure because the frequency will be outside the specified limits. If a component in the matching network is incorrect, this will affect the transmit power and will be detected in the Transmit Power Test. If a decoupling capacitor is the wrong value, it will affect the receiver performance of the device and will be detected in either the Receive Waterfall Test or the Receive Sensitivity Test.

###### Missing Component (heading level 8)

A missing component will also alter the performance of the board. For example, if there is a component missing from the RF path, there will be a major degradation in the transmit output power and overall receive sensitivity of the board. Also, if a component is missing that affects power distributed to the microprocessor or radio chips, the board will not be able to communicate serially in the case of the micro; the microprocessor will not configure the radio to transmit or receive in the case of the radio.

###### Solder Shorts or Opens (heading level 8)

A solder short or open on any component or device will cause any number of failures in manufacturing test. Any short or open in the RF circuitry of the board will cause degradation in performance and will be detected in various RF tests. A short or open in the DC circuitry or programming circuitry will prevent the device from programming properly and/or communicate properly with the test interface. For all of these cases mentioned, at least one test will detect a failure and flag this device as defective.

###### SiP Manufacturing (heading level 7)

SiP modules contain system-level components on a substrate inside a package and for this reason they require special guidance and considerations as part of a customer board manufacturing process. Refer to _AN1223: LGA Manufacturing Guidance_ for module manufacturing guidance.

##### Conclusions and Summary

As you can see from the descriptions of each test phase within this document, the recommended tests and test flow are different when comparing characterization testing with manufacturing testing.

The following table lists the test recommendations by phase and [Table : Comparison of Test Phases](#conclusions-and-summary) on page [16](06-conclusions-and-summary) compares the test phases.

> **Note**: In the following table, C denotes tests recommended for characterization testing, L denotes tests recommended for low-volume manufacturing testing, and H denotes tests recommended for high-volume manufacturing.

<table>
    <thead>
        <tr>
            <th rowspan="2"><strong>Test</strong></th>
            <th rowspan="2"><strong>Run?</strong></th>
            <th colspan="4"><strong>Channel</strong></th>
        </tr>
        <tr>
            <th><strong>Mid</strong></th>
            <th><strong>Low<br>Mid<br>High</strong></th>
            <th><strong>Subset</strong></th>
            <th><strong>All</strong></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Serial Communication</td>
            <td>CLH</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Supply Current</td>
            <td>CLH</td>
            <td>CLH</td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Transmit/Receive Verify</td>
            <td>CLH</td>
            <td>CLH</td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Transmit Power</td>
            <td>CL</td>
            <td>L</td>
            <td>C</td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Transmit Frequency Offset</td>
            <td>CL</td>
            <td>L</td>
            <td>C</td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Transmit EVM</td>
            <td>C</td>
            <td></td>
            <td>C</td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Transmit Sweep</td>
            <td>CL</td>
            <td></td>
            <td></td>
            <td>L</td>
            <td>C</td>
        </tr>
        <tr>
            <td>Spurious Emissions</td>
            <td>CL</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Receive Sweep</td>
            <td>CL</td>
            <td></td>
            <td></td>
            <td>L</td>
            <td>C</td>
        </tr>
        <tr>
            <td>Receive Sensitivity</td>
            <td>CLH</td>
            <td>LH</td>
            <td>C</td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Receive Waterfall</td>
            <td>C</td>
            <td></td>
            <td>C</td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>RSSI</td>
            <td>CL</td>
            <td>CL</td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>External 32kHz Crystal</td>
            <td>CLH</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td>Peripherals</td>
            <td>CLH</td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
    </tbody>
</table>

**Comparison of Test Phases**

|**Step**|**Characterization**|**Manufacturing Low-Volume**|**Manufacturing High-Volume**|
|---|---|---|---|
|Program bootloader (if applicable)|Functional Test|Functional Test or Preconfigured|Preconfigured|
|Program/load test application|Functional Test|Functional Test or Preconfigured|Standalone/Test Mode within application|
|Load stack information|Functional Test|Functional Test or Preconfigured|Preconfigured|
|Load manufacturing Information|Functional Test|Functional Test|Golden Node application|
|Load application information|Functional Test|Functional Test|Preconfigured|
|Verify DUT operation|Functional Test|Functional Test|Golden Node application|
|Program/load production application|Functional Test|Functional Test|Preconfigured|

In the characterization phase of testing, all programming and configuration steps can be automated to occur within the test itself. In the low-volume manufacturing phase, some of these steps can be done before actual manufacturing. For example, the device can be preconfigured with the appropriate bootloader and/or test application. In the case of high-volume manufacturing, the test functions can be included in the production application as a test mode or a standalone test application can be used. The Golden Node application can be developed by the customer to configure the appropriate unique manufacturing information for each DUT.

#### Multi-PAN RCP Performance for OpenThread and Zigbee

##### Performance Results for Concurrent Multiprotocol RCP for OpenThread and Zigbee

> **Note: This section replaces _AN1385: Performance Results for Concurrent Multiprotocol RCP for OpenThread and Zigbee_. Further updates to this application note will be provided here**.

This application note summarizes the results of simultaneous Thread and Zigbee throughput performance testing for the Concurrent Multiprotocol RCP, running both OpenThread and Zigbee on the host processor.

The measurements shown in this application note were taken in July of 2025 and are based on Simplicity SDK 2025.6.0.

###### Key Points

- Test plan strategy
- Approach and dependencies
- Test methodology
- Testbed and topologies
- Summary of results
- Results comparisons

##### Introduction

This document summarizes the results of simultaneous Thread and Zigbee throughput performance testing for the Concurrent Multiprotocol Radio Co-Processor (RCP), running both OpenThread and Zigbee on the host processor. The Multiprotocol RCP architecture relies heavily on the serial transport and multiplexing capabilities provided by the Co-processor Communication daemon (CPCd), so this is one of the crucial components under test in the system.

The goal of this testing is to validate the performance of the Multiprotocol RCP solution, including CPCd. In particular, results demonstrate that throughput performance of the Multiprotocol RCP for both Zigbee and Thread is comparable to that of a single-protocol SoC device.

See [Running Zigbee, OpenThread, and Bluetooth Concurrently on a Linux Host with a Multiprotocol RCP](https://docs.silabs.com/multiprotocol/latest/multiprotocol-solution-linux/) for a detailed description of the Multiprotocol RCP system's architecture, configuration, and usage, including CPCd.

Terms used below:

- OT: OpenThread
- Zig: Zigbee
- DuT: Device Under Test
- DMP: Dynamic Multiprotocol
- RCP: Radio Co-Processor
- SoC: System-on-Chip
- CPCd: Co-Processor Communication daemon

##### Test Plan Strategy

This test plan is focused on the performance of the Multiprotocol RCP using mixed protocol traffic streams. The tests are run with both protocols on the same channel, and with both protocols on different channels to test Concurrent Listening.

<table>
    <thead>
        <tr>
            <th>Scenario</th>
            <th>Devices</th>
            <th>Notes</th>
            <th>Illustrated Topology</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>9x9 single channel</td>
            <td>
                <p>9 SoC Devices OT</p>
                <p>9 Soc Devices Zigbee</p>
            </td>
            <td>Zigbee and OpenThread networks on the same channel.</td>
            <td><img src="/multi-pan-rcp-performance-for-openthread-and-zigbee/0.2/images/sld805-image1.png" alt="image"></td>
        </tr>
        <tr>
            <td>9x9 dual channel</td>
            <td>
                <p>9 SoC Devices OT</p>
                <p>9 Soc Devices Zigbee</p>
            </td>
            <td>Zigbee and OpenThread on different channels to test Concurrent Listening on the RCP.</td>
            <td><img src="/multi-pan-rcp-performance-for-openthread-and-zigbee/0.2/images/sld805-image1.png" alt="image"></td>
        </tr>
        <tr>
            <td>
                <p>Greater sizes</p>
                <p>(y)x(z)</p>
            </td>
            <td>
                <p>(y) SoC Devices OT</p>
                <p>(z) Soc Devices Zigbee</p>
            </td>
            <td></td>
            <td>The same setup as above but with more devices.</td>
        </tr>
    </tbody>
</table>

The SOC devices send periodic unicast traffic to the DuT (Device under Test) with staggered start times. We measure the reliability and latency for various packet sizes.

##### Test Approach and Dependencies

This test effort uses open wireless devices in the Silicon Labs Boston office. The automation is written in Java™ and leverages a set of utilities for communicating with the test device via TCP. This supports simultaneous command transmission and data collection for many devices via the TCP console ports.

Networks are formed for OpenThread (OT) and Zigbee (Zig) using the wireless SoC devices around the office, plus the Multiprotocol RCP DuT. Traffic is generated from the Zig and OT SoC devices by providing the address of the DuT for each protocol.

Collection is based on console output from OT ping and the Zigbee throughput component. The output is parsed and results are written to .csv files on completion of test runs.

##### Test Methodology

OpenThread traffic is generated using the ping command, which uses Internet Control Message Protocol (ICMP) Echo Request and Echo Reply packets. The Echo Reply packets sent by the DuT have the same packet length as the received Echo Request packet.

The Zigbee throughput plugin sends APS messages one-way from the SoC to the DuT. 802.15.4 acks are sent in return, but not APS acks.

The following illustrates the test setup.

- Traffic flow is directed at the DuT.
- Many > One, where the One is the DuT.
- Current testing assumes all devices are within 1 hop.

![image](/multi-pan-rcp-performance-for-openthread-and-zigbee/0.2/images/sld805-image1.png)

Additional details on the traffic created follow.

###### Open Thread

OT testing used ping (ICMP) with a variable IP payload of 16 bytes up to a maximum of 1232 bytes.

[https://github.com/openthread/openthread/blob/main/src/cli/README.md#ping--i-source-ipaddr-size-count-interval-hoplimit-timeout](https://github.com/openthread/openthread/blob/main/src/cli/README.md#ping--i-source-ipaddr-size-count-interval-hoplimit-timeout)

```C
ping [-I source] <ipaddr> [size] [count] [interval] [hoplimit] [timeout]
```

Example:

```C
> ping -I fd00:db8:0:0:76b:6a05:3ae9:a61a 8 3 1 1 1
> 16 bytes from fd00:db8:0:0:f605:fb4b:d429:d59a: icmp_seq=4 hlim=64 time=18ms
> 16 bytes from fd00:db8:0:0:f605:fb4b:d429:d59a: icmp_seq=5 hlim=64 time=20ms
> 16 bytes from fd00:db8:0:0:f605:fb4b:d429:d59a: icmp_seq=6 hlim=64 time=19ms
3 packets transmitted, 3 packets received. Packet loss = 0.0%. Round-trip min/avg/max = 18/19.0/20 ms.
Done
```

For the 8-byte payload size specified in the first argument to ping above, ICMP adds 8 bytes of header, and the total packet size including 40 bytes of 802.15.4 and IP headers comes to 56 bytes.

The maximum 802.15.4 packet is 127 bytes. Subtracting the 48 bytes of headers, the maximum IP payload for a single packet is 79 bytes. Larger payload sizes cause OT packets to be fragmented.

###### Zigbee

Zigbee testing uses the Zigbee throughput component.

As before, the maximum 802.15.4 packet size is 127 bytes. Subtracting 45 bytes of 802.15.4 and Zigbee headers, the maximum Zigbee payload size is 82 bytes. This component cannot send fragmented packets.

##### Testbed and Topologies

The Multiprotocol RCP DuT uses Silicon Labs’ wireless starter kit (WSTK) debug adapter and a Raspberry Pi as the host.

It is important to use a sufficiently fast serial link between the RCP and the host to handle the 250 kbps over-the-air bitrate of 802.15.4 radios. For a UART interface, Silicon Labs recommends a baud rate of 921kpbs with hardware flow control enabled, which is what is used for this testing. With a slower baud rate or without flow control, data may be lost over the serial link.

The test topology used for this document is the open wireless network in the Silicon Labs Boston office. This office space is approximately 20,000 square feet with hundreds of test devices uniformly distributed.

The 18-node tests in this document use the three clusters shown below (E5, E8, E9). The red ‘X’ identifies the location of the DuT.The 96 node tests use clusters E2-E23, excluding cluster E4, E12, E15, E16, E19, and E21.

![image](/multi-pan-rcp-performance-for-openthread-and-zigbee/0.2/images/sld805-image2.png)

##### Test Results Summary

The following results are from a 9x9 topology, meaning 9 OT nodes and 9 ZigBee nodes sending traffic to the DuT. The DuT uses a UART baud rate of 921kbps with hardware flow control.

The rate column is the number of milliseconds between transmits, and the JIT column is the offset between successive senders in milliseconds. The Min/Avg/Max columns give the latency in milliseconds.

Four packet sizes are tested, S, M, L, and XL. The size of the payload is indicated in the SIZE column. S corresponds to 8 byte payloads for both protocols. M corresponds to maximal payload sizes without causing fragmentation, 78 bytes for OT and 82 bytes  for ZigBee.  L corresponds to a 256 byte payload for OT, resulting in fragmentation into 4 packets. XL corresponds to a 1024 byte payload for OT, resulting in fragmentation into 14 packets. For L and XL the ZigBee payload size remains at 82.

The first table shows results for the single channel case, when both the OT and ZigBee networks are on the same channel.  The second table shows results for the dual channel case, when the OT and ZigBee networks are on different channels. This tests the Concurrent Listening feature of the Multiprotocol RCP.

Performance is excellent with no packet loss in all single channel tests, and no significant latency spikes. In the dual channel tests, performance is excellent for S, M, and L packet size. For the XL packet size test, where OT is using 1024 byte payloads, some packet loss and increased latency is observed on the ZigBee side. This is explained by the very high amount of traffic on the OT network, which monopolizes the radio on the OT channel resulting in missed receptions on the ZigBee side.

**Table: 9 OT x 9 Zig Network, Single Channel**

|Size|Results|
|---|---|
|S|![image](/multi-pan-rcp-performance-for-openthread-and-zigbee/0.2/images/sld805-image3.png)|
|M|![image](/multi-pan-rcp-performance-for-openthread-and-zigbee/0.2/images/sld805-image4.png)|
|L|![image](/multi-pan-rcp-performance-for-openthread-and-zigbee/0.2/images/sld805-image5.png)|
|XL|![image](/multi-pan-rcp-performance-for-openthread-and-zigbee/0.2/images/sld805-image6.png)|

**Table: 9 OT x 9 Zig Network, Dual Channel**

|Size|Results|
|---|---|
|S|![image](/multi-pan-rcp-performance-for-openthread-and-zigbee/0.2/images/sld805-image7.png)|
|M|![image](/multi-pan-rcp-performance-for-openthread-and-zigbee/0.2/images/sld805-image8.png)|
|L|![image](/multi-pan-rcp-performance-for-openthread-and-zigbee/0.2/images/sld805-image9.png)|
|XL|![image](/multi-pan-rcp-performance-for-openthread-and-zigbee/0.2/images/sld805-image10.png)|

The following results are from a large network topology of 48 OT devices and 48 Zig devices. All devices send 8 byte payloads to the DuT at a rate of one every five seconds. Performance is excellent for both single and dual channel tests. Reliability is almost perfect and there are only a few latency spikes, which are expected under the traffic stress of 96 devices sending and receiving from 1 device.

**Table: 48 OT x 48 Zig Network, Single Channel**

| |
|---|
|![image](/multi-pan-rcp-performance-for-openthread-and-zigbee/0.2/images/sld805-image11.png)|
|![image](/multi-pan-rcp-performance-for-openthread-and-zigbee/0.2/images/sld805-image12.png)|

**Table: 48 OT x 48 Zig Network, Dual Channel**

| |
|---|
|![image](/multi-pan-rcp-performance-for-openthread-and-zigbee/0.2/images/sld805-image13.png)|
|![image](/multi-pan-rcp-performance-for-openthread-and-zigbee/0.2/images/sld805-image14.png)|

##### Additional Test Results Comparisons

These results were collected in the initial version of this document in June 2022 for comparison of OT performance against other architectures.

|Brd4161a|8 bytes|
|---|---|
|SOC|![image](/multi-pan-rcp-performance-for-openthread-and-zigbee/0.2/images/sld805-image15.png)|
|NON-CPC-OTBR (UART)|![image](/multi-pan-rcp-performance-for-openthread-and-zigbee/0.2/images/sld805-image16.png)|
|CPC-SPI|![image](/multi-pan-rcp-performance-for-openthread-and-zigbee/0.2/images/sld805-image17.png)|

#### Mesh Network Performance Comparison

##### Mesh Network Performance Comparison

> **Note: This section replaces _AN1142: Mesh Network Performance Comparison_. Further updates to this application note will be provided here**.

It is important for designers to understand the capabilities and performance characteristics of mesh networking technologies to determine the appropriate one for a given application or product. Networking performance characteristics such as throughput, latency, and the impact of network size on scalability and reliability should be considered. Additional factors such as technologies used in different ecosystems, gateway interfaces, and cloud connectivity should also be factored in when [making a determination](https://www.silabs.com/whitepapers/selecting-the-appropriate-wireless-mesh-network-technology).

This application note reviews the Zigbee, Thread, and Bluetooth mesh networks to evaluate their differences in performance and behavior. Tests were conducted using Silicon Labs’ Wireless Gecko SoC platform capable of running Bluetooth Mesh, Thread, Zigbee, and Proprietary protocols. Silicon Labs Bluetooth Mesh, Thread, and Zigbee software stacks were utilized. The test environment was a commercial office building with active Wi-Fi and Zigbee networks in range. Wireless test clusters were deployed in hallways, meeting rooms, offices, and open areas. These results are intended to provide guidance on design practices and principles as well as expected field performance results.

Additional performance benchmarking information for other technologies is available at [http://www.silabs.com/mesh-performance](https://www.silabs.com/mesh-performance).

###### Summary

- Mesh network performance including throughput, latency, and large network scalability is presented.
- Benchmarks performed with Silicon Labs SDKs and Stacks for Bluetooth Mesh, Thread, and Zigbee.

**The information in this application note is based on the SLThread implementation of Thread. SLThread reached ‘end of service’ in December 2019. Silicon Labs is replacing SLThread with an implementation of the more popular OpenThread. We anticipate that the results from OpenThread will be very close to the SLThread results.**

##### Introduction and Background

Silicon Labs has provided performance testing results from embedded mesh networks as part of developer conferences and industry white papers. The basic performance data of throughput, latency, and impact of security can be used by system designers to define expected behavior. This testing has been previously presented for Zigbee and Thread networks as basic 15.4 mesh networking technologies. These were presented because performance varies even though both systems use the same underlying physical layer defined by IEEE 802.15.4. More recently, we have also published data on Bluetooth Mesh performance using the same test network in our Silicon Labs Research and Development (R&D) facility. The underlying performance details for each of these three mesh technologies are covered in the following individual application notes covering the testing conditions and results:

- [Bluetooth Mesh Network Performance](https://docs.silabs.com/btmesh/latest/btmesh-11-network-performance/)
- [Zigbee Mesh Network Performance](https://docs.silabs.com/zigbee/latest/zigbee-mesh-network-performance/)
- [AN1408: Thread Mesh Network Performance](https://www.silabs.com/documents/public/application-notes/an1408-openthread-mesh-network-performance.pdf)

This paper is intended to present results from the three technologies so they can be evaluated against each other. No single technology is expected to be used for connecting all devices, therefore, this paper provides performance comparisons so networks are used in situations best suited for the technology. Companies developing products can test the network’s performance for the expected use case, and then use this data to assist in selecting the appropriate technology.

This paper does not cover testing conditions and topology as this is covered in each of the individual performance papers. Each mesh network was tested under the same conditions and using the same wireless devices in our Silicon Labs R&D facility.

##### Comparison of Results

###### Throughput and Latency

Throughput and latency is tested in a controlled network (wired configuration) to test each hop against different packet payloads.

The normal configuration is to test to 6 hops. Testing is done with 1 source node and a series of routing nodes to allow the number of hops to be varied.

![Throughput and Latency Test Configuration](/mesh-network-performance-comparison/0.1/images/sld785-throughput-and-latency-test-configuration.png)

###### Mesh Multi-Hop Throughput (heading level 7)

Throughput testing uses the same single setup to evaluate the expected application level throughput of different payload sizes across a different number of hops.

For smaller payload sizes, the throughput is lower as the network spends more time sending packet overhead versus application data.

![Small Packet Throughput 8-byte BT Seg and UnSeg and 10-byte Thread](/mesh-network-performance-comparison/0.1/images/sld785-small-packet-throughput-8-byte-bt-seg-and-unseg-and-10-byte-thread.png)

For larger payload sizes, the throughput is increased overall for Zigbee and Thread; however, the smaller payload size of Bluetooth Mesh does not result in the same increase in throughput. Note that all three technologies are fragmenting packets at this size.

![Throughput Comparison vs Hops 100-byte Payload Zigbee, Thread, and 96-byte Bluetooth](/mesh-network-performance-comparison/0.1/images/sld785-throughput-comparison-vs-hops-100-byte-payload-zigbee-thread-and-96-byte-bluetooth.png)

If we measure latency instead of throughput, we look at the small payloads first and see similar latency numbers between the different technologies. Adding segmentation to Bluetooth increases the latency but everything stays below 200 milliseconds in round trip time.

![Small Payload - Latency vs Hops](/mesh-network-performance-comparison/0.1/images/sld785-small-payload-latency-vs-hops.png)

If we look at a 4-hop chart versus a different payload size, we can see the increase in latency versus payload for the different technologies. All different mesh networks increase latency as the application payload increases. How well the network does under these conditions is related to the underlying fragmentation method as well as the congestion in the network when sending the data. Bluetooth must send many more packets as the payload size increases, so this segmentation adds more latency.

![Zigbee, Thread, and Bluetooth Latency - 4-hop versus Payload Size](/mesh-network-performance-comparison/0.1/images/sld785-zigbee-thread-and-bluetooth-latency-4-hop-versus-payload-size.png)

###### Multicast Network Tests versus Network Size

The throughput and latency results shown in [Mesh Multi-Hop Throughput](02-comparison-of-results#mesh-multi-hop-throughput) are somewhat constrained by the nature of the testing since it is a single string of devices. Open-air testing is required to validate stack performance under less controlled conditions. These networks are configured within Silicon Labs office space with normal Wi-Fi interference, other network operations, and building control systems. No attempt is made to isolate this network’s RF conditions.

Because this testing is all multicast delivery, the Bluetooth flooding mesh is behaving the same as Zigbee and Thread because all-device multicasts generally flood the network with three re-broadcasts from each router. These tests are all for relatively lightly loaded networks where the multicast is sent every 3 seconds. In our individual testing, we noted that some network performances deteriorate further as this multicast load is increased.

The networks to be tested for each stack include:

- Small network: 24 devices
- Medium network: 1 – 48 devices
- Medium network: 2 – 96 devices
- Large network: 1 – 144 devices
- Large network: 2 – 192 devices

For any of these tests, any number of devices within +/- 10% of these network targets is acceptable. These networks are all configured as powered devices. For each of these networks, the testing will validate reliability and latency for a set of traffic conditions. Testing is intended for over 100 packets.

###### Small Networks and Different Packet Sizes (heading level 7)

For each of these networks, we want to start testing with a small network and small payload sizes as the simplest comparison point. As shown below, all three mesh networks provide very good performance with latency well under 200 milliseconds.

![Zigbee, Thread, and Bluetooth Multicast Latency 24 Node Network - 5 (or 8 Bluetooth) -byte Payload](/mesh-network-performance-comparison/0.1/images/sld785-24-node-network-5-or-8-bluetooth-byte-payload.png)

As the packet size increases, we start to see some differences in the multicast latency where it increases and spreads out. Thread latency increases slightly, Zigbee increases and spreads out, and Bluetooth Mesh becomes very distributed in latencies from 20 millseconds up to 220 milliseconds.

![Zigbee, Thread, and Bluetooth Multicast Latency 24 Node Network - 50 (or 32 Bluetooth) -byte Payload](/mesh-network-performance-comparison/0.1/images/sld785-24-node-network-50-or-32-bluetooth-byte-payload.png)

###### Large Networks and Different Packet Sizes (heading level 7)

As the network scales up, we expect an increase in latency due to added congestion and increased number of hops in the network. Since this is an open-air network, we do not control the number of hops a particular message takes, we only record the actual results. The chart below shows that for small packet sizes, all three networks behave reasonably with an increase and spreading of the latency to slightly longer latencies. The only point of note is the ~3% of Bluetooth packets that end up with latency larger than 250 milliseconds.

![Zigbee, Thread, and Bluetooth Multicast Latency 192 Node Network - 5 (or 8 Bluetooth) -byte Payload](/mesh-network-performance-comparison/0.1/images/sld785-192-node-network-5-or-8-bluetooth-byte-payload.png)

As the packet payload increases, we expect to see further increases in latencies across each of the mesh networks, particularly in the larger networks where fragmentation of packets will result in even more congestion due to the multicast flooding. We increase the payload only slightly and see the latencies spread substantially. In the graph below, we have increased the X axis to better show the latency spreading in this larger network. All the networks spread out, but the Bluetooth network spreads well past 500 milliseconds. Further increases in packet size only make this spreading worse.

![Zigbee, Thread, and Bluetooth Multicast Latency 192 Node Network - 25 (or 16 Bluetooth) -byte Payload](/mesh-network-performance-comparison/0.1/images/sld785-192-node-network-25-or-16-bluetooth-byte-payload.png)

##### Summary

Comparing different mesh technologies allows designers to review expected performance under different conditions. To allow as fair a comparison as possible, we ran these tests using the same wireless devices under generally the same open office conditions. We also attempted to minimize other factors impacting the testing such as the backchannel timing from the development kits, latency within the testing infrastructure, and differences in radio architecture or MCU speeds, which can all impact results. Even when controlling these factors, we must caution that these results are our first attempt and further optimizations and improvements are likely possible within the standard or within our implementation. We have been running tests like these with our Zigbee stack since 2006, with Thread since 2015, and on Bluetooth only within the past year.

Each mesh network is designed differently and behaves differently. One network is not expected to be perfect for all conditions, so the selection of a particular technology involves criteria such as battery life, expected ecosystem and preferred connectivity, and the performance requirements of the device and network. This application note only compares the performance of these networks and not some of these other critical parameters.

From a performance standpoint, we see that all relatively small networks with small payload sizes perform similarly. As payload sizes and throughput needs increase, both Thread and Zigbee are better able to carry the load and maintain lower latencies. Latencies increase for all large networks, but Bluetooth experiences the largest increase. If we increase the payload in these larger networks, we see even more substantial increases in latency. This increase is expected; therefore, larger networks should be moved to a routing solution to minimize this latency. In all our multicast testing, however, we should note that our reliability is generally 99.9% or better as the multicast does a good job of ensuring delivery of the messages.

###### Related Literature

This application note has provided a comparison of the three mesh network technologies, Bluetooth, Zigbee, and Thread. For specific information on each of these technologies, refer to the following application notes:

- [Bluetooth Mesh Network Performance](https://docs.silabs.com/btmesh/latest/btmesh-11-network-performance/)
- [Zigbee Mesh Network Performance](https://docs.silabs.com/zigbee/latest/zigbee-mesh-network-performance/)
- [AN1408: Thread Mesh Network Performance](https://www.silabs.com/documents/public/application-notes/an1408-openthread-mesh-network-performance.pdf)

## API Reference Guide

### Deprecated List

- Member [otCoapMessageGetToken](api-coap#ot-coap-message-get-token)  (const otMessage *aMessage)  
  This function is deprecated. Use `otCoapMessageReadToken()` instead.
- Member [otCoapMessageGetTokenLength](api-coap#ot-coap-message-get-token-length)  (const otMessage *aMessage)  
  This function is deprecated. Use `otCoapMessageReadToken()` instead.
- Member [otCoapMessageSetToken](api-coap#ot-coap-message-set-token)  (otMessage *aMessage, const uint8_t *aToken, uint8_t aTokenLength)  
  This function is deprecated. Use `otCoapMessageWriteToken()` instead.

### OpenThread Modules

|Modules||
|---|---|
|[Alarm](plat-alarm)|This module includes the platform abstraction for the alarm service.|
|[Backbone Router](api-backbone-router)|This module includes functions for the OpenThread Backbone Router Service.|
|[Border Agent](api-border-agent)|This module includes functions for the Thread Border Agent role.|
|[Border Router](api-border-router)|This module includes functions to manage local network data with the OpenThread Border Router.|
|[Border Routing Manager](api-border-routing)|This module includes definitions related to Border Routing Manager.|
|[Channel Manager](api-channel-manager)|This module includes functions for Channel Manager.|
|[Channel Monitoring](api-channel-monitor)|This module includes functions for channel monitoring feature.|
|[Child Supervision](api-child-supervision)|This module includes functions for Child Supervision feature.|
|[CoAP](api-coap)|This module includes functions that control CoAP communication.|
|[CoAP Secure](api-coap-secure)|This module includes functions that control CoAP Secure (CoAP over DTLS) communication.|
|[Command Line Interface](api-cli)|This module includes functions that control the Thread stack's execution.|
|[Commissioner](api-commissioner)|This module includes functions for the Thread Commissioner role.|
|[Crypto - Platform](plat-crypto)|This module includes the platform abstraction for Crypto.|
|[Crypto - Thread Stack](api-crypto)|This module includes cryptographic functions.|
|[DNS](api-dns)|This module includes functions that control DNS communication.|
|[DNS - Platform](plat-dns)|This module includes the platform abstraction for sending recursive DNS query to upstream DNS servers.|
|[DNS-SD Server](api-dnssd-server)|This module includes APIs for DNS-SD server.|
|[Entropy](plat-entropy)|This module includes the platform abstraction for entropy generation.|
|[Error](api-error)|This module includes error definitions used in OpenThread.|
|[Factory Diagnostics - Platform](plat-factory-diagnostics)|This module includes the platform abstraction for diagnostics features.|
|[Factory Diagnostics - Thread Stack](api-factory-diagnostics)|This module includes functions that control the Thread stack's execution.|
|[General](api-thread-general)|This module includes functions for all Thread roles.|
|[Heap](api-heap)|This module includes functions that set the external OpenThread heap.|
|[History Tracker](api-history-tracker)|Records the history of different events, for example RX and TX messages or network info changes.|
|[ICMPv6](api-icmp6)|This module includes functions that control ICMPv6 communication.|
|[IPv6](api-ip6)|This module includes functions that control IPv6 communication.|
|[Infrastructure Interface](plat-infra-if)|This module includes the platform abstraction for the adjacent infrastructure network interface.|
|[Instance](api-instance)|This module includes functions that control the OpenThread Instance.|
|[Jam Detection](api-jam-detection)|This module includes functions for signal jamming detection feature.|
|[Joiner](api-joiner)|This module includes functions for the Thread Joiner role.|
|[Link](api-link-link)|This module includes functions that control link-layer configuration.|
|[Link Metrics](api-link-metrics)|This module includes functions that control the Link Metrics protocol.|
|[Logging - Platform](plat-logging)|This module includes the platform abstraction for the debug log service.|
|[Logging - Thread Stack](api-logging)|This module includes OpenThread logging related definitions.|
|[Memory](plat-memory)|This module includes the platform abstraction for dynamic memory allocation.|
|[Mesh Diagnostics](api-mesh-diag)|This module includes definitions and functions for Mesh Diagnostics.|
|[Message](api-message)|This module includes functions that manipulate OpenThread message buffers.|
|[Message Pool](plat-messagepool)|This module includes the platform abstraction for the message pool.|
|[Miscellaneous](plat-misc)|This module includes platform abstractions for miscellaneous behaviors.|
|[Multi Radio Link](api-multi-radio)|This module includes definitions and functions for multi radio link.|
|[NAT64](api-nat64)|This module includes functions and structs for the NAT64 function on the border router.|
|[Network Co-Processor](api-ncp)|This module includes functions that control the Thread stack's execution.|
|[Network Simulator](plat-otns)|This module includes the platform abstraction for OTNS.|
|[Network Time Synchronization](api-network-time)|This module includes functions that control network time synchronization service.|
|[Operational Dataset](api-operational-dataset)|Includes functions for the Operational Dataset API.|
|[Ping Sender](api-ping-sender)|This file includes the OpenThread API for the ping sender module.|
|[RNG Cryptographic](api-random-crypto)|This module includes functions that generates cryptographic random numbers.|
|[RNG Non-cryptographic](api-random-non-crypto)|This module includes functions that generates non cryptographic random numbers.|
|[Radio Configuration](radio-config)|This module includes the platform abstraction for radio configuration.|
|[Radio Extension](radio-extension)|This module includes the Silicon Labs extension to the openthread platform radio interface.|
|[Radio Operation](radio-operation)|This module includes the platform abstraction for radio operations.|
|[Radio Types](radio-types)|This module includes the platform abstraction for a radio frame.|
|[Raw Link](api-link-raw)|This module includes functions that control the raw link-layer configuration.|
|[Router/Leader](api-thread-router)|This module includes functions for Thread Routers and Leaders.|
|[SNTP](api-sntp)|This module includes functions that control SNTP communication.|
|[SPI Slave](plat-spi-slave)|This module includes the platform abstraction for SPI slave communication.|
|[SRP](api-srp)|This module includes functions that control SRP client behavior.|
|[Server](api-server)|This module includes functions to manage local network data with the OpenThread Server.|
|[Settings](plat-settings)|This module includes the platform abstraction for non-volatile storage of settings.|
|[TCP](api-tcp)|This module includes functions that control TCP communication.|
|[TCP Abstractions](api-tcp-ext)|This module includes easy-to-use abstractions on top of the base TCP API.|
|[TREL - Platform](plat-trel)|This module includes the platform abstraction for Thread Radio Encapsulation Link (TREL) using DNS-SD and UDP/IPv6.|
|[TREL - Thread Stack](api-trel)|This module defines Thread Radio Encapsulation Link (TREL) APIs for Thread Over Infrastructure.|
|[Tasklets](api-tasklets)|This module includes functions that control the Thread stack's execution.|
|[Time Service](plat-time)|This module includes the platform abstraction for the time service.|
|[Toolchain](plat-toolchain)|This module defines a toolchain abstraction layer through macros.|
|[UDP](api-udp)|This module includes functions that control UDP communication.|
|[UDP Forward](api-udp-forward)|This module includes functions for UDP forward feature.|

### API Reference

This module includes the application programming interface to the OpenThread stack. 

Important note: The OpenThread stack is not re-entrant. All OpenThread public APIs, platform APIs, and callbacks MUST be invoked from the same OS context (e.g., the same thread/process or the same task in an RTOS). Any exceptions to this rule, where an API can be called from a different context, will be explicitly documented in that API's reference. Failure to follow this rule can lead to undefined and unexpected behaviors.

If an API call returns an error status (any value other than success), the caller MUST assume any of the output parameters passed to the API may have been modified and are in an indeterminate state. Assuming that an output parameter remains unchanged upon error is invalid. If an API deviates from this default behavior (e.g., by guaranteeing parameters are untouched on error), it will be explicitly documented. Otherwise, developers MUST NOT make this assumption. 

#### Modules

[Error](api-error)

[Execution](api-execution)

[IPv6 Networking](api-net)

[Link](api-link)

[Message](api-message)

[Multi Radio Link](api-multi-radio)

[TREL - Thread Stack](api-trel)

[Thread](api-thread)

[Add-Ons](api-addons)

[Provisional](api-provisional)

#### Error

This module includes error definitions used in OpenThread. 

##### Typedefs

###### otError

`enum OT_MUST_USE_RESULT otError`

**Description:**

Represents error codes used throughout OpenThread.

##### Functions

###### otThreadErrorToString

`const char * otThreadErrorToString(otError aError)`

**Description:** Converts an otError enum into a string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otError](api-error#ot-error)|[in]|aError|An otError enum.|

**Returns**

- A string representation of an otError.

#### Execution

##### Modules

[Instance](api-instance)

[Tasklets](api-tasklets)

##### Instance

This module includes functions that control the OpenThread Instance. 

###### Typedefs

###### otInstance (heading level 7)

`typedef struct otInstance otInstance`

**Description:**

Represents the OpenThread instance structure.

###### otChangedFlags (heading level 7)

`typedef uint32_t otChangedFlags`

**Description:**

Represents a bit-field indicating specific state/configuration that has changed.

**Details:**

See `OT_CHANGED_*` definitions.

###### otStateChangedCallback (heading level 7)

`typedef void(* otStateChangedCallback) (otChangedFlags aFlags, void *aContext)`

**Description:**

Pointer is called to notify certain configuration or state changes within OpenThread.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aFlags|A bit-field indicating specific state that has changed. See `OT_CHANGED_*` definitions.|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### Functions

###### otInstanceInit (heading level 7)

`otInstance * otInstanceInit(void *aInstanceBuffer, size_t *aInstanceBufferSize)`

**Description:** Initializes the OpenThread library.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void *|[in]|aInstanceBuffer|The buffer for OpenThread to use for allocating the otInstance structure.|
|size_t *|[inout]|aInstanceBufferSize|On input, the size of aInstanceBuffer. On output, if not enough space for otInstance, the number of bytes required for otInstance.|

Initializes OpenThread and prepares it for subsequent OpenThread API calls. This function must be called before any other calls to OpenThread.

Is available and can only be used when support for multiple OpenThread instances is enabled.

**Returns**

- A pointer to the new OpenThread instance.

**See Also**

- [otInstanceFinalize](api-instance#ot-instance-finalize)

###### otInstanceInitSingle (heading level 7)

`otInstance * otInstanceInitSingle(void)`

**Description:** Initializes the static single instance of the OpenThread library.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

Initializes OpenThread and prepares it for subsequent OpenThread API calls. This function must be called before any other calls to OpenThread.

Is available and can only be used when support for multiple OpenThread instances is disabled.

**Returns**

- A pointer to the single OpenThread instance.

###### otInstanceGetSingle (heading level 7)

`otInstance * otInstanceGetSingle(void)`

**Description:** Gets the pointer to the single OpenThread instance when multiple instances are not in use.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

Is available and can only be used when support for multiple OpenThread instances is disabled.

**Returns**

- A pointer to the single OpenThread instance.

###### otInstanceInitMultiple (heading level 7)

`otInstance * otInstanceInitMultiple(uint8_t aIdx)`

**Description:** Initializes the OpenThread instance.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t|[in]|aIdx|The index of the OpenThread instance to initialize.|

This function initializes OpenThread and prepares it for subsequent OpenThread API calls. This function must be called before any other calls to OpenThread. This method utilizes static buffer to initialize the OpenThread instance.

This function is available and can only be used when support for multiple OpenThread static instances is enabled (`OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE`)

**Returns**

- A pointer to the new OpenThread instance.

###### otInstanceGetInstance (heading level 7)

`otInstance * otInstanceGetInstance(uint8_t aIdx)`

**Description:** Gets the pointer to an OpenThread instance with the provided index when multiple instances are in use.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t|[in]|aIdx|The index of the OpenThread instance.|

This function is available when both `OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE` and `OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE` are enabled.

**Returns**

- A pointer to the corresponding OpenThread instance, or `NULL` if `aIdx` is out of bounds.

###### otInstanceGetIndex (heading level 7)

`uint8_t otInstanceGetIndex(otInstance *aInstance)`

**Description:** Gets the index of the OpenThread instance when multiple instance is in use.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The reference of the OpenThread instance to get index.|

This function is available when both `OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE` and `OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE` are enabled.

**Returns**

- The index of the OpenThread instance.

###### otInstanceGetId (heading level 7)

`uint32_t otInstanceGetId(otInstance *aInstance)`

**Description:** Gets the instance identifier.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

The instance identifier is set to a random value when the instance is constructed, and then its value will not change after initialization.

**Returns**

- The instance identifier.

###### otInstanceIsInitialized (heading level 7)

`bool otInstanceIsInitialized(otInstance *aInstance)`

**Description:** Indicates whether or not the instance is valid/initialized.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

The instance is considered valid if it is acquired and initialized using either `otInstanceInitSingle()` (in single instance case) or `otInstanceInit()` (in multi instance case). A subsequent call to `otInstanceFinalize()` causes the instance to be considered as uninitialized.

**Returns**

- TRUE if the given instance is valid/initialized, FALSE otherwise.

###### otInstanceFinalize (heading level 7)

`void otInstanceFinalize(otInstance *aInstance)`

**Description:** Disables the OpenThread library.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Call this function when OpenThread is no longer in use.

###### otInstanceGetUptime (heading level 7)

`uint64_t otInstanceGetUptime(otInstance *aInstance)`

**Description:** Returns the current instance uptime (in msec).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Requires `OPENTHREAD_CONFIG_UPTIME_ENABLE` to be enabled.

The uptime is given as number of milliseconds since OpenThread instance was initialized.

**Returns**

- The uptime (number of milliseconds).

###### otInstanceGetUptimeAsString (heading level 7)

`void otInstanceGetUptimeAsString(otInstance *aInstance, char *aBuffer, uint16_t aSize)`

**Description:** Returns the current instance uptime as a human-readable string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|char *|[out]|aBuffer|A pointer to a char array to output the string.|
|uint16_t|[in]|aSize|The size of `aBuffer` (in bytes). Recommended to use `OT_UPTIME_STRING_SIZE`.|

Requires `OPENTHREAD_CONFIG_UPTIME_ENABLE` to be enabled.

The string follows the format "<hh>:<mm>:<ss>.<mmmm>" for hours, minutes, seconds and millisecond (if uptime is shorter than one day) or "<dd>d.<hh>:<mm>:<ss>.<mmmm>" (if longer than a day).

If the resulting string does not fit in `aBuffer` (within its `aSize` characters), the string will be truncated but the outputted string is always null-terminated.

###### otSetStateChangedCallback (heading level 7)

`otError otSetStateChangedCallback(otInstance *aInstance, otStateChangedCallback aCallback, void *aContext)`

**Description:** Registers a callback to indicate when certain configuration or state changes within OpenThread.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otStateChangedCallback](api-instance#ot-state-changed-callback)|[in]|aCallback|A pointer to a function that is called with certain configuration or state changes.|
|void *|[in]|aContext|A pointer to application-specific context.|

###### otRemoveStateChangeCallback (heading level 7)

`void otRemoveStateChangeCallback(otInstance *aInstance, otStateChangedCallback aCallback, void *aContext)`

**Description:** Removes a callback to indicate when certain configuration or state changes within OpenThread.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otStateChangedCallback](api-instance#ot-state-changed-callback)|[in]|aCallback|A pointer to a function that is called with certain configuration or state changes.|
|void *|[in]|aContext|A pointer to application-specific context.|

###### otInstanceReset (heading level 7)

`void otInstanceReset(otInstance *aInstance)`

**Description:** Triggers a platform reset.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

The reset process ensures that all the OpenThread state/info (stored in volatile memory) is erased. Note that the `otPlatformReset` does not erase any persistent state/info saved in non-volatile memory.

###### otInstanceResetToBootloader (heading level 7)

`otError otInstanceResetToBootloader(otInstance *aInstance)`

**Description:** Triggers a platform reset to bootloader mode, if supported.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Requires `OPENTHREAD_CONFIG_PLATFORM_BOOTLOADER_MODE_ENABLE`.

###### otInstanceFactoryReset (heading level 7)

`void otInstanceFactoryReset(otInstance *aInstance)`

**Description:** Deletes all the settings stored on non-volatile memory, and then triggers a platform reset.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otInstanceResetRadioStack (heading level 7)

`void otInstanceResetRadioStack(otInstance *aInstance)`

**Description:** Resets the internal states of the OpenThread radio stack.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Callbacks and configurations are preserved.

This API is only available under radio builds (`OPENTHREAD_RADIO = 1`).

###### otInstanceErasePersistentInfo (heading level 7)

`otError otInstanceErasePersistentInfo(otInstance *aInstance)`

**Description:** Erases all the OpenThread persistent info (network settings) stored on non-volatile memory.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Erase is successful only if the device is in `disabled` state/role.

###### otGetVersionString (heading level 7)

`const char * otGetVersionString(void)`

**Description:** Gets the OpenThread version string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

**Returns**

- A pointer to the OpenThread version.

###### otGetRadioVersionString (heading level 7)

`const char * otGetRadioVersionString(otInstance *aInstance)`

**Description:** Gets the OpenThread radio version string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- A pointer to the OpenThread radio version.

###### Macros

`#define OT_UPTIME_STRING_SIZE 24`

**Description**: Recommended size for string representation of uptime.

`#define OT_CHANGED_IP6_ADDRESS_ADDED (1U << 0)`

**Description**: IPv6 address was added.

`#define OT_CHANGED_IP6_ADDRESS_REMOVED (1U << 1)`

**Description**: IPv6 address was removed.

`#define OT_CHANGED_THREAD_ROLE (1U << 2)`

**Description**: Role (disabled, detached, child, router, leader) changed.

`#define OT_CHANGED_THREAD_LL_ADDR (1U << 3)`

**Description**: The link-local address changed.

`#define OT_CHANGED_THREAD_ML_ADDR (1U << 4)`

**Description**: The mesh-local address changed.

`#define OT_CHANGED_THREAD_RLOC_ADDED (1U << 5)`

**Description**: RLOC was added.

`#define OT_CHANGED_THREAD_RLOC_REMOVED (1U << 6)`

**Description**: RLOC was removed.

`#define OT_CHANGED_THREAD_PARTITION_ID (1U << 7)`

**Description**: Partition ID changed.

`#define OT_CHANGED_THREAD_KEY_SEQUENCE_COUNTER (1U << 8)`

**Description**: Thread Key Sequence changed.

`#define OT_CHANGED_THREAD_NETDATA (1U << 9)`

**Description**: Thread Network Data changed.

`#define OT_CHANGED_THREAD_CHILD_ADDED (1U << 10)`

**Description**: Child was added.

`#define OT_CHANGED_THREAD_CHILD_REMOVED (1U << 11)`

**Description**: Child was removed.

`#define OT_CHANGED_IP6_MULTICAST_SUBSCRIBED (1U << 12)`

**Description**: Subscribed to a IPv6 multicast address.

`#define OT_CHANGED_IP6_MULTICAST_UNSUBSCRIBED (1U << 13)`

**Description**: Unsubscribed from a IPv6 multicast address.

`#define OT_CHANGED_THREAD_CHANNEL (1U << 14)`

**Description**: Thread network channel changed.

`#define OT_CHANGED_THREAD_PANID (1U << 15)`

**Description**: Thread network PAN Id changed.

`#define OT_CHANGED_THREAD_NETWORK_NAME (1U << 16)`

**Description**: Thread network name changed.

`#define OT_CHANGED_THREAD_EXT_PANID (1U << 17)`

**Description**: Thread network extended PAN ID changed.

`#define OT_CHANGED_NETWORK_KEY (1U << 18)`

**Description**: Network key changed.

`#define OT_CHANGED_PSKC (1U << 19)`

**Description**: PSKc changed.

`#define OT_CHANGED_SECURITY_POLICY (1U << 20)`

**Description**: Security Policy changed.

`#define OT_CHANGED_CHANNEL_MANAGER_NEW_CHANNEL (1U << 21)`

**Description**: Channel Manager new pending Thread channel changed.

`#define OT_CHANGED_SUPPORTED_CHANNEL_MASK (1U << 22)`

**Description**: Supported channel mask changed.

`#define OT_CHANGED_COMMISSIONER_STATE (1U << 23)`

**Description**: Commissioner state changed.

`#define OT_CHANGED_THREAD_NETIF_STATE (1U << 24)`

**Description**: Thread network interface state changed.

`#define OT_CHANGED_THREAD_BACKBONE_ROUTER_STATE (1U << 25)`

**Description**: Backbone Router state changed.

`#define OT_CHANGED_THREAD_BACKBONE_ROUTER_LOCAL (1U << 26)`

**Description**: Local Backbone Router configuration changed.

`#define OT_CHANGED_JOINER_STATE (1U << 27)`

**Description**: Joiner state changed.

`#define OT_CHANGED_ACTIVE_DATASET (1U << 28)`

**Description**: Active Operational Dataset changed.

`#define OT_CHANGED_PENDING_DATASET (1U << 29)`

**Description**: Pending Operational Dataset changed.

`#define OT_CHANGED_NAT64_TRANSLATOR_STATE (1U << 30)`

**Description**: The state of NAT64 translator changed.

`#define OT_CHANGED_PARENT_LINK_QUALITY (1U << 31)`

**Description**: Parent link quality changed.

##### Tasklets

This module includes functions that control the Thread stack's execution. 

###### Functions

###### otTaskletsProcess (heading level 7)

`void otTaskletsProcess(otInstance *aInstance)`

**Description:** Run all queued OpenThread tasklets at the time this is called.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otTaskletsArePending (heading level 7)

`bool otTaskletsArePending(otInstance *aInstance)`

**Description:** Indicates whether or not OpenThread has tasklets pending.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otTaskletsSignalPending (heading level 7)

`void otTaskletsSignalPending(otInstance *aInstance)`

**Description:** OpenThread calls this function when the tasklet queue transitions from empty to non-empty.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

#### IPv6 Networking

##### Modules

[BLE Secure](api-ble-secure)

[DNS](api-dns)

[DNS-SD Server](api-dnssd-server)

[ICMPv6](api-icmp6)

[IPv6](api-ip6)

[Multicast DNS](api-mdns)

[NAT64](api-nat64)

[SRP](api-srp)

[Ping Sender](api-ping-sender)

[TCP](api-tcp-group)

[UDP](api-udp-group)

##### BLE Secure

This module includes functions that control BLE Secure (TLS over BLE) communication. 

This module includes functions that implement TCAT communication.

The functions in this module are available when BLE Secure API feature (`OPENTHREAD_CONFIG_BLE_TCAT_ENABLE`) is enabled.

The functions in this module are available when TCAT feature (`OPENTHREAD_CONFIG_BLE_TCAT_ENABLE`) is enabled. 

###### Modules

[otTcatAdvertisedDeviceId](ot-tcat-advertised-device-id)

[otTcatGeneralDeviceId](ot-tcat-general-device-id)

[otTcatVendorInfo](ot-tcat-vendor-info)

###### Enumerations

###### otTcatStatusCode (heading level 7)

```
enum otTcatStatusCode {
    OT_TCAT_STATUS_SUCCESS = 0
    OT_TCAT_STATUS_UNSUPPORTED = 1
    OT_TCAT_STATUS_PARSE_ERROR = 2
    OT_TCAT_STATUS_VALUE_ERROR = 3
    OT_TCAT_STATUS_GENERAL_ERROR = 4
    OT_TCAT_STATUS_BUSY = 5
    OT_TCAT_STATUS_UNDEFINED = 6
    OT_TCAT_STATUS_HASH_ERROR = 7
    OT_TCAT_STATUS_INVALID_STATE = 8
    OT_TCAT_STATUS_UNAUTHORIZED = 16
}
```

**Description:**

Represents TCAT status code.

**Enumerator:**

|   |   |
|---|---|
|OT_TCAT_STATUS_SUCCESS|Command or request was successfully processed.|
|OT_TCAT_STATUS_UNSUPPORTED|Requested command or received TLV is not supported.|
|OT_TCAT_STATUS_PARSE_ERROR|Request / command could not be parsed correctly.|
|OT_TCAT_STATUS_VALUE_ERROR|The value of the transmitted TLV has an error.|
|OT_TCAT_STATUS_GENERAL_ERROR|An error not matching any other category occurred.|
|OT_TCAT_STATUS_BUSY|Command cannot be executed because the resource is busy.|
|OT_TCAT_STATUS_UNDEFINED|The requested value, data or service is not defined (currently) or not present.|
|OT_TCAT_STATUS_HASH_ERROR|The hash value presented by the commissioner was incorrect.|
|OT_TCAT_STATUS_INVALID_STATE|The TCAT device is not in a correct state for the given command.|
|OT_TCAT_STATUS_UNAUTHORIZED|Sender does not have sufficient authorization for the given command.|

###### otTcatApplicationProtocol (heading level 7)

```
enum otTcatApplicationProtocol {
    OT_TCAT_APPLICATION_PROTOCOL_NONE = 0
    OT_TCAT_APPLICATION_PROTOCOL_STATUS = 0x01
    OT_TCAT_APPLICATION_PROTOCOL_RESPONSE =
        0x02
    OT_TCAT_APPLICATION_PROTOCOL_1 = 0x81
    OT_TCAT_APPLICATION_PROTOCOL_2 = 0x82
    OT_TCAT_APPLICATION_PROTOCOL_3 = 0x83
    OT_TCAT_APPLICATION_PROTOCOL_4 = 0x84
    OT_TCAT_APPLICATION_PROTOCOL_VENDOR = 0x9F
}
```

**Description:**

Represents TCAT application protocol options.

**Enumerator:**

|   |   |
|---|---|
|OT_TCAT_APPLICATION_PROTOCOL_NONE|Message which has been sent without activating the TCAT agent.|
|OT_TCAT_APPLICATION_PROTOCOL_STATUS||
|OT_TCAT_APPLICATION_PROTOCOL_RESPONSE|Message directed to any application protocol indicating a response with status value (one byte otTcatStatusCode)|
|OT_TCAT_APPLICATION_PROTOCOL_1|Message directed to application protocol 1.|
|OT_TCAT_APPLICATION_PROTOCOL_2|Message directed to application protocol 2.|
|OT_TCAT_APPLICATION_PROTOCOL_3|Message directed to application protocol 3.|
|OT_TCAT_APPLICATION_PROTOCOL_4|Message directed to application protocol 4.|
|OT_TCAT_APPLICATION_PROTOCOL_VENDOR|Message directed to a vendor specific application protocol.|

###### otTcatCommandClass (heading level 7)

```
enum otTcatCommandClass {
    OT_TCAT_COMMAND_CLASS_GENERAL = 0
    OT_TCAT_COMMAND_CLASS_COMMISSIONING = 1
    OT_TCAT_COMMAND_CLASS_EXTRACTION = 2
    OT_TCAT_COMMAND_CLASS_DECOMMISSIONING = 3
    OT_TCAT_COMMAND_CLASS_APPLICATION = 4
}
```

**Description:**

Represents a TCAT command class.

**Enumerator:**

|   |   |
|---|---|
|OT_TCAT_COMMAND_CLASS_GENERAL|TCAT commands related to general operations.|
|OT_TCAT_COMMAND_CLASS_COMMISSIONING|TCAT commands related to commissioning.|
|OT_TCAT_COMMAND_CLASS_EXTRACTION|TCAT commands related to key extraction.|
|OT_TCAT_COMMAND_CLASS_DECOMMISSIONING|TCAT commands related to de-commissioning.|
|OT_TCAT_COMMAND_CLASS_APPLICATION|TCAT commands related to application layer.|

###### otTcatAdvertisedDeviceIdType (heading level 7)

```
enum otTcatAdvertisedDeviceIdType {
    OT_TCAT_DEVICE_ID_EMPTY = 0
    OT_TCAT_DEVICE_ID_OUI24 = 1
    OT_TCAT_DEVICE_ID_OUI36 = 2
    OT_TCAT_DEVICE_ID_DISCRIMINATOR = 3
    OT_TCAT_DEVICE_ID_IANAPEN = 4
    OT_TCAT_DEVICE_ID_MAX = 5
}
```

**Description:**

Represents Advertised Device ID type.

**Details:**

Used during TCAT advertisement.

**Enumerator:**

|   |   |
|---|---|
|OT_TCAT_DEVICE_ID_EMPTY|Advertised device ID type not set.|
|OT_TCAT_DEVICE_ID_OUI24|Advertised device ID type IEEE OUI-24.|
|OT_TCAT_DEVICE_ID_OUI36|Advertised device ID type IEEE OUI-36.|
|OT_TCAT_DEVICE_ID_DISCRIMINATOR|Advertised device ID type Device Discriminator.|
|OT_TCAT_DEVICE_ID_IANAPEN|Advertised device ID type IANA PEN.|
|OT_TCAT_DEVICE_ID_MAX|Advertised device ID max number of types.|

###### Typedefs

###### otHandleBleSecureConnect (heading level 7)

`typedef void(* otHandleBleSecureConnect) (otInstance *aInstance, bool aConnected, bool aBleConnectionOpen, void *aContext)`

**Description:**

Pointer to call when ble secure connection state changes.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aInstance|A pointer to an OpenThread instance.|
||[in]|aConnected|TRUE, if a secure connection was established, FALSE otherwise.|
||[in]|aBleConnectionOpen|TRUE if a BLE connection was established to carry a TLS data stream, FALSE otherwise.|
||[in]|aContext|A pointer to arbitrary context information.|

**Details:**

###### otHandleBleSecureReceive (heading level 7)

`typedef otHandleTcatApplicationDataReceive otHandleBleSecureReceive`

**Description:**

Pointer to call when data was received over a BLE Secure TLS connection.

**Details:**

When TCAT has been started, the TCAT agent automatically responds with status OT_TCAT_STATUS_UNSUPPORTED if no response has been generated or no handler is defined. The application may generate a response to incoming TCAT application data or vendor-specific data by calling [otBleSecureSendApplicationTlv](api-ble-secure#ot-ble-secure-send-application-tlv).

###### otTcatStatusCode (heading level 7)

`typedef enum otTcatStatusCode otTcatStatusCode`

**Description:**

Represents TCAT status code.

###### otTcatApplicationProtocol (heading level 7)

`typedef enum otTcatApplicationProtocol otTcatApplicationProtocol`

**Description:**

Represents TCAT application protocol options.

###### otTcatCommandClass (heading level 7)

`typedef enum otTcatCommandClass otTcatCommandClass`

**Description:**

Represents a TCAT command class.

###### otTcatAdvertisedDeviceIdType (heading level 7)

`typedef enum otTcatAdvertisedDeviceIdType otTcatAdvertisedDeviceIdType`

**Description:**

Represents Advertised Device ID type.

**Details:**

Used during TCAT advertisement.

###### otTcatAdvertisedDeviceId (heading level 7)

`typedef struct otTcatAdvertisedDeviceId otTcatAdvertisedDeviceId`

###### otTcatGeneralDeviceId (heading level 7)

`typedef struct otTcatGeneralDeviceId otTcatGeneralDeviceId`

**Description:**

Represents General Device ID type.

###### otTcatVendorInfo (heading level 7)

`typedef struct otTcatVendorInfo otTcatVendorInfo`

**Description:**

This structure represents a TCAT vendor information.

**Details:**

The content of this structure MUST persist and remain unchanged while a TCAT session is running.

###### otHandleTcatApplicationDataReceive (heading level 7)

`typedef void(* otHandleTcatApplicationDataReceive) (otInstance *aInstance, const otMessage *aMessage, int32_t aOffset, otTcatApplicationProtocol aTcatApplicationProtocol, void *aContext)`

**Description:**

Pointer to call when application data or vendor-specific data was received over a TCAT TLS connection.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aInstance|A pointer to an OpenThread instance.|
||[in]|aMessage|A pointer to the message.|
||[in]|aOffset|The offset where the application data begins.|
||[in]|aTcatApplicationProtocol|The application protocol the message is targeted to.|
||[in]|aContext|A pointer to arbitrary context information.|

**Details:**

The application may generate a response to an incoming TCAT application data packet. The TCAT agent automatically responds with status OT_TCAT_STATUS_UNSUPPORTED if no response has been generated or no handler is defined.

###### otHandleTcatJoin (heading level 7)

`typedef void(* otHandleTcatJoin) (otError aError, void *aContext)`

**Description:**

Pointer to call to notify the completion of a network join/leave operation performed under guidance of a TCAT Commissioner.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aError|OT_ERROR_NONE if the network join/leave operation was successfully started. OT_ERROR_INVALID_STATE if network join was requested but network credentials were missing or incomplete. OT_ERROR_REJECTED if a network join/leave operation was requested, but the TCAT Commissioner is not authorized to make such a request. OT_ERROR_SECURITY is reserved for future use for a failed join due to credential mismatch.|
||[in]|aContext|A pointer to arbitrary context information.|

**Details:**

###### Functions

###### otBleSecureStart (heading level 7)

`otError otBleSecureStart(otInstance *aInstance, otHandleBleSecureConnect aConnectHandler, otHandleBleSecureReceive aReceiveHandler, bool aTlvMode, void *aContext)`

**Description:** Starts the BLE Secure service.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otHandleBleSecureConnect](api-ble-secure#ot-handle-ble-secure-connect)|[in]|aConnectHandler|A pointer to a function that will be called when the connection state changes.|
|[otHandleBleSecureReceive](api-ble-secure#ot-handle-ble-secure-receive)|[in]|aReceiveHandler|A pointer to a function that will be called once data has been received over the TLS connection.|
|bool|[in]|aTlvMode|A boolean value indicating if TLV mode (TRUE) shall be activated, or line mode (FALSE).|
|void *|[in]|aContext|A pointer to arbitrary context information. May be NULL if not used.|

When TLV mode is active, the function `aReceiveHandler` will be called once a complete TLV or line was received and the message offset points to the TLV value.

###### otBleSecureSetTcatVendorInfo (heading level 7)

`otError otBleSecureSetTcatVendorInfo(otInstance *aInstance, const otTcatVendorInfo *aVendorInfo)`

**Description:** Sets TCAT vendor info.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otTcatVendorInfo](ot-tcat-vendor-info) *|[in]|aVendorInfo|A pointer to the Vendor Information (MUST remain valid after the method call).|

The vendor info is used for advertising in TCAT Advertisements, as well as for responding to particular TCAT commands that supply vendor info to the TCAT Commissioner.

###### otBleSecureTcatStart (heading level 7)

`otError otBleSecureTcatStart(otInstance *aInstance, otHandleTcatJoin aJoinHandler)`

**Description:** Enables the TCAT protocol over BLE Secure.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otHandleTcatJoin](api-ble-secure#ot-handle-tcat-join)|[in]|aJoinHandler|A pointer to a function that is called when a network join or leave operation is requested under guidance of the TCAT Commissioner.|

###### otBleSecureStop (heading level 7)

`void otBleSecureStop(otInstance *aInstance)`

**Description:** Stops the BLE Secure server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

If the TCAT agent is active, it is also stopped and any ongoing connection is forcibly ended.

###### otBleSecureSetTcatAgentState (heading level 7)

`otError otBleSecureSetTcatAgentState(otInstance *aInstance, bool aActive, uint32_t aDelayMs, uint32_t aDurationMs)`

**Description:** Sets the TCAT agent over BLE Secure into active or standby state.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aActive|If TRUE, attempts to set TCAT agent to active state. If FALSE, attempts to set TCAT agent to standby (inactive) state.|
|uint32_t|[in]|aDelayMs|Delay in ms before activating TCAT agent. If 0, activate immediately.|
|uint32_t|[in]|aDurationMs|Duration in ms of the activation of the TCAT agent. If 0, activate indefinitely.|

In standby state, no BLE advertisements are sent and TCAT Commissioners can't connect. TCAT can be automatically enabled via a TMF message while in standby.

###### otBleSecureSetPsk (heading level 7)

`void otBleSecureSetPsk(otInstance *aInstance, const uint8_t *aPsk, uint16_t aPskLength, const uint8_t *aPskIdentity, uint16_t aPskIdLength)`

**Description:** Sets the Pre-Shared Key (PSK) and cipher suite TLS_PSK_WITH_AES_128_CCM_8.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const uint8_t *|[in]|aPsk|A pointer to the PSK.|
|uint16_t|[in]|aPskLength|The PSK length.|
|const uint8_t *|[in]|aPskIdentity|The Identity Name for the PSK.|
|uint16_t|[in]|aPskIdLength|The PSK Identity Length.|

**Note**

- Requires the build-time feature `MBEDTLS_KEY_EXCHANGE_PSK_ENABLED` to be enabled.

###### otBleSecureGetPeerCertificateBase64 (heading level 7)

`otError otBleSecureGetPeerCertificateBase64(otInstance *aInstance, unsigned char *aPeerCert, size_t *aCertLength)`

**Description:** Returns the peer x509 certificate base64 encoded.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|unsigned char *|[out]|aPeerCert|A pointer to the base64 encoded certificate buffer.|
|size_t *|[inout]|aCertLength|On input, the size the max size of `aPeerCert`. On output, the length of the base64 encoded peer certificate.|

**Note**

- Requires the build-time features `MBEDTLS_BASE64_C` and `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` to be enabled.

###### otBleSecureGetPeerCertificateDer (heading level 7)

`otError otBleSecureGetPeerCertificateDer(otInstance *aInstance, unsigned char *aPeerCert, size_t *aCertLength)`

**Description:** Returns the DER encoded peer x509 certificate.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|unsigned char *|[out]|aPeerCert|A pointer to the DER encoded certificate buffer.|
|size_t *|[inout]|aCertLength|On input, the size the max size of `aPeerCert`. On output, the length of the DER encoded peer certificate.|

**Note**

- Requires the build-time feature `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` to be enabled.

###### otBleSecureGetPeerSubjectAttributeByOid (heading level 7)

`otError otBleSecureGetPeerSubjectAttributeByOid(otInstance *aInstance, const char *aOid, size_t aOidLength, uint8_t *aAttributeBuffer, size_t *aAttributeLength, int *aAsn1Type)`

**Description:** Returns an attribute value identified by its OID from the subject of the peer x509 certificate.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const char *|[in]|aOid|A pointer to the OID to be found.|
|size_t|[in]|aOidLength|The length of the OID.|
|uint8_t *|[out]|aAttributeBuffer|A pointer to the attribute buffer.|
|size_t *|[inout]|aAttributeLength|On input, the size the max size of `aAttributeBuffer`. On output, the length of the attribute written to the buffer.|
|int *|[out]|aAsn1Type|A pointer to the ASN.1 type of the attribute written to the buffer.|

The peer OID is provided in binary format. The attribute length is set if the attribute was successfully read or zero if unsuccessful. The ASN.1 type as is set as defineded in the ITU-T X.690 standard if the attribute was successfully read.

**Note**

- Requires the build-time feature `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` to be enabled.

###### otBleSecureGetThreadAttributeFromPeerCertificate (heading level 7)

`otError otBleSecureGetThreadAttributeFromPeerCertificate(otInstance *aInstance, int aThreadOidDescriptor, uint8_t *aAttributeBuffer, size_t *aAttributeLength)`

**Description:** Returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of the peer x509 certificate, where the last digit x is set to aThreadOidDescriptor.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|int|[in]|aThreadOidDescriptor|The last digit of the Thread attribute OID.|
|uint8_t *|[out]|aAttributeBuffer|A pointer to the attribute buffer.|
|size_t *|[inout]|aAttributeLength|On input, the size the max size of `aAttributeBuffer`. On output, the length of the attribute written to the buffer.|

The attribute length is set if the attribute was successfully read or zero if unsuccessful. Requires a connection to be active.

**Note**

- Requires the build-time feature `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` to be enabled.

###### otBleSecureGetThreadAttributeFromOwnCertificate (heading level 7)

`otError otBleSecureGetThreadAttributeFromOwnCertificate(otInstance *aInstance, int aThreadOidDescriptor, uint8_t *aAttributeBuffer, size_t *aAttributeLength)`

**Description:** Returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of the own x509 certificate, where the last digit x is set to aThreadOidDescriptor.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|int|[in]|aThreadOidDescriptor|The last digit of the Thread attribute OID.|
|uint8_t *|[out]|aAttributeBuffer|A pointer to the attribute buffer.|
|size_t *|[inout]|aAttributeLength|On input, the size the max size of `aAttributeBuffer`. On output, the length of the attribute written to the buffer.|

The attribute length is set if the attribute was successfully read or zero if unsuccessful. Requires a connection to be active.

###### otBleSecureSetSslAuthMode (heading level 7)

`void otBleSecureSetSslAuthMode(otInstance *aInstance, bool aVerifyPeerCertificate)`

**Description:** Sets the authentication mode for the BLE secure connection.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aVerifyPeerCertificate|true, to verify the peer certificate.|

Disable or enable the verification of peer certificate. Must be called before start.

###### otBleSecureSetCertificate (heading level 7)

`void otBleSecureSetCertificate(otInstance *aInstance, const uint8_t *aX509Cert, uint32_t aX509Length, const uint8_t *aPrivateKey, uint32_t aPrivateKeyLength)`

**Description:** Sets the local device's X509 certificate and corresponding private key.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const uint8_t *|[in]|aX509Cert|A pointer to the PEM formatted X509 certificate.|
|uint32_t|[in]|aX509Length|The length of certificate.|
|const uint8_t *|[in]|aPrivateKey|A pointer to the PEM formatted private key.|
|uint32_t|[in]|aPrivateKeyLength|The length of the private key.|

Used for TLS sessions with cipher suite TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.

**Note**

- Requires `MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=1`.

###### otBleSecureSetCaCertificateChain (heading level 7)

`void otBleSecureSetCaCertificateChain(otInstance *aInstance, const uint8_t *aX509CaCertificateChain, uint32_t aX509CaCertChainLength)`

**Description:** Sets the trusted top level CAs.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const uint8_t *|[in]|aX509CaCertificateChain|A pointer to the PEM formatted X509 CA chain.|
|uint32_t|[in]|aX509CaCertChainLength|The length of chain.|

It is needed for validating the certificate of the peer via TLS.

Used for TLS sessions with cipher suite TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.

**Note**

- Requires `MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=1`.

###### otBleSecureConnect (heading level 7)

`otError otBleSecureConnect(otInstance *aInstance)`

**Description:** Initializes TLS session with a peer using an already open BLE connection.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otBleSecureDisconnect (heading level 7)

`void otBleSecureDisconnect(otInstance *aInstance)`

**Description:** Stops the BLE and TLS connections.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otBleSecureIsConnectionActive (heading level 7)

`bool otBleSecureIsConnectionActive(otInstance *aInstance)`

**Description:** Indicates whether or not the TLS session is active (connected or connecting).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otBleSecureIsConnected (heading level 7)

`bool otBleSecureIsConnected(otInstance *aInstance)`

**Description:** Indicates whether or not the TLS session is connected.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otBleSecureIsTcatAgentStarted (heading level 7)

`bool otBleSecureIsTcatAgentStarted(otInstance *aInstance)`

**Description:** Indicates whether or not the TCAT agent is started over BLE secure.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

###### otBleSecureIsCommandClassAuthorized (heading level 7)

`bool otBleSecureIsCommandClassAuthorized(otInstance *aInstance, otTcatCommandClass aCommandClass)`

**Description:** Indicates whether or not a TCAT command class is authorized for the current TCAT Commissioner.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otTcatCommandClass](api-ble-secure#ot-tcat-command-class)|[in]|aCommandClass|A command class to check.|

###### otBleSecureSendMessage (heading level 7)

`otError otBleSecureSendMessage(otInstance *aInstance, otMessage *aMessage)`

**Description:** Sends a secure BLE message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the message to send.|

If the return value is OT_ERROR_NONE, OpenThread takes ownership of `aMessage`, and the caller should no longer reference `aMessage`. If the return value is not OT_ERROR_NONE, the caller retains ownership of `aMessage`, including freeing `aMessage` if the message buffer is no longer needed.

###### otBleSecureSend (heading level 7)

`otError otBleSecureSend(otInstance *aInstance, uint8_t *aBuf, uint16_t aLength)`

**Description:** Sends a secure BLE data packet.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t *|[in]|aBuf|A pointer to the data to send as the Value of the TCAT Send Application Data TLV.|
|uint16_t|[in]|aLength|A number indicating the length of the data buffer.|

###### otBleSecureSendApplicationTlv (heading level 7)

`otError otBleSecureSendApplicationTlv(otInstance *aInstance, otTcatApplicationProtocol aApplicationProtocol, uint8_t *aBuf, uint16_t aLength)`

**Description:** Sends a secure BLE data packet containing application data directed to the application layer `aApplicationProtocol` or a response to the latest received application data packet.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otTcatApplicationProtocol](api-ble-secure#ot-tcat-application-protocol)|[in]|aApplicationProtocol|An application protocol the data is directed to.|
|uint8_t *|[in]|aBuf|A pointer to the data to send as the Value of the TCAT Send Application Data TLV.|
|uint16_t|[in]|aLength|A number indicating the length of the data buffer.|

Only a single response can be sent while executing the `otHandleBleSecureReceive` handler. If no (further) response is expected `OT_ERROR_REJECTED` is returned.

For responses with a payload `aApplicationProtocol` shall be set to `OT_TCAT_APPLICATION_PROTOCOL_PAYLOAD`. For responses with a status `aApplicationProtocol` shall be `OT_TCAT_APPLICATION_PROTOCOL_STATUS` and @ aBuf shall contain a single byte `otTcatStatusCode` value.

###### otBleSecureFlush (heading level 7)

`otError otBleSecureFlush(otInstance *aInstance)`

**Description:** Flushes the send buffer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otBleSecureGetInstallCodeVerifyStatus (heading level 7)

`bool otBleSecureGetInstallCodeVerifyStatus(otInstance *aInstance)`

**Description:** Gets the Install Code Verify Status during the current session.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### Macros

`#define OT_TCAT_SERVICE_NAME_MAX_LENGTH     15`

**Description**: Maximum string length of a UDP or TCP service name (does not include null char).

`#define OT_TCAT_APPLICATION_LAYER_MAX_COUNT 4`

**Description**: Maximum number of application layer service names supported.

`#define OT_TCAT_ADVERTISEMENT_MAX_LEN 29`

**Description**: Maximum length of TCAT advertisement.

`#define OT_TCAT_OPCODE 0x2`

**Description**: TCAT Advertisement Operation Code.

`#define OT_TCAT_MAX_ADVERTISED_DEVICEID_SIZE 5`

**Description**: TCAT max size of any type of advertised Device ID.

`#define OT_TCAT_MAX_DEVICEID_SIZE 64`

**Description**: TCAT max size of device ID.

`#define OT_TCAT_ENABLE_MAX 600`

**Description**: TCAT_ENABLE_MAX, default max TMF TCAT enable time, in seconds.

###### Public Attributes (heading level 7)

###### mDeviceIdType (heading level 8)

```
otTcatAdvertisedDeviceIdType otTcatAdvertisedDeviceId::mDeviceIdType
```

###### mDeviceIdLen (heading level 8)

```
uint16_t otTcatAdvertisedDeviceId::mDeviceIdLen
```

###### mDeviceId (heading level 8)

```
uint8_t otTcatAdvertisedDeviceId::mDeviceId[OT_TCAT_MAX_ADVERTISED_DEVICEID_SIZE]
```

Represents General Device ID type. 

###### Public Attributes (heading level 7)

###### mDeviceIdLen (heading level 8)

```
uint16_t otTcatGeneralDeviceId::mDeviceIdLen
```

###### mDeviceId (heading level 8)

```
uint8_t otTcatGeneralDeviceId::mDeviceId[OT_TCAT_MAX_DEVICEID_SIZE]
```

This structure represents a TCAT vendor information. 

The content of this structure MUST persist and remain unchanged while a TCAT session is running. 

###### Public Attributes (heading level 7)

###### mProvisioningUrl (heading level 8)

```
const char* otTcatVendorInfo::mProvisioningUrl
```

**Description:** Provisioning URL path string.

###### mVendorName (heading level 8)

```
const char* otTcatVendorInfo::mVendorName
```

**Description:** Vendor name string.

###### mVendorModel (heading level 8)

```
const char* otTcatVendorInfo::mVendorModel
```

**Description:** Vendor model string.

###### mVendorSwVersion (heading level 8)

```
const char* otTcatVendorInfo::mVendorSwVersion
```

**Description:** Vendor software version string.

###### mVendorData (heading level 8)

```
const char* otTcatVendorInfo::mVendorData
```

**Description:** Vendor specific data string.

###### mPskdString (heading level 8)

```
const char* otTcatVendorInfo::mPskdString
```

**Description:** Vendor managed pre-shared key for device.

###### mInstallCode (heading level 8)

```
const char* otTcatVendorInfo::mInstallCode
```

**Description:** Vendor managed install code string.

###### mAdvertisedDeviceIds (heading level 8)

```
const otTcatAdvertisedDeviceId* otTcatVendorInfo::mAdvertisedDeviceIds
```

###### mGeneralDeviceId (heading level 8)

```
const otTcatGeneralDeviceId* otTcatVendorInfo::mGeneralDeviceId
```

**Description:** Vendor managed advertised device ID array.

**Details:** Array is terminated like C string with OT_TCAT_DEVICE_ID_EMPTY

###### mApplicationServiceName (heading level 8)

```
const char* otTcatVendorInfo::mApplicationServiceName[OT_TCAT_APPLICATION_LAYER_MAX_COUNT]
```

**Description:** Vendor managed general device ID array.

**Details:** (if NULL: device ID is set to EUI-64 in binary format)

###### mApplicationServiceIsTcp (heading level 8)

```
bool otTcatVendorInfo::mApplicationServiceIsTcp[OT_TCAT_APPLICATION_LAYER_MAX_COUNT]
```

**Description:** Array with application service names as C string with maximum length OT_TCAT_SERVICE_NAME_MAX_LENGTH or NULL if not supported.

##### DNS

This module includes functions that control DNS communication. 

The functions in this module are available only if feature `OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE` is enabled. 

###### Modules

[otDnsTxtEntry](ot-dns-txt-entry)

[otDnsTxtEntryIterator](ot-dns-txt-entry-iterator)

[otDnsQueryConfig](ot-dns-query-config)

[otDnsServiceInfo](ot-dns-service-info)

[otDnsRecordInfo](ot-dns-record-info)

###### Enumerations

###### otDnsRecursionFlag (heading level 7)

```
enum otDnsRecursionFlag {
    OT_DNS_FLAG_UNSPECIFIED = 0
    OT_DNS_FLAG_RECURSION_DESIRED = 1
    OT_DNS_FLAG_NO_RECURSION = 2
}
```

**Description:**

Type represents the "Recursion Desired" (RD) flag in an `otDnsQueryConfig`.

**Enumerator:**

|   |   |
|---|---|
|OT_DNS_FLAG_UNSPECIFIED|Indicates the flag is not specified.|
|OT_DNS_FLAG_RECURSION_DESIRED|Indicates DNS name server can resolve the query recursively.|
|OT_DNS_FLAG_NO_RECURSION|Indicates DNS name server can not resolve the query recursively.|

###### otDnsNat64Mode (heading level 7)

```
enum otDnsNat64Mode {
    OT_DNS_NAT64_UNSPECIFIED = 0
    OT_DNS_NAT64_ALLOW = 1
    OT_DNS_NAT64_DISALLOW = 2
}
```

**Description:**

Type represents the NAT64 mode in an `otDnsQueryConfig`.

**Details:**

The NAT64 mode indicates whether to allow or disallow NAT64 address translation during DNS client address resolution. This mode is only used when `OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE` is enabled.

**Enumerator:**

|   |   |
|---|---|
|OT_DNS_NAT64_UNSPECIFIED|NAT64 mode is not specified. Use default NAT64 mode.|
|OT_DNS_NAT64_ALLOW|Allow NAT64 address translation during DNS client address resolution.|
|OT_DNS_NAT64_DISALLOW|Do not allow NAT64 address translation during DNS client address resolution.|

###### otDnsServiceMode (heading level 7)

```
enum otDnsServiceMode {
    OT_DNS_SERVICE_MODE_UNSPECIFIED = 0
    OT_DNS_SERVICE_MODE_SRV = 1
    OT_DNS_SERVICE_MODE_TXT = 2
    OT_DNS_SERVICE_MODE_SRV_TXT = 3
    OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE = 4
    OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE = 5
}
```

**Description:**

Type represents the service resolution mode in an `otDnsQueryConfig`.

**Details:**

This is only used during DNS client service resolution `otDnsClientResolveService()`. It determines which record types to query.

**Enumerator:**

|   |   |
|---|---|
|OT_DNS_SERVICE_MODE_UNSPECIFIED|Mode is not specified. Use default service mode.|
|OT_DNS_SERVICE_MODE_SRV|Query for SRV record only.|
|OT_DNS_SERVICE_MODE_TXT|Query for TXT record only.|
|OT_DNS_SERVICE_MODE_SRV_TXT|Query for both SRV and TXT records in same message.|
|OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE|Query in parallel for SRV and TXT using separate messages.|
|OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE|Query for TXT/SRV together first, if fails then query separately.|

###### otDnsTransportProto (heading level 7)

```
enum otDnsTransportProto {
    OT_DNS_TRANSPORT_UNSPECIFIED = 0
    OT_DNS_TRANSPORT_UDP = 1
    OT_DNS_TRANSPORT_TCP = 2
}
```

**Description:**

Type represents the DNS transport protocol in an `otDnsQueryConfig`.

**Details:**

This `OT_DNS_TRANSPORT_TCP` is only supported when `OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE` is enabled.

**Enumerator:**

|   |   |
|---|---|
|OT_DNS_TRANSPORT_UNSPECIFIED||
|OT_DNS_TRANSPORT_UDP|DNS transport is unspecified.|
|OT_DNS_TRANSPORT_TCP|DNS query should be sent via UDP.|

###### otDnsRecordSection (heading level 7)

```
enum otDnsRecordSection {
    OT_DNS_SECTION_ANSWER
    OT_DNS_SECTION_AUTHORITY
    OT_DNS_SECTION_ADDITIONAL
}
```

**Description:**

Represents a section in a DNS query/response message.

**Enumerator:**

|   |   |
|---|---|
|OT_DNS_SECTION_ANSWER|Answer section.|
|OT_DNS_SECTION_AUTHORITY|Authority section.|
|OT_DNS_SECTION_ADDITIONAL|Additional section.|

###### Typedefs

###### otDnsTxtEntry (heading level 7)

`typedef struct otDnsTxtEntry otDnsTxtEntry`

**Description:**

Represents a TXT record entry representing a key/value pair (RFC 6763 - section 6.3).

**Details:**

The string buffers pointed to by `mKey` and `mValue` MUST persist and remain unchanged after an instance of such structure is passed to OpenThread (as part of `otSrpClientService` instance).

An array of `otDnsTxtEntry` entries are used in `otSrpClientService` to specify the full TXT record (a list of entries).

###### otDnsTxtEntryIterator (heading level 7)

`typedef struct otDnsTxtEntryIterator otDnsTxtEntryIterator`

**Description:**

Represents an iterator for TXT record entries (key/value pairs).

**Details:**

The data fields in this structure are intended for use by OpenThread core and caller should not read or change them.

###### otDnsQueryConfig (heading level 7)

`typedef struct otDnsQueryConfig otDnsQueryConfig`

**Description:**

Represents a DNS query configuration.

**Details:**

Any of the fields in this structure can be set to zero to indicate that it is not specified. How the unspecified fields are treated is determined by the function which uses the instance of `otDnsQueryConfig`.

###### otDnsAddressResponse (heading level 7)

`typedef struct otDnsAddressResponse otDnsAddressResponse`

**Description:**

An opaque representation of a response to an address resolution DNS query.

**Details:**

Pointers to instance of this type are provided from callback `otDnsAddressCallback`.

###### otDnsAddressCallback (heading level 7)

`typedef void(* otDnsAddressCallback) (otError aError, const otDnsAddressResponse *aResponse, void *aContext)`

**Description:**

Pointer is called when a DNS response is received for an address resolution query.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aError|The result of the DNS transaction.|
||[in]|aResponse|A pointer to the response (it is always non-NULL).|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

Within this callback the user can use `otDnsAddressResponseGet{Item}()` functions along with the `aResponse` pointer to get more info about the response.

The `aResponse` pointer can only be used within this callback and after returning from this function it will not stay valid, so the user MUST NOT retain the `aResponse` pointer for later use.

The `aError` can have the following:

- OT_ERROR_NONE A response was received successfully.
- OT_ERROR_ABORT A DNS transaction was aborted by stack.
- OT_ERROR_RESPONSE_TIMEOUT No DNS response has been received within timeout.

If the server rejects the address resolution request the error code from server is mapped as follow:

- (0) NOERROR Success (no error condition) -> OT_ERROR_NONE
- (1) FORMERR Server unable to interpret due to format error -> OT_ERROR_PARSE
- (2) SERVFAIL Server encountered an internal failure -> OT_ERROR_FAILED
- (3) NXDOMAIN Name that ought to exist, does not exist -> OT_ERROR_NOT_FOUND
- (4) NOTIMP Server does not support the query type (OpCode) -> OT_ERROR_NOT_IMPLEMENTED
- (5) REFUSED Server refused for policy/security reasons -> OT_ERROR_SECURITY
- (6) YXDOMAIN Some name that ought not to exist, does exist -> OT_ERROR_DUPLICATED
- (7) YXRRSET Some RRset that ought not to exist, does exist -> OT_ERROR_DUPLICATED
- (8) NXRRSET Some RRset that ought to exist, does not exist -> OT_ERROR_NOT_FOUND
- (9) NOTAUTH Service is not authoritative for zone -> OT_ERROR_SECURITY
- (10) NOTZONE A name is not in the zone -> OT_ERROR_PARSE
- (20) BADNAME Bad name -> OT_ERROR_PARSE
- (21) BADALG Bad algorithm -> OT_ERROR_SECURITY
- (22) BADTRUN Bad truncation -> OT_ERROR_PARSE
- Other response codes -> OT_ERROR_FAILED

###### otDnsBrowseResponse (heading level 7)

`typedef struct otDnsBrowseResponse otDnsBrowseResponse`

**Description:**

An opaque representation of a response to a browse (service instance enumeration) DNS query.

**Details:**

Pointers to instance of this type are provided from callback `otDnsBrowseCallback`.

###### otDnsBrowseCallback (heading level 7)

`typedef void(* otDnsBrowseCallback) (otError aError, const otDnsBrowseResponse *aResponse, void *aContext)`

**Description:**

Pointer is called when a DNS response is received for a browse (service instance enumeration) query.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aError|The result of the DNS transaction.|
||[in]|aResponse|A pointer to the response (it is always non-NULL).|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

Within this callback the user can use `otDnsBrowseResponseGet{Item}()` functions along with the `aResponse` pointer to get more info about the response.

The `aResponse` pointer can only be used within this callback and after returning from this function it will not stay valid, so the user MUST NOT retain the `aResponse` pointer for later use.

For the full list of possible values for `aError`, please see `otDnsAddressCallback()`.

###### otDnsServiceInfo (heading level 7)

`typedef struct otDnsServiceInfo otDnsServiceInfo`

**Description:**

Provides info for a DNS service instance.

###### otDnsServiceResponse (heading level 7)

`typedef struct otDnsServiceResponse otDnsServiceResponse`

**Description:**

An opaque representation of a response to a service instance resolution DNS query.

**Details:**

Pointers to instance of this type are provided from callback `otDnsAddressCallback`.

###### otDnsServiceCallback (heading level 7)

`typedef void(* otDnsServiceCallback) (otError aError, const otDnsServiceResponse *aResponse, void *aContext)`

**Description:**

Pointer is called when a DNS response is received for a service instance resolution query.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aError|The result of the DNS transaction.|
||[in]|aResponse|A pointer to the response (it is always non-NULL).|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

Within this callback the user can use `otDnsServiceResponseGet{Item}()` functions along with the `aResponse` pointer to get more info about the response.

The `aResponse` pointer can only be used within this callback and after returning from this function it will not stay valid, so the user MUST NOT retain the `aResponse` pointer for later use.

For the full list of possible values for `aError`, please see `otDnsAddressCallback()`.

###### otDnsRecordResponse (heading level 7)

`typedef struct otDnsRecordResponse otDnsRecordResponse`

**Description:**

An opaque representation of a response to a query for an arbitrary record type.

**Details:**

Pointers to instance of this type are provided from callback `otDnsRecordCallback`.

###### otDnsRecordCallback (heading level 7)

`typedef void(* otDnsRecordCallback) (otError aError, const otDnsRecordResponse *aResponse, void *aContext)`

**Description:**

Pointer is called when a DNS response is received for a DNS query to an arbitrary record type.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aError|The result of the DNS transaction.|
||[in]|aResponse|A pointer to the response (it is always non-NULL).|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

Within this callback the user can use `otDnsRecordResponseGet{Item}()` functions along with the `aResponse` pointer to get more info about the response.

The `aResponse` pointer can only be used within this callback and after returning from this function it will not stay valid, so the user MUST NOT retain the `aResponse` pointer for later use.

The `aError` can have the following:

- OT_ERROR_NONE A response was received successfully.
- OT_ERROR_ABORT A DNS transaction was aborted by the stack.
- OT_ERROR_RESPONSE_TIMEOUT No DNS response has been received within timeout.

###### otDnsRecordInfo (heading level 7)

`typedef struct otDnsRecordInfo otDnsRecordInfo`

**Description:**

Represents info for a record in an `otDnsRecordResponse`.

**Details:**

This struct is used as input to `otDnsRecordResponseGetRecordInfo()`.

###### Functions

###### otDnsInitTxtEntryIterator (heading level 7)

`void otDnsInitTxtEntryIterator(otDnsTxtEntryIterator *aIterator, const uint8_t *aTxtData, uint16_t aTxtDataLength)`

**Description:** Initializes a TXT record iterator.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otDnsTxtEntryIterator](ot-dns-txt-entry-iterator) *|[in]|aIterator|A pointer to the iterator to initialize (MUST NOT be NULL).|
|const uint8_t *|[in]|aTxtData|A pointer to buffer containing the encoded TXT data.|
|uint16_t|[in]|aTxtDataLength|The length (number of bytes) of `aTxtData`.|

The buffer pointer `aTxtData` and its content MUST persist and remain unchanged while `aIterator` object is being used.

###### otDnsGetNextTxtEntry (heading level 7)

`otError otDnsGetNextTxtEntry(otDnsTxtEntryIterator *aIterator, otDnsTxtEntry *aEntry)`

**Description:** Parses the TXT data from an iterator and gets the next TXT record entry (key/value pair).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otDnsTxtEntryIterator](ot-dns-txt-entry-iterator) *|[in]|aIterator|A pointer to the iterator (MUST NOT be NULL).|
|[otDnsTxtEntry](ot-dns-txt-entry) *|[out]|aEntry|A pointer to a `otDnsTxtEntry` structure to output the parsed/read entry (MUST NOT be NULL).|

The `aIterator` MUST be initialized using `otDnsInitTxtEntryIterator()` before calling this function and the TXT data buffer used to initialize the iterator MUST persist and remain unchanged. Otherwise the behavior of this function is undefined.

If the parsed key string length is smaller than or equal to `OT_DNS_TXT_KEY_ITER_MAX_LENGTH` the key string is returned in `mKey` in `aEntry`. But if the key is longer, then `mKey` is set to NULL and the entire encoded TXT entry string is returned in `mValue` and `mValueLength`.

###### otDnsEncodeTxtData (heading level 7)

`otError otDnsEncodeTxtData(const otDnsTxtEntry *aTxtEntries, uint16_t aNumTxtEntries, uint8_t *aTxtData, uint16_t *aTxtDataLength)`

**Description:** Encodes a given list of TXT record entries (key/value pairs) into TXT data (following format specified by RFC 6763).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otDnsTxtEntry](ot-dns-txt-entry) *|[in]|aTxtEntries|Pointer to an array of `otDnsTxtEntry`.|
|uint16_t|[in]|aNumTxtEntries|Number of entries in `aTxtEntries` array.|
|uint8_t *|[out]|aTxtData|A pointer to a buffer to output the encoded TXT data.|
|uint16_t *|[inout]|aTxtDataLength|On input, size of buffer `aTxtData`. On output, length of the encoded TXT data.|

###### otDnsSetNameCompressionEnabled (heading level 7)

`otError otDnsSetNameCompressionEnabled(otInstance *aInstance, bool aEnabled)`

**Description:** Enables/disables the "DNS name compression" mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnabled|TRUE to enable the "DNS name compression" mode, FALSE to disable.|

By default, DNS name compression is enabled. When disabled, DNS names are appended in full and are never compressed. This applies to OpenThread's DNS and SRP client/server modules.

DNS name compression cannot be disabled if the OpenThread mDNS module is enabled. Enabling the mDNS module will automatically enable name compression if it was previously disabled. Attempting to disable compression while the mDNS module is active will return `OT_ERROR_NOT_CAPABLE`.

This is intended for testing only and available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` config is enabled.

###### otDnsIsNameCompressionEnabled (heading level 7)

`bool otDnsIsNameCompressionEnabled(otInstance *aInstance)`

**Description:** Indicates whether the "DNS name compression" mode is enabled or not.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

This is intended for testing only and available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` config is enabled.

**Returns**

- TRUE if the "DNS name compression" mode is enabled, FALSE otherwise.

###### otDnsClientGetDefaultConfig (heading level 7)

`const otDnsQueryConfig * otDnsClientGetDefaultConfig(otInstance *aInstance)`

**Description:** Gets the current default query config used by DNS client.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

When OpenThread stack starts, the default DNS query config is determined from a set of OT config options such as `OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_IP6_ADDRESS`, `_DEFAULT_SERVER_PORT`, `_DEFAULT_RESPONSE_TIMEOUT`, etc. (see `config/dns_client.h` for all related config options).

**Returns**

- A pointer to the current default config being used by DNS client.

###### otDnsClientSetDefaultConfig (heading level 7)

`void otDnsClientSetDefaultConfig(otInstance *aInstance, const otDnsQueryConfig *aConfig)`

**Description:** Sets the default query config on DNS client.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otDnsQueryConfig](ot-dns-query-config) *|[in]|aConfig|A pointer to the new query config to use as default.|

**Note**

- Any ongoing query will continue to use the config from when it was started. The new default config will be used for any future DNS queries.

The `aConfig` can be NULL. In this case the default config will be set to the defaults from OT config options `OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_{}`. This resets the default query config back to to the config when the OpenThread stack starts.

In a non-NULL `aConfig`, caller can choose to leave some of the fields in `otDnsQueryConfig` instance unspecified (value zero). The unspecified fields are replaced by the corresponding OT config option definitions `OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_{}` to form the default query config.

When `OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE` is enabled, the server's IPv6 address in the default config is automatically set and updated by DNS client. This is done only when user does not explicitly set or specify it. This behavior requires SRP client and its auto-start feature to be enabled. SRP client will then monitor the Thread Network Data for DNS/SRP Service entries to select an SRP server. The selected SRP server address is also set as the DNS server address in the default config.

###### otDnsClientResolveAddress (heading level 7)

`otError otDnsClientResolveAddress(otInstance *aInstance, const char *aHostName, otDnsAddressCallback aCallback, void *aContext, const otDnsQueryConfig *aConfig)`

**Description:** Sends an address resolution DNS query for AAAA (IPv6) record(s) for a given host name.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const char *|[in]|aHostName|The host name for which to query the address (MUST NOT be NULL).|
|[otDnsAddressCallback](api-dns#ot-dns-address-callback)|[in]|aCallback|A function pointer that shall be called on response reception or time-out.|
|void *|[in]|aContext|A pointer to arbitrary context information.|
|const [otDnsQueryConfig](ot-dns-query-config) *|[in]|aConfig|A pointer to the config to use for this query.|

The `aConfig` can be NULL. In this case the default config (from `otDnsClientGetDefaultConfig()`) will be used as the config for this query. In a non-NULL `aConfig`, some of the fields can be left unspecified (value zero). The unspecified fields are then replaced by the values from the default config.

###### otDnsClientResolveIp4Address (heading level 7)

`otError otDnsClientResolveIp4Address(otInstance *aInstance, const char *aHostName, otDnsAddressCallback aCallback, void *aContext, const otDnsQueryConfig *aConfig)`

**Description:** Sends an address resolution DNS query for A (IPv4) record(s) for a given host name.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const char *|[in]|aHostName|The host name for which to query the address (MUST NOT be NULL).|
|[otDnsAddressCallback](api-dns#ot-dns-address-callback)|[in]|aCallback|A function pointer that shall be called on response reception or time-out.|
|void *|[in]|aContext|A pointer to arbitrary context information.|
|const [otDnsQueryConfig](ot-dns-query-config) *|[in]|aConfig|A pointer to the config to use for this query.|

Requires and is available when `OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE` is enabled.

When a successful response is received, the addresses are returned from `aCallback` as NAT64 IPv6 translated versions of the IPv4 addresses from the query response.

The `aConfig` can be NULL. In this case the default config (from `otDnsClientGetDefaultConfig()`) will be used as the config for this query. In a non-NULL `aConfig`, some of the fields can be left unspecified (value zero). The unspecified fields are then replaced by the values from the default config.

###### otDnsAddressResponseGetHostName (heading level 7)

`otError otDnsAddressResponseGetHostName(const otDnsAddressResponse *aResponse, char *aNameBuffer, uint16_t aNameBufferSize)`

**Description:** Gets the full host name associated with an address resolution DNS response.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otDnsAddressResponse](api-dns#ot-dns-address-response) *|[in]|aResponse|A pointer to the response.|
|char *|[out]|aNameBuffer|A buffer to char array to output the full host name (MUST NOT be NULL).|
|uint16_t|[in]|aNameBufferSize|The size of `aNameBuffer`.|

MUST only be used from `otDnsAddressCallback`.

###### otDnsAddressResponseGetAddress (heading level 7)

`otError otDnsAddressResponseGetAddress(const otDnsAddressResponse *aResponse, uint16_t aIndex, otIp6Address *aAddress, uint32_t *aTtl)`

**Description:** Gets an IPv6 address associated with an address resolution DNS response.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otDnsAddressResponse](api-dns#ot-dns-address-response) *|[in]|aResponse|A pointer to the response.|
|uint16_t|[in]|aIndex|The address record index to retrieve.|
|[otIp6Address](ot-ip6-address) *|[out]|aAddress|A pointer to a IPv6 address to output the address (MUST NOT be NULL).|
|uint32_t *|[out]|aTtl|A pointer to an `uint32_t` to output TTL for the address. It can be NULL if caller does not want to get the TTL.|

MUST only be used from `otDnsAddressCallback`.

The response may include multiple IPv6 address records. `aIndex` can be used to iterate through the list of addresses. Index zero gets the first address and so on. When we reach end of the list, `OT_ERROR_NOT_FOUND` is returned.

###### otDnsClientBrowse (heading level 7)

`otError otDnsClientBrowse(otInstance *aInstance, const char *aServiceName, otDnsBrowseCallback aCallback, void *aContext, const otDnsQueryConfig *aConfig)`

**Description:** Sends a DNS browse (service instance enumeration) query for a given service name.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const char *|[in]|aServiceName|The service name to query for (MUST NOT be NULL).|
|[otDnsBrowseCallback](api-dns#ot-dns-browse-callback)|[in]|aCallback|A function pointer that shall be called on response reception or time-out.|
|void *|[in]|aContext|A pointer to arbitrary context information.|
|const [otDnsQueryConfig](ot-dns-query-config) *|[in]|aConfig|A pointer to the config to use for this query.|

Is available when `OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE` is enabled.

The `aConfig` can be NULL. In this case the default config (from `otDnsClientGetDefaultConfig()`) will be used as the config for this query. In a non-NULL `aConfig`, some of the fields can be left unspecified (value zero). The unspecified fields are then replaced by the values from the default config.

###### otDnsBrowseResponseGetServiceName (heading level 7)

`otError otDnsBrowseResponseGetServiceName(const otDnsBrowseResponse *aResponse, char *aNameBuffer, uint16_t aNameBufferSize)`

**Description:** Gets the service name associated with a DNS browse (service instance enumeration) response.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otDnsBrowseResponse](api-dns#ot-dns-browse-response) *|[in]|aResponse|A pointer to the response.|
|char *|[out]|aNameBuffer|A buffer to char array to output the service name (MUST NOT be NULL).|
|uint16_t|[in]|aNameBufferSize|The size of `aNameBuffer`.|

MUST only be used from `otDnsBrowseCallback`.

###### otDnsBrowseResponseGetServiceInstance (heading level 7)

`otError otDnsBrowseResponseGetServiceInstance(const otDnsBrowseResponse *aResponse, uint16_t aIndex, char *aLabelBuffer, uint8_t aLabelBufferSize)`

**Description:** Gets a service instance associated with a DNS browse (service instance enumeration) response.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otDnsBrowseResponse](api-dns#ot-dns-browse-response) *|[in]|aResponse|A pointer to the response.|
|uint16_t|[in]|aIndex|The service instance record index to retrieve.|
|char *|[out]|aLabelBuffer|A buffer to char array to output the service instance label (MUST NOT be NULL).|
|uint8_t|[in]|aLabelBufferSize|The size of `aLabelBuffer`.|

MUST only be used from `otDnsBrowseCallback`.

The response may include multiple service instance records. `aIndex` can be used to iterate through the list. Index zero gives the first record. When we reach end of the list, `OT_ERROR_NOT_FOUND` is returned.

Note that this function gets the service instance label and not the full service instance name which is of the form `<Instance>.<Service>.<Domain>`.

###### otDnsBrowseResponseGetServiceInfo (heading level 7)

`otError otDnsBrowseResponseGetServiceInfo(const otDnsBrowseResponse *aResponse, const char *aInstanceLabel, otDnsServiceInfo *aServiceInfo)`

**Description:** Gets info for a service instance from a DNS browse (service instance enumeration) response.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otDnsBrowseResponse](api-dns#ot-dns-browse-response) *|[in]|aResponse|A pointer to the response.|
|const char *|[in]|aInstanceLabel|The service instance label (MUST NOT be NULL).|
|[otDnsServiceInfo](ot-dns-service-info) *|[out]|aServiceInfo|A `ServiceInfo` to output the service instance information (MUST NOT be NULL).|

MUST only be used from `otDnsBrowseCallback`.

A browse DNS response can include SRV, TXT, and AAAA records for the service instances that are enumerated. This is a SHOULD and not a MUST requirement, and servers/resolvers are not required to provide this. This function attempts to retrieve this info for a given service instance when available.

- If no matching SRV record is found in `aResponse`, `OT_ERROR_NOT_FOUND` is returned. In this case, no additional records (no TXT and/or AAAA) are read.
- If a matching SRV record is found in `aResponse`, `aServiceInfo` is updated and `OT_ERROR_NONE` is returned.
- If no matching TXT record is found in `aResponse`, `mTxtDataSize` in `aServiceInfo` is set to zero.
- If TXT data length is greater than `mTxtDataSize`, it is read partially and `mTxtDataTruncated` is set to true.
- If no matching AAAA record is found in `aResponse`, `mHostAddress is set to all zero or unspecified address.`
- `If there are multiple AAAA records for the host name in @p aResponse,`mHostAddress`is set to the first one. The other addresses can be retrieved using`[otDnsBrowseResponseGetHostAddress()](api-dns#ot-dns-browse-response-get-host-address)`.

###### otDnsBrowseResponseGetHostAddress (heading level 7)

`otError otDnsBrowseResponseGetHostAddress(const otDnsBrowseResponse *aResponse, const char *aHostName, uint16_t aIndex, otIp6Address *aAddress, uint32_t *aTtl)`

**Description:** Gets the host IPv6 address from a DNS browse (service instance enumeration) response.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otDnsBrowseResponse](api-dns#ot-dns-browse-response) *|[in]|aResponse|A pointer to the response.|
|const char *|[in]|aHostName|The host name to get the address (MUST NOT be NULL).|
|uint16_t|[in]|aIndex|The address record index to retrieve.|
|[otIp6Address](ot-ip6-address) *|[out]|aAddress|A pointer to a IPv6 address to output the address (MUST NOT be NULL).|
|uint32_t *|[out]|aTtl|A pointer to an `uint32_t` to output TTL for the address. It can be NULL if caller does not want to get the TTL.|

MUST only be used from `otDnsBrowseCallback`.

The response can include zero or more IPv6 address records. `aIndex` can be used to iterate through the list of addresses. Index zero gets the first address and so on. When we reach end of the list, `OT_ERROR_NOT_FOUND` is returned.

###### otDnsClientResolveService (heading level 7)

`otError otDnsClientResolveService(otInstance *aInstance, const char *aInstanceLabel, const char *aServiceName, otDnsServiceCallback aCallback, void *aContext, const otDnsQueryConfig *aConfig)`

**Description:** Starts a DNS service instance resolution for a given service instance.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const char *|[in]|aInstanceLabel|The service instance label.|
|const char *|[in]|aServiceName|The service name (together with `aInstanceLabel` form full instance name).|
|[otDnsServiceCallback](api-dns#ot-dns-service-callback)|[in]|aCallback|A function pointer that shall be called on response reception or time-out.|
|void *|[in]|aContext|A pointer to arbitrary context information.|
|const [otDnsQueryConfig](ot-dns-query-config) *|[in]|aConfig|A pointer to the config to use for this query.|

Is available when `OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE` is enabled.

The `aConfig` can be NULL. In this case the default config (from `otDnsClientGetDefaultConfig()`) will be used as the config for this query. In a non-NULL `aConfig`, some of the fields can be left unspecified (value zero). The unspecified fields are then replaced by the values from the default config.

The function sends queries for SRV and/or TXT records for the given service instance. The `mServiceMode` field in `otDnsQueryConfig` determines which records to query (SRV only, TXT only, or both SRV and TXT) and how to perform the query (together in the same message, separately in parallel, or in optimized mode where client will try in the same message first and then separately if it fails to get a response).

The SRV record provides information about service port, priority, and weight along with the host name associated with the service instance. This function DOES NOT perform address resolution for the host name discovered from SRV record. The server/resolver may provide AAAA/A record(s) for the host name in the Additional Data section of the response to SRV/TXT query and this information can be retrieved using `otDnsServiceResponseGetServiceInfo()` in `otDnsServiceCallback`. Users of this API MUST NOT assume that host address will always be available from `otDnsServiceResponseGetServiceInfo()`.

###### otDnsClientResolveServiceAndHostAddress (heading level 7)

`otError otDnsClientResolveServiceAndHostAddress(otInstance *aInstance, const char *aInstanceLabel, const char *aServiceName, otDnsServiceCallback aCallback, void *aContext, const otDnsQueryConfig *aConfig)`

**Description:** Starts a DNS service instance resolution for a given service instance, with a potential follow-up address resolution for the host name discovered for the service instance.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const char *|[in]|aInstanceLabel|The service instance label.|
|const char *|[in]|aServiceName|The service name (together with `aInstanceLabel` form full instance name).|
|[otDnsServiceCallback](api-dns#ot-dns-service-callback)|[in]|aCallback|A function pointer that shall be called on response reception or time-out.|
|void *|[in]|aContext|A pointer to arbitrary context information.|
|const [otDnsQueryConfig](ot-dns-query-config) *|[in]|aConfig|A pointer to the config to use for this query.|

Is available when `OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE` is enabled.

The `aConfig` can be NULL. In this case the default config (from `otDnsClientGetDefaultConfig()`) will be used as the config for this query. In a non-NULL `aConfig`, some of the fields can be left unspecified (value zero). The unspecified fields are then replaced by the values from the default config. This function cannot be used with `mServiceMode` in DNS config set to `OT_DNS_SERVICE_MODE_TXT` (i.e., querying for TXT record only) and will return `OT_ERROR_INVALID_ARGS`.

Behaves similarly to `otDnsClientResolveService()` sending queries for SRV and TXT records. However, if the server/resolver does not provide AAAA/A records for the host name in the response to SRV query (in the Additional Data section), it will perform host name resolution (sending an AAAA query) for the discovered host name from the SRV record. The callback `aCallback` is invoked when responses for all queries are received (i.e., both service and host address resolutions are finished).

###### otDnsServiceResponseGetServiceName (heading level 7)

`otError otDnsServiceResponseGetServiceName(const otDnsServiceResponse *aResponse, char *aLabelBuffer, uint8_t aLabelBufferSize, char *aNameBuffer, uint16_t aNameBufferSize)`

**Description:** Gets the service instance name associated with a DNS service instance resolution response.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otDnsServiceResponse](api-dns#ot-dns-service-response) *|[in]|aResponse|A pointer to the response.|
|char *|[out]|aLabelBuffer|A buffer to char array to output the service instance label (MUST NOT be NULL).|
|uint8_t|[in]|aLabelBufferSize|The size of `aLabelBuffer`.|
|char *|[out]|aNameBuffer|A buffer to char array to output the rest of service name (can be NULL if user is not interested in getting the name.|
|uint16_t|[in]|aNameBufferSize|The size of `aNameBuffer`.|

MUST only be used from `otDnsServiceCallback`.

###### otDnsServiceResponseGetServiceInfo (heading level 7)

`otError otDnsServiceResponseGetServiceInfo(const otDnsServiceResponse *aResponse, otDnsServiceInfo *aServiceInfo)`

**Description:** Gets info for a service instance from a DNS service instance resolution response.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otDnsServiceResponse](api-dns#ot-dns-service-response) *|[in]|aResponse|A pointer to the response.|
|[otDnsServiceInfo](ot-dns-service-info) *|[out]|aServiceInfo|A `ServiceInfo` to output the service instance information (MUST NOT be NULL).|

MUST only be used from a `otDnsServiceCallback` triggered from `otDnsClientResolveService()` or `otDnsClientResolveServiceAndHostAddress()`.

When this is is used from a `otDnsClientResolveService()` callback, the DNS response from server/resolver may include AAAA records in its Additional Data section for the host name associated with the service instance that is resolved. This is a SHOULD and not a MUST requirement so servers/resolvers are not required to provide this. This function attempts to parse AAAA record(s) if included in the response. If it is not included `mHostAddress` is set to all zeros (unspecified address). To also resolve the host address, user can use the DNS client API function `otDnsClientResolveServiceAndHostAddress()` which will perform service resolution followed up by a host name address resolution query (when AAAA records are not provided by server/resolver in the SRV query response).

- If a matching SRV record is found in `aResponse`, `aServiceInfo` is updated.
- If no matching SRV record is found, `OT_ERROR_NOT_FOUND` is returned unless the query config for this query used `OT_DNS_SERVICE_MODE_TXT` for `mServiceMode` (meaning the request was only for TXT record). In this case, we still try to parse the SRV record from Additional Data Section of response (in case server provided the info).
- If no matching TXT record is found in `aResponse`, `mTxtDataSize` in `aServiceInfo` is set to zero.
- If TXT data length is greater than `mTxtDataSize`, it is read partially and `mTxtDataTruncated` is set to true.
- If no matching AAAA record is found in `aResponse`, `mHostAddress is set to all zero or unspecified address.`
- `If there are multiple AAAA records for the host name in @p aResponse,`mHostAddress`is set to the first one. The other addresses can be retrieved using`[otDnsServiceResponseGetHostAddress()](api-dns#ot-dns-service-response-get-host-address)`.

###### otDnsServiceResponseGetHostAddress (heading level 7)

`otError otDnsServiceResponseGetHostAddress(const otDnsServiceResponse *aResponse, const char *aHostName, uint16_t aIndex, otIp6Address *aAddress, uint32_t *aTtl)`

**Description:** Gets the host IPv6 address from a DNS service instance resolution response.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otDnsServiceResponse](api-dns#ot-dns-service-response) *|[in]|aResponse|A pointer to the response.|
|const char *|[in]|aHostName|The host name to get the address (MUST NOT be NULL).|
|uint16_t|[in]|aIndex|The address record index to retrieve.|
|[otIp6Address](ot-ip6-address) *|[out]|aAddress|A pointer to a IPv6 address to output the address (MUST NOT be NULL).|
|uint32_t *|[out]|aTtl|A pointer to an `uint32_t` to output TTL for the address. It can be NULL if caller does not want to get the TTL.|

MUST only be used from `otDnsServiceCallback`.

The response can include zero or more IPv6 address records. `aIndex` can be used to iterate through the list of addresses. Index zero gets the first address and so on. When we reach end of the list, `OT_ERROR_NOT_FOUND` is returned.

###### otDnsClientQueryRecord (heading level 7)

`otError otDnsClientQueryRecord(otInstance *aInstance, uint16_t aRecordType, const char *aFirstLabel, const char *aNextLabels, otDnsRecordCallback aCallback, void *aContext, const otDnsQueryConfig *aConfig)`

**Description:** Sends a DNS query for a given record type and name.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aRecordType|The resource record type to query.|
|const char *|[in]|aFirstLabel|The first label of the name to be queried (can be NULL if not needed).|
|const char *|[in]|aNextLabels|The next labels of the name to be queried (MUST NOT be NULL).|
|[otDnsRecordCallback](api-dns#ot-dns-record-callback)|[in]|aCallback|A function pointer that shall be called on response reception or time-out.|
|void *|[in]|aContext|A pointer to arbitrary context information used when `aCallback` is invoked.|
|const [otDnsQueryConfig](ot-dns-query-config) *|[in]|aConfig|A pointer to the config to use for this query.|

Requires `OPENTHREAD_CONFIG_DNS_CLIENT_ARBITRARY_RECORD_QUERY_ENABLE`.

The `aConfig` can be NULL. In this case the default config (from `otDnsClientGetDefaultConfig()`) will be used as the config for this query. In a non-NULL `aConfig`, some of the fields can be left unspecified (value zero). The unspecified fields are then replaced by the values from the default config.

###### otDnsRecordResponseGetQueryName (heading level 7)

`otError otDnsRecordResponseGetQueryName(const otDnsRecordResponse *aResponse, char *aNameBuffer, uint16_t aNameBufferSize)`

**Description:** Gets the query name associated with a record query DNS response.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otDnsRecordResponse](api-dns#ot-dns-record-response) *|[in]|aResponse|A pointer to the response.|
|char *|[out]|aNameBuffer|A buffer to char array to output the full query name (MUST NOT be NULL).|
|uint16_t|[in]|aNameBufferSize|The size of `aNameBuffer`.|

MUST only be used from `otDnsRecordCallback`.

###### otDnsRecordResponseGetRecordInfo (heading level 7)

`otError otDnsRecordResponseGetRecordInfo(const otDnsRecordResponse *aResponse, uint16_t aIndex, otDnsRecordInfo *aRecordInfo)`

**Description:** Reads the records from a DNS query response.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otDnsRecordResponse](api-dns#ot-dns-record-response) *|[in]|aResponse|A pointer to the response.|
|uint16_t|[in]|aIndex|The record index to retrieve.|
|[otDnsRecordInfo](ot-dns-record-info) *|[out]|aRecordInfo|A pointer to a `otDnsRecordInfo` to populate with read record info.|

MUST only be used from `otDnsRecordCallback`.

The response may include multiple records. `aIndex` can be used to iterate through the list. Index zero gets the first record and so on. When we reach the end of the list, `OT_ERROR_NOT_FOUND` is returned.

Upon successful retrieval (`OT_ERROR_NONE`):

- `mRecordLength` is set to the actual length of the record's data.
- The data is copied into `mDataBuffer` (if not `NULL`) up to its capacity specified by `mDataBufferSize`.
- `mDataBufferSize` is then updated to reflect the number of bytes actually written into `mDataBuffer`.

If the retrieved record type is NS, CNAME, SOA, PTR, MX, RP, AFSDB, RT, PX, SRV, KX, DNAME, or NSEC, the record data in the received response contains a DNS name which may use DNS name compression. For these specific record types, the record data is first decompressed such that it contains the full uncompressed DNS name. This decompressed data is then provided in `mDataBuffer`, and `mRecordDataLength` will indicate the length of this decompressed data. For all other record types, the record data is read and provided as it appears in the received response message.

###### Macros

`#define OT_DNS_MAX_NAME_SIZE 255`

**Description**: Maximum name string size (includes null char at the end of string).

`#define OT_DNS_MAX_LABEL_SIZE 64`

**Description**: Maximum label string size (include null char at the end of string).

`#define OT_DNS_TXT_KEY_MIN_LENGTH 1`

**Description**: Minimum length of TXT record key string (RFC 6763 - section 6.4).

`#define OT_DNS_TXT_KEY_MAX_LENGTH 9`

**Description**: Recommended maximum length of TXT record key string (RFC 6763 - section 6.4).

`#define OT_DNS_TXT_KEY_ITER_MAX_LENGTH 64`

**Description**: Maximum length of TXT key string supported by `otDnsTxtEntryIterator`.

Represents a TXT record entry representing a key/value pair (RFC 6763 - section 6.3). 

The string buffers pointed to by `mKey` and `mValue` MUST persist and remain unchanged after an instance of such structure is passed to OpenThread (as part of `otSrpClientService` instance).

An array of `otDnsTxtEntry` entries are used in `otSrpClientService` to specify the full TXT record (a list of entries). 

###### Public Attributes (heading level 7)

###### mKey (heading level 8)

```
const char* otDnsTxtEntry::mKey
```

**Description:** The TXT record key string.

**Details:** If `mKey` is not NULL, then it MUST be a null-terminated C string. The entry is treated as key/value pair with `mValue` buffer providing the value.

- The entry is encoded as follows:  
  - A single string length byte followed by "key=value" format (without the quotation marks).  
  - In this case, the overall encoded length must be 255 bytes or less.
- If `mValue` is NULL, then key is treated as a boolean attribute and encoded as "key" (with no `=`).
- If `mValue` is not NULL but `mValueLength` is zero, then it is treated as empty value and encoded as "key=".

If `mKey` is NULL, then `mValue` buffer is treated as an already encoded TXT-DATA and is appended as is in the DNS message.

###### mValue (heading level 8)

```
const uint8_t* otDnsTxtEntry::mValue
```

**Description:** The TXT record value or already encoded TXT-DATA (depending on `mKey`).

###### mValueLength (heading level 8)

```
uint16_t otDnsTxtEntry::mValueLength
```

**Description:** Number of bytes in `mValue` buffer.

Represents an iterator for TXT record entries (key/value pairs). 

The data fields in this structure are intended for use by OpenThread core and caller should not read or change them. 

###### Public Attributes (heading level 7)

###### mPtr (heading level 8)

```
const void* otDnsTxtEntryIterator::mPtr
```

###### mData (heading level 8)

```
uint16_t otDnsTxtEntryIterator::mData[2]
```

###### mChar (heading level 8)

```
char otDnsTxtEntryIterator::mChar[OT_DNS_TXT_KEY_ITER_MAX_LENGTH+1]
```

Represents a DNS query configuration. 

Any of the fields in this structure can be set to zero to indicate that it is not specified. How the unspecified fields are treated is determined by the function which uses the instance of `otDnsQueryConfig`. 

###### Public Attributes (heading level 7)

###### mServerSockAddr (heading level 8)

```
otSockAddr otDnsQueryConfig::mServerSockAddr
```

**Description:** Server address (IPv6 addr/port). All zero or zero port for unspecified.

###### mResponseTimeout (heading level 8)

```
uint32_t otDnsQueryConfig::mResponseTimeout
```

**Description:** Wait time (in msec) to rx response. Zero indicates unspecified value.

###### mMaxTxAttempts (heading level 8)

```
uint8_t otDnsQueryConfig::mMaxTxAttempts
```

**Description:** Maximum tx attempts before reporting failure. Zero for unspecified value.

###### mRecursionFlag (heading level 8)

```
otDnsRecursionFlag otDnsQueryConfig::mRecursionFlag
```

**Description:** Indicates whether the server can resolve the query recursively or not.

###### mNat64Mode (heading level 8)

```
otDnsNat64Mode otDnsQueryConfig::mNat64Mode
```

**Description:** Allow/Disallow NAT64 address translation during address resolution.

###### mServiceMode (heading level 8)

```
otDnsServiceMode otDnsQueryConfig::mServiceMode
```

**Description:** Determines which records to query during service resolution.

###### mTransportProto (heading level 8)

```
otDnsTransportProto otDnsQueryConfig::mTransportProto
```

**Description:** Select default transport protocol.

Provides info for a DNS service instance. 

###### Public Attributes (heading level 7)

###### mTtl (heading level 8)

```
uint32_t otDnsServiceInfo::mTtl
```

**Description:** Service record TTL (in seconds).

###### mPort (heading level 8)

```
uint16_t otDnsServiceInfo::mPort
```

**Description:** Service port number.

###### mPriority (heading level 8)

```
uint16_t otDnsServiceInfo::mPriority
```

**Description:** Service priority.

###### mWeight (heading level 8)

```
uint16_t otDnsServiceInfo::mWeight
```

**Description:** Service weight.

###### mHostNameBuffer (heading level 8)

```
char* otDnsServiceInfo::mHostNameBuffer
```

**Description:** Buffer to output the service host name (can be NULL if not needed).

###### mHostNameBufferSize (heading level 8)

```
uint16_t otDnsServiceInfo::mHostNameBufferSize
```

**Description:** Size of `mHostNameBuffer`.

###### mHostAddress (heading level 8)

```
otIp6Address otDnsServiceInfo::mHostAddress
```

**Description:** The host IPv6 address. Set to all zero if not available.

###### mHostAddressTtl (heading level 8)

```
uint32_t otDnsServiceInfo::mHostAddressTtl
```

**Description:** The host address TTL.

###### mTxtData (heading level 8)

```
uint8_t* otDnsServiceInfo::mTxtData
```

**Description:** Buffer to output TXT data (can be NULL if not needed).

###### mTxtDataSize (heading level 8)

```
uint16_t otDnsServiceInfo::mTxtDataSize
```

**Description:** On input, size of `mTxtData` buffer. On output number bytes written.

###### mTxtDataTruncated (heading level 8)

```
bool otDnsServiceInfo::mTxtDataTruncated
```

**Description:** Indicates if TXT data could not fit in `mTxtDataSize` and was truncated.

###### mTxtDataTtl (heading level 8)

```
uint32_t otDnsServiceInfo::mTxtDataTtl
```

**Description:** The TXT data TTL.

Represents info for a record in an `otDnsRecordResponse`. 

This struct is used as input to `otDnsRecordResponseGetRecordInfo()`. 

###### Public Attributes (heading level 7)

###### mNameBuffer (heading level 8)

```
char* otDnsRecordInfo::mNameBuffer
```

**Description:** Buffer to output the name (MUST NOT be NULL).

###### mNameBufferSize (heading level 8)

```
uint16_t otDnsRecordInfo::mNameBufferSize
```

**Description:** Size of `mNameBuffer`.

###### mRecordType (heading level 8)

```
uint16_t otDnsRecordInfo::mRecordType
```

**Description:** The record type.

###### mRecordLength (heading level 8)

```
uint16_t otDnsRecordInfo::mRecordLength
```

**Description:** The record data length (in bytes).

###### mTtl (heading level 8)

```
uint32_t otDnsRecordInfo::mTtl
```

**Description:** Record TTL (in seconds).

###### mDataBuffer (heading level 8)

```
uint8_t* otDnsRecordInfo::mDataBuffer
```

**Description:** Buffer to output the data (Can be NULL if data not needed).

###### mDataBufferSize (heading level 8)

```
uint16_t otDnsRecordInfo::mDataBufferSize
```

**Description:** On input, size of `mDataBuffer`. On output number of bytes written.

###### mSection (heading level 8)

```
otDnsRecordSection otDnsRecordInfo::mSection
```

**Description:** Indicates the section of the record.

##### DNS-SD Server

This module includes APIs for DNS-SD server. 

###### Modules

[otDnssdServiceInstanceInfo](ot-dnssd-service-instance-info)

[otDnssdHostInfo](ot-dnssd-host-info)

[otUpstreamDnsCounters](ot-upstream-dns-counters)

[otDnssdCounters](ot-dnssd-counters)

###### Enumerations

###### otDnssdQueryType (heading level 7)

```
enum otDnssdQueryType {
    OT_DNSSD_QUERY_TYPE_NONE = 0
    OT_DNSSD_QUERY_TYPE_BROWSE = 1
    OT_DNSSD_QUERY_TYPE_RESOLVE = 2
    OT_DNSSD_QUERY_TYPE_RESOLVE_HOST = 3
}
```

**Description:**

Specifies a DNS-SD query type.

**Enumerator:**

|   |   |
|---|---|
|OT_DNSSD_QUERY_TYPE_NONE|Service type unspecified.|
|OT_DNSSD_QUERY_TYPE_BROWSE|Service type browse service.|
|OT_DNSSD_QUERY_TYPE_RESOLVE|Service type resolve service instance.|
|OT_DNSSD_QUERY_TYPE_RESOLVE_HOST|Service type resolve hostname.|

###### Typedefs

###### otDnssdQuerySubscribeCallback (heading level 7)

`typedef void(* otDnssdQuerySubscribeCallback) (void *aContext, const char *aFullName)`

**Description:**

Is called when a DNS-SD query subscribes one of:

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to the application-specific context.|
||[in]|aFullName|The null-terminated full service name (e.g. "_ipps._tcp.default.service.arpa."), or full service instance name (e.g. "OpenThread._ipps._tcp.default.service.arpa."), or full host name (e.g. "ot-host.default.service.arpa.").|

**Details:**

1. a service name.
2. a service instance name.
3. a host name.

The DNS-SD query implementation is responsible for identifying what `aFullName` is. If `aFullName` is a service name or service instance name, the DNS-SD query implementation should discover corresponding service instance information and notify the DNS-SD server using `otDnssdQueryHandleDiscoveredServiceInstance`. If `aFullName` is a host name, the DNS-SD query implementation should discover the host information and notify the DNS-SD server using `otDnssdQueryHandleDiscoveredHost`.

**Note**

- There can be multiple subscription to the same name. DNS-SD query implementation should record the number of active subscriptions and stop notifying when there is no active subscription for `aFullName`.

**See Also**

- [otDnssdQueryHandleDiscoveredServiceInstance](api-dnssd-server#ot-dnssd-query-handle-discovered-service-instance)
- [otDnssdQueryHandleDiscoveredHost](api-dnssd-server#ot-dnssd-query-handle-discovered-host)

###### otDnssdQueryUnsubscribeCallback (heading level 7)

`typedef void(* otDnssdQueryUnsubscribeCallback) (void *aContext, const char *aFullName)`

**Description:**

Is called when a DNS-SD query unsubscribes one of:

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to the application-specific context.|
||[in]|aFullName|The null-terminated full service name (e.g. "_ipps._tcp.default.service.arpa."), or full service instance name (e.g. "OpenThread._ipps._tcp.default.service.arpa.").|

**Details:**

1. a service name.
2. a service instance name.
3. a host name.

The DNS-SD query implementation is responsible for identifying what `aFullName` is.

**Note**

- There can be multiple subscription to the same name. DNS-SD query implementation should record the number of active subscriptions and stop notifying when there is no active subscription for `aFullName`.

###### otDnssdQuery (heading level 7)

`typedef void otDnssdQuery`

**Description:**

This opaque type represents a DNS-SD query.

###### otDnssdServiceInstanceInfo (heading level 7)

`typedef struct otDnssdServiceInstanceInfo otDnssdServiceInstanceInfo`

**Description:**

Represents information of a discovered service instance for a DNS-SD query.

###### otDnssdHostInfo (heading level 7)

`typedef struct otDnssdHostInfo otDnssdHostInfo`

**Description:**

Represents information of a discovered host for a DNS-SD query.

###### otUpstreamDnsCounters (heading level 7)

`typedef struct otUpstreamDnsCounters otUpstreamDnsCounters`

**Description:**

Represents the count of queries, responses, failures handled by upstream DNS server.

**Details:**

Requires `OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE`.

###### otDnssdCounters (heading level 7)

`typedef struct otDnssdCounters otDnssdCounters`

**Description:**

Contains the counters of DNS-SD server.

###### Functions

###### otDnssdQuerySetCallbacks (heading level 7)

`void otDnssdQuerySetCallbacks(otInstance *aInstance, otDnssdQuerySubscribeCallback aSubscribe, otDnssdQueryUnsubscribeCallback aUnsubscribe, void *aContext)`

**Description:** Sets DNS-SD server query callbacks.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otDnssdQuerySubscribeCallback](api-dnssd-server#ot-dnssd-query-subscribe-callback)|[in]|aSubscribe|A pointer to the callback function to subscribe a service or service instance.|
|[otDnssdQueryUnsubscribeCallback](api-dnssd-server#ot-dnssd-query-unsubscribe-callback)|[in]|aUnsubscribe|A pointer to the callback function to unsubscribe a service or service instance.|
|void *|[in]|aContext|A pointer to the application-specific context.|

The DNS-SD server calls `aSubscribe` to subscribe to a service or service instance to resolve a DNS-SD query and `aUnsubscribe` to unsubscribe when the query is resolved or timeout.

**Note**

- `aSubscribe` and `aUnsubscribe` must be both set or unset.

###### otDnssdQueryHandleDiscoveredServiceInstance (heading level 7)

`void otDnssdQueryHandleDiscoveredServiceInstance(otInstance *aInstance, const char *aServiceFullName, otDnssdServiceInstanceInfo *aInstanceInfo)`

**Description:** Notifies a discovered service instance.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|const char *|[in]|aServiceFullName|The null-terminated full service name.|
|[otDnssdServiceInstanceInfo](ot-dnssd-service-instance-info) *|[in]|aInstanceInfo|A pointer to the discovered service instance information.|

The external query resolver (e.g. Discovery Proxy) should call this function to notify OpenThread core of the subscribed services or service instances.

**Note**

- `aInstanceInfo` must not contain unspecified or link-local or loop-back or multicast IP addresses.

###### otDnssdQueryHandleDiscoveredHost (heading level 7)

`void otDnssdQueryHandleDiscoveredHost(otInstance *aInstance, const char *aHostFullName, otDnssdHostInfo *aHostInfo)`

**Description:** Notifies a discovered host.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|const char *|[in]|aHostFullName|The null-terminated full host name.|
|[otDnssdHostInfo](ot-dnssd-host-info) *|[in]|aHostInfo|A pointer to the discovered service instance information.|

The external query resolver (e.g. Discovery Proxy) should call this function to notify OpenThread core of the subscribed hosts.

**Note**

- `aHostInfo` must not contain unspecified or link-local or loop-back or multicast IP addresses.

###### otDnssdGetNextQuery (heading level 7)

`const otDnssdQuery * otDnssdGetNextQuery(otInstance *aInstance, const otDnssdQuery *aQuery)`

**Description:** Acquires the next query in the DNS-SD server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|const [otDnssdQuery](api-dnssd-server#ot-dnssd-query) *|[in]|aQuery|The query pointer. Pass NULL to get the first query.|

**Returns**

- A pointer to the query or NULL if no more queries.

###### otDnssdGetQueryTypeAndName (heading level 7)

`otDnssdQueryType otDnssdGetQueryTypeAndName(const otDnssdQuery *aQuery, char(*aNameOutput)[OT_DNS_MAX_NAME_SIZE])`

**Description:** Acquires the DNS-SD query type and name for a specific query.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otDnssdQuery](api-dnssd-server#ot-dnssd-query) *|[in]|aQuery|The query pointer acquired from `otDnssdGetNextQuery`.|
|char(*)|[out]|aNameOutput|The name output buffer, which should be `OT_DNS_MAX_NAME_SIZE` bytes long.|

**Returns**

- The DNS-SD query type.

###### otDnssdGetCounters (heading level 7)

`const otDnssdCounters * otDnssdGetCounters(otInstance *aInstance)`

**Description:** Returns the counters of the DNS-SD server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

**Returns**

- A pointer to the counters of the DNS-SD server.

###### otDnssdUpstreamQuerySetEnabled (heading level 7)

`void otDnssdUpstreamQuerySetEnabled(otInstance *aInstance, bool aEnabled)`

**Description:** Enable or disable forwarding DNS queries to platform DNS upstream API.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnabled|A boolean to enable/disable forwarding DNS queries to upstream.|

Available when `OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE` is enabled.

**See Also**

- [otPlatDnsStartUpstreamQuery](plat-dns#ot-plat-dns-start-upstream-query)
- [otPlatDnsCancelUpstreamQuery](plat-dns#ot-plat-dns-cancel-upstream-query)
- [otPlatDnsUpstreamQueryDone](plat-dns#ot-plat-dns-upstream-query-done)

###### otDnssdUpstreamQueryIsEnabled (heading level 7)

`bool otDnssdUpstreamQueryIsEnabled(otInstance *aInstance)`

**Description:** Returns whether the DNSSD server will forward DNS queries to the platform DNS upstream API.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Available when `OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE` is enabled.

**See Also**

- [otDnssdUpstreamQuerySetEnabled](api-dnssd-server#ot-dnssd-upstream-query-set-enabled)

Represents information of a discovered service instance for a DNS-SD query. 

###### Public Attributes (heading level 7)

###### mFullName (heading level 8)

```
const char* otDnssdServiceInstanceInfo::mFullName
```

**Description:** Full instance name (e.g. "OpenThread._ipps._tcp.default.service.arpa.").

###### mHostName (heading level 8)

```
const char* otDnssdServiceInstanceInfo::mHostName
```

**Description:** Host name (e.g. "ot-host.default.service.arpa.").

###### mAddressNum (heading level 8)

```
uint8_t otDnssdServiceInstanceInfo::mAddressNum
```

**Description:** Number of host IPv6 addresses.

###### mAddresses (heading level 8)

```
const otIp6Address* otDnssdServiceInstanceInfo::mAddresses
```

**Description:** Host IPv6 addresses.

###### mPort (heading level 8)

```
uint16_t otDnssdServiceInstanceInfo::mPort
```

**Description:** Service port.

###### mPriority (heading level 8)

```
uint16_t otDnssdServiceInstanceInfo::mPriority
```

**Description:** Service priority.

###### mWeight (heading level 8)

```
uint16_t otDnssdServiceInstanceInfo::mWeight
```

**Description:** Service weight.

###### mTxtLength (heading level 8)

```
uint16_t otDnssdServiceInstanceInfo::mTxtLength
```

**Description:** Service TXT RDATA length.

###### mTxtData (heading level 8)

```
const uint8_t* otDnssdServiceInstanceInfo::mTxtData
```

**Description:** Service TXT RDATA.

###### mTtl (heading level 8)

```
uint32_t otDnssdServiceInstanceInfo::mTtl
```

**Description:** Service TTL (in seconds).

Represents information of a discovered host for a DNS-SD query. 

###### Public Attributes (heading level 7)

###### mAddressNum (heading level 8)

```
uint8_t otDnssdHostInfo::mAddressNum
```

**Description:** Number of host IPv6 addresses.

###### mAddresses (heading level 8)

```
const otIp6Address* otDnssdHostInfo::mAddresses
```

**Description:** Host IPv6 addresses.

###### mTtl (heading level 8)

```
uint32_t otDnssdHostInfo::mTtl
```

**Description:** Service TTL (in seconds).

Represents the count of queries, responses, failures handled by upstream DNS server. 

Requires `OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE`. 

###### Public Attributes (heading level 7)

###### mQueries (heading level 8)

```
uint32_t otUpstreamDnsCounters::mQueries
```

**Description:** The number of queries forwarded.

###### mResponses (heading level 8)

```
uint32_t otUpstreamDnsCounters::mResponses
```

**Description:** The number of responses forwarded.

###### mFailures (heading level 8)

```
uint32_t otUpstreamDnsCounters::mFailures
```

**Description:** The number of upstream DNS failures.

Contains the counters of DNS-SD server. 

###### Public Attributes (heading level 7)

###### mSuccessResponse (heading level 8)

```
uint32_t otDnssdCounters::mSuccessResponse
```

**Description:** The number of successful responses.

###### mServerFailureResponse (heading level 8)

```
uint32_t otDnssdCounters::mServerFailureResponse
```

**Description:** The number of server failure responses.

###### mFormatErrorResponse (heading level 8)

```
uint32_t otDnssdCounters::mFormatErrorResponse
```

**Description:** The number of format error responses.

###### mNameErrorResponse (heading level 8)

```
uint32_t otDnssdCounters::mNameErrorResponse
```

**Description:** The number of name error responses.

###### mNotImplementedResponse (heading level 8)

```
uint32_t otDnssdCounters::mNotImplementedResponse
```

**Description:** The number of 'not implemented' responses.

###### mOtherResponse (heading level 8)

```
uint32_t otDnssdCounters::mOtherResponse
```

**Description:** The number of other responses.

###### mResolvedBySrp (heading level 8)

```
uint32_t otDnssdCounters::mResolvedBySrp
```

**Description:** The number of queries resolved by the local SRP server.

###### mUpstreamDnsCounters (heading level 8)

```
otUpstreamDnsCounters otDnssdCounters::mUpstreamDnsCounters
```

**Description:** The number of queries, responses, failures handled by upstream DNS server.

##### ICMPv6

This module includes functions that control ICMPv6 communication. 

###### Modules

[otIcmp6Header](ot-icmp6-header)

[otIcmp6Handler](ot-icmp6-handler)

###### Enumerations

###### otIcmp6Type (heading level 7)

```
enum otIcmp6Type {
    OT_ICMP6_TYPE_DST_UNREACH = 1
    OT_ICMP6_TYPE_PACKET_TO_BIG = 2
    OT_ICMP6_TYPE_TIME_EXCEEDED = 3
    OT_ICMP6_TYPE_PARAMETER_PROBLEM = 4
    OT_ICMP6_TYPE_ECHO_REQUEST = 128
    OT_ICMP6_TYPE_ECHO_REPLY = 129
    OT_ICMP6_TYPE_ROUTER_SOLICIT = 133
    OT_ICMP6_TYPE_ROUTER_ADVERT = 134
    OT_ICMP6_TYPE_NEIGHBOR_SOLICIT = 135
    OT_ICMP6_TYPE_NEIGHBOR_ADVERT = 136
}
```

**Description:**

ICMPv6 Message Types.

**Enumerator:**

|   |   |
|---|---|
|OT_ICMP6_TYPE_DST_UNREACH|Destination Unreachable.|
|OT_ICMP6_TYPE_PACKET_TO_BIG|Packet To Big.|
|OT_ICMP6_TYPE_TIME_EXCEEDED|Time Exceeded.|
|OT_ICMP6_TYPE_PARAMETER_PROBLEM|Parameter Problem.|
|OT_ICMP6_TYPE_ECHO_REQUEST|Echo Request.|
|OT_ICMP6_TYPE_ECHO_REPLY|Echo Reply.|
|OT_ICMP6_TYPE_ROUTER_SOLICIT|Router Solicitation.|
|OT_ICMP6_TYPE_ROUTER_ADVERT|Router Advertisement.|
|OT_ICMP6_TYPE_NEIGHBOR_SOLICIT|Neighbor Solicitation.|
|OT_ICMP6_TYPE_NEIGHBOR_ADVERT|Neighbor Advertisement.|

###### otIcmp6Code (heading level 7)

```
enum otIcmp6Code {
    OT_ICMP6_CODE_DST_UNREACH_NO_ROUTE = 0
    OT_ICMP6_CODE_DST_UNREACH_PROHIBITED = 1
    OT_ICMP6_CODE_FRAGM_REAS_TIME_EX = 1
}
```

**Description:**

ICMPv6 Message Codes.

**Enumerator:**

|   |   |
|---|---|
|OT_ICMP6_CODE_DST_UNREACH_NO_ROUTE|Destination Unreachable (Type 1) - No Route.|
|OT_ICMP6_CODE_DST_UNREACH_PROHIBITED|Destination Unreachable (Type 1) - Administratively Prohibited.|
|OT_ICMP6_CODE_FRAGM_REAS_TIME_EX|Time Exceeded (Type 3) - Fragment Reassembly.|

###### otIcmp6EchoMode (heading level 7)

```
enum otIcmp6EchoMode {
    OT_ICMP6_ECHO_HANDLER_DISABLED = 0
    OT_ICMP6_ECHO_HANDLER_UNICAST_ONLY = 1
    OT_ICMP6_ECHO_HANDLER_MULTICAST_ONLY = 2
    OT_ICMP6_ECHO_HANDLER_ALL = 3
    OT_ICMP6_ECHO_HANDLER_RLOC_ALOC_ONLY = 4
}
```

**Description:**

ICMPv6 Echo Reply Modes.

**Enumerator:**

|   |   |
|---|---|
|OT_ICMP6_ECHO_HANDLER_DISABLED|ICMPv6 Echo processing disabled.|
|OT_ICMP6_ECHO_HANDLER_UNICAST_ONLY|ICMPv6 Echo processing enabled only for unicast requests only.|
|OT_ICMP6_ECHO_HANDLER_MULTICAST_ONLY|ICMPv6 Echo processing enabled only for multicast requests only.|
|OT_ICMP6_ECHO_HANDLER_ALL|ICMPv6 Echo processing enabled for unicast and multicast requests.|
|OT_ICMP6_ECHO_HANDLER_RLOC_ALOC_ONLY|ICMPv6 Echo processing enabled for RLOC/ALOC destinations only.|

###### Typedefs

###### otIcmp6Type (heading level 7)

`typedef enum otIcmp6Type otIcmp6Type`

**Description:**

ICMPv6 Message Types.

###### otIcmp6Code (heading level 7)

`typedef enum otIcmp6Code otIcmp6Code`

**Description:**

ICMPv6 Message Codes.

###### otIcmp6Header (heading level 7)

`typedef struct otIcmp6Header otIcmp6Header`

**Description:**

Represents an ICMPv6 header.

###### otIcmp6ReceiveCallback (heading level 7)

`typedef void(* otIcmp6ReceiveCallback) (void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, const otIcmp6Header *aIcmpHeader)`

**Description:**

This callback allows OpenThread to inform the application of a received ICMPv6 message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to arbitrary context information.|
||[in]|aMessage|A pointer to the received message.|
||[in]|aMessageInfo|A pointer to message information associated with `aMessage`.|
||[in]|aIcmpHeader|A pointer to the received ICMPv6 header.|

**Details:**

###### otIcmp6Handler (heading level 7)

`typedef struct otIcmp6Handler otIcmp6Handler`

**Description:**

Implements ICMPv6 message handler.

###### otIcmp6EchoMode (heading level 7)

`typedef enum otIcmp6EchoMode otIcmp6EchoMode`

**Description:**

ICMPv6 Echo Reply Modes.

###### Variables

###### OT_TOOL_PACKED_END (heading level 7)

```
OT_TOOL_PACKED_BEGIN struct otIcmp6Header OT_TOOL_PACKED_END
```

###### Functions

###### otIcmp6GetEchoMode (heading level 7)

`otIcmp6EchoMode otIcmp6GetEchoMode(otInstance *aInstance)`

**Description:** Indicates whether or not ICMPv6 Echo processing is enabled.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otIcmp6SetEchoMode (heading level 7)

`void otIcmp6SetEchoMode(otInstance *aInstance, otIcmp6EchoMode aMode)`

**Description:** Sets whether or not ICMPv6 Echo processing is enabled.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otIcmp6EchoMode](api-icmp6#ot-icmp6-echo-mode)|[in]|aMode|The ICMPv6 Echo processing mode.|

###### otIcmp6RegisterHandler (heading level 7)

`otError otIcmp6RegisterHandler(otInstance *aInstance, otIcmp6Handler *aHandler)`

**Description:** Registers a handler to provide received ICMPv6 messages.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otIcmp6Handler](ot-icmp6-handler) *|[in]|aHandler|A pointer to a handler containing callback that is called when an ICMPv6 message is received.|

**Note**

- A handler structure `aHandler` has to be stored in persistent (static) memory. OpenThread does not make a copy of handler structure.

###### otIcmp6SendEchoRequest (heading level 7)

`otError otIcmp6SendEchoRequest(otInstance *aInstance, otMessage *aMessage, const otMessageInfo *aMessageInfo, uint16_t aIdentifier)`

**Description:** Sends an ICMPv6 Echo Request via the Thread interface.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the message buffer containing the ICMPv6 payload.|
|const [otMessageInfo](ot-message-info) *|[in]|aMessageInfo|A reference to message information associated with `aMessage`.|
|uint16_t|[in]|aIdentifier|An identifier to aid in matching Echo Replies to this Echo Request. May be zero.|

###### Macros

`#define OT_ICMP6_HEADER_DATA_SIZE 4`

**Description**: Size of ICMPv6 Header.

`#define OT_ICMP6_ROUTER_ADVERT_MIN_SIZE 16`

**Description**: Size of a Router Advertisement message without any options.

Implements ICMPv6 message handler. 

###### Public Attributes (heading level 7)

###### mReceiveCallback (heading level 8)

```
otIcmp6ReceiveCallback otIcmp6Handler::mReceiveCallback
```

**Description:** The ICMPv6 received callback.

###### mContext (heading level 8)

```
void* otIcmp6Handler::mContext
```

**Description:** A pointer to arbitrary context information.

###### mNext (heading level 8)

```
struct otIcmp6Handler* otIcmp6Handler::mNext
```

**Description:** A pointer to the next handler in the list.

Represents an ICMPv6 header. 

###### Modules (heading level 7)

[otIcmp6Header::OT_TOOL_PACKED_FIELD](ot-icmp6-header-ot-tool-packed-field)

###### Public Attributes (heading level 7)

###### mType (heading level 8)

```
uint8_t otIcmp6Header::mType
```

**Description:** Type.

###### mCode (heading level 8)

```
uint8_t otIcmp6Header::mCode
```

**Description:** Code.

###### mChecksum (heading level 8)

```
uint16_t otIcmp6Header::mChecksum
```

**Description:** Checksum.

###### mData (heading level 8)

```
union otIcmp6Header::OT_TOOL_PACKED_FIELD otIcmp6Header::mData
```

**Description:** Message-specific data.

###### Public Attributes (heading level 8)

###### m8 (heading level 9)

```
uint8_t otIcmp6Header::OT_TOOL_PACKED_FIELD::m8[OT_ICMP6_HEADER_DATA_SIZE/sizeof(uint8_t)]
```

###### m16 (heading level 9)

```
uint16_t otIcmp6Header::OT_TOOL_PACKED_FIELD::m16[OT_ICMP6_HEADER_DATA_SIZE/sizeof(uint16_t)]
```

###### m32 (heading level 9)

```
uint32_t otIcmp6Header::OT_TOOL_PACKED_FIELD::m32[OT_ICMP6_HEADER_DATA_SIZE/sizeof(uint32_t)]
```

##### IPv6

This module includes functions that control IPv6 communication. 

###### Modules

[otIp6InterfaceIdentifier](ot-ip6-interface-identifier)

[otIp6NetworkPrefix](ot-ip6-network-prefix)

[otIp6AddressComponents](ot-ip6-address-components)

[otIp6Address](ot-ip6-address)

[otIp6Prefix](ot-ip6-prefix)

[otNetifAddress](ot-netif-address)

[otNetifMulticastAddress](ot-netif-multicast-address)

[otSockAddr](ot-sock-addr)

[otMessageInfo](ot-message-info)

[otIp6AddressInfo](ot-ip6-address-info)

[otPacketsAndBytes](ot-packets-and-bytes)

[otBorderRoutingCounters](ot-border-routing-counters)

###### Enumerations

###### @2 (heading level 7)

```
enum @2 {
    OT_ADDRESS_ORIGIN_THREAD = 0
    OT_ADDRESS_ORIGIN_SLAAC = 1
    OT_ADDRESS_ORIGIN_DHCPV6 = 2
    OT_ADDRESS_ORIGIN_MANUAL = 3
}
```

**Description:**

IPv6 Address origins.

**Enumerator:**

|   |   |
|---|---|
|OT_ADDRESS_ORIGIN_THREAD|Thread assigned (ALOC, RLOC, MLEID, etc.)|
|OT_ADDRESS_ORIGIN_SLAAC|SLAAC assigned (used in `otNetifAddress` and not in `otNetifMulticastAddress`).|
|OT_ADDRESS_ORIGIN_DHCPV6|DHCPv6 assigned (used in `otNetifAddress` and not in `otNetifMulticastAddress`).|
|OT_ADDRESS_ORIGIN_MANUAL|Manually assigned address.|

###### @3 (heading level 7)

```
enum @3 {
    OT_ECN_NOT_CAPABLE = 0x0
    OT_ECN_CAPABLE_0 = 0x2
    OT_ECN_CAPABLE_1 = 0x1
    OT_ECN_MARKED = 0x3
}
```

**Description:**

ECN statuses, represented as in the IP header.

**Enumerator:**

|   |   |
|---|---|
|OT_ECN_NOT_CAPABLE|Non-ECT.|
|OT_ECN_CAPABLE_0|ECT(0)|
|OT_ECN_CAPABLE_1|ECT(1)|
|OT_ECN_MARKED|Congestion encountered (CE)|

###### @4 (heading level 7)

```
enum @4 {
    OT_IP6_PROTO_HOP_OPTS = 0
    OT_IP6_PROTO_TCP = 6
    OT_IP6_PROTO_UDP = 17
    OT_IP6_PROTO_IP6 = 41
    OT_IP6_PROTO_ROUTING = 43
    OT_IP6_PROTO_FRAGMENT = 44
    OT_IP6_PROTO_ICMP6 = 58
    OT_IP6_PROTO_NONE = 59
    OT_IP6_PROTO_DST_OPTS = 60
}
```

**Description:**

Internet Protocol Numbers.

**Enumerator:**

|   |   |
|---|---|
|OT_IP6_PROTO_HOP_OPTS|IPv6 Hop-by-Hop Option.|
|OT_IP6_PROTO_TCP|Transmission Control Protocol.|
|OT_IP6_PROTO_UDP|User Datagram.|
|OT_IP6_PROTO_IP6|IPv6 encapsulation.|
|OT_IP6_PROTO_ROUTING|Routing Header for IPv6.|
|OT_IP6_PROTO_FRAGMENT|Fragment Header for IPv6.|
|OT_IP6_PROTO_ICMP6|ICMP for IPv6.|
|OT_IP6_PROTO_NONE|No Next Header for IPv6.|
|OT_IP6_PROTO_DST_OPTS|Destination Options for IPv6.|

###### Typedefs

###### otIp6InterfaceIdentifier (heading level 7)

`typedef struct otIp6InterfaceIdentifier otIp6InterfaceIdentifier`

**Description:**

Represents the Interface Identifier of an IPv6 address.

###### otIp6NetworkPrefix (heading level 7)

`typedef struct otIp6NetworkPrefix otIp6NetworkPrefix`

**Description:**

Represents the Network Prefix of an IPv6 address (most significant 64 bits of the address).

###### otIp6AddressComponents (heading level 7)

`typedef struct otIp6AddressComponents otIp6AddressComponents`

**Description:**

Represents the components of an IPv6 address.

###### otIp6Address (heading level 7)

`typedef struct otIp6Address otIp6Address`

**Description:**

Represents an IPv6 address.

###### otIp6Prefix (heading level 7)

`typedef struct otIp6Prefix otIp6Prefix`

**Description:**

Represents an IPv6 prefix.

###### otNetifAddress (heading level 7)

`typedef struct otNetifAddress otNetifAddress`

**Description:**

Represents an IPv6 network interface unicast address.

###### otNetifMulticastAddress (heading level 7)

`typedef struct otNetifMulticastAddress otNetifMulticastAddress`

**Description:**

Represents an IPv6 network interface multicast address.

**Details:**

The `mAddressOrigin` field is set to either `OT_ADDRESS_ORIGIN_THREAD` if the multicast address is subscribed by OpenThread core or `OT_ADDRESS_ORIGIN_MANUAL` if it is subscribed manually using `otIp6SubscribeMulticastAddress()`.

The multicast addresses subscribed by OpenThread core include addresses such as

- link-local all nodes (`ff02::01`),
- realm-local all nodes (`ff03::01`),
- link-local all routers (`ff02::02`),
- realm-local all routers (`ff03::02`),
- realm-local all MPL forwarders (`ff03::fc`),
- link-local all Thread nodes,
- realm-local all Thread nodes.

###### otSockAddr (heading level 7)

`typedef struct otSockAddr otSockAddr`

**Description:**

Represents an IPv6 socket address.

###### otMessageInfo (heading level 7)

`typedef struct otMessageInfo otMessageInfo`

**Description:**

Represents the local and peer IPv6 socket addresses.

###### otIp6ReceiveCallback (heading level 7)

`typedef void(* otIp6ReceiveCallback) (otMessage *aMessage, void *aContext)`

**Description:**

Pointer is called when an IPv6 datagram is received.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aMessage|A pointer to the message buffer containing the received IPv6 datagram. This function transfers the ownership of the `aMessage` to the receiver of the callback. The message should be freed by the receiver of the callback after it is processed (see [otMessageFree()](api-message#ot-message-free)).|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### otIp6AddressInfo (heading level 7)

`typedef struct otIp6AddressInfo otIp6AddressInfo`

**Description:**

Represents IPv6 address information.

###### otIp6AddressCallback (heading level 7)

`typedef void(* otIp6AddressCallback) (const otIp6AddressInfo *aAddressInfo, bool aIsAdded, void *aContext)`

**Description:**

Pointer is called when an internal IPv6 address is added or removed.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aAddressInfo|A pointer to the IPv6 address information.|
||[in]|aIsAdded|TRUE if the `aAddress` was added, FALSE if `aAddress` was removed.|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### otIp6SlaacPrefixFilter (heading level 7)

`typedef bool(* otIp6SlaacPrefixFilter) (otInstance *aInstance, const otIp6Prefix *aPrefix)`

**Description:**

Pointer allows user to filter prefixes and not allow an SLAAC address based on a prefix to be added.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aInstance|A pointer to an OpenThread instance.|
||[in]|aPrefix|A pointer to prefix for which SLAAC address is about to be added.|

**Details:**

`otIp6SetSlaacPrefixFilter()` can be used to set the filter handler. The filter handler is invoked by SLAAC module when it is about to add a SLAAC address based on a prefix. Its boolean return value determines whether the address is filtered (not added) or not.

###### otIp6RegisterMulticastListenersCallback (heading level 7)

`typedef void(* otIp6RegisterMulticastListenersCallback) (void *aContext, otError aError, uint8_t aMlrStatus, const otIp6Address *aFailedAddresses, uint8_t aFailedAddressNum)`

**Description:**

Pointer is called with results of `otIp6RegisterMulticastListeners`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to the user context.|
||[in]|aError|OT_ERROR_NONE when successfully sent MLR.req and received MLR.rsp, OT_ERROR_RESPONSE_TIMEOUT when failed to receive MLR.rsp, OT_ERROR_PARSE when failed to parse MLR.rsp.|
||[in]|aMlrStatus|The Multicast Listener Registration status when `aError` is OT_ERROR_NONE.|
||[in]|aFailedAddresses|A pointer to the failed IPv6 addresses when `aError` is OT_ERROR_NONE.|
||[in]|aFailedAddressNum|The number of failed IPv6 addresses when `aError` is OT_ERROR_NONE.|

**Details:**

**See Also**

- [otIp6RegisterMulticastListeners](api-ip6#ot-ip6-register-multicast-listeners)

###### otPacketsAndBytes (heading level 7)

`typedef struct otPacketsAndBytes otPacketsAndBytes`

**Description:**

Represents the counters for packets and bytes.

###### otBorderRoutingCounters (heading level 7)

`typedef struct otBorderRoutingCounters otBorderRoutingCounters`

**Description:**

Represents the counters of packets forwarded via Border Routing.

###### Variables

###### OT_TOOL_PACKED_END (heading level 7)

```
OT_TOOL_PACKED_BEGIN struct otIp6Prefix OT_TOOL_PACKED_END
```

###### Functions

###### otIp6Init (heading level 7)

`otError otIp6Init(otInstance *aInstance, otNetifAddress *aUnicastAddrPool, uint16_t aUnicastAddrPoolSize, otNetifMulticastAddress *aMulticastAddrPool, uint16_t aMulticastAddrPoolSize)`

**Description:** Initializes the IPv6 interface and its external address pools.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otNetifAddress](ot-netif-address) *|[in]|aUnicastAddrPool|A pointer to an array of `otNetifAddress`.|
|uint16_t|[in]|aUnicastAddrPoolSize|The number of entries in `aUnicastAddrPool`.|
|[otNetifMulticastAddress](ot-netif-multicast-address) *|[in]|aMulticastAddrPool|A pointer to an array of `otNetifMulticastAddress`.|
|uint16_t|[in]|aMulticastAddrPoolSize|The number of entries in `aMulticastAddrPool`.|

Requires `OPENTHREAD_CONFIG_IP6_INIT_EXT_ADDR_POOL_ENABLE`.

It provides the memory buffers for the external unicast and multicast address pools and must be called before enabling the IPv6 interface.

The provided memory buffers MUST persist and remain valid as long as the OpenThread instance is initialized. OpenThread will use these provided buffers to manage the pools of externally added unicast and multicast addresses (i.e., those added via `otIp6AddUnicastAddress()` and `otIp6SubscribeMulticastAddress()`).

This function can only be called once. Subsequent calls will return `OT_ERROR_ALREADY`.

The `OPENTHREAD_CONFIG_IP6_INIT_EXT_ADDR_POOL_ENABLE` feature and this function allow the external unicast/multicast address pools to be configured at run-time after OpenThread instance initialization, rather than build-time. When this feature is disabled, the build-time configs `OPENTHREAD_CONFIG_IP6_MAX_EXT_UCAST_ADDRS` and `OPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS` specify the pool sizes used by the OpenThread stack.

This feature allows the OpenThread stack to be compiled as a library without specifying the address pool sizes. It delegates the configuration of the pools to the next layer, allowing the OpenThread stack to be integrated into various projects without requiring a new OpenThread stack configuration to be built.

###### otIp6SetEnabled (heading level 7)

`otError otIp6SetEnabled(otInstance *aInstance, bool aEnabled)`

**Description:** Brings the IPv6 interface up or down.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnabled|TRUE to enable IPv6, FALSE otherwise.|

Call this to enable or disable IPv6 communication.

When `OPENTHREAD_CONFIG_IP6_INIT_EXT_ADDR_POOL_ENABLE` is enabled, `otIp6Init()` MUST be called prior to calling this function. If it is not, this function will return `OT_ERROR_INVALID_STATE`.

###### otIp6IsEnabled (heading level 7)

`bool otIp6IsEnabled(otInstance *aInstance)`

**Description:** Indicates whether or not the IPv6 interface is up.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otIp6AddUnicastAddress (heading level 7)

`otError otIp6AddUnicastAddress(otInstance *aInstance, const otNetifAddress *aAddress)`

**Description:** Adds a Network Interface Address to the Thread interface.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otNetifAddress](ot-netif-address) *|[in]|aAddress|A pointer to a Network Interface Address.|

The passed-in instance `aAddress` is copied by the Thread interface. The Thread interface only supports a fixed number of externally added unicast addresses. See `OPENTHREAD_CONFIG_IP6_MAX_EXT_UCAST_ADDRS` and `OPENTHREAD_CONFIG_IP6_INIT_EXT_ADDR_POOL_ENABLE`.

###### otIp6RemoveUnicastAddress (heading level 7)

`otError otIp6RemoveUnicastAddress(otInstance *aInstance, const otIp6Address *aAddress)`

**Description:** Removes a Network Interface Address from the Thread interface.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Address](ot-ip6-address) *|[in]|aAddress|A pointer to an IP Address.|

###### otIp6GetUnicastAddresses (heading level 7)

`const otNetifAddress * otIp6GetUnicastAddresses(otInstance *aInstance)`

**Description:** Gets the list of IPv6 addresses assigned to the Thread interface.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- A pointer to the first Network Interface Address.

###### otIp6HasUnicastAddress (heading level 7)

`bool otIp6HasUnicastAddress(otInstance *aInstance, const otIp6Address *aAddress)`

**Description:** Indicates whether or not a unicast IPv6 address is assigned to the Thread interface.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Address](ot-ip6-address) *|[in]|aAddress|A pointer to the unicast address.|

###### otIp6SubscribeMulticastAddress (heading level 7)

`otError otIp6SubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aAddress)`

**Description:** Subscribes the Thread interface to a Network Interface Multicast Address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Address](ot-ip6-address) *|[in]|aAddress|A pointer to an IP Address.|

The passed in instance `aAddress` will be copied by the Thread interface. The Thread interface only supports a fixed number of externally added multicast addresses. See `OPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS` and `OPENTHREAD_CONFIG_IP6_INIT_EXT_ADDR_POOL_ENABLE`.

###### otIp6UnsubscribeMulticastAddress (heading level 7)

`otError otIp6UnsubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aAddress)`

**Description:** Unsubscribes the Thread interface to a Network Interface Multicast Address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Address](ot-ip6-address) *|[in]|aAddress|A pointer to an IP Address.|

###### otIp6GetMulticastAddresses (heading level 7)

`const otNetifMulticastAddress * otIp6GetMulticastAddresses(otInstance *aInstance)`

**Description:** Gets the list of IPv6 multicast addresses subscribed to the Thread interface.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- A pointer to the first Network Interface Multicast Address.

###### otIp6NewMessage (heading level 7)

`otMessage * otIp6NewMessage(otInstance *aInstance, const otMessageSettings *aSettings)`

**Description:** Allocate a new message buffer for sending an IPv6 message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otMessageSettings](ot-message-settings) *|[in]|aSettings|A pointer to the message settings or NULL to set default settings.|

**Note**

- If `aSettings` is 'NULL', the link layer security is enabled and the message priority is set to OT_MESSAGE_PRIORITY_NORMAL by default.

**Returns**

- A pointer to the message buffer or NULL if no message buffers are available or parameters are invalid.

**See Also**

- [otMessageFree](api-message#ot-message-free)

###### otIp6NewMessageFromBuffer (heading level 7)

`otMessage * otIp6NewMessageFromBuffer(otInstance *aInstance, const uint8_t *aData, uint16_t aDataLength, const otMessageSettings *aSettings)`

**Description:** Allocate a new message buffer and write the IPv6 datagram to the message buffer for sending an IPv6 message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const uint8_t *|[in]|aData|A pointer to the IPv6 datagram buffer.|
|uint16_t|[in]|aDataLength|The size of the IPv6 datagram buffer pointed by `aData`.|
|const [otMessageSettings](ot-message-settings) *|[in]|aSettings|A pointer to the message settings or NULL to set default settings.|

**Note**

- If `aSettings` is NULL, the link layer security is enabled and the message priority is obtained from IPv6 message itself. If `aSettings` is not NULL, the `aSetting->mPriority` is ignored and obtained from IPv6 message itself.

**Returns**

- A pointer to the message or NULL if malformed IPv6 header or insufficient message buffers are available.

**See Also**

- [otMessageFree](api-message#ot-message-free)

###### otIp6SetReceiveCallback (heading level 7)

`void otIp6SetReceiveCallback(otInstance *aInstance, otIp6ReceiveCallback aCallback, void *aCallbackContext)`

**Description:** Registers a callback to provide received IPv6 datagrams.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otIp6ReceiveCallback](api-ip6#ot-ip6-receive-callback)|[in]|aCallback|A pointer to a function that is called when an IPv6 datagram is received or NULL to disable the callback.|
|void *|[in]|aCallbackContext|A pointer to application-specific context.|

By default, this callback does not pass Thread control traffic. See [otIp6SetReceiveFilterEnabled()](api-ip6#ot-ip6-set-receive-filter-enabled) to change the Thread control traffic filter setting.

**See Also**

- [otIp6IsReceiveFilterEnabled](api-ip6#ot-ip6-is-receive-filter-enabled)
- [otIp6SetReceiveFilterEnabled](api-ip6#ot-ip6-set-receive-filter-enabled)

###### otIp6SetAddressCallback (heading level 7)

`void otIp6SetAddressCallback(otInstance *aInstance, otIp6AddressCallback aCallback, void *aCallbackContext)`

**Description:** Registers a callback to notify internal IPv6 address changes.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otIp6AddressCallback](api-ip6#ot-ip6-address-callback)|[in]|aCallback|A pointer to a function that is called when an internal IPv6 address is added or removed. NULL to disable the callback.|
|void *|[in]|aCallbackContext|A pointer to application-specific context.|

###### otIp6IsReceiveFilterEnabled (heading level 7)

`bool otIp6IsReceiveFilterEnabled(otInstance *aInstance)`

**Description:** Indicates whether or not Thread control traffic is filtered out when delivering IPv6 datagrams via the callback specified in [otIp6SetReceiveCallback()](api-ip6#ot-ip6-set-receive-callback).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- TRUE if Thread control traffic is filtered out, FALSE otherwise.

**See Also**

- [otIp6SetReceiveCallback](api-ip6#ot-ip6-set-receive-callback)
- [otIp6SetReceiveFilterEnabled](api-ip6#ot-ip6-set-receive-filter-enabled)

###### otIp6SetReceiveFilterEnabled (heading level 7)

`void otIp6SetReceiveFilterEnabled(otInstance *aInstance, bool aEnabled)`

**Description:** Sets whether or not Thread control traffic is filtered out when delivering IPv6 datagrams via the callback specified in [otIp6SetReceiveCallback()](api-ip6#ot-ip6-set-receive-callback).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnabled|TRUE if Thread control traffic is filtered out, FALSE otherwise.|

**See Also**

- [otIp6SetReceiveCallback](api-ip6#ot-ip6-set-receive-callback)
- otIsReceiveIp6FilterEnabled

###### otIp6Send (heading level 7)

`otError otIp6Send(otInstance *aInstance, otMessage *aMessage)`

**Description:** Sends an IPv6 datagram via the Thread interface.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the message buffer containing the IPv6 datagram.|

The caller transfers ownership of `aMessage` when making this call. OpenThread will free `aMessage` when processing is complete, including when a value other than `OT_ERROR_NONE` is returned.

###### otIp6AddUnsecurePort (heading level 7)

`otError otIp6AddUnsecurePort(otInstance *aInstance, uint16_t aPort)`

**Description:** Adds a port to the allowed unsecured port list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aPort|The port value.|

###### otIp6RemoveUnsecurePort (heading level 7)

`otError otIp6RemoveUnsecurePort(otInstance *aInstance, uint16_t aPort)`

**Description:** Removes a port from the allowed unsecure port list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aPort|The port value.|

**Note**

- This function removes `aPort` by overwriting `aPort` with the element after `aPort` in the internal port list. Be careful when calling [otIp6GetUnsecurePorts()](api-ip6#ot-ip6-get-unsecure-ports) followed by [otIp6RemoveUnsecurePort()](api-ip6#ot-ip6-remove-unsecure-port) to remove unsecure ports.

###### otIp6RemoveAllUnsecurePorts (heading level 7)

`void otIp6RemoveAllUnsecurePorts(otInstance *aInstance)`

**Description:** Removes all ports from the allowed unsecure port list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otIp6GetUnsecurePorts (heading level 7)

`const uint16_t * otIp6GetUnsecurePorts(otInstance *aInstance, uint8_t *aNumEntries)`

**Description:** Returns a pointer to the unsecure port list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t *|[out]|aNumEntries|The number of entries in the list.|

**Note**

- Port value 0 is used to indicate an invalid entry.

**Returns**

- A pointer to the unsecure port list.

###### otIp6IsAddressEqual (heading level 7)

`bool otIp6IsAddressEqual(const otIp6Address *aFirst, const otIp6Address *aSecond)`

**Description:** Test if two IPv6 addresses are the same.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otIp6Address](ot-ip6-address) *|[in]|aFirst|A pointer to the first IPv6 address to compare.|
|const [otIp6Address](ot-ip6-address) *|[in]|aSecond|A pointer to the second IPv6 address to compare.|

###### otIp6IsLinkLocalUnicast (heading level 7)

`bool otIp6IsLinkLocalUnicast(const otIp6Address *aAddress)`

**Description:** Test whether or not the IPv6 address is a link-local unicast address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otIp6Address](ot-ip6-address) *|[in]|aAddress|A pointer to the IPv6 address to test.|

###### otIp6FormLinkLocalAddressFromExtAddress (heading level 7)

`void otIp6FormLinkLocalAddressFromExtAddress(const otExtAddress *aExtAddress, otIp6Address *aAddress)`

**Description:** Forms a link-local unicast IPv6 address from the Interface Identifier generated from the given MAC Extended Address with the universal/local bit inverted.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otExtAddress](ot-ext-address) *|[in]|aExtAddress|A pointer to the MAC Extended Address (used to generate the IID).|
|[otIp6Address](ot-ip6-address) *|[out]|aAddress|A pointer to output the IPv6 link-local unicast address.|

###### otIp6ExtractExtAddressFromIp6AddressIid (heading level 7)

`void otIp6ExtractExtAddressFromIp6AddressIid(const otIp6Address *aAddress, otExtAddress *aExtAddress)`

**Description:** Extracts the MAC Extended Address from the Interface Identifier of the given IPv6 address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otIp6Address](ot-ip6-address) *|[in]|aAddress|A pointer to the IPv6 address.|
|[otExtAddress](ot-ext-address) *|[out]|aExtAddress|A pointer to output the MAC Extended Address (generated from the IID).|

###### otIp6ArePrefixesEqual (heading level 7)

`bool otIp6ArePrefixesEqual(const otIp6Prefix *aFirst, const otIp6Prefix *aSecond)`

**Description:** Test if two IPv6 prefixes are the same.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otIp6Prefix](ot-ip6-prefix) *|[in]|aFirst|A pointer to the first IPv6 prefix to compare.|
|const [otIp6Prefix](ot-ip6-prefix) *|[in]|aSecond|A pointer to the second IPv6 prefix to compare.|

###### otIp6AddressFromString (heading level 7)

`otError otIp6AddressFromString(const char *aString, otIp6Address *aAddress)`

**Description:** Converts a human-readable IPv6 address string into a binary representation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aString|A pointer to a NULL-terminated string.|
|[otIp6Address](ot-ip6-address) *|[out]|aAddress|A pointer to an IPv6 address.|

###### otIp6PrefixFromString (heading level 7)

`otError otIp6PrefixFromString(const char *aString, otIp6Prefix *aPrefix)`

**Description:** Converts a human-readable IPv6 prefix string into a binary representation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aString|A pointer to a NULL-terminated string.|
|[otIp6Prefix](ot-ip6-prefix) *|[out]|aPrefix|A pointer to an IPv6 prefix.|

The `aString` parameter should be a string in the format "<address>/<plen>", where `<address>` is an IPv6 address and `<plen>` is a prefix length.

###### otIp6AddressToString (heading level 7)

`void otIp6AddressToString(const otIp6Address *aAddress, char *aBuffer, uint16_t aSize)`

**Description:** Converts a given IPv6 address to a human-readable string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otIp6Address](ot-ip6-address) *|[in]|aAddress|A pointer to an IPv6 address (MUST NOT be NULL).|
|char *|[out]|aBuffer|A pointer to a char array to output the string (MUST NOT be NULL).|
|uint16_t|[in]|aSize|The size of `aBuffer` (in bytes). Recommended to use `OT_IP6_ADDRESS_STRING_SIZE`.|

The IPv6 address string is formatted as 16 hex values separated by ':' (i.e., "%x:%x:%x:...:%x").

If the resulting string does not fit in `aBuffer` (within its `aSize` characters), the string will be truncated but the outputted string is always null-terminated.

###### otIp6SockAddrToString (heading level 7)

`void otIp6SockAddrToString(const otSockAddr *aSockAddr, char *aBuffer, uint16_t aSize)`

**Description:** Converts a given IPv6 socket address to a human-readable string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSockAddr](ot-sock-addr) *|[in]|aSockAddr|A pointer to an IPv6 socket address (MUST NOT be NULL).|
|char *|[out]|aBuffer|A pointer to a char array to output the string (MUST NOT be NULL).|
|uint16_t|[in]|aSize|The size of `aBuffer` (in bytes). Recommended to use `OT_IP6_SOCK_ADDR_STRING_SIZE`.|

The IPv6 socket address string is formatted as [`address`]:`port` where `address` is shown as 16 hex values separated by `:` and `port` is the port number in decimal format, for example "[%x:%x:...:%x]:%u".

If the resulting string does not fit in `aBuffer` (within its `aSize` characters), the string will be truncated but the outputted string is always null-terminated.

###### otIp6PrefixToString (heading level 7)

`void otIp6PrefixToString(const otIp6Prefix *aPrefix, char *aBuffer, uint16_t aSize)`

**Description:** Converts a given IPv6 prefix to a human-readable string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otIp6Prefix](ot-ip6-prefix) *|[in]|aPrefix|A pointer to an IPv6 prefix (MUST NOT be NULL).|
|char *|[out]|aBuffer|A pointer to a char array to output the string (MUST NOT be NULL).|
|uint16_t|[in]|aSize|The size of `aBuffer` (in bytes). Recommended to use `OT_IP6_PREFIX_STRING_SIZE`.|

The IPv6 address string is formatted as "%x:%x:%x:...[::]/plen".

If the resulting string does not fit in `aBuffer` (within its `aSize` characters), the string will be truncated but the outputted string is always null-terminated.

###### otIp6PrefixMatch (heading level 7)

`uint8_t otIp6PrefixMatch(const otIp6Address *aFirst, const otIp6Address *aSecond)`

**Description:** Returns the prefix match length (bits) for two IPv6 addresses.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otIp6Address](ot-ip6-address) *|[in]|aFirst|A pointer to the first IPv6 address.|
|const [otIp6Address](ot-ip6-address) *|[in]|aSecond|A pointer to the second IPv6 address.|

**Returns**

- The prefix match length in bits.

###### otIp6GetPrefix (heading level 7)

`void otIp6GetPrefix(const otIp6Address *aAddress, uint8_t aLength, otIp6Prefix *aPrefix)`

**Description:** Gets a prefix with `aLength` from `aAddress`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otIp6Address](ot-ip6-address) *|[in]|aAddress|A pointer to an IPv6 address.|
|uint8_t|[in]|aLength|The length of prefix in bits.|
|[otIp6Prefix](ot-ip6-prefix) *|[out]|aPrefix|A pointer to output the IPv6 prefix.|

###### otIp6IsAddressUnspecified (heading level 7)

`bool otIp6IsAddressUnspecified(const otIp6Address *aAddress)`

**Description:** Indicates whether or not a given IPv6 address is the Unspecified Address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otIp6Address](ot-ip6-address) *|[in]|aAddress|A pointer to an IPv6 address.|

###### otIp6SelectSourceAddress (heading level 7)

`otError otIp6SelectSourceAddress(otInstance *aInstance, otMessageInfo *aMessageInfo)`

**Description:** Perform OpenThread source address selection.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMessageInfo](ot-message-info) *|[inout]|aMessageInfo|A pointer to the message information.|

###### otIp6IsSlaacEnabled (heading level 7)

`bool otIp6IsSlaacEnabled(otInstance *aInstance)`

**Description:** Indicates whether the SLAAC module is enabled or not.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

`OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE` build-time feature must be enabled.

###### otIp6SetSlaacEnabled (heading level 7)

`void otIp6SetSlaacEnabled(otInstance *aInstance, bool aEnabled)`

**Description:** Enables/disables the SLAAC module.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnabled|TRUE to enable, FALSE to disable.|

`OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE` build-time feature must be enabled.

When SLAAC module is enabled, SLAAC addresses (based on on-mesh prefixes in Network Data) are added to the interface. When SLAAC module is disabled any previously added SLAAC address is removed.

###### otIp6SetSlaacPrefixFilter (heading level 7)

`void otIp6SetSlaacPrefixFilter(otInstance *aInstance, otIp6SlaacPrefixFilter aFilter)`

**Description:** Sets the SLAAC module filter handler.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otIp6SlaacPrefixFilter](api-ip6#ot-ip6-slaac-prefix-filter)|[in]|aFilter|A pointer to SLAAC prefix filter handler, or NULL to disable filtering.|

`OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE` build-time feature must be enabled.

The filter handler is called by SLAAC module when it is about to add a SLAAC address based on a prefix to decide whether the address should be added or not.

A NULL filter handler disables filtering and allows all SLAAC addresses to be added.

If this function is not called, the default filter used by SLAAC module will be NULL (filtering is disabled).

###### otIp6RegisterMulticastListeners (heading level 7)

`otError otIp6RegisterMulticastListeners(otInstance *aInstance, const otIp6Address *aAddresses, uint8_t aAddressNum, const uint32_t *aTimeout, otIp6RegisterMulticastListenersCallback aCallback, void *aContext)`

**Description:** Registers Multicast Listeners to Primary Backbone Router.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Address](ot-ip6-address) *|[in]|aAddresses|A Multicast Address Array to register.|
|uint8_t|[in]|aAddressNum|The number of Multicast Address to register (0 if `aAddresses` is NULL).|
|const uint32_t *|[in]|aTimeout|A pointer to the timeout value (in seconds) to be included in MLR.req. A timeout value of 0 removes the corresponding Multicast Listener. If NULL, MLR.req would have no Timeout Tlv by default.|
|[otIp6RegisterMulticastListenersCallback](api-ip6#ot-ip6-register-multicast-listeners-callback)|[in]|aCallback|A pointer to the callback function.|
|void *|[in]|aContext|A pointer to the user context.|

`OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE` and `OPENTHREAD_CONFIG_COMMISSIONER_ENABLE` must be enabled.

**See Also**

- [otIp6RegisterMulticastListenersCallback](api-ip6#ot-ip6-register-multicast-listeners-callback)

###### otIp6SetMeshLocalIid (heading level 7)

`otError otIp6SetMeshLocalIid(otInstance *aInstance, const otIp6InterfaceIdentifier *aIid)`

**Description:** Sets the Mesh Local IID (for test purpose).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6InterfaceIdentifier](ot-ip6-interface-identifier) *|[in]|aIid|A pointer to the Mesh Local IID to set.|

Requires `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE`.

###### otIp6ProtoToString (heading level 7)

`const char * otIp6ProtoToString(uint8_t aIpProto)`

**Description:** Converts a given IP protocol number to a human-readable string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t|[in]|aIpProto|An IP protocol number (`OT_IP6_PROTO_*` enumeration).|

**Returns**

- A string representing `aIpProto`.

###### otIp6GetBorderRoutingCounters (heading level 7)

`const otBorderRoutingCounters * otIp6GetBorderRoutingCounters(otInstance *aInstance)`

**Description:** Gets the Border Routing counters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

`OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE` build-time feature must be enabled.

**Returns**

- A pointer to the Border Routing counters.

###### otIp6ResetBorderRoutingCounters (heading level 7)

`void otIp6ResetBorderRoutingCounters(otInstance *aInstance)`

**Description:** Resets the Border Routing counters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### Macros

`#define OT_IP6_PREFIX_SIZE 8`

**Description**: Size of an IPv6 prefix (bytes)

`#define OT_IP6_PREFIX_BITSIZE (OT_IP6_PREFIX_SIZE * 8)`

**Description**: Size of an IPv6 prefix (bits)

`#define OT_IP6_IID_SIZE 8`

**Description**: Size of an IPv6 Interface Identifier (bytes)

`#define OT_IP6_ADDRESS_SIZE 16`

**Description**: Size of an IPv6 address (bytes)

`#define OT_IP6_ADDRESS_BITSIZE (OT_IP6_ADDRESS_SIZE * 8)`

**Description**: Size of an IPv6 address (bits)

`#define OT_IP6_HEADER_SIZE 40`

**Description**: Size of an IPv6 header (bytes)

`#define OT_IP6_HEADER_PROTO_OFFSET 6`

**Description**: Offset of the proto field in the IPv6 header (bytes)

`#define OT_IP6_ADDRESS_STRING_SIZE 40`

**Description**: Recommended size for string representation of an IPv6 address.

`#define OT_IP6_SOCK_ADDR_STRING_SIZE 48`

**Description**: Recommended size for string representation of an IPv6 socket address.

`#define OT_IP6_PREFIX_STRING_SIZE 45`

**Description**: Recommended size for string representation of an IPv6 prefix.

`#define OT_IP6_MAX_MLR_ADDRESSES 15`

**Description**: Max number of IPv6 addresses supported by Multicast Listener Registration.

Represents the Network Prefix of an IPv6 address (most significant 64 bits of the address). 

###### Public Attributes (heading level 7)

###### m8 (heading level 8)

```
uint8_t otIp6NetworkPrefix::m8[OT_IP6_PREFIX_SIZE]
```

**Description:** The Network Prefix.

Represents the components of an IPv6 address. 

###### Public Attributes (heading level 7)

###### mNetworkPrefix (heading level 8)

```
otIp6NetworkPrefix otIp6AddressComponents::mNetworkPrefix
```

**Description:** The Network Prefix (most significant 64 bits of the address)

###### mIid (heading level 8)

```
otIp6InterfaceIdentifier otIp6AddressComponents::mIid
```

**Description:** The Interface Identifier (least significant 64 bits of the address)

Represents an IPv6 prefix. 

###### Public Attributes (heading level 7)

###### mPrefix (heading level 8)

```
otIp6Address otIp6Prefix::mPrefix
```

**Description:** The IPv6 prefix.

###### mLength (heading level 8)

```
uint8_t otIp6Prefix::mLength
```

**Description:** The IPv6 prefix length (in bits).

Represents an IPv6 network interface unicast address. 

###### Public Attributes (heading level 7)

###### mAddress (heading level 8)

```
otIp6Address otNetifAddress::mAddress
```

**Description:** The IPv6 unicast address.

###### mPrefixLength (heading level 8)

```
uint8_t otNetifAddress::mPrefixLength
```

**Description:** The Prefix length (in bits).

###### mAddressOrigin (heading level 8)

```
uint8_t otNetifAddress::mAddressOrigin
```

**Description:** The IPv6 address origin (OT_ADDRESS_ORIGIN_* values).

###### mPreferred (heading level 8)

```
bool otNetifAddress::mPreferred
```

**Description:** TRUE if the address is preferred, FALSE otherwise.

###### mValid (heading level 8)

```
bool otNetifAddress::mValid
```

**Description:** TRUE if the address is valid, FALSE otherwise.

###### mScopeOverrideValid (heading level 8)

```
bool otNetifAddress::mScopeOverrideValid
```

**Description:** TRUE if the mScopeOverride value is valid, FALSE otherwise.

###### mScopeOverride (heading level 8)

```
unsigned int otNetifAddress::mScopeOverride
```

**Description:** The IPv6 scope of this address.

###### mRloc (heading level 8)

```
bool otNetifAddress::mRloc
```

**Description:** TRUE if the address is an RLOC, FALSE otherwise.

###### mMeshLocal (heading level 8)

```
bool otNetifAddress::mMeshLocal
```

**Description:** TRUE if the address is mesh-local, FALSE otherwise.

###### mSrpRegistered (heading level 8)

```
bool otNetifAddress::mSrpRegistered
```

**Description:** Used by OT core only (indicates whether registered by SRP Client).

###### mNext (heading level 8)

```
const struct otNetifAddress* otNetifAddress::mNext
```

**Description:** A pointer to the next network interface address.

Represents an IPv6 network interface multicast address. 

The `mAddressOrigin` field is set to either `OT_ADDRESS_ORIGIN_THREAD` if the multicast address is subscribed by OpenThread core or `OT_ADDRESS_ORIGIN_MANUAL` if it is subscribed manually using `otIp6SubscribeMulticastAddress()`.

The multicast addresses subscribed by OpenThread core include addresses such as

- link-local all nodes (`ff02::01`),
- realm-local all nodes (`ff03::01`),
- link-local all routers (`ff02::02`),
- realm-local all routers (`ff03::02`),
- realm-local all MPL forwarders (`ff03::fc`),
- link-local all Thread nodes,
- realm-local all Thread nodes.

###### Public Attributes (heading level 7)

###### mAddress (heading level 8)

```
otIp6Address otNetifMulticastAddress::mAddress
```

**Description:** The IPv6 multicast address.

###### mNext (heading level 8)

```
const struct otNetifMulticastAddress* otNetifMulticastAddress::mNext
```

**Description:** A pointer to the next multicast address.

###### mAddressOrigin (heading level 8)

```
uint8_t otNetifMulticastAddress::mAddressOrigin
```

**Description:** The multicast address origin.

###### mData (heading level 8)

```
uint8_t otNetifMulticastAddress::mData
```

**Description:** Opaque data used by OpenThread core.

Represents an IPv6 socket address. 

###### Public Attributes (heading level 7)

###### mAddress (heading level 8)

```
otIp6Address otSockAddr::mAddress
```

**Description:** An IPv6 address.

###### mPort (heading level 8)

```
uint16_t otSockAddr::mPort
```

**Description:** A transport-layer port.

Represents the local and peer IPv6 socket addresses. 

###### Public Attributes (heading level 7)

###### mSockAddr (heading level 8)

```
otIp6Address otMessageInfo::mSockAddr
```

**Description:** The local IPv6 address.

###### mPeerAddr (heading level 8)

```
otIp6Address otMessageInfo::mPeerAddr
```

**Description:** The peer IPv6 address.

###### mSockPort (heading level 8)

```
uint16_t otMessageInfo::mSockPort
```

**Description:** The local transport-layer port.

###### mPeerPort (heading level 8)

```
uint16_t otMessageInfo::mPeerPort
```

**Description:** The peer transport-layer port.

###### mHopLimit (heading level 8)

```
uint8_t otMessageInfo::mHopLimit
```

**Description:** The IPv6 Hop Limit value.

**Details:** Only applies if `mAllowZeroHopLimit` is FALSE. If `0`, IPv6 Hop Limit is default value `OPENTHREAD_CONFIG_IP6_HOP_LIMIT_DEFAULT`. Otherwise, specifies the IPv6 Hop Limit.

###### mEcn (heading level 8)

```
uint8_t otMessageInfo::mEcn
```

**Description:** The ECN status of the packet, represented as in the IPv6 header.

###### mIsHostInterface (heading level 8)

```
bool otMessageInfo::mIsHostInterface
```

**Description:** TRUE if packets sent/received via host interface, FALSE otherwise.

###### mAllowZeroHopLimit (heading level 8)

```
bool otMessageInfo::mAllowZeroHopLimit
```

**Description:** TRUE to allow IPv6 Hop Limit 0 in `mHopLimit`, FALSE otherwise.

###### mMulticastLoop (heading level 8)

```
bool otMessageInfo::mMulticastLoop
```

**Description:** TRUE to allow looping back multicast, FALSE otherwise.

Represents IPv6 address information. 

###### Public Attributes (heading level 7)

###### mAddress (heading level 8)

```
const otIp6Address* otIp6AddressInfo::mAddress
```

**Description:** A pointer to the IPv6 address.

###### mPrefixLength (heading level 8)

```
uint8_t otIp6AddressInfo::mPrefixLength
```

**Description:** The prefix length of mAddress if it is a unicast address.

###### mScope (heading level 8)

```
uint8_t otIp6AddressInfo::mScope
```

**Description:** The scope of this address.

###### mPreferred (heading level 8)

```
bool otIp6AddressInfo::mPreferred
```

**Description:** Whether this is a preferred address.

###### mMeshLocal (heading level 8)

```
bool otIp6AddressInfo::mMeshLocal
```

**Description:** Whether this is a mesh-local unicast/anycast address.

Represents the counters for packets and bytes. 

###### Public Attributes (heading level 7)

###### mPackets (heading level 8)

```
uint64_t otPacketsAndBytes::mPackets
```

**Description:** The number of packets.

###### mBytes (heading level 8)

```
uint64_t otPacketsAndBytes::mBytes
```

**Description:** The number of bytes.

Represents the counters of packets forwarded via Border Routing. 

###### Public Attributes (heading level 7)

###### mInboundUnicast (heading level 8)

```
otPacketsAndBytes otBorderRoutingCounters::mInboundUnicast
```

**Description:** The counters for inbound unicast.

###### mInboundMulticast (heading level 8)

```
otPacketsAndBytes otBorderRoutingCounters::mInboundMulticast
```

**Description:** The counters for inbound multicast.

###### mOutboundUnicast (heading level 8)

```
otPacketsAndBytes otBorderRoutingCounters::mOutboundUnicast
```

**Description:** The counters for outbound unicast.

###### mOutboundMulticast (heading level 8)

```
otPacketsAndBytes otBorderRoutingCounters::mOutboundMulticast
```

**Description:** The counters for outbound multicast.

###### mInboundInternet (heading level 8)

```
otPacketsAndBytes otBorderRoutingCounters::mInboundInternet
```

**Description:** The counters for inbound Internet when DHCPv6 PD enabled.

###### mOutboundInternet (heading level 8)

```
otPacketsAndBytes otBorderRoutingCounters::mOutboundInternet
```

**Description:** The counters for outbound Internet when DHCPv6 PD enabled.

###### mRaRx (heading level 8)

```
uint32_t otBorderRoutingCounters::mRaRx
```

**Description:** The number of received RA packets.

###### mRaTxSuccess (heading level 8)

```
uint32_t otBorderRoutingCounters::mRaTxSuccess
```

**Description:** The number of RA packets successfully transmitted.

###### mRaTxFailure (heading level 8)

```
uint32_t otBorderRoutingCounters::mRaTxFailure
```

**Description:** The number of RA packets failed to transmit.

###### mRsRx (heading level 8)

```
uint32_t otBorderRoutingCounters::mRsRx
```

**Description:** The number of received RS packets.

###### mRsTxSuccess (heading level 8)

```
uint32_t otBorderRoutingCounters::mRsTxSuccess
```

**Description:** The number of RS packets successfully transmitted.

###### mRsTxFailure (heading level 8)

```
uint32_t otBorderRoutingCounters::mRsTxFailure
```

**Description:** The number of RS packets failed to transmit.

Represents the Interface Identifier of an IPv6 address. 

###### Modules (heading level 7)

[otIp6InterfaceIdentifier::OT_TOOL_PACKED_FIELD](ot-ip6-interface-identifier-ot-tool-packed-field)

###### Public Attributes (heading level 7)

###### mFields (heading level 8)

```
union otIp6InterfaceIdentifier::OT_TOOL_PACKED_FIELD otIp6InterfaceIdentifier::mFields
```

**Description:** The Interface Identifier accessor fields.

###### Public Attributes (heading level 8)

###### m8 (heading level 9)

```
uint8_t otIp6InterfaceIdentifier::OT_TOOL_PACKED_FIELD::m8[OT_IP6_IID_SIZE]
```

**Description:** 8-bit fields

###### m16 (heading level 9)

```
uint16_t otIp6InterfaceIdentifier::OT_TOOL_PACKED_FIELD::m16[OT_IP6_IID_SIZE/sizeof(uint16_t)]
```

**Description:** 16-bit fields

###### m32 (heading level 9)

```
uint32_t otIp6InterfaceIdentifier::OT_TOOL_PACKED_FIELD::m32[OT_IP6_IID_SIZE/sizeof(uint32_t)]
```

**Description:** 32-bit fields

Represents an IPv6 address. 

###### Modules (heading level 7)

[otIp6Address::OT_TOOL_PACKED_FIELD](ot-ip6-address-ot-tool-packed-field)

###### Public Attributes (heading level 7)

###### mFields (heading level 8)

```
union otIp6Address::OT_TOOL_PACKED_FIELD otIp6Address::mFields
```

**Description:** IPv6 accessor fields.

###### Public Attributes (heading level 8)

###### m8 (heading level 9)

```
uint8_t otIp6Address::OT_TOOL_PACKED_FIELD::m8[OT_IP6_ADDRESS_SIZE]
```

**Description:** 8-bit fields

###### m16 (heading level 9)

```
uint16_t otIp6Address::OT_TOOL_PACKED_FIELD::m16[OT_IP6_ADDRESS_SIZE/sizeof(uint16_t)]
```

**Description:** 16-bit fields

###### m32 (heading level 9)

```
uint32_t otIp6Address::OT_TOOL_PACKED_FIELD::m32[OT_IP6_ADDRESS_SIZE/sizeof(uint32_t)]
```

**Description:** 32-bit fields

###### mComponents (heading level 9)

```
otIp6AddressComponents otIp6Address::OT_TOOL_PACKED_FIELD::mComponents
```

**Description:** IPv6 address components.

##### Multicast DNS

This module includes APIs for Multicast DNS (mDNS). 

The mDNS APIs are available when the mDNS support `OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE` is enabled and the `OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE` is also enabled. 

###### Modules

[otMdnsLocalHostAddress](ot-mdns-local-host-address)

[otMdnsCacheInfo](ot-mdns-cache-info)

###### Enumerations

###### otMdnsEntryState (heading level 7)

```
enum otMdnsEntryState {
    OT_MDNS_ENTRY_STATE_PROBING
    OT_MDNS_ENTRY_STATE_REGISTERED
    OT_MDNS_ENTRY_STATE_CONFLICT
    OT_MDNS_ENTRY_STATE_REMOVING
}
```

**Description:**

Represents a host/service/key entry state.

**Enumerator:**

|   |   |
|---|---|
|OT_MDNS_ENTRY_STATE_PROBING|Probing to claim the name.|
|OT_MDNS_ENTRY_STATE_REGISTERED|Entry is successfully registered.|
|OT_MDNS_ENTRY_STATE_CONFLICT|Name conflict was detected.|
|OT_MDNS_ENTRY_STATE_REMOVING|Entry is being removed (sending "goodbye" announcements).|

###### Typedefs

###### otMdnsRequestId (heading level 7)

`typedef otPlatDnssdRequestId otMdnsRequestId`

**Description:**

Represents a request ID (`uint32_t` value) for registering a host, a service, or a key service.

###### otMdnsRegisterCallback (heading level 7)

`typedef otPlatDnssdRegisterCallback otMdnsRegisterCallback`

**Description:**

Represents the callback function to report the outcome of a host, service, or key registration request.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aInstance|The OpenThread instance.|
||[in]|aRequestId|The request ID.|
||[in]|aError|Error indicating the outcome of request.|

**Details:**

The outcome of a registration request is reported back by invoking this callback with one of the following `aError` inputs:

- `OT_ERROR_NONE` indicates registration was successful.
- `OT_ERROR_DUPLICATED` indicates a name conflict while probing, i.e., name is claimed by another mDNS responder.

See `otMdnsRegisterHost()`, `otMdnsRegisterService()`, and `otMdnsRegisterKey()` for more details about when the callback will be invoked.

###### otMdnsConflictCallback (heading level 7)

`typedef void(* otMdnsConflictCallback) (otInstance *aInstance, const char *aName, const char *aServiceType)`

**Description:**

Represents the callback function to report a detected name conflict after successful registration of an entry.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aInstance|The OpenThread instance.|
||[in]|aName|The host name or the service instance label.|
||[in]|aServiceType|The service type (e.g., `_tst._udp`).|

**Details:**

If a conflict is detected while registering an entry, it is reported through the provided `otMdnsRegisterCallback`. The `otMdnsConflictCallback` is used only when a name conflict is detected after an entry has been successfully registered.

A non-NULL `aServiceType` indicates that conflict is for a service entry. In this case `aName` specifies the service instance label (treated as as a single DNS label and can potentially include dot `.` character).

A NULL `aServiceType` indicates that conflict is for a host entry. In this case `Name` specifies the host name. It does not include the domain name.

###### otMdnsHost (heading level 7)

`typedef otPlatDnssdHost otMdnsHost`

**Description:**

Represents an mDNS host.

**Details:**

This type is used to register or unregister a host (`otMdnsRegisterHost()` and `otMdnsUnregisterHost()`).

See the description of each function for more details on how different fields are used in each case.

###### otMdnsService (heading level 7)

`typedef otPlatDnssdService otMdnsService`

**Description:**

Represents an mDNS service.

**Details:**

This type is used to register or unregister a service (`otMdnsRegisterService()` and `otMdnsUnregisterService()`).

See the description of each function for more details on how different fields are used in each case.

###### otMdnsKey (heading level 7)

`typedef otPlatDnssdKey otMdnsKey`

**Description:**

Represents an mDNS key record.

**Details:**

See `otMdnsRegisterKey()`, `otMdnsUnregisterKey()` for more details about fields in each case.

###### otMdnsIterator (heading level 7)

`typedef struct otMdnsIterator otMdnsIterator`

**Description:**

Represents an mDNS entry iterator.

###### otMdnsEntryState (heading level 7)

`typedef enum otMdnsEntryState otMdnsEntryState`

**Description:**

Represents a host/service/key entry state.

###### otMdnsLocalHostAddress (heading level 7)

`typedef struct otMdnsLocalHostAddress otMdnsLocalHostAddress`

**Description:**

Represents a local host IPv4 or IPv6 address entry.

###### otMdnsBrowser (heading level 7)

`typedef otPlatDnssdBrowser otMdnsBrowser`

**Description:**

Represents a service browser.

**Details:**

Refer to `otPlatDnssdBrowser` for documentation of member fields and `otMdnsStartBrowser()` for how they are used.

###### otMdnsBrowseCallback (heading level 7)

`typedef otPlatDnssdBrowseCallback otMdnsBrowseCallback`

**Description:**

Represents the callback function pointer type used to report a browse result.

###### otMdnsBrowseResult (heading level 7)

`typedef otPlatDnssdBrowseResult otMdnsBrowseResult`

**Description:**

Represents a browse result.

###### otMdnsSrvResolver (heading level 7)

`typedef otPlatDnssdSrvResolver otMdnsSrvResolver`

**Description:**

Represents an SRV service resolver.

**Details:**

Refer to `otPlatDnssdSrvResolver` for documentation of member fields and `otMdnsStartSrvResolver()` for how they are used.

###### otMdnsSrvCallback (heading level 7)

`typedef otPlatDnssdSrvCallback otMdnsSrvCallback`

**Description:**

Represents the callback function pointer type used to report an SRV resolve result.

###### otMdnsSrvResult (heading level 7)

`typedef otPlatDnssdSrvResult otMdnsSrvResult`

**Description:**

Represents an SRV resolver result.

###### otMdnsTxtResolver (heading level 7)

`typedef otPlatDnssdTxtResolver otMdnsTxtResolver`

**Description:**

Represents a TXT service resolver.

**Details:**

Refer to `otPlatDnssdTxtResolver` for documentation of member fields and `otMdnsStartTxtResolver()` for how they are used.

###### otMdnsTxtCallback (heading level 7)

`typedef otPlatDnssdTxtCallback otMdnsTxtCallback`

**Description:**

Represents the callback function pointer type used to report a TXT resolve result.

###### otMdnsTxtResult (heading level 7)

`typedef otPlatDnssdTxtResult otMdnsTxtResult`

**Description:**

Represents a TXT resolver result.

###### otMdnsAddressResolver (heading level 7)

`typedef otPlatDnssdAddressResolver otMdnsAddressResolver`

**Description:**

Represents an address resolver.

**Details:**

Refer to `otPlatDnssdAddressResolver` for documentation of member fields and `otMdnsStartIp6AddressResolver()` or `otMdnsStartIp4AddressResolver()` for how they are used.

###### otMdnsAddressCallback (heading level 7)

`typedef otPlatDnssdAddressCallback otMdnsAddressCallback`

**Description:**

Represents the callback function pointer type use to report an IPv6/IPv4 address resolve result.

###### otMdnsAddressAndTtl (heading level 7)

`typedef otPlatDnssdAddressAndTtl otMdnsAddressAndTtl`

**Description:**

Represents a discovered host address and its TTL.

###### otMdnsAddressResult (heading level 7)

`typedef otPlatDnssdAddressResult otMdnsAddressResult`

**Description:**

Represents address resolver result.

###### otMdnsRecordResult (heading level 7)

`typedef otPlatDnssdRecordResult otMdnsRecordResult`

**Description:**

Represents a record query result.

###### otMdnsRecordCallback (heading level 7)

`typedef otPlatDnssdRecordCallback otMdnsRecordCallback`

**Description:**

Represents the callback function used to report a record querier result.

###### otMdnsRecordQuerier (heading level 7)

`typedef otPlatDnssdRecordQuerier otMdnsRecordQuerier`

**Description:**

Represents a record querier.

###### otMdnsCacheInfo (heading level 7)

`typedef struct otMdnsCacheInfo otMdnsCacheInfo`

**Description:**

Represents additional information about a browser/resolver and its cached results.

###### Functions

###### otMdnsSetEnabled (heading level 7)

`otError otMdnsSetEnabled(otInstance *aInstance, bool aEnable, uint32_t aInfraIfIndex)`

**Description:** Enables or disables the mDNS module.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|bool|[in]|aEnable|Boolean to indicate whether to enable (on `TRUE`) or disable (on `FALSE`).|
|uint32_t|[in]|aInfraIfIndex|The network interface index for mDNS operation. Value is ignored when disabling|

The mDNS module should be enabled before registration any host, service, or key entries. Disabling mDNS will immediately stop all operations and any communication (multicast or unicast tx) and remove any previously registered entries without sending any "goodbye" announcements or invoking their callback. Once disabled, all currently active browsers and resolvers are stopped.

###### otMdnsIsEnabled (heading level 7)

`bool otMdnsIsEnabled(otInstance *aInstance)`

**Description:** Indicates whether the mDNS module is enabled.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|

###### otMdnsSetAutoEnableMode (heading level 7)

`void otMdnsSetAutoEnableMode(otInstance *aInstance, bool aEnable)`

**Description:** Enables or disables the mDNS auto-enable mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|bool|[in]|aEnable|A boolean to enable or disable the auto-enable mode.|

Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE`.

When this mode is enabled, the mDNS module uses the same infrastructure network interface as the Border Routing manager. The mDNS module is then automatically enabled or disabled based on the operational state of that interface (see `otBorderRoutingInit()` and `otPlatInfraIfStateChanged()`).

It is recommended to use the auto-enable mode on Border Routers. The default state of this mode at initialization is controlled by the `OPENTHREAD_CONFIG_MULTICAST_DNS_AUTO_ENABLE_ON_INFRA_IF` configuration.

The auto-enable mode can be disabled by a call to `otMdnsSetAutoEnableMode(false)` or by an explicit call to `otMdnsSetEnabled()`. Deactivating the auto-enable mode with `otMdnsSetAutoEnableMode(false)` will not change the current operational state of the mDNS module (e.g., if it is currently enabled, it remains enabled).

###### otMdnsGetAutoEnableMode (heading level 7)

`bool otMdnsGetAutoEnableMode(otInstance *aInstance)`

**Description:** Indicates whether the auto-enable mode is enabled or disabled.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|

Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE`.

###### otMdnsSetQuestionUnicastAllowed (heading level 7)

`void otMdnsSetQuestionUnicastAllowed(otInstance *aInstance, bool aAllow)`

**Description:** Sets whether the mDNS module is allowed to send questions requesting unicast responses referred to as "QU" questions.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|bool|[in]|aAllow|Indicates whether or not to allow "QU" questions.|

The "QU" questions request unicast responses, in contrast to "QM" questions which request multicast responses.

When allowed, the first probe will be sent as a "QU" question. This API can be used to address platform limitation where platform socket cannot accept unicast response received on mDNS port (due to it being already bound).

###### otMdnsIsQuestionUnicastAllowed (heading level 7)

`bool otMdnsIsQuestionUnicastAllowed(otInstance *aInstance)`

**Description:** Indicates whether mDNS module is allowed to send "QU" questions requesting unicast response.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

###### otMdnsSetConflictCallback (heading level 7)

`void otMdnsSetConflictCallback(otInstance *aInstance, otMdnsConflictCallback aCallback)`

**Description:** Sets the post-registration conflict callback.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otMdnsConflictCallback](api-mdns#ot-mdns-conflict-callback)|[in]|aCallback|The conflict callback.|

If a conflict is detected while registering an entry, it is reported through the provided `otMdnsRegisterCallback`. The `otMdnsConflictCallback` is used only when a name conflict is detected after an entry has been successfully registered.

`aCallback` can be set to `NULL` if not needed. Subsequent calls will replace any previously set callback.

###### otMdnsGetLocalHostName (heading level 7)

`const char * otMdnsGetLocalHostName(otInstance *aInstance)`

**Description:** Gets the local host name.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|

**Returns**

- The local host name.

###### otMdnsSetLocalHostName (heading level 7)

`otError otMdnsSetLocalHostName(otInstance *aInstance, const char *aName)`

**Description:** Sets the local host name.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const char *|[in]|aName|The local host name to use, can be to `NULL` to allow the mDNS module to choose the name.|

The local host name can be set only when the mDNS module is disabled. If not set the mDNS module itself will auto-generate the local host name.

###### otMdnsRegisterHost (heading level 7)

`otError otMdnsRegisterHost(otInstance *aInstance, const otMdnsHost *aHost, otMdnsRequestId aRequestId, otMdnsRegisterCallback aCallback)`

**Description:** Registers or updates a host on mDNS.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otMdnsHost](api-mdns#ot-mdns-host) *|[in]|aHost|Information about the host to register.|
|[otMdnsRequestId](api-mdns#ot-mdns-request-id)|[in]|aRequestId|The ID associated with this request.|
|[otMdnsRegisterCallback](api-mdns#ot-mdns-register-callback)|[in]|aCallback|The callback function pointer to report the outcome (can be NULL if not needed).|

The fields in `aHost` follow these rules:

- The `mHostName` field specifies the host name to register (e.g., "myhost"). MUST NOT contain the domain name.
- The `mAddresses` is array of IPv6 addresses to register with the host. `mAddressesLength` provides the number of entries in `mAddresses` array.
- The `mAddresses` array can be empty with zero `mAddressesLength`. In this case, mDNS will treat it as if host is unregistered and stops advertising any addresses for this the host name.
- The `mTtl` specifies the TTL if non-zero. If zero, the mDNS core will choose the default TTL of 120 seconds.
- Other fields in `aHost` structure are ignored in an `otMdnsRegisterHost()` call.

This function can be called again for the same `mHostName` to update a previously registered host entry, for example, to change the list of addresses of the host. In this case, the mDNS module will send "goodbye" announcements for any previously registered and now removed addresses and announce any newly added addresses.

The outcome of the registration request is reported back by invoking the provided `aCallback` with `aRequestId` as its input and one of the following `aError` inputs:

- `OT_ERROR_NONE` indicates registration was successful.
- `OT_ERROR_DULICATED` indicates a name conflict while probing, i.e., name is claimed by another mDNS responder.

For caller convenience, the OpenThread mDNS module guarantees that the callback will be invoked after this function returns, even in cases of immediate registration success. The `aCallback` can be `NULL` if caller does not want to be notified of the outcome.

###### otMdnsUnregisterHost (heading level 7)

`otError otMdnsUnregisterHost(otInstance *aInstance, const otMdnsHost *aHost)`

**Description:** Unregisters a host on mDNS.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otMdnsHost](api-mdns#ot-mdns-host) *|[in]|aHost|Information about the host to unregister.|

The fields in `aHost` follow these rules:

- The `mHostName` field specifies the host name to unregister (e.g., "myhost"). MUST NOT contain the domain name.
- Other fields in `aHost` structure are ignored in an `otMdnsUnregisterHost()` call.

If there is no previously registered host with the same name, no action is performed.

If there is a previously registered host with the same name, the mDNS module will send "goodbye" announcement for all previously advertised address records.

###### otMdnsRegisterService (heading level 7)

`otError otMdnsRegisterService(otInstance *aInstance, const otMdnsService *aService, otMdnsRequestId aRequestId, otMdnsRegisterCallback aCallback)`

**Description:** Registers or updates a service on mDNS.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otMdnsService](api-mdns#ot-mdns-service) *|[in]|aService|Information about the service to register.|
|[otMdnsRequestId](api-mdns#ot-mdns-request-id)|[in]|aRequestId|The ID associated with this request.|
|[otMdnsRegisterCallback](api-mdns#ot-mdns-register-callback)|[in]|aCallback|The callback function pointer to report the outcome (can be NULL if not needed).|

The fields in `aService` follow these rules:

- The `mServiceInstance` specifies the service instance label. It is treated as a single DNS name label. It may contain dot `.` character which is allowed in a service instance label.
- The `mServiceType` specifies the service type (e.g., "_tst._udp"). It is treated as multiple dot `.` separated labels. It MUST NOT contain the domain name.
- The `mHostName` field specifies the host name of the service if it is not NULL. Otherwise, if it is NULL, it indicates that this service is for the local host (this device itself).
- The `mSubTypeLabels` is an array of strings representing sub-types associated with the service. Each array entry is a sub-type label. The `mSubTypeLabels can be NULL if there is no sub-type. Otherwise, the array length is specified by`mSubTypeLabelsLength`.`
- `The`mTxtData`and`mTxtDataLength`specify the encoded TXT data. The`mTxtData`can be NULL or`mTxtDataLength` can be zero to specify an empty TXT data. In this case mDNS module will use a single zero byte`[ 0 ]` as the TXT data.
- The `mPort`, `mWeight`, and `mPriority` specify the service's parameters as specified in DNS SRV record.
- The `mTtl` specifies the TTL if non-zero. If zero, the mDNS module will use the default TTL of 120 seconds.
- Other fields in `aService` structure are ignored in an `otMdnsRegisterService()` call.

This function can be called again for the same `mServiceInstance` and `mServiceType` to update a previously registered service entry, for example, to change the sub-types list, or update any parameter such as port, weight, priority, TTL, or host name. The mDNS module will send announcements for any changed info, e.g., will send "goodbye" announcements for any removed sub-types and announce any newly added sub-types.

Regarding the invocation of the `aCallback`, this function behaves in the same way as described in `otMdnsRegisterHost()`.

###### otMdnsUnregisterService (heading level 7)

`otError otMdnsUnregisterService(otInstance *aInstance, const otMdnsService *aService)`

**Description:** Unregisters a service on mDNS module.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otMdnsService](api-mdns#ot-mdns-service) *|[in]|aService|Information about the service to unregister.|

The fields in `aService` follow these rules:

- The `mServiceInstance` specifies the service instance label. It is treated as a single DNS name label. It may contain dot `.` character which is allowed in a service instance label.
- The `mServiceType` specifies the service type (e.g., "_tst._udp"). It is treated as multiple dot `.` separated labels. It MUST NOT contain the domain name.
- Other fields in `aService` structure are ignored in an `otMdnsUnregisterService()` call.

If there is no previously registered service with the same name, no action is performed.

If there is a previously registered service with the same name, the mDNS module will send "goodbye" announcements for all related records.

###### otMdnsRegisterKey (heading level 7)

`otError otMdnsRegisterKey(otInstance *aInstance, const otMdnsKey *aKey, otMdnsRequestId aRequestId, otMdnsRegisterCallback aCallback)`

**Description:** Registers or updates a key record on mDNS module.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otMdnsKey](api-mdns#ot-mdns-key) *|[in]|aKey|Information about the key record to register.|
|[otMdnsRequestId](api-mdns#ot-mdns-request-id)|[in]|aRequestId|The ID associated with this request.|
|[otMdnsRegisterCallback](api-mdns#ot-mdns-register-callback)|[in]|aCallback|The callback function pointer to report the outcome (can be NULL if not needed).|

The fields in `aKey` follow these rules:

- If the key is associated with a host entry, the `mName` field specifies the host name and the `mServiceType` MUST be NULL.
- If the key is associated with a service entry, the `mName` filed specifies the service instance label (always treated as a single label) and the `mServiceType` filed specifies the service type (e.g., "_tst._udp"). In this case the DNS name for key record is `<mName>.<mServiceTye>`.
- The `mKeyData` field contains the key record's data with `mKeyDataLength` as its length in byes.
- The `mTtl` specifies the TTL if non-zero. If zero, the mDNS module will use the default TTL of 120 seconds.
- Other fields in `aKey` structure are ignored in an `otMdnsRegisterKey()` call.

This function can be called again for the same name to updated a previously registered key entry, for example, to change the key data or TTL.

Regarding the invocation of the `aCallback`, this function behaves in the same way as described in `otMdnsRegisterHost()`.

###### otMdnsUnregisterKey (heading level 7)

`otError otMdnsUnregisterKey(otInstance *aInstance, const otMdnsKey *aKey)`

**Description:** Unregisters a key record on mDNS.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otMdnsKey](api-mdns#ot-mdns-key) *|[in]|aKey|Information about the key to unregister.|

The fields in `aKey` follow these rules:

- If the key is associated with a host entry, the `mName` field specifies the host name and the `mServiceType` MUST be NULL.
- If the key is associated with a service entry, the `mName` filed specifies the service instance label (always treated as a single label) and the `mServiceType` filed specifies the service type (e.g., "_tst._udp"). In this case the DNS name for key record is `<mName>.<mServiceTye>`.
- Other fields in `aKey` structure are ignored in an `otMdnsUnregisterKey()` call.

If there is no previously registered key with the same name, no action is performed.

If there is a previously registered key with the same name, the mDNS module will send "goodbye" announcements for the key record.

###### otMdnsAllocateIterator (heading level 7)

`otMdnsIterator * otMdnsAllocateIterator(otInstance *aInstance)`

**Description:** Allocates a new iterator.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|

Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`.

An allocated iterator must be freed by the caller using `otMdnsFreeIterator()`.

**Returns**

- A pointer to the allocated iterator, or `NULL` if it fails to allocate.

###### otMdnsFreeIterator (heading level 7)

`void otMdnsFreeIterator(otInstance *aInstance, otMdnsIterator *aIterator)`

**Description:** Frees a previously allocated iterator.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otMdnsIterator](api-mdns#ot-mdns-iterator) *|[in]|aIterator|The iterator to free.|

Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`.

###### otMdnsGetNextHost (heading level 7)

`otError otMdnsGetNextHost(otInstance *aInstance, otMdnsIterator *aIterator, otMdnsHost *aHost, otMdnsEntryState *aState)`

**Description:** Iterates over registered host entries.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otMdnsIterator](api-mdns#ot-mdns-iterator) *|[in]|aIterator|Pointer to the iterator.|
|[otMdnsHost](api-mdns#ot-mdns-host) *|[out]|aHost|Pointer to an `otMdnsHost` to return the information about the next host entry.|
|[otMdnsEntryState](api-mdns#ot-mdns-entry-state) *|[out]|aState|Pointer to an `otMdnsEntryState` to return the entry state.|

Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`.

On success, `aHost` is populated with information about the next host. Pointers within the `otMdnsHost` structure (like `mName`) remain valid until the next call to any OpenThread stack's public or platform API/callback.

###### otMdnsGetNextService (heading level 7)

`otError otMdnsGetNextService(otInstance *aInstance, otMdnsIterator *aIterator, otMdnsService *aService, otMdnsEntryState *aState)`

**Description:** Iterates over registered service entries.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otMdnsIterator](api-mdns#ot-mdns-iterator) *|[in]|aIterator|Pointer to the iterator to use.|
|[otMdnsService](api-mdns#ot-mdns-service) *|[out]|aService|Pointer to an `otMdnsService` to return the information about the next service entry.|
|[otMdnsEntryState](api-mdns#ot-mdns-entry-state) *|[out]|aState|Pointer to an `otMdnsEntryState` to return the entry state.|

Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`.

On success, `aService` is populated with information about the next service . Pointers within the `otMdnsService` structure (like `mServiceType`, `mSubTypeLabels`) remain valid until the next call to any OpenThread stack's public or platform API/callback.

###### otMdnsGetNextKey (heading level 7)

`otError otMdnsGetNextKey(otInstance *aInstance, otMdnsIterator *aIterator, otMdnsKey *aKey, otMdnsEntryState *aState)`

**Description:** Iterates over registered key entries.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otMdnsIterator](api-mdns#ot-mdns-iterator) *|[in]|aIterator|Pointer to the iterator to use.|
|[otMdnsKey](api-mdns#ot-mdns-key) *|[out]|aKey|Pointer to an `otMdnsKey` to return the information about the next key entry.|
|[otMdnsEntryState](api-mdns#ot-mdns-entry-state) *|[out]|aState|Pointer to an `otMdnsEntryState` to return the entry state.|

Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`.

On success, `aKey` is populated with information about the next key. Pointers within the `otMdnsKey` structure (like `mName`) remain valid until the next call to any OpenThread stack's public or platform API/callback.

###### otMdnsGetNextLocalHostAddress (heading level 7)

`otError otMdnsGetNextLocalHostAddress(otInstance *aInstance, otMdnsIterator *aIterator, otMdnsLocalHostAddress *aAddress)`

**Description:** Iterates over the local host IPv6 and IPv4 addresses tracked by OpenThread mDNS module.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otMdnsIterator](api-mdns#ot-mdns-iterator) *|[out]|aIterator|Pointer to the iterator to use.|
|[otMdnsLocalHostAddress](ot-mdns-local-host-address) *|[out]|aAddress|Pointer to an `otMdnsLocalHostAddress` to output the next address entry.|

Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`.

The platform layer is responsible for monitoring and reporting all host IPv4 and IPv6 addresses to the OpenThread mDNS module, which then tracks the full address list (see `otPlatMdnsHandleHostAddressEvent()`). This function allows iteration through this tracked list, primarily intended for information and debugging purposes.

###### otMdnsStartBrowser (heading level 7)

`otError otMdnsStartBrowser(otInstance *aInstance, const otMdnsBrowser *aBrowser)`

**Description:** Starts a service browser.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otMdnsBrowser](api-mdns#ot-mdns-browser) *|[in]|aBrowser|The browser to be started.|

Initiates a continuous search for the specified `mServiceType` in `aBrowser`. For sub-type services, use `mSubTypeLabel` to define the sub-type, for base services, set `mSubTypeLabel` to NULL.

Discovered services are reported through the `mCallback` function in `aBrowser`. Services that have been removed are reported with a TTL value of zero. The callback may be invoked immediately with cached information (if available) and potentially before this function returns. When cached results are used, the reported TTL value will reflect the original TTL from the last received response.

Multiple browsers can be started for the same service, provided they use different callback functions.

###### otMdnsStopBrowser (heading level 7)

`otError otMdnsStopBrowser(otInstance *aInstance, const otMdnsBrowser *aBroswer)`

**Description:** Stops a service browser.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otMdnsBrowser](api-mdns#ot-mdns-browser) *|[in]|aBroswer|The browser to stop.|

No action is performed if no matching browser with the same service and callback is currently active.

###### otMdnsStartSrvResolver (heading level 7)

`otError otMdnsStartSrvResolver(otInstance *aInstance, const otMdnsSrvResolver *aResolver)`

**Description:** Starts an SRV record resolver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otMdnsSrvResolver](api-mdns#ot-mdns-srv-resolver) *|[in]|aResolver|The resolver to be started.|

Initiates a continuous SRV record resolver for the specified service in `aResolver`.

Discovered information is reported through the `mCallback` function in `aResolver`. When the service is removed it is reported with a TTL value of zero. In this case, `mHostName` may be NULL and other result fields (such as `mPort`) should be ignored.

The callback may be invoked immediately with cached information (if available) and potentially before this function returns. When cached result is used, the reported TTL value will reflect the original TTL from the last received response.

Multiple resolvers can be started for the same service, provided they use different callback functions.

###### otMdnsStopSrvResolver (heading level 7)

`otError otMdnsStopSrvResolver(otInstance *aInstance, const otMdnsSrvResolver *aResolver)`

**Description:** Stops an SRV record resolver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otMdnsSrvResolver](api-mdns#ot-mdns-srv-resolver) *|[in]|aResolver|The resolver to stop.|

No action is performed if no matching resolver with the same service and callback is currently active.

###### otMdnsStartTxtResolver (heading level 7)

`otError otMdnsStartTxtResolver(otInstance *aInstance, const otMdnsTxtResolver *aResolver)`

**Description:** Starts a TXT record resolver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otMdnsTxtResolver](api-mdns#ot-mdns-txt-resolver) *|[in]|aResolver|The resolver to be started.|

Initiates a continuous TXT record resolver for the specified service in `aResolver`.

Discovered information is reported through the `mCallback` function in `aResolver`. When the TXT record is removed it is reported with a TTL value of zero. In this case, `mTxtData` may be NULL, and other result fields (such as `mTxtDataLength`) should be ignored.

The callback may be invoked immediately with cached information (if available) and potentially before this function returns. When cached result is used, the reported TTL value will reflect the original TTL from the last received response.

Multiple resolvers can be started for the same service, provided they use different callback functions.

###### otMdnsStopTxtResolver (heading level 7)

`otError otMdnsStopTxtResolver(otInstance *aInstance, const otMdnsTxtResolver *aResolver)`

**Description:** Stops a TXT record resolver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otMdnsTxtResolver](api-mdns#ot-mdns-txt-resolver) *|[in]|aResolver|The resolver to stop.|

No action is performed if no matching resolver with the same service and callback is currently active.

###### otMdnsStartIp6AddressResolver (heading level 7)

`otError otMdnsStartIp6AddressResolver(otInstance *aInstance, const otMdnsAddressResolver *aResolver)`

**Description:** Starts an IPv6 address resolver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otMdnsAddressResolver](api-mdns#ot-mdns-address-resolver) *|[in]|aResolver|The resolver to be started.|

Initiates a continuous IPv6 address resolver for the specified host name in `aResolver`.

Discovered addresses are reported through the `mCallback` function in `aResolver`. The callback is invoked whenever addresses are added or removed, providing an updated list. If all addresses are removed, the callback is invoked with an empty list (`mAddresses` will be NULL, and `mAddressesLength` will be zero).

The callback may be invoked immediately with cached information (if available) and potentially before this function returns. When cached result is used, the reported TTL values will reflect the original TTL from the last received response.

Multiple resolvers can be started for the same host name, provided they use different callback functions.

###### otMdnsStopIp6AddressResolver (heading level 7)

`otError otMdnsStopIp6AddressResolver(otInstance *aInstance, const otMdnsAddressResolver *aResolver)`

**Description:** Stops an IPv6 address resolver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otMdnsAddressResolver](api-mdns#ot-mdns-address-resolver) *|[in]|aResolver|The resolver to stop.|

No action is performed if no matching resolver with the same host name and callback is currently active.

###### otMdnsStartIp4AddressResolver (heading level 7)

`otError otMdnsStartIp4AddressResolver(otInstance *aInstance, const otMdnsAddressResolver *aResolver)`

**Description:** Starts an IPv4 address resolver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otMdnsAddressResolver](api-mdns#ot-mdns-address-resolver) *|[in]|aResolver|The resolver to be started.|

Initiates a continuous IPv4 address resolver for the specified host name in `aResolver`.

Discovered addresses are reported through the `mCallback` function in `aResolver`. The IPv4 addresses are represented using the IPv4-mapped IPv6 address format in `mAddresses` array. The callback is invoked whenever addresses are added or removed, providing an updated list. If all addresses are removed, the callback is invoked with an empty list (`mAddresses` will be NULL, and `mAddressesLength` will be zero).

The callback may be invoked immediately with cached information (if available) and potentially before this function returns. When cached result is used, the reported TTL values will reflect the original TTL from the last received response.

Multiple resolvers can be started for the same host name, provided they use different callback functions.

###### otMdnsStopIp4AddressResolver (heading level 7)

`otError otMdnsStopIp4AddressResolver(otInstance *aInstance, const otMdnsAddressResolver *aResolver)`

**Description:** Stops an IPv4 address resolver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otMdnsAddressResolver](api-mdns#ot-mdns-address-resolver) *|[in]|aResolver|The resolver to stop.|

No action is performed if no matching resolver with the same host name and callback is currently active.

###### otMdnsStartRecordQuerier (heading level 7)

`otError otMdnsStartRecordQuerier(otInstance *aInstance, const otMdnsRecordQuerier *aQuerier)`

**Description:** Starts a record querier.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otMdnsRecordQuerier](api-mdns#ot-mdns-record-querier) *|[in]|aQuerier|The record querier to be started.|

Initiates a continuous query for a given `mRecordType` as specified in `aQuerier`. The queried name is specified by the combination of `mFirstLabel` and `mNextLabels` (optional rest of the labels) in `aQuerier`. The `mFirstLabel` MUST be non-NULL but `mNextLabels` can be `NULL` if there are no other labels. The `mNextLabels` MUST NOT include the domain name. The reason for a separate first label is to allow it to include a dot `.` character (as allowed for service instance labels).

Discovered results are reported through the `mCallback` function in `aQuerier`, providing the record data bytes (RDATA). For NS, CNAME, SOA, PTR, MX, RP, AFSDB, RT, PX, SRV, KX, DNAME, and NSEC record types, the RDATA format contains one or more DNS names (which may use DNS name compression). For the above list, the reported record data bytes via `mCallback` will be decompressed to contain the full DNS name(s). For all other record types, the record data bytes are provided exactly as they appear in the received mDNS response. This aligns the implementation with RFC 6762 (section 18.14) regarding the use of name compression.

A removed record data is indicated with a TTL value of zero. The callback may be invoked immediately with cached information (if available) and potentially before this function returns. When cached results are used, the reported TTL value will reflect the original TTL from the last received response.

Multiple querier instances can be started for the same name, provided they use different callback functions.

The record querier MUST not be used for record types PTR, SRV, TXT, A, and AAAA. Otherwise, `OT_ERROR_INVALID_ARGS` will be returned. For these, browsers/resolvers can be used. This design is intentional to enable the implementation of an "opportunistic cache mechanism", where, depending on currently active service browsers/resolvers, the mDNS implementation will also monitor and cache related records (e.g., when a service is resolved, the address records associated with its host name are cached even if there is no active address resolver for this hostname).

The `aQuerier` and all its contained information (strings) are only valid during this call. The platform MUST save a copy of the information if it wants to retain the information after returning from this function.

###### otMdnsStopRecordQuerier (heading level 7)

`otError otMdnsStopRecordQuerier(otInstance *aInstance, const otMdnsRecordQuerier *aQuerier)`

**Description:** Stops a record querier.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otMdnsRecordQuerier](api-mdns#ot-mdns-record-querier) *|[in]|aQuerier|The record querier to be stopped.|

No action is performed if no matching querier with the same name and callback is currently active.

The `aQuerier` and all its contained information (strings) are only valid during this call. The platform MUST save a copy of the information if it wants to retain the information after returning from this function.

###### otMdnsGetNextBrowser (heading level 7)

`otError otMdnsGetNextBrowser(otInstance *aInstance, otMdnsIterator *aIterator, otMdnsBrowser *aBrowser, otMdnsCacheInfo *aInfo)`

**Description:** Iterates over browsers.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otMdnsIterator](api-mdns#ot-mdns-iterator) *|[in]|aIterator|Pointer to the iterator.|
|[otMdnsBrowser](api-mdns#ot-mdns-browser) *|[out]|aBrowser|Pointer to an `otMdnsBrowser` to return the information about the next browser.|
|[otMdnsCacheInfo](ot-mdns-cache-info) *|[out]|aInfo|Pointer to an `otMdnsCacheInfo` to return additional information.|

Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`.

On success, `aBrowser` is populated with information about the next browser. The `mCallback` field is always set to `NULL` as there may be multiple active browsers with different callbacks. Other pointers within the `otMdnsBrowser` structure remain valid until the next call to any OpenThread stack's public or platform API/callback.

###### otMdnsGetNextSrvResolver (heading level 7)

`otError otMdnsGetNextSrvResolver(otInstance *aInstance, otMdnsIterator *aIterator, otMdnsSrvResolver *aResolver, otMdnsCacheInfo *aInfo)`

**Description:** Iterates over SRV resolvers.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otMdnsIterator](api-mdns#ot-mdns-iterator) *|[in]|aIterator|Pointer to the iterator.|
|[otMdnsSrvResolver](api-mdns#ot-mdns-srv-resolver) *|[out]|aResolver|Pointer to an `otMdnsSrvResolver` to return the information about the next resolver.|
|[otMdnsCacheInfo](ot-mdns-cache-info) *|[out]|aInfo|Pointer to an `otMdnsCacheInfo` to return additional information.|

Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`.

On success, `aResolver` is populated with information about the next resolver. The `mCallback` field is always set to `NULL` as there may be multiple active resolvers with different callbacks. Other pointers within the `otMdnsSrvResolver` structure remain valid until the next call to any OpenThread stack's public or platform API/callback.

###### otMdnsGetNextTxtResolver (heading level 7)

`otError otMdnsGetNextTxtResolver(otInstance *aInstance, otMdnsIterator *aIterator, otMdnsTxtResolver *aResolver, otMdnsCacheInfo *aInfo)`

**Description:** Iterates over TXT resolvers.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otMdnsIterator](api-mdns#ot-mdns-iterator) *|[in]|aIterator|Pointer to the iterator.|
|[otMdnsTxtResolver](api-mdns#ot-mdns-txt-resolver) *|[out]|aResolver|Pointer to an `otMdnsTxtResolver` to return the information about the next resolver.|
|[otMdnsCacheInfo](ot-mdns-cache-info) *|[out]|aInfo|Pointer to an `otMdnsCacheInfo` to return additional information.|

Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`.

On success, `aResolver` is populated with information about the next resolver. The `mCallback` field is always set to `NULL` as there may be multiple active resolvers with different callbacks. Other pointers within the `otMdnsTxtResolver` structure remain valid until the next call to any OpenThread stack's public or platform API/callback.

###### otMdnsGetNextIp6AddressResolver (heading level 7)

`otError otMdnsGetNextIp6AddressResolver(otInstance *aInstance, otMdnsIterator *aIterator, otMdnsAddressResolver *aResolver, otMdnsCacheInfo *aInfo)`

**Description:** Iterates over IPv6 address resolvers.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otMdnsIterator](api-mdns#ot-mdns-iterator) *|[in]|aIterator|Pointer to the iterator.|
|[otMdnsAddressResolver](api-mdns#ot-mdns-address-resolver) *|[out]|aResolver|Pointer to an `otMdnsAddressResolver` to return the information about the next resolver.|
|[otMdnsCacheInfo](ot-mdns-cache-info) *|[out]|aInfo|Pointer to an `otMdnsCacheInfo` to return additional information.|

Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`.

On success, `aResolver` is populated with information about the next resolver. The `mCallback` field is always set to `NULL` as there may be multiple active resolvers with different callbacks. Other pointers within the `otMdnsAddressResolver` structure remain valid until the next call to any OpenThread stack's public or platform API/callback.

###### otMdnsGetNextIp4AddressResolver (heading level 7)

`otError otMdnsGetNextIp4AddressResolver(otInstance *aInstance, otMdnsIterator *aIterator, otMdnsAddressResolver *aResolver, otMdnsCacheInfo *aInfo)`

**Description:** Iterates over IPv4 address resolvers.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otMdnsIterator](api-mdns#ot-mdns-iterator) *|[in]|aIterator|Pointer to the iterator.|
|[otMdnsAddressResolver](api-mdns#ot-mdns-address-resolver) *|[out]|aResolver|Pointer to an `otMdnsAddressResolver` to return the information about the next resolver.|
|[otMdnsCacheInfo](ot-mdns-cache-info) *|[out]|aInfo|Pointer to an `otMdnsCacheInfo` to return additional information.|

Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`.

On success, `aResolver` is populated with information about the next resolver. The `mCallback` field is always set to `NULL` as there may be multiple active resolvers with different callbacks. Other pointers within the `otMdnsAddressResolver` structure remain valid until the next call to any OpenThread stack's public or platform API/callback.

###### otMdnsGetNextRecordQuerier (heading level 7)

`otError otMdnsGetNextRecordQuerier(otInstance *aInstance, otMdnsIterator *aIterator, otMdnsRecordQuerier *aQuerier, otMdnsCacheInfo *aInfo)`

**Description:** Iterates over record querier entries.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otMdnsIterator](api-mdns#ot-mdns-iterator) *|[in]|aIterator|Pointer to the iterator.|
|[otMdnsRecordQuerier](api-mdns#ot-mdns-record-querier) *|[out]|aQuerier|Pointer to an `otMdnsRecordQuerier` to return the information about the next one.|
|[otMdnsCacheInfo](ot-mdns-cache-info) *|[out]|aInfo|Pointer to an `otMdnsCacheInfo` to return additional information.|

Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`.

On success, `aQuerier` is populated with information about the next querier . The `mCallback` field is always set to `NULL` as there may be multiple active querier with different callbacks. Other pointers within the `otMdnsRecordQuerier` structure remain valid until the next call to any OpenThread stack's public or platform API/callback.

###### otMdnsSetVerboseLoggingEnabled (heading level 7)

`void otMdnsSetVerboseLoggingEnabled(otInstance *aInstance, bool aEnable)`

**Description:** Enables or disables verbose logging for the mDNS module at run-time.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnable|TRUE to enable verbose logging, FALSE to disable.|

Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_VERBOSE_LOGGING_ENABLE`.

The initial state of verbose logging (enabled or disabled at startup) is determined by the configuration `OPENTHREAD_CONFIG_MULTICAST_DEFAULT_DNS_VERBOSE_LOGGING_STATE`.

When enabled, the mDNS module emits verbose logs for every sent or received mDNS message, including the header and all question and resource records. These logs are generated regardless of the current log level configured on the device.

This feature can generate a large volume of logs, so its use is recommended only during development, integration, or debugging.

###### otMdnsIsVerboseLoggingEnabled (heading level 7)

`bool otMdnsIsVerboseLoggingEnabled(otInstance *aInstance)`

**Description:** Indicates whether verbose logging is enabled for the mDNS module.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_VERBOSE_LOGGING_ENABLE`.

Represents a local host IPv4 or IPv6 address entry. 

###### Public Attributes (heading level 7)

###### mIsIp6 (heading level 8)

```
bool otMdnsLocalHostAddress::mIsIp6
```

**Description:** Indicates whether the address is IPv6 (`true`) or IPv4 (`false`).

###### mInfraIfIndex (heading level 8)

```
uint32_t otMdnsLocalHostAddress::mInfraIfIndex
```

**Description:** The infrastructure network interface index.

###### mIp6 (heading level 8)

```
otIp6Address otMdnsLocalHostAddress::mIp6
```

**Description:** The IPv6 address (valid when `mIsIp6` is true).

###### mIp4 (heading level 8)

```
otIp4Address otMdnsLocalHostAddress::mIp4
```

**Description:** The IPv4 address (valid when `mIsIp6` is false).

###### mAddress (heading level 8)

```
union otMdnsLocalHostAddress::@5 otMdnsLocalHostAddress::mAddress
```

**Description:** The address.

Represents additional information about a browser/resolver and its cached results. 

###### Public Attributes (heading level 7)

###### mIsActive (heading level 8)

```
bool otMdnsCacheInfo::mIsActive
```

**Description:** Whether this is an active browser/resolver vs an opportunistic cached one.

###### mHasCachedResults (heading level 8)

```
bool otMdnsCacheInfo::mHasCachedResults
```

**Description:** Whether there is any cached results.

##### NAT64

This module includes functions and structs for the NAT64 function on the border router. 

These functions are only available when `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` is enabled. 

###### Modules

[otIp4Address](ot-ip4-address)

[otIp4Cidr](ot-ip4-cidr)

[otNat64Counters](ot-nat64-counters)

[otNat64ProtocolCounters](ot-nat64-protocol-counters)

[otNat64ErrorCounters](ot-nat64-error-counters)

[otNat64AddressMapping](ot-nat64-address-mapping)

[otNat64AddressMappingIterator](ot-nat64-address-mapping-iterator)

###### Enumerations

###### otNat64DropReason (heading level 7)

```
enum otNat64DropReason {
    OT_NAT64_DROP_REASON_UNKNOWN = 0
    OT_NAT64_DROP_REASON_ILLEGAL_PACKET
    OT_NAT64_DROP_REASON_UNSUPPORTED_PROTO
    OT_NAT64_DROP_REASON_NO_MAPPING
    OT_NAT64_DROP_REASON_COUNT
}
```

**Description:**

Packet drop reasons.

**Enumerator:**

|   |   |
|---|---|
|OT_NAT64_DROP_REASON_UNKNOWN|Packet drop for unknown reasons.|
|OT_NAT64_DROP_REASON_ILLEGAL_PACKET|Packet drop due to failed to parse the datagram.|
|OT_NAT64_DROP_REASON_UNSUPPORTED_PROTO|Packet drop due to unsupported IP protocol.|
|OT_NAT64_DROP_REASON_NO_MAPPING|Packet drop due to no mappings found or mapping pool exhausted.|
|OT_NAT64_DROP_REASON_COUNT||

###### otNat64State (heading level 7)

```
enum otNat64State {
    OT_NAT64_STATE_DISABLED = 0
    OT_NAT64_STATE_NOT_RUNNING
    OT_NAT64_STATE_IDLE
    OT_NAT64_STATE_ACTIVE
}
```

**Description:**

States of NAT64.

**Enumerator:**

|   |   |
|---|---|
|OT_NAT64_STATE_DISABLED|NAT64 is disabled.|
|OT_NAT64_STATE_NOT_RUNNING|NAT64 is enabled, but one or more dependencies of NAT64 are not running.|
|OT_NAT64_STATE_IDLE|NAT64 is enabled, but this BR is not an active NAT64 BR.|
|OT_NAT64_STATE_ACTIVE|The BR is publishing a NAT64 prefix and/or translating packets.|

###### Typedefs

###### otIp4Address (heading level 7)

`typedef struct otIp4Address otIp4Address`

**Description:**

Represents an IPv4 address.

###### otIp4Cidr (heading level 7)

`typedef struct otIp4Cidr otIp4Cidr`

###### otNat64Counters (heading level 7)

`typedef struct otNat64Counters otNat64Counters`

**Description:**

Represents the counters for NAT64.

###### otNat64ProtocolCounters (heading level 7)

`typedef struct otNat64ProtocolCounters otNat64ProtocolCounters`

**Description:**

Represents the counters for the protocols supported by NAT64.

###### otNat64DropReason (heading level 7)

`typedef enum otNat64DropReason otNat64DropReason`

**Description:**

Packet drop reasons.

###### otNat64ErrorCounters (heading level 7)

`typedef struct otNat64ErrorCounters otNat64ErrorCounters`

**Description:**

Represents the counters of dropped packets due to errors when handling NAT64 packets.

###### otNat64AddressMapping (heading level 7)

`typedef struct otNat64AddressMapping otNat64AddressMapping`

**Description:**

Represents an address mapping record for NAT64.

**Details:**

**Note**

- The counters will be reset for each mapping session even for the same address pair. Applications can use `mId` to identify different sessions to calculate the packets correctly.

###### otNat64AddressMappingIterator (heading level 7)

`typedef struct otNat64AddressMappingIterator otNat64AddressMappingIterator`

**Description:**

Used to iterate through NAT64 address mappings.

**Details:**

The fields in this type are opaque (intended for use by OpenThread core only) and therefore should not be accessed or used by caller.

Before using an iterator, it MUST be initialized using `otNat64InitAddressMappingIterator()`.

The member fields in this struct are for internal OpenThread stack use and should not be accessed directly.

###### otNat64ReceiveIp4Callback (heading level 7)

`typedef void(* otNat64ReceiveIp4Callback) (otMessage *aMessage, void *aContext)`

**Description:**

Pointer is called when an IPv4 datagram (translated by NAT64 translator) is received.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aMessage|A pointer to the message buffer containing the received IPv6 datagram. This function transfers the ownership of the `aMessage` to the receiver of the callback. The message should be freed by the receiver of the callback after it is processed.|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### Variables

###### OT_TOOL_PACKED_END (heading level 7)

```
OT_TOOL_PACKED_BEGIN struct otIp4Address OT_TOOL_PACKED_END
```

###### Functions

###### otNat64GetCounters (heading level 7)

`void otNat64GetCounters(otInstance *aInstance, otNat64ProtocolCounters *aCounters)`

**Description:** Gets NAT64 translator counters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otNat64ProtocolCounters](ot-nat64-protocol-counters) *|[out]|aCounters|A pointer to an `otNat64Counters` where the counters of NAT64 translator will be placed.|

The counter is counted since the instance initialized.

Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.

###### otNat64GetErrorCounters (heading level 7)

`void otNat64GetErrorCounters(otInstance *aInstance, otNat64ErrorCounters *aCounters)`

**Description:** Gets the NAT64 translator error counters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otNat64ErrorCounters](ot-nat64-error-counters) *|[out]|aCounters|A pointer to an `otNat64Counters` where the counters of NAT64 translator will be placed.|

The counters are initialized to zero when the OpenThread instance is initialized.

###### otNat64InitAddressMappingIterator (heading level 7)

`void otNat64InitAddressMappingIterator(otInstance *aInstance, otNat64AddressMappingIterator *aIterator)`

**Description:** Initializes an `otNat64AddressMappingIterator`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otNat64AddressMappingIterator](ot-nat64-address-mapping-iterator) *|[out]|aIterator|A pointer to the iterator to initialize.|

Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.

An iterator MUST be initialized before it is used. An iterator can be initialized again to restart from the beginning of the mapping info list.

The iterator initialization time is used to report the `mRemainingTimeMs` in the `otNat64AddressMapping` retrieved when calling `otNat64GetNextAddressMapping()` to iterate over the list. This ensures that all entry `mRemainingTimeMs` values are consistent and are from the same time origin, regardless of how or when `otNat64GetNextAddressMapping()` is called.

###### otNat64GetNextAddressMapping (heading level 7)

`otError otNat64GetNextAddressMapping(otInstance *aInstance, otNat64AddressMappingIterator *aIterator, otNat64AddressMapping *aMapping)`

**Description:** Gets the next AddressMapping info (using an iterator).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otNat64AddressMappingIterator](ot-nat64-address-mapping-iterator) *|[inout]|aIterator|A pointer to the iterator. On success the iterator will be updated to point to next NAT64 address mapping record.|
|[otNat64AddressMapping](ot-nat64-address-mapping) *|[out]|aMapping|A pointer to an `otNat64AddressMapping` where information of next NAT64 address mapping record is placed (on success).|

Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.

###### otNat64GetTranslatorState (heading level 7)

`otNat64State otNat64GetTranslatorState(otInstance *aInstance)`

**Description:** Gets the state of NAT64 translator.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.

###### otNat64GetPrefixManagerState (heading level 7)

`otNat64State otNat64GetPrefixManagerState(otInstance *aInstance)`

**Description:** Gets the state of NAT64 prefix manager.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Available when `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` is enabled.

###### otNat64SetEnabled (heading level 7)

`void otNat64SetEnabled(otInstance *aInstance, bool aEnabled)`

**Description:** Enable or disable NAT64 functions.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnabled|A boolean to enable/disable the NAT64 functions|

Note: This includes the NAT64 Translator (when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled) and the NAT64 Prefix Manager (when `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` is enabled).

When `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled, setting disabled to true resets the mapping table in the translator.

Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` or `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` is enabled.

**See Also**

- [otNat64GetTranslatorState](api-nat64#ot-nat64-get-translator-state)
- [otNat64GetPrefixManagerState](api-nat64#ot-nat64-get-prefix-manager-state)

###### otIp4NewMessage (heading level 7)

`otMessage * otIp4NewMessage(otInstance *aInstance, const otMessageSettings *aSettings)`

**Description:** Allocate a new message buffer for sending an IPv4 message to the NAT64 translator.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otMessageSettings](ot-message-settings) *|[in]|aSettings|A pointer to the message settings or NULL to set default settings.|

Message buffers allocated by this function will have 20 bytes (difference between the size of IPv6 headers and IPv4 header sizes) reserved.

Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.

**Note**

- If `aSettings` is `NULL`, the link layer security is enabled and the message priority is set to OT_MESSAGE_PRIORITY_NORMAL by default.

**Returns**

- A pointer to the message buffer or NULL if no message buffers are available or parameters are invalid.

**See Also**

- [otNat64Send](api-nat64#ot-nat64-send)

###### otNat64SetIp4Cidr (heading level 7)

`otError otNat64SetIp4Cidr(otInstance *aInstance, const otIp4Cidr *aCidr)`

**Description:** Sets the CIDR used when setting the source address of the outgoing translated IPv4 packets.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp4Cidr](ot-ip4-cidr) *|[in]|aCidr|A pointer to an [otIp4Cidr](ot-ip4-cidr) for the IPv4 CIDR block for NAT64.|

Is available only when OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE is enabled.

**Note**

- A valid CIDR must have a non-zero prefix length. The actual addresses pool is limited by the size of the mapping pool and the number of addresses available in the CIDR block.
- This function can be called at any time, but the NAT64 translator will be reset and all existing sessions will be expired when updating the configured CIDR.

**See Also**

- [otNat64Send](api-nat64#ot-nat64-send)
- [otNat64SetReceiveIp4Callback](api-nat64#ot-nat64-set-receive-ip4-callback)

###### otNat64ClearIp4Cidr (heading level 7)

`void otNat64ClearIp4Cidr(otInstance *aInstance)`

**Description:** Clears the CIDR used when setting the source address of the outgoing translated IPv4 packets.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Is available only when OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE is enabled.

**Note**

- This function can be called at any time, but the NAT64 translator will be reset and all existing sessions will be expired when clearing the configured CIDR.

**See Also**

- [otNat64SetIp4Cidr](api-nat64#ot-nat64-set-ip4-cidr)

###### otNat64Send (heading level 7)

`otError otNat64Send(otInstance *aInstance, otMessage *aMessage)`

**Description:** Translates an IPv4 datagram to an IPv6 datagram and sends via the Thread interface.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the message buffer containing the IPv4 datagram.|

The caller transfers ownership of `aMessage` when making this call. OpenThread will free `aMessage` when processing is complete, including when a value other than `OT_ERROR_NONE` is returned.

###### otNat64SetReceiveIp4Callback (heading level 7)

`void otNat64SetReceiveIp4Callback(otInstance *aInstance, otNat64ReceiveIp4Callback aCallback, void *aContext)`

**Description:** Registers a callback to provide received IPv4 datagrams.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otNat64ReceiveIp4Callback](api-nat64#ot-nat64-receive-ip4-callback)|[in]|aCallback|A pointer to a function that is called when an IPv4 datagram is received or NULL to disable the callback.|
|void *|[in]|aContext|A pointer to application-specific context.|

###### otNat64GetCidr (heading level 7)

`otError otNat64GetCidr(otInstance *aInstance, otIp4Cidr *aCidr)`

**Description:** Gets the IPv4 CIDR configured in the NAT64 translator.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otIp4Cidr](ot-ip4-cidr) *|[out]|aCidr|A pointer to an [otIp4Cidr](ot-ip4-cidr). Where the CIDR will be filled.|

Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.

###### otIp4IsAddressEqual (heading level 7)

`bool otIp4IsAddressEqual(const otIp4Address *aFirst, const otIp4Address *aSecond)`

**Description:** Test if two IPv4 addresses are the same.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otIp4Address](ot-ip4-address) *|[in]|aFirst|A pointer to the first IPv4 address to compare.|
|const [otIp4Address](ot-ip4-address) *|[in]|aSecond|A pointer to the second IPv4 address to compare.|

###### otIp4ExtractFromIp6Address (heading level 7)

`void otIp4ExtractFromIp6Address(uint8_t aPrefixLength, const otIp6Address *aIp6Address, otIp4Address *aIp4Address)`

**Description:** Set `aIp4Address` by performing NAT64 address translation from `aIp6Address` as specified in RFC 6052.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t|[in]|aPrefixLength|The prefix length to use for IPv4/IPv6 translation.|
|const [otIp6Address](ot-ip6-address) *|[in]|aIp6Address|A pointer to an IPv6 address.|
|[otIp4Address](ot-ip4-address) *|[out]|aIp4Address|A pointer to output the IPv4 address.|

The NAT64 `aPrefixLength` MUST be one of the following values: 32, 40, 48, 56, 64, or 96, otherwise the behavior of this method is undefined.

###### otIp4FromIp4MappedIp6Address (heading level 7)

`otError otIp4FromIp4MappedIp6Address(const otIp6Address *aIp6Address, otIp4Address *aIp4Address)`

**Description:** Extracts the IPv4 address from a given IPv4-mapped IPv6 address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otIp6Address](ot-ip6-address) *|[in]|aIp6Address|An IPv6 address to extract IPv4 from.|
|[otIp4Address](ot-ip4-address) *|[out]|aIp4Address|An IPv4 address to output the extracted address.|

An IPv4-mapped IPv6 address consists of an 80-bit prefix of zeros, the next 16 bits set to ones, and the remaining, least-significant 32 bits contain the IPv4 address, e.g., `::ffff:192.0.2.128` representing `192.0.2.128`.

###### otIp4ToIp4MappedIp6Address (heading level 7)

`void otIp4ToIp4MappedIp6Address(const otIp4Address *aIp4Address, otIp6Address *aIp6Address)`

**Description:** Converts a given IP4 address to an IPv6 address following the IPv4-mapped IPv6 address format.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otIp4Address](ot-ip4-address) *|[in]|aIp4Address|An IPv4 address to convert.|
|[otIp6Address](ot-ip6-address) *|[out]|aIp6Address|An IPv6 address to set.|

###### otIp4AddressToString (heading level 7)

`void otIp4AddressToString(const otIp4Address *aAddress, char *aBuffer, uint16_t aSize)`

**Description:** Converts the address to a string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otIp4Address](ot-ip4-address) *|[in]|aAddress|A pointer to an IPv4 address (MUST NOT be NULL).|
|char *|[out]|aBuffer|A pointer to a char array to output the string (MUST NOT be NULL).|
|uint16_t|[in]|aSize|The size of `aBuffer` (in bytes).|

The string format uses quad-dotted notation of four bytes in the address (e.g., "127.0.0.1").

If the resulting string does not fit in `aBuffer` (within its `aSize` characters), the string will be truncated but the outputted string is always null-terminated.

###### otIp4CidrFromString (heading level 7)

`otError otIp4CidrFromString(const char *aString, otIp4Cidr *aCidr)`

**Description:** Converts a human-readable IPv4 CIDR string into a binary representation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aString|A pointer to a NULL-terminated string.|
|[otIp4Cidr](ot-ip4-cidr) *|[out]|aCidr|A pointer to an IPv4 CIDR.|

###### otIp4CidrToString (heading level 7)

`void otIp4CidrToString(const otIp4Cidr *aCidr, char *aBuffer, uint16_t aSize)`

**Description:** Converts the IPv4 CIDR to a string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otIp4Cidr](ot-ip4-cidr) *|[in]|aCidr|A pointer to an IPv4 CIDR (MUST NOT be NULL).|
|char *|[out]|aBuffer|A pointer to a char array to output the string (MUST NOT be NULL).|
|uint16_t|[in]|aSize|The size of `aBuffer` (in bytes).|

The string format uses quad-dotted notation of four bytes in the address with the length of prefix (e.g., "127.0.0.1/32").

If the resulting string does not fit in `aBuffer` (within its `aSize` characters), the string will be truncated but the outputted string is always null-terminated.

###### otIp4AddressFromString (heading level 7)

`otError otIp4AddressFromString(const char *aString, otIp4Address *aAddress)`

**Description:** Converts a human-readable IPv4 address string into a binary representation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aString|A pointer to a NULL-terminated string.|
|[otIp4Address](ot-ip4-address) *|[out]|aAddress|A pointer to an IPv4 address.|

###### otNat64SynthesizeIp6Address (heading level 7)

`otError otNat64SynthesizeIp6Address(otInstance *aInstance, const otIp4Address *aIp4Address, otIp6Address *aIp6Address)`

**Description:** Sets the IPv6 address by performing NAT64 address translation from the preferred NAT64 prefix and the given IPv4 address as specified in RFC 6052.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp4Address](ot-ip4-address) *|[in]|aIp4Address|A pointer to the IPv4 address to translate to IPv6.|
|[otIp6Address](ot-ip6-address) *|[out]|aIp6Address|A pointer to the synthesized IPv6 address.|

**Returns**

- OT_ERROR_NONE Successfully synthesized the IPv6 address from NAT64 prefix and IPv4 address.
- OT_ERROR_INVALID_STATE No valid NAT64 prefix in the network data.

###### otNat64StateToString (heading level 7)

`const char * otNat64StateToString(otNat64State aState)`

**Description:** Converts a given `otNat64State` to a human-readable string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otNat64State](api-nat64#ot-nat64-state)|[in]|aState|The NAT64 state.|

**Returns**

- The string representation of `aState`.

###### Macros

`#define OT_IP4_ADDRESS_SIZE 4`

**Description**: Size of an IPv4 address (bytes)

`#define OT_IP4_ADDRESS_STRING_SIZE 17`

**Description**: Length of 000.000.000.000 plus a suffix NUL.

`#define OT_IP4_CIDR_STRING_SIZE 20`

**Description**: Length of 000.000.000.000/00 plus a suffix NUL.

Represents an IPv4 CIDR block. 

###### Public Attributes (heading level 7)

###### mAddress (heading level 8)

```
otIp4Address otIp4Cidr::mAddress
```

###### mLength (heading level 8)

```
uint8_t otIp4Cidr::mLength
```

Represents the counters for NAT64. 

###### Public Attributes (heading level 7)

###### m4To6Packets (heading level 8)

```
uint64_t otNat64Counters::m4To6Packets
```

**Description:** Number of packets translated from IPv4 to IPv6.

###### m4To6Bytes (heading level 8)

```
uint64_t otNat64Counters::m4To6Bytes
```

**Description:** Sum of size of packets translated from IPv4 to IPv6.

###### m6To4Packets (heading level 8)

```
uint64_t otNat64Counters::m6To4Packets
```

**Description:** Number of packets translated from IPv6 to IPv4.

###### m6To4Bytes (heading level 8)

```
uint64_t otNat64Counters::m6To4Bytes
```

**Description:** Sum of size of packets translated from IPv6 to IPv4.

Represents the counters for the protocols supported by NAT64. 

###### Public Attributes (heading level 7)

###### mTotal (heading level 8)

```
otNat64Counters otNat64ProtocolCounters::mTotal
```

**Description:** Counters for sum of all protocols.

###### mIcmp (heading level 8)

```
otNat64Counters otNat64ProtocolCounters::mIcmp
```

**Description:** Counters for ICMP and ICMPv6.

###### mUdp (heading level 8)

```
otNat64Counters otNat64ProtocolCounters::mUdp
```

**Description:** Counters for UDP.

###### mTcp (heading level 8)

```
otNat64Counters otNat64ProtocolCounters::mTcp
```

**Description:** Counters for TCP.

Represents the counters of dropped packets due to errors when handling NAT64 packets. 

###### Public Attributes (heading level 7)

###### mCount4To6 (heading level 8)

```
uint64_t otNat64ErrorCounters::mCount4To6[OT_NAT64_DROP_REASON_COUNT]
```

**Description:** Errors translating IPv4 packets.

###### mCount6To4 (heading level 8)

```
uint64_t otNat64ErrorCounters::mCount6To4[OT_NAT64_DROP_REASON_COUNT]
```

**Description:** Errors translating IPv6 packets.

Represents an address mapping record for NAT64. 

**Note**

- The counters will be reset for each mapping session even for the same address pair. Applications can use `mId` to identify different sessions to calculate the packets correctly.

###### Public Attributes (heading level 7)

###### mId (heading level 8)

```
uint64_t otNat64AddressMapping::mId
```

**Description:** The unique id for a mapping session.

###### mIp4 (heading level 8)

```
otIp4Address otNat64AddressMapping::mIp4
```

**Description:** The IPv4 address of the mapping.

###### mIp6 (heading level 8)

```
otIp6Address otNat64AddressMapping::mIp6
```

**Description:** The IPv6 address of the mapping.

###### mSrcPortOrId (heading level 8)

```
uint16_t otNat64AddressMapping::mSrcPortOrId
```

**Description:** The source port or ICMP ID of the mapping.

**Details:** Used when OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE is true.

###### mTranslatedPortOrId (heading level 8)

```
uint16_t otNat64AddressMapping::mTranslatedPortOrId
```

**Description:** The translated port or ICMP ID of the mapping.

**Details:** Used when OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE is true.

###### mRemainingTimeMs (heading level 8)

```
uint32_t otNat64AddressMapping::mRemainingTimeMs
```

**Description:** Remaining time in milliseconds before the entry expires.

**Details:** The remaining time is relative to the initialization of the `otNat64AddressMappingIterator`, i.e., when `otNat64InitAddressMappingIterator()` was called.

###### mCounters (heading level 8)

```
otNat64ProtocolCounters otNat64AddressMapping::mCounters
```

**Description:** Counters.

Used to iterate through NAT64 address mappings. 

The fields in this type are opaque (intended for use by OpenThread core only) and therefore should not be accessed or used by caller.

Before using an iterator, it MUST be initialized using `otNat64InitAddressMappingIterator()`.

The member fields in this struct are for internal OpenThread stack use and should not be accessed directly. 

###### Public Attributes (heading level 7)

###### mPtr (heading level 8)

```
const void* otNat64AddressMappingIterator::mPtr
```

###### mData32 (heading level 8)

```
uint32_t otNat64AddressMappingIterator::mData32
```

Represents an IPv4 address. 

###### Modules (heading level 7)

[otIp4Address::OT_TOOL_PACKED_FIELD](ot-ip4-address-ot-tool-packed-field)

###### Public Attributes (heading level 7)

###### mFields (heading level 8)

```
union otIp4Address::OT_TOOL_PACKED_FIELD otIp4Address::mFields
```

###### Public Attributes (heading level 8)

###### m8 (heading level 9)

```
uint8_t otIp4Address::OT_TOOL_PACKED_FIELD::m8[OT_IP4_ADDRESS_SIZE]
```

**Description:** 8-bit fields

###### m32 (heading level 9)

```
uint32_t otIp4Address::OT_TOOL_PACKED_FIELD::m32
```

**Description:** 32-bit representation

##### SRP

This module includes functions that control SRP client behavior. 

This module includes functions of the Service Registration Protocol.

This module includes functions for SRP client buffers and service pool.

Functions in this module are only available when feature OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_ENABLE is enabled. 

###### Modules

[otSrpClientHostInfo](ot-srp-client-host-info)

[otSrpClientService](ot-srp-client-service)

[otSrpClientBuffersServiceEntry](ot-srp-client-buffers-service-entry)

[otSrpServerTtlConfig](ot-srp-server-ttl-config)

[otSrpServerLeaseConfig](ot-srp-server-lease-config)

[otSrpServerLeaseInfo](ot-srp-server-lease-info)

[otSrpServerResponseCounters](ot-srp-server-response-counters)

###### Enumerations

###### otSrpClientItemState (heading level 7)

```
enum otSrpClientItemState {
    OT_SRP_CLIENT_ITEM_STATE_TO_ADD
    OT_SRP_CLIENT_ITEM_STATE_ADDING
    OT_SRP_CLIENT_ITEM_STATE_TO_REFRESH
    OT_SRP_CLIENT_ITEM_STATE_REFRESHING
    OT_SRP_CLIENT_ITEM_STATE_TO_REMOVE
    OT_SRP_CLIENT_ITEM_STATE_REMOVING
    OT_SRP_CLIENT_ITEM_STATE_REGISTERED
    OT_SRP_CLIENT_ITEM_STATE_REMOVED
}
```

**Description:**

Specifies an SRP client item (service or host info) state.

**Enumerator:**

|   |   |
|---|---|
|OT_SRP_CLIENT_ITEM_STATE_TO_ADD|Item to be added/registered.|
|OT_SRP_CLIENT_ITEM_STATE_ADDING|Item is being added/registered.|
|OT_SRP_CLIENT_ITEM_STATE_TO_REFRESH|Item to be refreshed (re-register to renew lease).|
|OT_SRP_CLIENT_ITEM_STATE_REFRESHING|Item is being refreshed.|
|OT_SRP_CLIENT_ITEM_STATE_TO_REMOVE|Item to be removed.|
|OT_SRP_CLIENT_ITEM_STATE_REMOVING|Item is being removed.|
|OT_SRP_CLIENT_ITEM_STATE_REGISTERED|Item is registered with server.|
|OT_SRP_CLIENT_ITEM_STATE_REMOVED|Item is removed.|

###### otSrpServerState (heading level 7)

```
enum otSrpServerState {
    OT_SRP_SERVER_STATE_DISABLED = 0
    OT_SRP_SERVER_STATE_RUNNING = 1
    OT_SRP_SERVER_STATE_STOPPED = 2
}
```

**Description:**

Represents the state of the SRP server.

**Enumerator:**

|   |   |
|---|---|
|OT_SRP_SERVER_STATE_DISABLED|The SRP server is disabled.|
|OT_SRP_SERVER_STATE_RUNNING|The SRP server is enabled and running.|
|OT_SRP_SERVER_STATE_STOPPED|The SRP server is enabled but stopped.|

###### otSrpServerAddressMode (heading level 7)

```
enum otSrpServerAddressMode {
    OT_SRP_SERVER_ADDRESS_MODE_UNICAST = 0
    OT_SRP_SERVER_ADDRESS_MODE_ANYCAST = 1
    OT_SRP_SERVER_ADDRESS_MODE_UNICAST_FORCE_ADD = 2
}
```

**Description:**

Represents the address mode used by the SRP server.

**Details:**

Address mode specifies how the address and port number are determined by the SRP server and how this info is published in the Thread Network Data.

**Warnings**

- Using the `OT_SRP_SERVER_ADDRESS_MODE_UNICAST_FORCE_ADD` option will make the implementation non-compliant with the Thread specification. This option is intended for testing and specific use-cases. When selected, the SRP server, upon being enabled, will bypass the Network Data publisher and always add the "SRP/DNS unicast" entry directly to the Network Data, regardless of how many other similar entries are present.

**Enumerator:**

|   |   |
|---|---|
|OT_SRP_SERVER_ADDRESS_MODE_UNICAST|Unicast address mode. Use Network Data publisher.|
|OT_SRP_SERVER_ADDRESS_MODE_ANYCAST|Anycast address mode. Use Network Data publisher.|
|OT_SRP_SERVER_ADDRESS_MODE_UNICAST_FORCE_ADD|Unicast address mode. Immediately force add to Network Data.|

###### Typedefs

###### otSrpClientHostInfo (heading level 7)

`typedef struct otSrpClientHostInfo otSrpClientHostInfo`

**Description:**

Represents an SRP client host info.

###### otSrpClientService (heading level 7)

`typedef struct otSrpClientService otSrpClientService`

**Description:**

Represents an SRP client service.

**Details:**

The values in this structure, including the string buffers for the names and the TXT record entries, MUST persist and stay constant after an instance of this structure is passed to OpenThread from `otSrpClientAddService()` or `otSrpClientRemoveService()`.

The `mState`, `mData`, `mNext` fields are used/managed by OT core only. Their value is ignored when an instance of `otSrpClientService` is passed in `otSrpClientAddService()` or `otSrpClientRemoveService()` or other functions. The caller does not need to set these fields.

The `mLease` and `mKeyLease` fields specify the desired lease and key lease intervals for this service. Zero value indicates that the interval is unspecified and then the default lease or key lease intervals from `otSrpClientGetLeaseInterval()` and `otSrpClientGetKeyLeaseInterval()` are used for this service. If the key lease interval (whether set explicitly or determined from the default) is shorter than the lease interval for a service, SRP client will re-use the lease interval value for key lease interval as well. For example, if in service `mLease` is explicitly set to 2 days and `mKeyLease` is set to zero and default key lease is set to 1 day, then when registering this service, the requested key lease for this service is also set to 2 days.

###### otSrpClientCallback (heading level 7)

`typedef void(* otSrpClientCallback) (otError aError, const otSrpClientHostInfo *aHostInfo, const otSrpClientService *aServices, const otSrpClientService *aRemovedServices, void *aContext)`

**Description:**

Pointer type defines the callback used by SRP client to notify user of changes/events/errors.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aError|The error (see above).|
||[in]|aHostInfo|A pointer to host info.|
||[in]|aServices|The head of linked-list containing all services (excluding the ones removed). NULL if the list is empty.|
||[in]|aRemovedServices|The head of linked-list containing all removed services. NULL if the list is empty.|
||[in]|aContext|A pointer to an arbitrary context (provided when callback was registered).|

**Details:**

This callback is invoked on a successful registration of an update (i.e., add/remove of host-info and/or some service(s)) with the SRP server, or if there is a failure or error (e.g., server rejects a update request or client times out waiting for response, etc).

In case of a successful reregistration of an update, `aError` parameter would be `OT_ERROR_NONE` and the host info and the full list of services is provided as input parameters to the callback. Note that host info and services each track its own state in the corresponding `mState` member variable of the related data structure (the state indicating whether the host-info/service is registered or removed or still being added/removed, etc).

The list of removed services is passed as its own linked-list `aRemovedServices` in the callback. Note that when the callback is invoked, the SRP client (OpenThread implementation) is done with the removed service instances listed in `aRemovedServices` and no longer tracks/stores them (i.e., if from the callback we call `otSrpClientGetServices()` the removed services will not be present in the returned list). Providing a separate list of removed services in the callback helps indicate to user which items are now removed and allow user to re-claim/reuse the instances.

If the server rejects an SRP update request, the DNS response code (RFC 2136) is mapped to the following errors:

- (0) NOERROR Success (no error condition) -> OT_ERROR_NONE
- (1) FORMERR Server unable to interpret due to format error -> OT_ERROR_PARSE
- (2) SERVFAIL Server encountered an internal failure -> OT_ERROR_FAILED
- (3) NXDOMAIN Name that ought to exist, does not exist -> OT_ERROR_NOT_FOUND
- (4) NOTIMP Server does not support the query type (OpCode) -> OT_ERROR_NOT_IMPLEMENTED
- (5) REFUSED Server refused for policy/security reasons -> OT_ERROR_SECURITY
- (6) YXDOMAIN Some name that ought not to exist, does exist -> OT_ERROR_DUPLICATED
- (7) YXRRSET Some RRset that ought not to exist, does exist -> OT_ERROR_DUPLICATED
- (8) NXRRSET Some RRset that ought to exist, does not exist -> OT_ERROR_NOT_FOUND
- (9) NOTAUTH Service is not authoritative for zone -> OT_ERROR_SECURITY
- (10) NOTZONE A name is not in the zone -> OT_ERROR_PARSE
- (20) BADNAME Bad name -> OT_ERROR_PARSE
- (21) BADALG Bad algorithm -> OT_ERROR_SECURITY
- (22) BADTRUN Bad truncation -> OT_ERROR_PARSE
- Other response codes -> OT_ERROR_FAILED

The following errors are also possible:

- OT_ERROR_RESPONSE_TIMEOUT : Timed out waiting for response from server (client would continue to retry).
- OT_ERROR_INVALID_ARGS : The provided service structure is invalid (e.g., bad service name or `otDnsTxtEntry`).
- OT_ERROR_NO_BUFS : Insufficient buffer to prepare or send the update message.

Note that in case of any failure, the client continues the operation, i.e. it prepares and (re)transmits the SRP update message to the server, after some wait interval. The retry wait interval starts from the minimum value and is increased by the growth factor every failure up to the max value (please see configuration parameter `OPENTHREAD_CONFIG_SRP_CLIENT_MIN_RETRY_WAIT_INTERVAL` and the related ones for more details).

###### otSrpClientAutoStartCallback (heading level 7)

`typedef void(* otSrpClientAutoStartCallback) (const otSockAddr *aServerSockAddr, void *aContext)`

**Description:**

Pointer type defines the callback used by SRP client to notify user when it is auto-started or stopped.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aServerSockAddr|A non-NULL pointer indicates SRP server was started and pointer will give the selected server socket address. A NULL pointer indicates SRP server was stopped.|
||[in]|aContext|A pointer to an arbitrary context (provided when callback was registered).|

**Details:**

This is only used when auto-start feature `OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE` is enabled.

This callback is invoked when auto-start mode is enabled and the SRP client is either automatically started or stopped.

###### otSrpClientBuffersServiceEntry (heading level 7)

`typedef struct otSrpClientBuffersServiceEntry otSrpClientBuffersServiceEntry`

**Description:**

Represents a SRP client service pool entry.

###### otSrpServerHost (heading level 7)

`typedef struct otSrpServerHost otSrpServerHost`

**Description:**

This opaque type represents a SRP service host.

###### otSrpServerService (heading level 7)

`typedef struct otSrpServerService otSrpServerService`

**Description:**

This opaque type represents a SRP service.

###### otSrpServerServiceUpdateId (heading level 7)

`typedef uint32_t otSrpServerServiceUpdateId`

**Description:**

The ID of a SRP service update transaction on the SRP Server.

###### otSrpServerAddressMode (heading level 7)

`typedef enum otSrpServerAddressMode otSrpServerAddressMode`

**Description:**

Represents the address mode used by the SRP server.

**Details:**

Address mode specifies how the address and port number are determined by the SRP server and how this info is published in the Thread Network Data.

**Warnings**

- Using the `OT_SRP_SERVER_ADDRESS_MODE_UNICAST_FORCE_ADD` option will make the implementation non-compliant with the Thread specification. This option is intended for testing and specific use-cases. When selected, the SRP server, upon being enabled, will bypass the Network Data publisher and always add the "SRP/DNS unicast" entry directly to the Network Data, regardless of how many other similar entries are present.

###### otSrpServerTtlConfig (heading level 7)

`typedef struct otSrpServerTtlConfig otSrpServerTtlConfig`

**Description:**

Includes SRP server TTL configurations.

###### otSrpServerLeaseConfig (heading level 7)

`typedef struct otSrpServerLeaseConfig otSrpServerLeaseConfig`

**Description:**

Includes SRP server LEASE and KEY-LEASE configurations.

###### otSrpServerLeaseInfo (heading level 7)

`typedef struct otSrpServerLeaseInfo otSrpServerLeaseInfo`

**Description:**

Includes SRP server lease information of a host/service.

###### otSrpServerResponseCounters (heading level 7)

`typedef struct otSrpServerResponseCounters otSrpServerResponseCounters`

**Description:**

Includes the statistics of SRP server responses.

###### otSrpServerServiceUpdateHandler (heading level 7)

`typedef void(* otSrpServerServiceUpdateHandler) (otSrpServerServiceUpdateId aId, const otSrpServerHost *aHost, uint32_t aTimeout, void *aContext)`

**Description:**

Handles SRP service updates.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aId|The service update transaction ID. This ID must be passed back with `otSrpServerHandleServiceUpdateResult`.|
||[in]|aHost|A pointer to the otSrpServerHost object which contains the SRP updates. The handler should publish/un-publish the host and each service points to this host with below rules:<br/><br/>1. If the host is not deleted (indicated by `otSrpServerHostIsDeleted`), then it should be published or updated with mDNS. Otherwise, the host should be un-published (remove AAAA RRs).<br/>2. For each service points to this host, it must be un-published if the host is to be un-published. Otherwise, the handler should publish or update the service when it is not deleted (indicated by `otSrpServerServiceIsDeleted`) and un-publish it when deleted.|
||[in]|aTimeout|The maximum time in milliseconds for the handler to process the service event.|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

Is called by the SRP server to notify that a SRP host and possibly SRP services are being updated. It is important that the SRP updates are not committed until the handler returns the result by calling otSrpServerHandleServiceUpdateResult or times out after `aTimeout`.

A SRP service observer should always call otSrpServerHandleServiceUpdateResult with error code OT_ERROR_NONE immediately after receiving the update events.

A more generic handler may perform validations on the SRP host/services and rejects the SRP updates if any validation fails. For example, an Advertising Proxy should advertise (or remove) the host and services on a multicast-capable link and returns specific error code if any failure occurs.

**See Also**

- [otSrpServerSetServiceUpdateHandler](api-srp#ot-srp-server-set-service-update-handler)
- [otSrpServerHandleServiceUpdateResult](api-srp#ot-srp-server-handle-service-update-result)

###### Functions

###### otSrpClientStart (heading level 7)

`otError otSrpClientStart(otInstance *aInstance, const otSockAddr *aServerSockAddr)`

**Description:** Starts the SRP client operation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|const [otSockAddr](ot-sock-addr) *|[in]|aServerSockAddr|The socket address (IPv6 address and port number) of the SRP server.|

SRP client will prepare and send "SRP Update" message to the SRP server once all the following conditions are met:

- The SRP client is started - `otSrpClientStart()` is called.
- Host name is set - `otSrpClientSetHostName()` is called.
- At least one host IPv6 address is set - `otSrpClientSetHostAddresses()` is called.
- At least one service is added - `otSrpClientAddService()` is called.

It does not matter in which order these functions are called. When all conditions are met, the SRP client will wait for a short delay before preparing an "SRP Update" message and sending it to server. This delay allows for user to add multiple services and/or IPv6 addresses before the first SRP Update message is sent (ensuring a single SRP Update is sent containing all the info). The config `OPENTHREAD_CONFIG_SRP_CLIENT_UPDATE_TX_DELAY` specifies the delay interval.

###### otSrpClientStop (heading level 7)

`void otSrpClientStop(otInstance *aInstance)`

**Description:** Stops the SRP client operation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

Stops any further interactions with the SRP server. Note that it does not remove or clear host info and/or list of services. It marks all services to be added/removed again once the client is (re)started.

###### otSrpClientIsRunning (heading level 7)

`bool otSrpClientIsRunning(otInstance *aInstance)`

**Description:** Indicates whether the SRP client is running or not.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

**Returns**

- TRUE if the SRP client is running, FALSE otherwise.

###### otSrpClientGetServerAddress (heading level 7)

`const otSockAddr * otSrpClientGetServerAddress(otInstance *aInstance)`

**Description:** Gets the socket address (IPv6 address and port number) of the SRP server which is being used by SRP client.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

If the client is not running, the address is unspecified (all zero) with zero port number.

**Returns**

- A pointer to the SRP server's socket address (is always non-NULL).

###### otSrpClientSetCallback (heading level 7)

`void otSrpClientSetCallback(otInstance *aInstance, otSrpClientCallback aCallback, void *aContext)`

**Description:** Sets the callback to notify caller of events/changes from SRP client.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otSrpClientCallback](api-srp#ot-srp-client-callback)|[in]|aCallback|The callback to notify of events and changes. Can be NULL if not needed.|
|void *|[in]|aContext|An arbitrary context used with `aCallback`.|

The SRP client allows a single callback to be registered. So consecutive calls to this function will overwrite any previously set callback functions.

###### otSrpClientEnableAutoStartMode (heading level 7)

`void otSrpClientEnableAutoStartMode(otInstance *aInstance, otSrpClientAutoStartCallback aCallback, void *aContext)`

**Description:** Enables the auto-start mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otSrpClientAutoStartCallback](api-srp#ot-srp-client-auto-start-callback)|[in]|aCallback|A callback to notify when client is auto-started/stopped. Can be NULL if not needed.|
|void *|[in]|aContext|A context to be passed when invoking `aCallback`.|

This is only available when auto-start feature `OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE` is enabled.

Config option `OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_DEFAULT_MODE` specifies the default auto-start mode (whether it is enabled or disabled at the start of OT stack).

When auto-start is enabled, the SRP client will monitor the Thread Network Data to discover SRP servers and select the preferred server and automatically start and stop the client when an SRP server is detected.

There are three categories of Network Data entries indicating presence of SRP sever. They are preferred in the following order:

1) Preferred unicast entries where server address is included in the service data. If there are multiple options, the one with numerically lowest IPv6 address is preferred.

2) Anycast entries each having a seq number. A larger sequence number in the sense specified by Serial Number Arithmetic logic in RFC-1982 is considered more recent and therefore preferred. The largest seq number using serial number arithmetic is preferred if it is well-defined (i.e., the seq number is larger than all other seq numbers). If it is not well-defined, then the numerically largest seq number is preferred.

3) Unicast entries where the server address info is included in server data. If there are multiple options, the one with numerically lowest IPv6 address is preferred.

When there is a change in the Network Data entries, client will check that the currently selected server is still present in the Network Data and is still the preferred one. Otherwise the client will switch to the new preferred server or stop if there is none.

When the SRP client is explicitly started through a successful call to `otSrpClientStart()`, the given SRP server address in `otSrpClientStart()` will continue to be used regardless of the state of auto-start mode and whether the same SRP server address is discovered or not in the Thread Network Data. In this case, only an explicit `otSrpClientStop()` call will stop the client.

###### otSrpClientDisableAutoStartMode (heading level 7)

`void otSrpClientDisableAutoStartMode(otInstance *aInstance)`

**Description:** Disables the auto-start mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

This is only available when auto-start feature `OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE` is enabled.

Disabling the auto-start mode will not stop the client if it is already running but the client stops monitoring the Thread Network Data to verify that the selected SRP server is still present in it.

Note that a call to `otSrpClientStop()` will also disable the auto-start mode.

###### otSrpClientIsAutoStartModeEnabled (heading level 7)

`bool otSrpClientIsAutoStartModeEnabled(otInstance *aInstance)`

**Description:** Indicates the current state of auto-start mode (enabled or disabled).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

This is only available when auto-start feature `OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE` is enabled.

**Returns**

- TRUE if the auto-start mode is enabled, FALSE otherwise.

###### otSrpClientGetTtl (heading level 7)

`uint32_t otSrpClientGetTtl(otInstance *aInstance)`

**Description:** Gets the TTL value in every record included in SRP update requests.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

Note that this is the TTL requested by the SRP client. The server may choose to accept a different TTL.

By default, the TTL will equal the lease interval. Passing 0 or a value larger than the lease interval via `otSrpClientSetTtl()` will also cause the TTL to equal the lease interval.

**Returns**

- The TTL (in seconds).

###### otSrpClientSetTtl (heading level 7)

`void otSrpClientSetTtl(otInstance *aInstance, uint32_t aTtl)`

**Description:** Sets the TTL value in every record included in SRP update requests.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|uint32_t|[in]|aTtl|The TTL (in seconds). If value is zero or greater than lease interval, the TTL is set to the lease interval.|

Changing the TTL does not impact the TTL of already registered services/host-info. It only affects future SRP update messages (i.e., adding new services and/or refreshes of the existing services).

###### otSrpClientGetLeaseInterval (heading level 7)

`uint32_t otSrpClientGetLeaseInterval(otInstance *aInstance)`

**Description:** Gets the default lease interval used in SRP update requests.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

The default interval is used only for `otSrpClientService` instances with `mLease` set to zero.

Note that this is the lease duration requested by the SRP client. The server may choose to accept a different lease interval.

**Returns**

- The lease interval (in seconds).

###### otSrpClientSetLeaseInterval (heading level 7)

`void otSrpClientSetLeaseInterval(otInstance *aInstance, uint32_t aInterval)`

**Description:** Sets the default lease interval used in SRP update requests.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|uint32_t|[in]|aInterval|The lease interval (in seconds). If zero, the default value specified by `OPENTHREAD_CONFIG_SRP_CLIENT_DEFAULT_LEASE` would be used.|

The default interval is used only for `otSrpClientService` instances with `mLease` set to zero.

Changing the lease interval does not impact the accepted lease interval of already registered services/host-info. It only affects any future SRP update messages (i.e., adding new services and/or refreshes of the existing services).

###### otSrpClientGetKeyLeaseInterval (heading level 7)

`uint32_t otSrpClientGetKeyLeaseInterval(otInstance *aInstance)`

**Description:** Gets the default key lease interval used in SRP update requests.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

The default interval is used only for `otSrpClientService` instances with `mKeyLease` set to zero.

Note that this is the lease duration requested by the SRP client. The server may choose to accept a different lease interval.

**Returns**

- The key lease interval (in seconds).

###### otSrpClientSetKeyLeaseInterval (heading level 7)

`void otSrpClientSetKeyLeaseInterval(otInstance *aInstance, uint32_t aInterval)`

**Description:** Sets the default key lease interval used in SRP update requests.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|uint32_t|[in]|aInterval|The key lease interval (in seconds). If zero, the default value specified by `OPENTHREAD_CONFIG_SRP_CLIENT_DEFAULT_KEY_LEASE` would be used.|

The default interval is used only for `otSrpClientService` instances with `mKeyLease` set to zero.

Changing the lease interval does not impact the accepted lease interval of already registered services/host-info. It only affects any future SRP update messages (i.e., adding new services and/or refreshes of existing services).

###### otSrpClientGetHostInfo (heading level 7)

`const otSrpClientHostInfo * otSrpClientGetHostInfo(otInstance *aInstance)`

**Description:** Gets the host info.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

**Returns**

- A pointer to host info structure.

###### otSrpClientSetHostName (heading level 7)

`otError otSrpClientSetHostName(otInstance *aInstance, const char *aName)`

**Description:** Sets the host name label.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|const char *|[in]|aName|A pointer to host name label string (MUST NOT be NULL). Pointer to the string buffer MUST persist and remain valid and constant after return from this function.|

After a successful call to this function, `otSrpClientCallback` will be called to report the status of host info registration with SRP server.

The name string buffer pointed to by `aName` MUST persist and stay unchanged after returning from this function. OpenThread will keep the pointer to the string.

The host name can be set before client is started or after start but before host info is registered with server (host info should be in either `STATE_TO_ADD` or `STATE_REMOVED`).

###### otSrpClientEnableAutoHostAddress (heading level 7)

`otError otSrpClientEnableAutoHostAddress(otInstance *aInstance)`

**Description:** Enables auto host address mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

When enabled host IPv6 addresses are automatically set by SRP client using all the preferred unicast addresses on Thread netif excluding all link-local and mesh-local addresses. If there is no preferred address, then Mesh Local EID address is added. The SRP client will automatically re-register when/if addresses on Thread netif are updated (new addresses are added or existing addresses are removed or marked as non-preferred).

The auto host address mode can be enabled before start or during operation of SRP client except when the host info is being removed (client is busy handling a remove request from an call to `otSrpClientRemoveHostAndServices()` and host info still being in either `STATE_TO_REMOVE` or `STATE_REMOVING` states).

After auto host address mode is enabled, it can be disabled by a call to `otSrpClientSetHostAddresses()` which then explicitly sets the host addresses.

###### otSrpClientSetHostAddresses (heading level 7)

`otError otSrpClientSetHostAddresses(otInstance *aInstance, const otIp6Address *aIp6Addresses, uint8_t aNumAddresses)`

**Description:** Sets/updates the list of host IPv6 address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|const [otIp6Address](ot-ip6-address) *|[in]|aIp6Addresses|A pointer to the an array containing the host IPv6 addresses.|
|uint8_t|[in]|aNumAddresses|The number of addresses in the `aIp6Addresses` array.|

Host IPv6 addresses can be set/changed before start or during operation of SRP client (e.g. to add/remove or change a previously registered host address), except when the host info is being removed (client is busy handling a remove request from an earlier call to `otSrpClientRemoveHostAndServices()` and host info still being in either `STATE_TO_REMOVE` or `STATE_REMOVING` states).

The host IPv6 address array pointed to by `aIp6Addresses` MUST persist and remain unchanged after returning from this function (with `OT_ERROR_NONE`). OpenThread will save the pointer to the array.

After a successful call to this function, `otSrpClientCallback` will be called to report the status of the address registration with SRP server.

Calling this function disables auto host address mode if it was previously enabled from a successful call to `otSrpClientEnableAutoHostAddress()`.

###### otSrpClientAddService (heading level 7)

`otError otSrpClientAddService(otInstance *aInstance, otSrpClientService *aService)`

**Description:** Adds a service to be registered with server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otSrpClientService](ot-srp-client-service) *|[in]|aService|A pointer to a `otSrpClientService` instance to add.|

After a successful call to this function, `otSrpClientCallback` will be called to report the status of the service addition/registration with SRP server.

The `otSrpClientService` instance being pointed to by `aService` MUST persist and remain unchanged after returning from this function (with `OT_ERROR_NONE`). OpenThread will save the pointer to the service instance.

The `otSrpClientService` instance is not longer tracked by OpenThread and can be reclaimed only when

- It is removed explicitly by a call to `otSrpClientRemoveService()` or removed along with other services by a call to `otSrpClientRemoveHostAndServices() and only after the`otSrpClientCallback` is called indicating the service was removed. Or,
- A call to `otSrpClientClearHostAndServices()` which removes the host and all related services immediately.

###### otSrpClientRemoveService (heading level 7)

`otError otSrpClientRemoveService(otInstance *aInstance, otSrpClientService *aService)`

**Description:** Requests a service to be unregistered with server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otSrpClientService](ot-srp-client-service) *|[in]|aService|A pointer to a `otSrpClientService` instance to remove.|

After a successful call to this function, `otSrpClientCallback` will be called to report the status of remove request with SRP server.

The `otSrpClientService` instance being pointed to by `aService` MUST persist and remain unchanged after returning from this function (with `OT_ERROR_NONE`). OpenThread will keep the service instance during the remove process. Only after the `otSrpClientCallback` is called indicating the service instance is removed from SRP client service list and can be be freed/reused.

###### otSrpClientClearService (heading level 7)

`otError otSrpClientClearService(otInstance *aInstance, otSrpClientService *aService)`

**Description:** Clears a service, immediately removing it from the client service list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otSrpClientService](ot-srp-client-service) *|[in]|aService|A pointer to a `otSrpClientService` instance to delete.|

Unlike `otSrpClientRemoveService()` which sends an update message to the server to remove the service, this function clears the service from the client's service list without any interaction with the server. On a successful call to this function, the `otSrpClientCallback` will NOT be called and the `aService` entry can be reclaimed and re-used by the caller immediately.

Can be used along with a subsequent call to `otSrpClientAddService()` (potentially reusing the same `aService` entry with the same service and instance names) to update some of the parameters in an existing service.

###### otSrpClientGetServices (heading level 7)

`const otSrpClientService * otSrpClientGetServices(otInstance *aInstance)`

**Description:** Gets the list of services being managed by client.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

**Returns**

- A pointer to the head of linked-list of all services or NULL if the list is empty.

###### otSrpClientRemoveHostAndServices (heading level 7)

`otError otSrpClientRemoveHostAndServices(otInstance *aInstance, bool aRemoveKeyLease, bool aSendUnregToServer)`

**Description:** Starts the remove process of the host info and all services.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|bool|[in]|aRemoveKeyLease|A boolean indicating whether or not the host key lease should also be removed.|
|bool|[in]|aSendUnregToServer|A boolean indicating whether to send update to server when host info is not registered.|

After returning from this function, `otSrpClientCallback` will be called to report the status of remove request with SRP server.

If the host info is to be permanently removed from server, `aRemoveKeyLease` should be set to `true` which removes the key lease associated with host on server. Otherwise, the key lease record is kept as before, which ensures that the server holds the host name in reserve for when the client is once again able to provide and register its service(s).

The `aSendUnregToServer` determines the behavior when the host info is not yet registered with the server. If `aSendUnregToServer` is set to `false` (which is the default/expected value) then the SRP client will immediately remove the host info and services without sending an update message to server (no need to update the server if nothing is yet registered with it). If `aSendUnregToServer` is set to `true` then the SRP client will send an update message to the server. Note that if the host info is registered then the value of `aSendUnregToServer` does not matter and the SRP client will always send an update message to server requesting removal of all info.

One situation where `aSendUnregToServer` can be useful is on a device reset/reboot, caller may want to remove any previously registered services with the server. In this case, caller can `otSrpClientSetHostName()` and then request `otSrpClientRemoveHostAndServices()` with `aSendUnregToServer` as `true`.

###### otSrpClientClearHostAndServices (heading level 7)

`void otSrpClientClearHostAndServices(otInstance *aInstance)`

**Description:** Clears all host info and all the services.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

Unlike `otSrpClientRemoveHostAndServices()` which sends an update message to the server to remove all the info, this function clears all the info immediately without any interaction with the server.

###### otSrpClientGetDomainName (heading level 7)

`const char * otSrpClientGetDomainName(otInstance *aInstance)`

**Description:** Gets the domain name being used by SRP client.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

Requires `OPENTHREAD_CONFIG_SRP_CLIENT_DOMAIN_NAME_API_ENABLE` to be enabled.

If domain name is not set, "default.service.arpa" will be used.

**Returns**

- The domain name string.

###### otSrpClientSetDomainName (heading level 7)

`otError otSrpClientSetDomainName(otInstance *aInstance, const char *aName)`

**Description:** Sets the domain name to be used by SRP client.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|const char *|[in]|aName|A pointer to the domain name string. If NULL sets it to default "default.service.arpa".|

Requires `OPENTHREAD_CONFIG_SRP_CLIENT_DOMAIN_NAME_API_ENABLE` to be enabled.

If not set "default.service.arpa" will be used.

The name string buffer pointed to by `aName` MUST persist and stay unchanged after returning from this function. OpenThread will keep the pointer to the string.

The domain name can be set before client is started or after start but before host info is registered with server (host info should be in either `STATE_TO_ADD` or `STATE_TO_REMOVE`).

###### otSrpClientItemStateToString (heading level 7)

`const char * otSrpClientItemStateToString(otSrpClientItemState aItemState)`

**Description:** Converts a `otSrpClientItemState` to a string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otSrpClientItemState](api-srp#ot-srp-client-item-state)|[in]|aItemState|An item state.|

**Returns**

- A string representation of `aItemState`.

###### otSrpClientSetServiceKeyRecordEnabled (heading level 7)

`void otSrpClientSetServiceKeyRecordEnabled(otInstance *aInstance, bool aEnabled)`

**Description:** Enables/disables "service key record inclusion" mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|bool|[in]|aEnabled|TRUE to enable, FALSE to disable the "service key record inclusion" mode.|

When enabled, SRP client will include KEY record in Service Description Instructions in the SRP update messages that it sends.

Is available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` configuration is enabled.

**Note**

- KEY record is optional in Service Description Instruction (it is required and always included in the Host Description Instruction). The default behavior of SRP client is to not include it. This function is intended to override the default behavior for testing only.

###### otSrpClientIsServiceKeyRecordEnabled (heading level 7)

`bool otSrpClientIsServiceKeyRecordEnabled(otInstance *aInstance)`

**Description:** Indicates whether the "service key record inclusion" mode is enabled or disabled.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

Is available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` configuration is enabled.

**Returns**

- TRUE if "service key record inclusion" mode is enabled, FALSE otherwise.

###### otSrpClientBuffersGetHostNameString (heading level 7)

`char * otSrpClientBuffersGetHostNameString(otInstance *aInstance, uint16_t *aSize)`

**Description:** Gets the string buffer to use for SRP client host name.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|uint16_t *|[out]|aSize|Pointer to a variable to return the size (number of bytes) of the string buffer (MUST NOT be NULL).|

**Returns**

- A pointer to char buffer to use for SRP client host name.

###### otSrpClientBuffersGetHostAddressesArray (heading level 7)

`otIp6Address * otSrpClientBuffersGetHostAddressesArray(otInstance *aInstance, uint8_t *aArrayLength)`

**Description:** Gets the array of IPv6 address entries to use as SRP client host address list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|uint8_t *|[out]|aArrayLength|Pointer to a variable to return the array length i.e., number of IPv6 address entries in the array (MUST NOT be NULL).|

**Returns**

- A pointer to an array of `otIp6Address` entries (number of entries is returned in `aArrayLength`).

###### otSrpClientBuffersAllocateService (heading level 7)

`otSrpClientBuffersServiceEntry * otSrpClientBuffersAllocateService(otInstance *aInstance)`

**Description:** Allocates a new service entry from the pool.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

The returned service entry instance will be initialized as follows:

- `mService.mName` will point to an allocated string buffer which can be retrieved using the function `otSrpClientBuffersGetServiceEntryServiceNameString()`.
- `mService.mInstanceName` will point to an allocated string buffer which can be retrieved using the function `otSrpClientBuffersGetServiceEntryInstanceNameString()`.
- `mService.mSubTypeLabels` points to an array that is returned from `otSrpClientBuffersGetSubTypeLabelsArray()`.
- `mService.mTxtEntries` will point to `mTxtEntry`.
- `mService.mNumTxtEntries` will be set to one.
- Other `mService` fields (port, priority, weight) are set to zero.
- `mTxtEntry.mKey` is set to NULL (value is treated as already encoded).
- `mTxtEntry.mValue` will point to an allocated buffer which can be retrieved using the function `otSrpClientBuffersGetServiceEntryTxtBuffer()`.
- `mTxtEntry.mValueLength` is set to zero.
- All related data/string buffers and arrays are cleared to all zero.

**Returns**

- A pointer to the newly allocated service entry or NULL if not more entry available in the pool.

###### otSrpClientBuffersFreeService (heading level 7)

`void otSrpClientBuffersFreeService(otInstance *aInstance, otSrpClientBuffersServiceEntry *aService)`

**Description:** Frees a previously allocated service entry.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otSrpClientBuffersServiceEntry](ot-srp-client-buffers-service-entry) *|[in]|aService|A pointer to the service entry to free (MUST NOT be NULL).|

The `aService` MUST be previously allocated using `otSrpClientBuffersAllocateService()` and not yet freed. Otherwise the behavior of this function is undefined.

###### otSrpClientBuffersFreeAllServices (heading level 7)

`void otSrpClientBuffersFreeAllServices(otInstance *aInstance)`

**Description:** Frees all previously allocated service entries.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

###### otSrpClientBuffersGetServiceEntryServiceNameString (heading level 7)

`char * otSrpClientBuffersGetServiceEntryServiceNameString(otSrpClientBuffersServiceEntry *aEntry, uint16_t *aSize)`

**Description:** Gets the string buffer for service name from a service entry.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otSrpClientBuffersServiceEntry](ot-srp-client-buffers-service-entry) *|[in]|aEntry|A pointer to a previously allocated service entry (MUST NOT be NULL).|
|uint16_t *|[out]|aSize|A pointer to a variable to return the size (number of bytes) of the string buffer (MUST NOT be NULL).|

**Returns**

- A pointer to the string buffer.

###### otSrpClientBuffersGetServiceEntryInstanceNameString (heading level 7)

`char * otSrpClientBuffersGetServiceEntryInstanceNameString(otSrpClientBuffersServiceEntry *aEntry, uint16_t *aSize)`

**Description:** Gets the string buffer for service instance name from a service entry.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otSrpClientBuffersServiceEntry](ot-srp-client-buffers-service-entry) *|[in]|aEntry|A pointer to a previously allocated service entry (MUST NOT be NULL).|
|uint16_t *|[out]|aSize|A pointer to a variable to return the size (number of bytes) of the string buffer (MUST NOT be NULL).|

**Returns**

- A pointer to the string buffer.

###### otSrpClientBuffersGetServiceEntryTxtBuffer (heading level 7)

`uint8_t * otSrpClientBuffersGetServiceEntryTxtBuffer(otSrpClientBuffersServiceEntry *aEntry, uint16_t *aSize)`

**Description:** Gets the buffer for TXT record from a service entry.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otSrpClientBuffersServiceEntry](ot-srp-client-buffers-service-entry) *|[in]|aEntry|A pointer to a previously allocated service entry (MUST NOT be NULL).|
|uint16_t *|[out]|aSize|A pointer to a variable to return the size (number of bytes) of the buffer (MUST NOT be NULL).|

**Returns**

- A pointer to the buffer.

###### otSrpClientBuffersGetSubTypeLabelsArray (heading level 7)

`const char ** otSrpClientBuffersGetSubTypeLabelsArray(otSrpClientBuffersServiceEntry *aEntry, uint16_t *aArrayLength)`

**Description:** Gets the array for service subtype labels from the service entry.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otSrpClientBuffersServiceEntry](ot-srp-client-buffers-service-entry) *|[in]|aEntry|A pointer to a previously allocated service entry (MUST NOT be NULL).|
|uint16_t *|[out]|aArrayLength|A pointer to a variable to return the array length (MUST NOT be NULL).|

**Returns**

- A pointer to the array.

###### otSrpServerGetDomain (heading level 7)

`const char * otSrpServerGetDomain(otInstance *aInstance)`

**Description:** Returns the domain authorized to the SRP server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

If the domain if not set by SetDomain, "default.service.arpa." will be returned. A trailing dot is always appended even if the domain is set without it.

**Returns**

- A pointer to the dot-joined domain string.

###### otSrpServerSetDomain (heading level 7)

`otError otSrpServerSetDomain(otInstance *aInstance, const char *aDomain)`

**Description:** Sets the domain on the SRP server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const char *|[in]|aDomain|The domain to be set. MUST NOT be NULL.|

A trailing dot will be appended to `aDomain` if it is not already there. Should only be called before the SRP server is enabled.

###### otSrpServerGetState (heading level 7)

`otSrpServerState otSrpServerGetState(otInstance *aInstance)`

**Description:** Returns the state of the SRP server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The current state of the SRP server.

###### otSrpServerGetPort (heading level 7)

`uint16_t otSrpServerGetPort(otInstance *aInstance)`

**Description:** Returns the port the SRP server is listening to.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The port of the SRP server. It returns 0 if the server is not running.

###### otSrpServerGetAddressMode (heading level 7)

`otSrpServerAddressMode otSrpServerGetAddressMode(otInstance *aInstance)`

**Description:** Returns the address mode being used by the SRP server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The SRP server's address mode.

###### otSrpServerSetAddressMode (heading level 7)

`otError otSrpServerSetAddressMode(otInstance *aInstance, otSrpServerAddressMode aMode)`

**Description:** Sets the address mode to be used by the SRP server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otSrpServerAddressMode](api-srp#ot-srp-server-address-mode)|[in]|aMode|The address mode to use.|

###### otSrpServerGetAnycastModeSequenceNumber (heading level 7)

`uint8_t otSrpServerGetAnycastModeSequenceNumber(otInstance *aInstance)`

**Description:** Returns the sequence number used with anycast address mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

The sequence number is included in "DNS/SRP Service Anycast Address" entry published in the Network Data.

**Returns**

- The anycast sequence number.

###### otSrpServerSetAnycastModeSequenceNumber (heading level 7)

`otError otSrpServerSetAnycastModeSequenceNumber(otInstance *aInstance, uint8_t aSequenceNumber)`

**Description:** Sets the sequence number used with anycast address mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aSequenceNumber|The sequence number to use.|

###### otSrpServerSetEnabled (heading level 7)

`void otSrpServerSetEnabled(otInstance *aInstance, bool aEnabled)`

**Description:** Enables/disables the SRP server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnabled|A boolean to enable/disable the SRP server.|

On a Border Router, it is recommended to use `otSrpServerSetAutoEnableMode()` instead.

###### otSrpServerSetAutoEnableMode (heading level 7)

`void otSrpServerSetAutoEnableMode(otInstance *aInstance, bool aEnabled)`

**Description:** Enables/disables the auto-enable mode on SRP server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnabled|A boolean to enable/disable the auto-enable mode.|

Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` feature.

When this mode is enabled, the Border Routing Manager controls if/when to enable or disable the SRP server. SRP sever is auto-enabled if/when Border Routing is started and it is done with the initial prefix and route configurations (when the OMR and on-link prefixes are determined, advertised in emitted Router Advertisement message on infrastructure side and published in the Thread Network Data). The SRP server is auto-disabled if/when BR is stopped (e.g., if the infrastructure network interface is brought down or if BR gets detached).

This mode can be disabled by a `otSrpServerSetAutoEnableMode()` call with `aEnabled` set to `false` or if the SRP server is explicitly enabled or disabled by a call to `otSrpServerSetEnabled()` function. Disabling auto-enable mode using `otSrpServerSetAutoEnableMode(false)` will not change the current state of SRP sever (e.g., if it is enabled it stays enabled).

###### otSrpServerIsAutoEnableMode (heading level 7)

`bool otSrpServerIsAutoEnableMode(otInstance *aInstance)`

**Description:** Indicates whether the auto-enable mode is enabled or disabled.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` feature.

###### otSrpServerEnableFastStartMode (heading level 7)

`otError otSrpServerEnableFastStartMode(otInstance *aInstance)`

**Description:** Enables the "Fast Start Mode" on the SRP server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

Requires the `OPENTHREAD_CONFIG_SRP_SERVER_FAST_START_MODE_ENABLE` feature to be enabled.

The Fast Start Mode is designed for scenarios where a device, often a mobile device, needs to act as a provisional SRP server (e.g., functioning as a temporary Border Router). The SRP server function is enabled only if no other Border Routers (BRs) are already providing the SRP service within the Thread network. A common use case is a mobile device joining a Thread network where it may be the first, or only, BR. Importantly, Fast Start Mode allows the device to quickly start its SRP server functionality upon joining the network, allowing other Thread devices to quickly connect and register their services without the typical delays associated with standard Border Router initialization (and SRP server startup).

When Fast Start Mode is enabled, the SRP server manages when to start or stop based on the presence of other BRs, following this process:

- Upon initial attachment to the Thread network, the device immediately inspects the received Network Data for any existing "SRP/DNS" entries. These entries indicate the presence of other active BRs providing SRP server service:  
  - If no "SRP/DNS" entries from other BRs are found, the device immediately enables its own SRP server. This activation uses `OT_SRP_SERVER_ADDRESS_MODE_UNICAST_FORCE_ADD`, which bypasses the usual delay associated with the standard Network Data publisher, directly adding its own "SRP/DNS unicast" entry to the Network Data.  
  - If "SRP/DNS" entries from other BRs are detected, the device will not enable its SRP server, deferring to the existing ones.
- After starting its SRP server in Fast Start Mode, the device continuously monitors the Network Data. If, at any point, new "SRP/DNS" entries appear (indicating that another BR has become active), the device automatically disables its own SRP server functionality, relinquishing the role to the newly available BR.

The Fast Start Mode can be enabled when the device is in the detached or disabled state, the SRP server is currently disabled, and "auto-enable mode" is not in use (i.e., `otSrpServerIsAutoEnableMode()` returns `false`).

After successfully enabling Fast Start Mode, it can be disabled either by a call to `otSrpServerSetEnabled()`, explicitly enabling or disabling the SRP server, or by a call to `otSrpServerSetAutoEnableMode()`, enabling or disabling the auto-enable mode. If the Fast Start Mode (while active) enables the SRP server, upon disabling Fast Start Mode (regardless of how it is done), the SRP server will also be stopped, and the use of the `OT_SRP_SERVER_ADDRESS_MODE_UNICAST_FORCE_ADD` address mode will be stopped, and the address mode will be automatically reverted back to its previous setting before Fast Start Mode was enabled.

###### otSrpServerIsFastStartModeEnabled (heading level 7)

`bool otSrpServerIsFastStartModeEnabled(otInstance *aInstance)`

**Description:** Indicates whether the Fast Start Mode is enabled or disabled.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Requires `OPENTHREAD_CONFIG_SRP_SERVER_FAST_START_MODE_ENABLE` feature to be enabled.

###### otSrpServerGetTtlConfig (heading level 7)

`void otSrpServerGetTtlConfig(otInstance *aInstance, otSrpServerTtlConfig *aTtlConfig)`

**Description:** Returns SRP server TTL configuration.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otSrpServerTtlConfig](ot-srp-server-ttl-config) *|[out]|aTtlConfig|A pointer to an `otSrpServerTtlConfig` instance.|

###### otSrpServerSetTtlConfig (heading level 7)

`otError otSrpServerSetTtlConfig(otInstance *aInstance, const otSrpServerTtlConfig *aTtlConfig)`

**Description:** Sets SRP server TTL configuration.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otSrpServerTtlConfig](ot-srp-server-ttl-config) *|[in]|aTtlConfig|A pointer to an `otSrpServerTtlConfig` instance.|

The granted TTL will always be no greater than the max lease interval configured via `otSrpServerSetLeaseConfig()`, regardless of the minimum and maximum TTL configuration.

###### otSrpServerGetLeaseConfig (heading level 7)

`void otSrpServerGetLeaseConfig(otInstance *aInstance, otSrpServerLeaseConfig *aLeaseConfig)`

**Description:** Returns SRP server LEASE and KEY-LEASE configurations.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otSrpServerLeaseConfig](ot-srp-server-lease-config) *|[out]|aLeaseConfig|A pointer to an `otSrpServerLeaseConfig` instance.|

###### otSrpServerSetLeaseConfig (heading level 7)

`otError otSrpServerSetLeaseConfig(otInstance *aInstance, const otSrpServerLeaseConfig *aLeaseConfig)`

**Description:** Sets SRP server LEASE and KEY-LEASE configurations.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otSrpServerLeaseConfig](ot-srp-server-lease-config) *|[in]|aLeaseConfig|A pointer to an `otSrpServerLeaseConfig` instance.|

When a non-zero LEASE time is requested from a client, the granted value will be limited in range [aMinLease, aMaxLease]; and a non-zero KEY-LEASE will be granted in range [aMinKeyLease, aMaxKeyLease]. For zero LEASE or KEY-LEASE time, zero will be granted.

###### otSrpServerSetServiceUpdateHandler (heading level 7)

`void otSrpServerSetServiceUpdateHandler(otInstance *aInstance, otSrpServerServiceUpdateHandler aServiceHandler, void *aContext)`

**Description:** Sets the SRP service updates handler on SRP server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otSrpServerServiceUpdateHandler](api-srp#ot-srp-server-service-update-handler)|[in]|aServiceHandler|A pointer to a service handler. Use NULL to remove the handler.|
|void *|[in]|aContext|A pointer to arbitrary context information. May be NULL if not used.|

###### otSrpServerHandleServiceUpdateResult (heading level 7)

`void otSrpServerHandleServiceUpdateResult(otInstance *aInstance, otSrpServerServiceUpdateId aId, otError aError)`

**Description:** Reports the result of processing a SRP update to the SRP server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otSrpServerServiceUpdateId](api-srp#ot-srp-server-service-update-id)|[in]|aId|The service update transaction ID. This should be the same ID provided via `otSrpServerServiceUpdateHandler`.|
|[otError](api-error#ot-error)|[in]|aError|An error to be returned to the SRP server. Use OT_ERROR_DUPLICATED to represent DNS name conflicts.|

The Service Update Handler should call this function to return the result of its processing of a SRP update.

###### otSrpServerGetNextHost (heading level 7)

`const otSrpServerHost * otSrpServerGetNextHost(otInstance *aInstance, const otSrpServerHost *aHost)`

**Description:** Returns the next registered host on the SRP server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otSrpServerHost](api-srp#ot-srp-server-host) *|[in]|aHost|A pointer to current host; use NULL to get the first host.|

**Returns**

- A pointer to the registered host. NULL, if no more hosts can be found.

###### otSrpServerGetResponseCounters (heading level 7)

`const otSrpServerResponseCounters * otSrpServerGetResponseCounters(otInstance *aInstance)`

**Description:** Returns the response counters of the SRP server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- A pointer to the response counters of the SRP server.

###### otSrpServerHostIsDeleted (heading level 7)

`bool otSrpServerHostIsDeleted(const otSrpServerHost *aHost)`

**Description:** Tells if the SRP service host has been deleted.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerHost](api-srp#ot-srp-server-host) *|[in]|aHost|A pointer to the SRP service host.|

A SRP service host can be deleted but retains its name for future uses. In this case, the host instance is not removed from the SRP server/registry.

**Returns**

- TRUE if the host has been deleted, FALSE if not.

###### otSrpServerHostGetFullName (heading level 7)

`const char * otSrpServerHostGetFullName(const otSrpServerHost *aHost)`

**Description:** Returns the full name of the host.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerHost](api-srp#ot-srp-server-host) *|[in]|aHost|A pointer to the SRP service host.|

**Returns**

- A pointer to the null-terminated host name string.

###### otSrpServerHostMatchesFullName (heading level 7)

`bool otSrpServerHostMatchesFullName(const otSrpServerHost *aHost, const char *aFullName)`

**Description:** Indicates whether the host matches a given host name.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerHost](api-srp#ot-srp-server-host) *|[in]|aHost|A pointer to the SRP service host.|
|const char *|[in]|aFullName|A full host name.|

DNS name matches are performed using a case-insensitive string comparison (i.e., "Abc" and "aBc" are considered to be the same).

###### otSrpServerHostGetAddresses (heading level 7)

`const otIp6Address * otSrpServerHostGetAddresses(const otSrpServerHost *aHost, uint8_t *aAddressesNum)`

**Description:** Returns the addresses of given host.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerHost](api-srp#ot-srp-server-host) *|[in]|aHost|A pointer to the SRP service host.|
|uint8_t *|[out]|aAddressesNum|A pointer to where we should output the number of the addresses to.|

**Returns**

- A pointer to the array of IPv6 Address.

###### otSrpServerHostGetLeaseInfo (heading level 7)

`void otSrpServerHostGetLeaseInfo(const otSrpServerHost *aHost, otSrpServerLeaseInfo *aLeaseInfo)`

**Description:** Returns the LEASE and KEY-LEASE information of a given host.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerHost](api-srp#ot-srp-server-host) *|[in]|aHost|A pointer to the SRP server host.|
|[otSrpServerLeaseInfo](ot-srp-server-lease-info) *|[out]|aLeaseInfo|A pointer to where to output the LEASE and KEY-LEASE information.|

###### otSrpServerHostGetNextService (heading level 7)

`const otSrpServerService * otSrpServerHostGetNextService(const otSrpServerHost *aHost, const otSrpServerService *aService)`

**Description:** Returns the next service of given host.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerHost](api-srp#ot-srp-server-host) *|[in]|aHost|A pointer to the SRP service host.|
|const [otSrpServerService](api-srp#ot-srp-server-service) *|[in]|aService|A pointer to current SRP service instance; use NULL to get the first service.|

**Returns**

- A pointer to the next service or NULL if there is no more services.

###### otSrpServerServiceIsDeleted (heading level 7)

`bool otSrpServerServiceIsDeleted(const otSrpServerService *aService)`

**Description:** Indicates whether or not the SRP service has been deleted.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerService](api-srp#ot-srp-server-service) *|[in]|aService|A pointer to the SRP service.|

A SRP service can be deleted but retains its name for future uses. In this case, the service instance is not removed from the SRP server/registry. It is guaranteed that all services are deleted if the host is deleted.

**Returns**

- TRUE if the service has been deleted, FALSE if not.

###### otSrpServerServiceGetInstanceName (heading level 7)

`const char * otSrpServerServiceGetInstanceName(const otSrpServerService *aService)`

**Description:** Returns the full service instance name of the service.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerService](api-srp#ot-srp-server-service) *|[in]|aService|A pointer to the SRP service.|

**Returns**

- A pointer to the null-terminated service instance name string.

###### otSrpServerServiceMatchesInstanceName (heading level 7)

`bool otSrpServerServiceMatchesInstanceName(const otSrpServerService *aService, const char *aInstanceName)`

**Description:** Indicates whether this service matches a given service instance name.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerService](api-srp#ot-srp-server-service) *|[in]|aService|A pointer to the SRP service.|
|const char *|[in]|aInstanceName|The service instance name.|

DNS name matches are performed using a case-insensitive string comparison (i.e., "Abc" and "aBc" are considered to be the same).

###### otSrpServerServiceGetInstanceLabel (heading level 7)

`const char * otSrpServerServiceGetInstanceLabel(const otSrpServerService *aService)`

**Description:** Returns the service instance label (first label in instance name) of the service.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerService](api-srp#ot-srp-server-service) *|[in]|aService|A pointer to the SRP service.|

**Returns**

- A pointer to the null-terminated service instance label string..

###### otSrpServerServiceGetServiceName (heading level 7)

`const char * otSrpServerServiceGetServiceName(const otSrpServerService *aService)`

**Description:** Returns the full service name of the service.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerService](api-srp#ot-srp-server-service) *|[in]|aService|A pointer to the SRP service.|

**Returns**

- A pointer to the null-terminated service name string.

###### otSrpServerServiceMatchesServiceName (heading level 7)

`bool otSrpServerServiceMatchesServiceName(const otSrpServerService *aService, const char *aServiceName)`

**Description:** Indicates whether this service matches a given service name.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerService](api-srp#ot-srp-server-service) *|[in]|aService|A pointer to the SRP service.|
|const char *|[in]|aServiceName|The service name.|

DNS name matches are performed using a case-insensitive string comparison (i.e., "Abc" and "aBc" are considered to be the same).

###### otSrpServerServiceGetNumberOfSubTypes (heading level 7)

`uint16_t otSrpServerServiceGetNumberOfSubTypes(const otSrpServerService *aService)`

**Description:** Gets the number of sub-types of the service.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerService](api-srp#ot-srp-server-service) *|[in]|aService|A pointer to the SRP service.|

**Returns**

- The number of sub-types of `aService`.

###### otSrpServerServiceGetSubTypeServiceNameAt (heading level 7)

`const char * otSrpServerServiceGetSubTypeServiceNameAt(const otSrpServerService *aService, uint16_t aIndex)`

**Description:** Gets the sub-type service name (full name) of the service at a given index.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerService](api-srp#ot-srp-server-service) *|[in]|aService|A pointer to the SRP service.|
|uint16_t|[in]|aIndex|The index to get.|

The full service name for a sub-type service follows "<sub-label>._sub.<service-labels>.<domain>.".

**Returns**

- A pointer to sub-type service name at `aIndex`, or `NULL` if no sub-type at this index.

###### otSrpServerServiceHasSubTypeServiceName (heading level 7)

`bool otSrpServerServiceHasSubTypeServiceName(const otSrpServerService *aService, const char *aSubTypeServiceName)`

**Description:** Indicates whether or not the service has a given sub-type.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerService](api-srp#ot-srp-server-service) *|[in]|aService|A pointer to the SRP service.|
|const char *|[in]|aSubTypeServiceName|The sub-type service name (full name) to check.|

DNS name matches are performed using a case-insensitive string comparison (i.e., "Abc" and "aBc" are considered to be the same).

###### otSrpServerParseSubTypeServiceName (heading level 7)

`otError otSrpServerParseSubTypeServiceName(const char *aSubTypeServiceName, char *aLabel, uint8_t aLabelSize)`

**Description:** Parses a sub-type service name (full name) and extracts the sub-type label.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aSubTypeServiceName|A sub-type service name (full name).|
|char *|[out]|aLabel|A pointer to a buffer to copy the extracted sub-type label.|
|uint8_t|[in]|aLabelSize|Maximum size of `aLabel` buffer.|

The full service name for a sub-type service follows "<sub-label>._sub.<service-labels>.<domain>.".

###### otSrpServerServiceGetPort (heading level 7)

`uint16_t otSrpServerServiceGetPort(const otSrpServerService *aService)`

**Description:** Returns the port of the service instance.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerService](api-srp#ot-srp-server-service) *|[in]|aService|A pointer to the SRP service.|

**Returns**

- The port of the service.

###### otSrpServerServiceGetWeight (heading level 7)

`uint16_t otSrpServerServiceGetWeight(const otSrpServerService *aService)`

**Description:** Returns the weight of the service instance.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerService](api-srp#ot-srp-server-service) *|[in]|aService|A pointer to the SRP service.|

**Returns**

- The weight of the service.

###### otSrpServerServiceGetPriority (heading level 7)

`uint16_t otSrpServerServiceGetPriority(const otSrpServerService *aService)`

**Description:** Returns the priority of the service instance.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerService](api-srp#ot-srp-server-service) *|[in]|aService|A pointer to the SRP service.|

**Returns**

- The priority of the service.

###### otSrpServerServiceGetTtl (heading level 7)

`uint32_t otSrpServerServiceGetTtl(const otSrpServerService *aService)`

**Description:** Returns the TTL of the service instance.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerService](api-srp#ot-srp-server-service) *|[in]|aService|A pointer to the SRP service.|

**Returns**

- The TTL of the service instance..

###### otSrpServerServiceGetTxtData (heading level 7)

`const uint8_t * otSrpServerServiceGetTxtData(const otSrpServerService *aService, uint16_t *aDataLength)`

**Description:** Returns the TXT record data of the service instance.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerService](api-srp#ot-srp-server-service) *|[in]|aService|A pointer to the SRP service.|
|uint16_t *|[out]|aDataLength|A pointer to return the TXT record data length. MUST NOT be NULL.|

**Returns**

- A pointer to the buffer containing the TXT record data (the TXT data length is returned in `aDataLength`).

###### otSrpServerServiceGetHost (heading level 7)

`const otSrpServerHost * otSrpServerServiceGetHost(const otSrpServerService *aService)`

**Description:** Returns the host which the service instance reside on.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerService](api-srp#ot-srp-server-service) *|[in]|aService|A pointer to the SRP service.|

**Returns**

- A pointer to the host instance.

###### otSrpServerServiceGetLeaseInfo (heading level 7)

`void otSrpServerServiceGetLeaseInfo(const otSrpServerService *aService, otSrpServerLeaseInfo *aLeaseInfo)`

**Description:** Returns the LEASE and KEY-LEASE information of a given service.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSrpServerService](api-srp#ot-srp-server-service) *|[in]|aService|A pointer to the SRP server service.|
|[otSrpServerLeaseInfo](ot-srp-server-lease-info) *|[out]|aLeaseInfo|A pointer to where to output the LEASE and KEY-LEASE information.|

Represents an SRP client host info. 

###### Public Attributes (heading level 7)

###### mName (heading level 8)

```
const char* otSrpClientHostInfo::mName
```

**Description:** Host name (label) string (NULL if not yet set).

###### mAddresses (heading level 8)

```
const otIp6Address* otSrpClientHostInfo::mAddresses
```

**Description:** Array of host IPv6 addresses (NULL if not set or auto address is enabled).

###### mNumAddresses (heading level 8)

```
uint8_t otSrpClientHostInfo::mNumAddresses
```

**Description:** Number of IPv6 addresses in `mAddresses` array.

###### mAutoAddress (heading level 8)

```
bool otSrpClientHostInfo::mAutoAddress
```

**Description:** Indicates whether auto address mode is enabled or not.

###### mState (heading level 8)

```
otSrpClientItemState otSrpClientHostInfo::mState
```

**Description:** Host info state.

Represents an SRP client service. 

The values in this structure, including the string buffers for the names and the TXT record entries, MUST persist and stay constant after an instance of this structure is passed to OpenThread from `otSrpClientAddService()` or `otSrpClientRemoveService()`.

The `mState`, `mData`, `mNext` fields are used/managed by OT core only. Their value is ignored when an instance of `otSrpClientService` is passed in `otSrpClientAddService()` or `otSrpClientRemoveService()` or other functions. The caller does not need to set these fields.

The `mLease` and `mKeyLease` fields specify the desired lease and key lease intervals for this service. Zero value indicates that the interval is unspecified and then the default lease or key lease intervals from `otSrpClientGetLeaseInterval()` and `otSrpClientGetKeyLeaseInterval()` are used for this service. If the key lease interval (whether set explicitly or determined from the default) is shorter than the lease interval for a service, SRP client will re-use the lease interval value for key lease interval as well. For example, if in service `mLease` is explicitly set to 2 days and `mKeyLease` is set to zero and default key lease is set to 1 day, then when registering this service, the requested key lease for this service is also set to 2 days. 

###### Public Attributes (heading level 7)

###### mName (heading level 8)

```
const char* otSrpClientService::mName
```

**Description:** The service labels (e.g., "_mt._udp", not the full domain name).

###### mInstanceName (heading level 8)

```
const char* otSrpClientService::mInstanceName
```

**Description:** The service instance name label (not the full name).

###### mSubTypeLabels (heading level 8)

```
const char* const* otSrpClientService::mSubTypeLabels
```

**Description:** Array of sub-type labels (must end with `NULL` or can be `NULL`).

###### mTxtEntries (heading level 8)

```
const otDnsTxtEntry* otSrpClientService::mTxtEntries
```

**Description:** Array of TXT entries (`mNumTxtEntries` gives num of entries).

###### mPort (heading level 8)

```
uint16_t otSrpClientService::mPort
```

**Description:** The service port number.

###### mPriority (heading level 8)

```
uint16_t otSrpClientService::mPriority
```

**Description:** The service priority.

###### mWeight (heading level 8)

```
uint16_t otSrpClientService::mWeight
```

**Description:** The service weight.

###### mNumTxtEntries (heading level 8)

```
uint8_t otSrpClientService::mNumTxtEntries
```

**Description:** Number of entries in the `mTxtEntries` array.

###### mState (heading level 8)

```
otSrpClientItemState otSrpClientService::mState
```

**Description:** Service state (managed by OT core).

###### mData (heading level 8)

```
uint32_t otSrpClientService::mData
```

**Description:** Internal data (used by OT core).

###### mNext (heading level 8)

```
struct otSrpClientService* otSrpClientService::mNext
```

**Description:** Pointer to next entry in a linked-list (managed by OT core).

###### mLease (heading level 8)

```
uint32_t otSrpClientService::mLease
```

**Description:** Desired lease interval in sec - zero to use default.

###### mKeyLease (heading level 8)

```
uint32_t otSrpClientService::mKeyLease
```

**Description:** Desired key lease interval in sec - zero to use default.

Represents a SRP client service pool entry. 

###### Public Attributes (heading level 7)

###### mService (heading level 8)

```
otSrpClientService otSrpClientBuffersServiceEntry::mService
```

**Description:** The SRP client service structure.

###### mTxtEntry (heading level 8)

```
otDnsTxtEntry otSrpClientBuffersServiceEntry::mTxtEntry
```

**Description:** The SRP client TXT entry.

Includes SRP server TTL configurations. 

###### Public Attributes (heading level 7)

###### mMinTtl (heading level 8)

```
uint32_t otSrpServerTtlConfig::mMinTtl
```

**Description:** The minimum TTL in seconds.

###### mMaxTtl (heading level 8)

```
uint32_t otSrpServerTtlConfig::mMaxTtl
```

**Description:** The maximum TTL in seconds.

Includes SRP server LEASE and KEY-LEASE configurations. 

###### Public Attributes (heading level 7)

###### mMinLease (heading level 8)

```
uint32_t otSrpServerLeaseConfig::mMinLease
```

**Description:** The minimum LEASE interval in seconds.

###### mMaxLease (heading level 8)

```
uint32_t otSrpServerLeaseConfig::mMaxLease
```

**Description:** The maximum LEASE interval in seconds.

###### mMinKeyLease (heading level 8)

```
uint32_t otSrpServerLeaseConfig::mMinKeyLease
```

**Description:** The minimum KEY-LEASE interval in seconds.

###### mMaxKeyLease (heading level 8)

```
uint32_t otSrpServerLeaseConfig::mMaxKeyLease
```

**Description:** The maximum KEY-LEASE interval in seconds.

Includes SRP server lease information of a host/service. 

###### Public Attributes (heading level 7)

###### mLease (heading level 8)

```
uint32_t otSrpServerLeaseInfo::mLease
```

**Description:** The lease time of a host/service in milliseconds.

###### mKeyLease (heading level 8)

```
uint32_t otSrpServerLeaseInfo::mKeyLease
```

**Description:** The key lease time of a host/service in milliseconds.

###### mRemainingLease (heading level 8)

```
uint32_t otSrpServerLeaseInfo::mRemainingLease
```

**Description:** The remaining lease time of the host/service in milliseconds.

###### mRemainingKeyLease (heading level 8)

```
uint32_t otSrpServerLeaseInfo::mRemainingKeyLease
```

**Description:** The remaining key lease time of a host/service in milliseconds.

Includes the statistics of SRP server responses. 

###### Public Attributes (heading level 7)

###### mSuccess (heading level 8)

```
uint32_t otSrpServerResponseCounters::mSuccess
```

**Description:** The number of successful responses.

###### mServerFailure (heading level 8)

```
uint32_t otSrpServerResponseCounters::mServerFailure
```

**Description:** The number of server failure responses.

###### mFormatError (heading level 8)

```
uint32_t otSrpServerResponseCounters::mFormatError
```

**Description:** The number of format error responses.

###### mNameExists (heading level 8)

```
uint32_t otSrpServerResponseCounters::mNameExists
```

**Description:** The number of 'name exists' responses.

###### mRefused (heading level 8)

```
uint32_t otSrpServerResponseCounters::mRefused
```

**Description:** The number of refused responses.

###### mOther (heading level 8)

```
uint32_t otSrpServerResponseCounters::mOther
```

**Description:** The number of other responses.

##### Ping Sender

This file includes the OpenThread API for the ping sender module. 

###### Modules

[otPingSenderReply](ot-ping-sender-reply)

[otPingSenderStatistics](ot-ping-sender-statistics)

[otPingSenderConfig](ot-ping-sender-config)

###### Typedefs

###### otPingSenderReply (heading level 7)

`typedef struct otPingSenderReply otPingSenderReply`

**Description:**

Represents a ping reply.

###### otPingSenderStatistics (heading level 7)

`typedef struct otPingSenderStatistics otPingSenderStatistics`

**Description:**

Represents statistics of a ping request.

###### otPingSenderReplyCallback (heading level 7)

`typedef void(* otPingSenderReplyCallback) (const otPingSenderReply *aReply, void *aContext)`

**Description:**

Pointer type specifies the callback to notify receipt of a ping reply.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aReply|A pointer to a `otPingSenderReply` containing info about the received ping reply.|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### otPingSenderStatisticsCallback (heading level 7)

`typedef void(* otPingSenderStatisticsCallback) (const otPingSenderStatistics *aStatistics, void *aContext)`

**Description:**

Pointer type specifies the callback to report the ping statistics.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aStatistics|A pointer to a `otPingSenderStatistics` containing info about the received ping statistics.|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### otPingSenderConfig (heading level 7)

`typedef struct otPingSenderConfig otPingSenderConfig`

**Description:**

Represents a ping request configuration.

###### Functions

###### otPingSenderPing (heading level 7)

`otError otPingSenderPing(otInstance *aInstance, const otPingSenderConfig *aConfig)`

**Description:** Starts a ping.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otPingSenderConfig](ot-ping-sender-config) *|[in]|aConfig|The ping config to use.|

###### otPingSenderStop (heading level 7)

`void otPingSenderStop(otInstance *aInstance)`

**Description:** Stops an ongoing ping.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Represents a ping reply. 

###### Public Attributes (heading level 7)

###### mSenderAddress (heading level 8)

```
otIp6Address otPingSenderReply::mSenderAddress
```

**Description:** Sender IPv6 address (address from which ping reply was received).

###### mRoundTripTime (heading level 8)

```
uint16_t otPingSenderReply::mRoundTripTime
```

**Description:** Round trip time in msec.

###### mSize (heading level 8)

```
uint16_t otPingSenderReply::mSize
```

**Description:** Data size (number of bytes) in reply (excluding IPv6 and ICMP6 headers).

###### mSequenceNumber (heading level 8)

```
uint16_t otPingSenderReply::mSequenceNumber
```

**Description:** Sequence number.

###### mHopLimit (heading level 8)

```
uint8_t otPingSenderReply::mHopLimit
```

**Description:** Hop limit.

Represents statistics of a ping request. 

###### Public Attributes (heading level 7)

###### mSentCount (heading level 8)

```
uint16_t otPingSenderStatistics::mSentCount
```

**Description:** The number of ping requests already sent.

###### mReceivedCount (heading level 8)

```
uint16_t otPingSenderStatistics::mReceivedCount
```

**Description:** The number of ping replies received.

###### mTotalRoundTripTime (heading level 8)

```
uint32_t otPingSenderStatistics::mTotalRoundTripTime
```

**Description:** The total round trip time of ping requests.

###### mMinRoundTripTime (heading level 8)

```
uint16_t otPingSenderStatistics::mMinRoundTripTime
```

**Description:** The min round trip time among ping requests.

###### mMaxRoundTripTime (heading level 8)

```
uint16_t otPingSenderStatistics::mMaxRoundTripTime
```

**Description:** The max round trip time among ping requests.

###### mIsMulticast (heading level 8)

```
bool otPingSenderStatistics::mIsMulticast
```

**Description:** Whether this is a multicast ping request.

Represents a ping request configuration. 

###### Public Attributes (heading level 7)

###### mSource (heading level 8)

```
otIp6Address otPingSenderConfig::mSource
```

**Description:** Source address of the ping.

###### mDestination (heading level 8)

```
otIp6Address otPingSenderConfig::mDestination
```

**Description:** Destination address to ping.

###### mReplyCallback (heading level 8)

```
otPingSenderReplyCallback otPingSenderConfig::mReplyCallback
```

**Description:** Callback function to report replies (can be NULL if not needed).

###### mStatisticsCallback (heading level 8)

```
otPingSenderStatisticsCallback otPingSenderConfig::mStatisticsCallback
```

**Description:** Callback function to report statistics (can be NULL if not needed).

###### mCallbackContext (heading level 8)

```
void* otPingSenderConfig::mCallbackContext
```

**Description:** A pointer to the callback application-specific context.

###### mSize (heading level 8)

```
uint16_t otPingSenderConfig::mSize
```

**Description:** Data size (# of bytes) excludes IPv6/ICMPv6 header. Zero for default.

###### mCount (heading level 8)

```
uint16_t otPingSenderConfig::mCount
```

**Description:** Number of ping messages to send. Zero to use default.

###### mInterval (heading level 8)

```
uint32_t otPingSenderConfig::mInterval
```

**Description:** Ping tx interval in milliseconds. Zero to use default.

###### mTimeout (heading level 8)

```
uint16_t otPingSenderConfig::mTimeout
```

**Description:** Time in milliseconds to wait for final reply after sending final request.

**Details:** Zero to use default.

###### mHopLimit (heading level 8)

```
uint8_t otPingSenderConfig::mHopLimit
```

**Description:** Hop limit (used if `mAllowZeroHopLimit` is false). Zero for default.

###### mAllowZeroHopLimit (heading level 8)

```
bool otPingSenderConfig::mAllowZeroHopLimit
```

**Description:** Indicates whether hop limit is zero.

###### mMulticastLoop (heading level 8)

```
bool otPingSenderConfig::mMulticastLoop
```

**Description:** Allow looping back pings to multicast address that device is subscribed to.

##### TCP

###### Modules

[TCP](api-tcp)

[TCP Abstractions](api-tcp-ext)

###### TCP

This module includes functions that control TCP communication. 

###### Modules (heading level 7)

[otLinkedBuffer](ot-linked-buffer)

[otTcpEndpoint](ot-tcp-endpoint)

[otTcpEndpointInitializeArgs](ot-tcp-endpoint-initialize-args)

[otTcpListener](ot-tcp-listener)

[otTcpListenerInitializeArgs](ot-tcp-listener-initialize-args)

###### Enumerations (heading level 7)

###### otTcpDisconnectedReason (heading level 8)

```
enum otTcpDisconnectedReason {
    OT_TCP_DISCONNECTED_REASON_NORMAL
    OT_TCP_DISCONNECTED_REASON_REFUSED
    OT_TCP_DISCONNECTED_REASON_RESET
    OT_TCP_DISCONNECTED_REASON_TIME_WAIT
    OT_TCP_DISCONNECTED_REASON_TIMED_OUT
}
```

**Enumerator:**

|   |   |
|---|---|
|OT_TCP_DISCONNECTED_REASON_NORMAL||
|OT_TCP_DISCONNECTED_REASON_REFUSED||
|OT_TCP_DISCONNECTED_REASON_RESET||
|OT_TCP_DISCONNECTED_REASON_TIME_WAIT||
|OT_TCP_DISCONNECTED_REASON_TIMED_OUT||

###### @19 (heading level 8)

```
enum @19 {
    OT_TCP_CONNECT_NO_FAST_OPEN = 1 << 0
}
```

**Description:**

Defines flags passed to [otTcpConnect()](api-tcp#ot-tcp-connect).

**Enumerator:**

|   |   |
|---|---|
|OT_TCP_CONNECT_NO_FAST_OPEN||

###### @20 (heading level 8)

```
enum @20 {
    OT_TCP_SEND_MORE_TO_COME = 1 << 0
}
```

**Description:**

Defines flags passed to `otTcpSendByReference`.

**Enumerator:**

|   |   |
|---|---|
|OT_TCP_SEND_MORE_TO_COME||

###### otTcpIncomingConnectionAction (heading level 8)

```
enum otTcpIncomingConnectionAction {
    OT_TCP_INCOMING_CONNECTION_ACTION_ACCEPT
    OT_TCP_INCOMING_CONNECTION_ACTION_DEFER
    OT_TCP_INCOMING_CONNECTION_ACTION_REFUSE
}
```

**Description:**

Defines incoming connection actions.

**Details:**

This is used in [otTcpAcceptReady()](api-tcp#ot-tcp-accept-ready) callback.

**Enumerator:**

|   |   |
|---|---|
|OT_TCP_INCOMING_CONNECTION_ACTION_ACCEPT|Accept the incoming connection.|
|OT_TCP_INCOMING_CONNECTION_ACTION_DEFER|Defer (silently ignore) the incoming connection.|
|OT_TCP_INCOMING_CONNECTION_ACTION_REFUSE|Refuse the incoming connection.|

###### Typedefs (heading level 7)

###### otLinkedBuffer (heading level 8)

`typedef struct otLinkedBuffer otLinkedBuffer`

**Description:**

A linked buffer structure for use with TCP.

**Details:**

A single [otLinkedBuffer](ot-linked-buffer) structure references an array of bytes in memory, via mData and mLength. The mNext field is used to form a chain of [otLinkedBuffer](ot-linked-buffer) structures.

###### otTcpEndpoint (heading level 8)

`typedef struct otTcpEndpoint otTcpEndpoint`

###### otTcpEstablished (heading level 8)

`typedef void(* otTcpEstablished) (otTcpEndpoint *aEndpoint)`

**Description:**

This callback informs the application that the TCP 3-way handshake is complete and that the connection is now established.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aEndpoint|The TCP endpoint whose connection is now established.|

**Details:**

###### otTcpSendDone (heading level 8)

`typedef void(* otTcpSendDone) (otTcpEndpoint *aEndpoint, otLinkedBuffer *aData)`

**Description:**

This callback informs the application that data in the provided `aData` have been acknowledged by the connection peer and that `aData` and the data it contains can be reclaimed by the application.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aEndpoint|The TCP endpoint for the connection.|
||[in]|aData|A pointer to the [otLinkedBuffer](ot-linked-buffer) that can be reclaimed.|

**Details:**

The `aData` are guaranteed to be identical to those passed in to TCP via [otTcpSendByReference()](api-tcp#ot-tcp-send-by-reference), including any extensions effected via [otTcpSendByExtension()](api-tcp#ot-tcp-send-by-extension).

###### otTcpForwardProgress (heading level 8)

`typedef void(* otTcpForwardProgress) (otTcpEndpoint *aEndpoint, size_t aInSendBuffer, size_t aBacklog)`

**Description:**

This callback informs the application if forward progress has been made in transferring data from the send buffer to the recipient.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aEndpoint|The TCP endpoint for the connection.|
||[in]|aInSendBuffer|The number of bytes in the send buffer (sum of "in-flight" and "backlog" regions).|
||[in]|aBacklog|The number of bytes that are queued for sending but have not yet been sent (the "backlog" region).|

**Details:**

This callback is not necessary for correct TCP operation. Most applications can just rely on the [otTcpSendDone()](api-tcp#ot-tcp-send-done) callback to reclaim linked buffers once the TCP stack is done using them. The purpose of this callback is to support advanced applications that benefit from finer-grained information about how the the connection is making forward progress in transferring data to the connection peer.

This callback's operation is closely tied to TCP's send buffer. The send buffer can be understood as having two regions. First, there is the "in-flight" region at the head (front) of the send buffer. It corresponds to data which has been sent to the recipient, but is not yet acknowledged. Second, there is the "backlog" region, which consists of all data in the send buffer that is not in the "in-flight" region. The "backlog" region corresponds to data that is queued for sending, but has not yet been sent.

The callback is invoked in response to two types of events. First, the "in-flight" region of the send buffer may shrink (e.g., when the recipient acknowledges data that we sent earlier). Second, the "backlog" region of the send buffer may shrink (e.g., new data was sent out). These two conditions often occur at the same time, in response to an ACK segment from the connection peer, which is why they are combined in a single callback.

The TCP stack only uses the `aInSendBuffer` bytes at the tail of the send buffer; when `aInSendBuffer` decreases by an amount x, it means that x additional bytes that were formerly at the head of the send buffer are no longer part of the send buffer and can now be reclaimed (i.e., overwritten) by the application. Note that the [otLinkedBuffer](ot-linked-buffer) structure itself can only be reclaimed once all bytes that it references are no longer part of the send buffer.

This callback subsumes [otTcpSendDone()](api-tcp#ot-tcp-send-done), in the following sense: applications can determine when linked buffers can be reclaimed by comparing `aInSendBuffer` with how many bytes are in each linked buffer. However, we expect [otTcpSendDone()](api-tcp#ot-tcp-send-done), which directly conveys which otLinkedBuffers can be reclaimed, to be much simpler to use. If both callbacks are registered and are triggered by the same event (e.g., the same ACK segment received), then the [otTcpSendDone()](api-tcp#ot-tcp-send-done) callback will be triggered first, followed by this callback.

Additionally, this callback provides `aBacklog`, which indicates how many bytes of data in the send buffer are not yet in flight. For applications that only want to add data to the send buffer when there is an assurance that it will be sent out soon, it may be desirable to only send out data when `aBacklog` is suitably small (0 or close to 0). For example, an application may use `aBacklog` so that it can react to queue buildup by dropping or aggregating data to avoid creating a backlog of data.

After a call to [otTcpSendByReference()](api-tcp#ot-tcp-send-by-reference) or [otTcpSendByExtension()](api-tcp#ot-tcp-send-by-extension) with a positive number of bytes, the [otTcpForwardProgress()](api-tcp#ot-tcp-forward-progress) callback is guaranteed to be called, to indicate when the bytes that were added to the send buffer are sent out. The call to [otTcpForwardProgress()](api-tcp#ot-tcp-forward-progress) may be made immediately after the bytes are added to the send buffer (if some of those bytes are immediately sent out, reducing the backlog), or sometime in the future (once the connection sends out some or all of the data, reducing the backlog). By "immediately," we mean that the callback is immediately scheduled for execution in a tasklet; to avoid reentrancy-related complexity, the [otTcpForwardProgress()](api-tcp#ot-tcp-forward-progress) callback is never directly called from the [otTcpSendByReference()](api-tcp#ot-tcp-send-by-reference) or [otTcpSendByExtension()](api-tcp#ot-tcp-send-by-extension) functions.

###### otTcpReceiveAvailable (heading level 8)

`typedef void(* otTcpReceiveAvailable) (otTcpEndpoint *aEndpoint, size_t aBytesAvailable, bool aEndOfStream, size_t aBytesRemaining)`

**Description:**

This callback indicates the number of bytes available for consumption from the receive buffer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aEndpoint|The TCP endpoint for the connection.|
||[in]|aBytesAvailable|The number of bytes in the connection's receive buffer.|
||[in]|aEndOfStream|Indicates if additional data, beyond what is already in the connection's receive buffer, can be received.|
||[in]|aBytesRemaining|The number of additional bytes that can be received before the receive buffer becomes full.|

**Details:**

It is called whenever bytes are added to the receive buffer and when the end of stream is reached. If the end of the stream has been reached (i.e., if no more data will become available to read because the connection peer has closed their end of the connection for writing), then `aEndOfStream` is true. Finally, `aBytesRemaining` indicates how much capacity is left in the receive buffer to hold additional data that arrives.

###### otTcpDisconnectedReason (heading level 8)

`typedef enum otTcpDisconnectedReason otTcpDisconnectedReason`

###### otTcpDisconnected (heading level 8)

`typedef void(* otTcpDisconnected) (otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason)`

**Description:**

This callback indicates that the connection was broken and should no longer be used, or that a connection has entered the TIME-WAIT state.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aEndpoint|The TCP endpoint whose connection has been lost.|
||[in]|aReason|The reason why the connection was lost.|

**Details:**

It can occur if a connection establishment attempt (initiated by calling [otTcpConnect()](api-tcp#ot-tcp-connect)) fails, or any point thereafter (e.g., if the connection times out or an RST segment is received from the connection peer). Once this callback fires, all resources that the application provided for this connection (i.e., any `otLinkedBuffers` and memory they reference, but not the TCP endpoint itself or space for the receive buffers) can be reclaimed. In the case of a connection entering the TIME-WAIT state, this callback is called twice, once upon entry into the TIME-WAIT state (with OT_TCP_DISCONNECTED_REASON_TIME_WAIT, and again when the TIME-WAIT state expires (with OT_TCP_DISCONNECTED_REASON_NORMAL).

###### otTcpEndpointInitializeArgs (heading level 8)

`typedef struct otTcpEndpointInitializeArgs otTcpEndpointInitializeArgs`

**Description:**

Contains arguments to the [otTcpEndpointInitialize()](api-tcp#ot-tcp-endpoint-initialize) function.

###### otTcpListener (heading level 8)

`typedef struct otTcpListener otTcpListener`

###### otTcpIncomingConnectionAction (heading level 8)

`typedef enum otTcpIncomingConnectionAction otTcpIncomingConnectionAction`

**Description:**

Defines incoming connection actions.

**Details:**

This is used in [otTcpAcceptReady()](api-tcp#ot-tcp-accept-ready) callback.

###### otTcpAcceptReady (heading level 8)

`typedef otTcpIncomingConnectionAction(* otTcpAcceptReady) (otTcpListener *aListener, const otSockAddr *aPeer, otTcpEndpoint **aAcceptInto)`

**Description:**

This callback indicates that an incoming connection that matches this TCP listener has arrived.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aListener|The TCP listener that matches the incoming connection.|
||[in]|aPeer|The host and port from which the incoming connection originates.|
||[out]|aAcceptInto|The TCP endpoint into which to accept the incoming connection.|

**Details:**

The typical response is for the application to accept the incoming connection. It does so by populating `aAcceptInto` with a pointer to the [otTcpEndpoint](ot-tcp-endpoint) into which to accept the incoming connection. This [otTcpEndpoint](ot-tcp-endpoint) must already be initialized using [otTcpEndpointInitialize()](api-tcp#ot-tcp-endpoint-initialize). Then, the application returns OT_TCP_INCOMING_CONNECTION_ACTION_ACCEPT.

Alternatively, the application can decline to accept the incoming connection. There are two ways for the application to do this. First, if the application returns OT_TCP_INCOMING_CONNECTION_ACTION_DEFER, then OpenThread silently ignores the connection establishment request; the connection peer will likely retransmit the request, at which point the callback will be called again. This is valuable if resources are not presently available to accept the connection, but they may be available when the connection peer retransmits its connection establishment attempt. Second, if the application returns OT_TCP_INCOMING_CONNECTION_ACTION_REFUSE, then OpenThread sends a "connection refused" message to the host that attempted to establish a connection. If the application declines the incoming connection, it is not required to populate `aAcceptInto`.

**Returns**

- Description of how to handle the incoming connection.

###### otTcpAcceptDone (heading level 8)

`typedef void(* otTcpAcceptDone) (otTcpListener *aListener, otTcpEndpoint *aEndpoint, const otSockAddr *aPeer)`

**Description:**

This callback indicates that the TCP connection is now ready for two-way communication.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aListener|The TCP listener that matches the incoming connection.|
||[in]|aEndpoint|The TCP endpoint into which the incoming connection was accepted.|
||[in]|aPeer|the host and port from which the incoming connection originated.|

**Details:**

In the case of TCP Fast Open, this may be before the TCP connection handshake has actually completed. The application is provided with the context pointers both for the TCP listener that accepted the connection and the TCP endpoint into which it was accepted. The provided context is the one associated with the TCP listener.

###### otTcpListenerInitializeArgs (heading level 8)

`typedef struct otTcpListenerInitializeArgs otTcpListenerInitializeArgs`

**Description:**

Contains arguments to the [otTcpListenerInitialize()](api-tcp#ot-tcp-listener-initialize) function.

###### Functions (heading level 7)

###### otTcpEndpointInitialize (heading level 8)

`otError otTcpEndpointInitialize(otInstance *aInstance, otTcpEndpoint *aEndpoint, const otTcpEndpointInitializeArgs *aArgs)`

**Description:** Initializes a TCP endpoint.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otTcpEndpoint](ot-tcp-endpoint) *|[in]|aEndpoint|A pointer to a TCP endpoint structure.|
|const [otTcpEndpointInitializeArgs](ot-tcp-endpoint-initialize-args) *|[in]|aArgs|A pointer to a structure of arguments.|

Calling this function causes OpenThread to keep track of the TCP endpoint and store and retrieve TCP data inside the `aEndpoint`. The application should refrain from directly accessing or modifying the fields in `aEndpoint`. If the application needs to reclaim the memory backing `aEndpoint`, it should call [otTcpEndpointDeinitialize()](api-tcp#ot-tcp-endpoint-deinitialize).

###### otTcpEndpointGetInstance (heading level 8)

`otInstance * otTcpEndpointGetInstance(otTcpEndpoint *aEndpoint)`

**Description:** Obtains the otInstance that was associated with `aEndpoint` upon initialization.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpEndpoint](ot-tcp-endpoint) *|[in]|aEndpoint|The TCP endpoint whose instance to obtain.|

**Returns**

- The otInstance pointer associated with `aEndpoint`.

###### otTcpEndpointGetContext (heading level 8)

`void * otTcpEndpointGetContext(otTcpEndpoint *aEndpoint)`

**Description:** Obtains the context pointer that was associated with `aEndpoint` upon initialization.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpEndpoint](ot-tcp-endpoint) *|[in]|aEndpoint|The TCP endpoint whose context to obtain.|

**Returns**

- The context pointer associated with `aEndpoint`.

###### otTcpGetLocalAddress (heading level 8)

`const otSockAddr * otTcpGetLocalAddress(const otTcpEndpoint *aEndpoint)`

**Description:** Obtains a pointer to a TCP endpoint's local host and port.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otTcpEndpoint](ot-tcp-endpoint) *|[in]|aEndpoint|The TCP endpoint whose local host and port to obtain.|

The contents of the host and port may be stale if this socket is not in a connected state and has not been bound after it was last disconnected.

**Returns**

- The local host and port of `aEndpoint`.

###### otTcpGetPeerAddress (heading level 8)

`const otSockAddr * otTcpGetPeerAddress(const otTcpEndpoint *aEndpoint)`

**Description:** Obtains a pointer to a TCP endpoint's peer's host and port.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otTcpEndpoint](ot-tcp-endpoint) *|[in]|aEndpoint|The TCP endpoint whose peer's host and port to obtain.|

The contents of the host and port may be stale if this socket is not in a connected state.

**Returns**

- The host and port of the connection peer of `aEndpoint`.

###### otTcpBind (heading level 8)

`otError otTcpBind(otTcpEndpoint *aEndpoint, const otSockAddr *aSockName)`

**Description:** Binds the TCP endpoint to an IP address and port.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpEndpoint](ot-tcp-endpoint) *|[in]|aEndpoint|A pointer to the TCP endpoint structure to bind.|
|const [otSockAddr](ot-sock-addr) *|[in]|aSockName|The address and port to which to bind this TCP endpoint.|

###### otTcpConnect (heading level 8)

`otError otTcpConnect(otTcpEndpoint *aEndpoint, const otSockAddr *aSockName, uint32_t aFlags)`

**Description:** Records the remote host and port for this connection.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpEndpoint](ot-tcp-endpoint) *|[in]|aEndpoint|A pointer to the TCP endpoint structure to connect.|
|const [otSockAddr](ot-sock-addr) *|[in]|aSockName|The IP address and port of the host to which to connect.|
|uint32_t|[in]|aFlags|Flags specifying options for this operation (see enumeration above).|

TCP Fast Open must be enabled or disabled using `aFlags`. If it is disabled, then the TCP connection establishment handshake is initiated immediately. If it is enabled, then this function merely records the the remote host and port, and the TCP connection establishment handshake only happens on the first call to `otTcpSendByReference()`.

If TCP Fast Open is disabled, then the caller must wait for the `otTcpEstablished` callback indicating that TCP connection establishment handshake is done before it can start sending data e.g., by calling `otTcpSendByReference()`.

###### otTcpSendByReference (heading level 8)

`otError otTcpSendByReference(otTcpEndpoint *aEndpoint, otLinkedBuffer *aBuffer, uint32_t aFlags)`

**Description:** Adds data referenced by the linked buffer pointed to by `aBuffer` to the send buffer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpEndpoint](ot-tcp-endpoint) *|[in]|aEndpoint|A pointer to the TCP endpoint structure representing the TCP endpoint on which to send data.|
|[otLinkedBuffer](ot-linked-buffer) *|[in]|aBuffer|A pointer to the linked buffer chain referencing data to add to the send buffer.|
|uint32_t|[in]|aFlags|Flags specifying options for this operation (see enumeration above).|

Upon a successful call to this function, the linked buffer and data it references are owned by the TCP stack; they should not be modified by the application until a "send done" callback returns ownership of those objects to the application. It is acceptable to call this function to add another linked buffer to the send queue, even if the "send done" callback for a previous invocation of this function has not yet fired.

Note that `aBuffer` should not be chained; its mNext field should be NULL. If additional data will be added right after this call, then the OT_TCP_SEND_MORE_TO_COME flag should be used as a hint to the TCP implementation.

###### otTcpSendByExtension (heading level 8)

`otError otTcpSendByExtension(otTcpEndpoint *aEndpoint, size_t aNumBytes, uint32_t aFlags)`

**Description:** Adds data to the send buffer by extending the length of the final [otLinkedBuffer](ot-linked-buffer) in the send buffer by the specified amount.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpEndpoint](ot-tcp-endpoint) *|[in]|aEndpoint|A pointer to the TCP endpoint structure representing the TCP endpoint on which to send data.|
|size_t|[in]|aNumBytes|The number of bytes by which to extend the length of the final linked buffer.|
|uint32_t|[in]|aFlags|Flags specifying options for this operation (see enumeration above).|

If the send buffer is empty, then the operation fails.

###### otTcpReceiveByReference (heading level 8)

`otError otTcpReceiveByReference(otTcpEndpoint *aEndpoint, const otLinkedBuffer **aBuffer)`

**Description:** Provides the application with a linked buffer chain referencing data currently in the TCP receive buffer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpEndpoint](ot-tcp-endpoint) *|[in]|aEndpoint|A pointer to the TCP endpoint structure representing the TCP endpoint on which to receive data.|
|const [otLinkedBuffer](ot-linked-buffer) **|[out]|aBuffer|A pointer to the linked buffer chain referencing data currently in the receive buffer.|

The linked buffer chain is valid until the "receive ready" callback is next invoked, or until the next call to [otTcpReceiveContiguify()](api-tcp#ot-tcp-receive-contiguify) or [otTcpCommitReceive()](api-tcp#ot-tcp-commit-receive).

###### otTcpReceiveContiguify (heading level 8)

`otError otTcpReceiveContiguify(otTcpEndpoint *aEndpoint)`

**Description:** Reorganizes the receive buffer to be entirely contiguous in memory.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpEndpoint](ot-tcp-endpoint) *|[in]|aEndpoint|A pointer to the TCP endpoint whose receive buffer to reorganize.|

This is optional; an application can simply traverse the linked buffer chain obtained by calling `otTcpReceiveByReference`. Some applications may wish to call this function to make the receive buffer contiguous to simplify their data processing, but this comes at the expense of CPU time to reorganize the data in the receive buffer.

###### otTcpCommitReceive (heading level 8)

`otError otTcpCommitReceive(otTcpEndpoint *aEndpoint, size_t aNumBytes, uint32_t aFlags)`

**Description:** Informs the TCP stack that the application has finished processing `aNumBytes` bytes of data at the start of the receive buffer and that the TCP stack need not continue maintaining those bytes in the receive buffer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpEndpoint](ot-tcp-endpoint) *|[in]|aEndpoint|A pointer to the TCP endpoint structure representing the TCP endpoint on which to receive data.|
|size_t|[in]|aNumBytes|The number of bytes consumed.|
|uint32_t|[in]|aFlags|Flags specifying options for this operation (none yet).|

###### otTcpSendEndOfStream (heading level 8)

`otError otTcpSendEndOfStream(otTcpEndpoint *aEndpoint)`

**Description:** Informs the connection peer that this TCP endpoint will not send more data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpEndpoint](ot-tcp-endpoint) *|[in]|aEndpoint|A pointer to the TCP endpoint structure representing the TCP endpoint to shut down.|

This should be used when the application has no more data to send to the connection peer. For this connection, future reads on the connection peer will result in the "end of stream" condition, and future writes on this connection endpoint will fail.

The "end of stream" condition only applies after any data previously provided to the TCP stack to send out has been received by the connection peer.

###### otTcpAbort (heading level 8)

`otError otTcpAbort(otTcpEndpoint *aEndpoint)`

**Description:** Forcibly ends the TCP connection associated with this TCP endpoint.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpEndpoint](ot-tcp-endpoint) *|[in]|aEndpoint|A pointer to the TCP endpoint structure representing the TCP endpoint to abort.|

This immediately makes the TCP endpoint free for use for another connection and empties the send and receive buffers, transferring ownership of any data provided by the application in [otTcpSendByReference()](api-tcp#ot-tcp-send-by-reference) and [otTcpSendByExtension()](api-tcp#ot-tcp-send-by-extension) calls back to the application. The TCP endpoint's callbacks and memory for the receive buffer remain associated with the TCP endpoint.

###### otTcpEndpointDeinitialize (heading level 8)

`otError otTcpEndpointDeinitialize(otTcpEndpoint *aEndpoint)`

**Description:** Deinitializes this TCP endpoint.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpEndpoint](ot-tcp-endpoint) *|[in]|aEndpoint|A pointer to the TCP endpoint structure to deinitialize.|

This means that OpenThread no longer keeps track of this TCP endpoint and deallocates all resources it has internally allocated for this TCP endpoint. The application can reuse the memory backing the TCP endpoint as it sees fit.

If it corresponds to a live TCP connection, the connection is terminated unceremoniously (as in [otTcpAbort()](api-tcp#ot-tcp-abort)). All resources the application has provided for this TCP endpoint (linked buffers for the send buffer, memory for the receive buffer, the `aEndpoint` structure itself, etc.) are immediately returned to the application.

###### otTcpListenerInitialize (heading level 8)

`otError otTcpListenerInitialize(otInstance *aInstance, otTcpListener *aListener, const otTcpListenerInitializeArgs *aArgs)`

**Description:** Initializes a TCP listener.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otTcpListener](ot-tcp-listener) *|[in]|aListener|A pointer to a TCP listener structure.|
|const [otTcpListenerInitializeArgs](ot-tcp-listener-initialize-args) *|[in]|aArgs|A pointer to a structure of arguments.|

Calling this function causes OpenThread to keep track of the TCP listener and store and retrieve TCP data inside `aListener`. The application should refrain from directly accessing or modifying the fields in `aListener`. If the application needs to reclaim the memory backing `aListener`, it should call [otTcpListenerDeinitialize()](api-tcp#ot-tcp-listener-deinitialize).

###### otTcpListenerGetInstance (heading level 8)

`otInstance * otTcpListenerGetInstance(otTcpListener *aListener)`

**Description:** Obtains the otInstance that was associated with `aListener` upon initialization.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpListener](ot-tcp-listener) *|[in]|aListener|The TCP listener whose instance to obtain.|

**Returns**

- The otInstance pointer associated with `aListener`.

###### otTcpListenerGetContext (heading level 8)

`void * otTcpListenerGetContext(otTcpListener *aListener)`

**Description:** Obtains the context pointer that was associated with `aListener` upon initialization.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpListener](ot-tcp-listener) *|[in]|aListener|The TCP listener whose context to obtain.|

**Returns**

- The context pointer associated with `aListener`.

###### otTcpListen (heading level 8)

`otError otTcpListen(otTcpListener *aListener, const otSockAddr *aSockName)`

**Description:** Causes incoming TCP connections that match the specified IP address and port to trigger this TCP listener's callbacks.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpListener](ot-tcp-listener) *|[in]|aListener|A pointer to the TCP listener structure that should begin listening.|
|const [otSockAddr](ot-sock-addr) *|[in]|aSockName|The address and port on which to listen for incoming connections.|

###### otTcpStopListening (heading level 8)

`otError otTcpStopListening(otTcpListener *aListener)`

**Description:** Causes this TCP listener to stop listening for incoming connections.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpListener](ot-tcp-listener) *|[in]|aListener|A pointer to the TCP listener structure that should stop listening.|

###### otTcpListenerDeinitialize (heading level 8)

`otError otTcpListenerDeinitialize(otTcpListener *aListener)`

**Description:** Deinitializes this TCP listener.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpListener](ot-tcp-listener) *|[in]|aListener|A pointer to the TCP listener structure to deinitialize.|

This means that OpenThread no longer keeps track of this TCP listener and deallocates all resources it has internally allocated for this TCP listener. The application can reuse the memory backing the TCP listener as it sees fit.

If the TCP listener is currently listening, it stops listening.

###### Macros (heading level 7)

`#define OT_TCP_ENDPOINT_TCB_SIZE_BASE 392`

**Description**: OT_TCP_ENDPOINT_TCB_SIZE_BASE and OT_TCP_ENDPOINT_TCB_NUM_POINTERS are chosen such that the mTcb field of [otTcpEndpoint](ot-tcp-endpoint) has the same size as struct tcpcb in TCPlp.

`#define OT_TCP_ENDPOINT_TCB_NUM_PTR 36`

`#define OT_TCP_RECEIVE_BUFFER_SIZE_FEW_HOPS 2598`

**Description**: Recommended buffer size for TCP connections that traverse about 3 wireless hops or fewer.

`#define OT_TCP_RECEIVE_BUFFER_SIZE_MANY_HOPS 4157`

**Description**: Recommended buffer size for TCP connections that traverse many wireless hops.

`#define OT_TCP_LISTENER_TCB_SIZE_BASE 16`

**Description**: OT_TCP_LISTENER_TCB_SIZE_BASE and OT_TCP_LISTENER_TCB_NUM_POINTERS are chosen such that the mTcbListener field of [otTcpListener](ot-tcp-listener) has the same size as struct tcpcb_listen in TCPlp.

`#define OT_TCP_LISTENER_TCB_NUM_PTR 3`

A linked buffer structure for use with TCP. 

A single [otLinkedBuffer](ot-linked-buffer) structure references an array of bytes in memory, via mData and mLength. The mNext field is used to form a chain of [otLinkedBuffer](ot-linked-buffer) structures. 

###### Public Attributes (heading level 8)

###### mNext (heading level 9)

```
struct otLinkedBuffer* otLinkedBuffer::mNext
```

**Description:** Pointer to the next linked buffer in the chain, or NULL if it is the end.

###### mData (heading level 9)

```
const uint8_t* otLinkedBuffer::mData
```

**Description:** Pointer to data referenced by this linked buffer.

###### mLength (heading level 9)

```
size_t otLinkedBuffer::mLength
```

**Description:** Length of this linked buffer (number of bytes).

Represents a TCP endpoint. 

A TCP endpoint acts an endpoint of TCP connection. It can be used to initiate TCP connections, and, once a TCP connection is established, send data to and receive data from the connection peer.

The application should not inspect the fields of this structure directly; it should only interact with it via the TCP API functions whose signatures are provided in this file. 

###### Public Attributes (heading level 8)

###### mSize (heading level 9)

```
uint8_t otTcpEndpoint::mSize[OT_TCP_ENDPOINT_TCB_SIZE_BASE+OT_TCP_ENDPOINT_TCB_NUM_PTR *sizeof(void *)]
```

###### mAlign (heading level 9)

```
uint64_t otTcpEndpoint::mAlign
```

###### mTcb (heading level 9)

```
union otTcpEndpoint::@21 otTcpEndpoint::mTcb
```

###### mNext (heading level 9)

```
struct otTcpEndpoint* otTcpEndpoint::mNext
```

**Description:** A pointer to the next TCP endpoint (internal use only)

###### mContext (heading level 9)

```
void* otTcpEndpoint::mContext
```

**Description:** A pointer to application-specific context.

###### mEstablishedCallback (heading level 9)

```
otTcpEstablished otTcpEndpoint::mEstablishedCallback
```

**Description:** "Established" callback function

###### mSendDoneCallback (heading level 9)

```
otTcpSendDone otTcpEndpoint::mSendDoneCallback
```

**Description:** "Send done" callback function

###### mForwardProgressCallback (heading level 9)

```
otTcpForwardProgress otTcpEndpoint::mForwardProgressCallback
```

**Description:** "Forward progress" callback function

###### mReceiveAvailableCallback (heading level 9)

```
otTcpReceiveAvailable otTcpEndpoint::mReceiveAvailableCallback
```

**Description:** "Receive available" callback function

###### mDisconnectedCallback (heading level 9)

```
otTcpDisconnected otTcpEndpoint::mDisconnectedCallback
```

**Description:** "Disconnected" callback function

###### mTimers (heading level 9)

```
uint32_t otTcpEndpoint::mTimers[4]
```

###### mReceiveLinks (heading level 9)

```
otLinkedBuffer otTcpEndpoint::mReceiveLinks[2]
```

###### mSockAddr (heading level 9)

```
otSockAddr otTcpEndpoint::mSockAddr
```

###### mPendingCallbacks (heading level 9)

```
uint8_t otTcpEndpoint::mPendingCallbacks
```

Contains arguments to the [otTcpEndpointInitialize()](api-tcp#ot-tcp-endpoint-initialize) function. 

###### Public Attributes (heading level 8)

###### mContext (heading level 9)

```
void* otTcpEndpointInitializeArgs::mContext
```

**Description:** Pointer to application-specific context.

###### mEstablishedCallback (heading level 9)

```
otTcpEstablished otTcpEndpointInitializeArgs::mEstablishedCallback
```

**Description:** "Established" callback function

###### mSendDoneCallback (heading level 9)

```
otTcpSendDone otTcpEndpointInitializeArgs::mSendDoneCallback
```

**Description:** "Send done" callback function

###### mForwardProgressCallback (heading level 9)

```
otTcpForwardProgress otTcpEndpointInitializeArgs::mForwardProgressCallback
```

**Description:** "Forward progress" callback function

###### mReceiveAvailableCallback (heading level 9)

```
otTcpReceiveAvailable otTcpEndpointInitializeArgs::mReceiveAvailableCallback
```

**Description:** "Receive available" callback function

###### mDisconnectedCallback (heading level 9)

```
otTcpDisconnected otTcpEndpointInitializeArgs::mDisconnectedCallback
```

**Description:** "Disconnected" callback function

###### mReceiveBuffer (heading level 9)

```
void* otTcpEndpointInitializeArgs::mReceiveBuffer
```

**Description:** Pointer to memory provided to the system for the TCP receive buffer.

###### mReceiveBufferSize (heading level 9)

```
size_t otTcpEndpointInitializeArgs::mReceiveBufferSize
```

**Description:** Size of memory provided to the system for the TCP receive buffer.

Represents a TCP listener. 

A TCP listener is used to listen for and accept incoming TCP connections.

The application should not inspect the fields of this structure directly; it should only interact with it via the TCP API functions whose signatures are provided in this file. 

###### Public Attributes (heading level 8)

###### mSize (heading level 9)

```
uint8_t otTcpListener::mSize[OT_TCP_LISTENER_TCB_SIZE_BASE+OT_TCP_LISTENER_TCB_NUM_PTR *sizeof(void *)]
```

###### mAlign (heading level 9)

```
void* otTcpListener::mAlign
```

###### mTcbListen (heading level 9)

```
union otTcpListener::@22 otTcpListener::mTcbListen
```

###### mNext (heading level 9)

```
struct otTcpListener* otTcpListener::mNext
```

**Description:** A pointer to the next TCP listener (internal use only)

###### mContext (heading level 9)

```
void* otTcpListener::mContext
```

**Description:** A pointer to application-specific context.

###### mAcceptReadyCallback (heading level 9)

```
otTcpAcceptReady otTcpListener::mAcceptReadyCallback
```

**Description:** "Accept ready" callback function

###### mAcceptDoneCallback (heading level 9)

```
otTcpAcceptDone otTcpListener::mAcceptDoneCallback
```

**Description:** "Accept done" callback function

Contains arguments to the [otTcpListenerInitialize()](api-tcp#ot-tcp-listener-initialize) function. 

###### Public Attributes (heading level 8)

###### mContext (heading level 9)

```
void* otTcpListenerInitializeArgs::mContext
```

**Description:** Pointer to application-specific context.

###### mAcceptReadyCallback (heading level 9)

```
otTcpAcceptReady otTcpListenerInitializeArgs::mAcceptReadyCallback
```

**Description:** "Accept ready" callback function

###### mAcceptDoneCallback (heading level 9)

```
otTcpAcceptDone otTcpListenerInitializeArgs::mAcceptDoneCallback
```

**Description:** "Accept done" callback function

###### TCP Abstractions

This module includes easy-to-use abstractions on top of the base TCP API. 

###### Modules (heading level 7)

[otTcpCircularSendBuffer](ot-tcp-circular-send-buffer)

[otTcpEndpointAndCircularSendBuffer](ot-tcp-endpoint-and-circular-send-buffer)

###### Enumerations (heading level 7)

###### @23 (heading level 8)

```
enum @23 {
    OT_TCP_CIRCULAR_SEND_BUFFER_WRITE_MORE_TO_COME = 1 << 0
}
```

**Description:**

Defines flags passed to `otTcpCircularSendBufferWrite`.

**Enumerator:**

|   |   |
|---|---|
|OT_TCP_CIRCULAR_SEND_BUFFER_WRITE_MORE_TO_COME||

###### Typedefs (heading level 7)

###### otTcpCircularSendBuffer (heading level 8)

`typedef struct otTcpCircularSendBuffer otTcpCircularSendBuffer`

**Description:**

Represents a circular send buffer for use with a TCP endpoint.

**Details:**

Using a circular send buffer is optional. Applications can use a TCP endpoint to send data by managing otLinkedBuffers directly. However, some applications may find it more convenient to have a circular send buffer; such applications can call [otTcpCircularSendBufferWrite()](api-tcp-ext#ot-tcp-circular-send-buffer-write) to "attach" a circular send buffer to a TCP endpoint and send out data on that TCP endpoint, relying on the circular send buffer to manage the underlying otLinkedBuffers.

[otTcpCircularSendBuffer](ot-tcp-circular-send-buffer) is implemented on top of the otLinkedBuffer-based API provided by an [otTcpEndpoint](ot-tcp-endpoint). Once attached to an [otTcpEndpoint](ot-tcp-endpoint), an [otTcpCircularSendBuffer](ot-tcp-circular-send-buffer) performs all the work of managing otLinkedBuffers for the connection. This means that, once an [otTcpCircularSendBuffer](ot-tcp-circular-send-buffer) is attached to an [otTcpEndpoint](ot-tcp-endpoint), the application should not call [otTcpSendByReference()](api-tcp#ot-tcp-send-by-reference) or [otTcpSendByExtension()](api-tcp#ot-tcp-send-by-extension) on that [otTcpEndpoint](ot-tcp-endpoint). Instead, the application should use [otTcpCircularSendBufferWrite()](api-tcp-ext#ot-tcp-circular-send-buffer-write) to add data to the send buffer.

The [otTcpForwardProgress()](api-tcp#ot-tcp-forward-progress) callback is the intended way for users to learn when space becomes available in the circular send buffer. On an [otTcpEndpoint](ot-tcp-endpoint) to which an [otTcpCircularSendBuffer](ot-tcp-circular-send-buffer) is attached, the application MUST install an [otTcpForwardProgress()](api-tcp#ot-tcp-forward-progress) callback and call [otTcpCircularSendBufferHandleForwardProgress()](api-tcp-ext#ot-tcp-circular-send-buffer-handle-forward-progress) on the attached [otTcpCircularSendBuffer](ot-tcp-circular-send-buffer) at the start of the callback function. It is recommended that the user NOT install an [otTcpSendDone()](api-tcp#ot-tcp-send-done) callback, as all management of otLinkedBuffers is handled by the circular send buffer.

The application should not inspect the fields of this structure directly; it should only interact with it via the TCP Circular Send Buffer API functions whose signature are provided in this file.

###### otTcpEndpointAndCircularSendBuffer (heading level 8)

`typedef struct otTcpEndpointAndCircularSendBuffer otTcpEndpointAndCircularSendBuffer`

**Description:**

Context structure to use with mbedtls_ssl_set_bio.

###### Functions (heading level 7)

###### otTcpCircularSendBufferInitialize (heading level 8)

`void otTcpCircularSendBufferInitialize(otTcpCircularSendBuffer *aSendBuffer, void *aDataBuffer, size_t aCapacity)`

**Description:** Initializes a TCP circular send buffer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpCircularSendBuffer](ot-tcp-circular-send-buffer) *|[in]|aSendBuffer|A pointer to the TCP circular send buffer to initialize.|
|void *|[in]|aDataBuffer|A pointer to memory to use to store data in the TCP circular send buffer.|
|size_t|[in]|aCapacity|The capacity, in bytes, of the TCP circular send buffer, which must equal the size of the memory pointed to by `aDataBuffer` .|

###### otTcpCircularSendBufferWrite (heading level 8)

`otError otTcpCircularSendBufferWrite(otTcpEndpoint *aEndpoint, otTcpCircularSendBuffer *aSendBuffer, const void *aData, size_t aLength, size_t *aWritten, uint32_t aFlags)`

**Description:** Sends out data on a TCP endpoint, using the provided TCP circular send buffer to manage buffering.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpEndpoint](ot-tcp-endpoint) *|[in]|aEndpoint|The TCP endpoint on which to send out data.|
|[otTcpCircularSendBuffer](ot-tcp-circular-send-buffer) *|[in]|aSendBuffer|The TCP circular send buffer into which to copy data.|
|const void *|[in]|aData|A pointer to data to copy into the TCP circular send buffer.|
|size_t|[in]|aLength|The length of the data pointed to by `aData` to copy into the TCP circular send buffer.|
|size_t *|[out]|aWritten|Populated with the amount of data copied into the send buffer, which might be less than `aLength` if the send buffer reaches capacity.|
|uint32_t|[in]|aFlags|Flags specifying options for this operation (see enumeration above).|

Once this function is called, `aSendBuffer` and `aEndpoint` are considered "attached" to each other. While they are attached, ALL send operations for `aEndpoint` must be made using `aSendBuffer` and ALL operations on `aSendBuffer` must be associated with `aEndpoint` .

The only way to "detach" a TCP circular send buffer and a TCP endpoint is to wait for the send buffer to become completely empty. This can happen in two ways: (1) all data in the send buffer is sent and acknowledged in the normal course of TCP protocol operation, or (2) the connection is terminated.

The recommended usage pattern is to use a single TCP circular send buffer with a TCP endpoint, and to send data on that TCP endpoint only via its associated TCP circular buffer. This recommended usage pattern sidesteps the issues described above by always using a TCP endpoint and TCP circular send buffer together.

If the circular send buffer reaches capacity, only a prefix of the provided data is copied into the circular send buffer.

###### otTcpCircularSendBufferHandleForwardProgress (heading level 8)

`void otTcpCircularSendBufferHandleForwardProgress(otTcpCircularSendBuffer *aSendBuffer, size_t aInSendBuffer)`

**Description:** Performs circular-send-buffer-specific handling in the otTcpForwardProgress callback.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpCircularSendBuffer](ot-tcp-circular-send-buffer) *|[in]|aSendBuffer|A pointer to the TCP circular send buffer for the endpoint for which [otTcpForwardProgress()](api-tcp#ot-tcp-forward-progress) was invoked.|
|size_t|[in]|aInSendBuffer|Value of `aInSendBuffer` passed to the [otTcpForwardProgress()](api-tcp#ot-tcp-forward-progress) callback.|

The application is expected to install an [otTcpForwardProgress()](api-tcp#ot-tcp-forward-progress) callback on the [otTcpEndpoint](ot-tcp-endpoint), and call this function at the start of the callback function for circular-send-buffer-specific processing.

In the callback function, the application can determine the amount of free space in the circular send buffer by calling otTcpCircularSendBufferFreeSpace(), or by comparing `aInSendBuffer` with the send buffer's capacity, chosen by the user when calling [otTcpCircularSendBufferInitialize()](api-tcp-ext#ot-tcp-circular-send-buffer-initialize).

###### otTcpCircularSendBufferGetFreeSpace (heading level 8)

`size_t otTcpCircularSendBufferGetFreeSpace(const otTcpCircularSendBuffer *aSendBuffer)`

**Description:** Returns the amount of free space in the TCP circular send buffer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otTcpCircularSendBuffer](ot-tcp-circular-send-buffer) *|[in]|aSendBuffer|A pointer to the TCP circular send buffer whose amount of free space to return.|

This operation will always succeed.

**Returns**

- The amount of free space in the send buffer.

###### otTcpCircularSendBufferForceDiscardAll (heading level 8)

`void otTcpCircularSendBufferForceDiscardAll(otTcpCircularSendBuffer *aSendBuffer)`

**Description:** Forcibly discards all data in the circular send buffer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpCircularSendBuffer](ot-tcp-circular-send-buffer) *|[in]|aSendBuffer|The TCP circular send buffer whose data to discard.|

The application is expected to call this function when a TCP connection is terminated unceremoniously (e.g., if the application calls otTcpEndpointAbort() or is informed of a reset connection via the otTcpConnectionLost() callback).

Calling this function on a nonempty TCP circular send buffer attached to a TCP endpoint results in undefined behavior.

###### otTcpCircularSendBufferDeinitialize (heading level 8)

`otError otTcpCircularSendBufferDeinitialize(otTcpCircularSendBuffer *aSendBuffer)`

**Description:** Deinitializes a TCP circular send buffer, detaching it if attached.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otTcpCircularSendBuffer](ot-tcp-circular-send-buffer) *|[in]|aSendBuffer|The TCP circular send buffer to deinitialize.|

If the TCP circular send buffer is not empty, then this operation will fail.

###### otTcpMbedTlsSslSendCallback (heading level 8)

`int otTcpMbedTlsSslSendCallback(void *aCtx, const unsigned char *aBuf, size_t aLen)`

**Description:** Non-blocking send callback to pass to mbedtls_ssl_set_bio.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void *|[in]|aCtx|A pointer to an [otTcpEndpointAndCircularSendBuffer](ot-tcp-endpoint-and-circular-send-buffer).|
|const unsigned char *|[in]|aBuf|The data to add to the send buffer.|
|size_t|[in]|aLen|The amount of data to add to the send buffer.|

**Returns**

- The number of bytes sent, or an mbedtls error code.

###### otTcpMbedTlsSslRecvCallback (heading level 8)

`int otTcpMbedTlsSslRecvCallback(void *aCtx, unsigned char *aBuf, size_t aLen)`

**Description:** Non-blocking receive callback to pass to mbedtls_ssl_set_bio.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void *|[in]|aCtx|A pointer to an [otTcpEndpointAndCircularSendBuffer](ot-tcp-endpoint-and-circular-send-buffer).|
|unsigned char *|[out]|aBuf|The buffer into which to receive data.|
|size_t|[in]|aLen|The maximum amount of data that can be received.|

**Returns**

- The number of bytes received, or an mbedtls error code.

Represents a circular send buffer for use with a TCP endpoint. 

Using a circular send buffer is optional. Applications can use a TCP endpoint to send data by managing otLinkedBuffers directly. However, some applications may find it more convenient to have a circular send buffer; such applications can call [otTcpCircularSendBufferWrite()](api-tcp-ext#ot-tcp-circular-send-buffer-write) to "attach" a circular send buffer to a TCP endpoint and send out data on that TCP endpoint, relying on the circular send buffer to manage the underlying otLinkedBuffers.

[otTcpCircularSendBuffer](ot-tcp-circular-send-buffer) is implemented on top of the otLinkedBuffer-based API provided by an [otTcpEndpoint](ot-tcp-endpoint). Once attached to an [otTcpEndpoint](ot-tcp-endpoint), an [otTcpCircularSendBuffer](ot-tcp-circular-send-buffer) performs all the work of managing otLinkedBuffers for the connection. This means that, once an [otTcpCircularSendBuffer](ot-tcp-circular-send-buffer) is attached to an [otTcpEndpoint](ot-tcp-endpoint), the application should not call [otTcpSendByReference()](api-tcp#ot-tcp-send-by-reference) or [otTcpSendByExtension()](api-tcp#ot-tcp-send-by-extension) on that [otTcpEndpoint](ot-tcp-endpoint). Instead, the application should use [otTcpCircularSendBufferWrite()](api-tcp-ext#ot-tcp-circular-send-buffer-write) to add data to the send buffer.

The [otTcpForwardProgress()](api-tcp#ot-tcp-forward-progress) callback is the intended way for users to learn when space becomes available in the circular send buffer. On an [otTcpEndpoint](ot-tcp-endpoint) to which an [otTcpCircularSendBuffer](ot-tcp-circular-send-buffer) is attached, the application MUST install an [otTcpForwardProgress()](api-tcp#ot-tcp-forward-progress) callback and call [otTcpCircularSendBufferHandleForwardProgress()](api-tcp-ext#ot-tcp-circular-send-buffer-handle-forward-progress) on the attached [otTcpCircularSendBuffer](ot-tcp-circular-send-buffer) at the start of the callback function. It is recommended that the user NOT install an [otTcpSendDone()](api-tcp#ot-tcp-send-done) callback, as all management of otLinkedBuffers is handled by the circular send buffer.

The application should not inspect the fields of this structure directly; it should only interact with it via the TCP Circular Send Buffer API functions whose signature are provided in this file. 

###### Public Attributes (heading level 8)

###### mDataBuffer (heading level 9)

```
uint8_t* otTcpCircularSendBuffer::mDataBuffer
```

**Description:** Pointer to data in the circular send buffer.

###### mCapacity (heading level 9)

```
size_t otTcpCircularSendBuffer::mCapacity
```

**Description:** Length of the circular send buffer.

###### mStartIndex (heading level 9)

```
size_t otTcpCircularSendBuffer::mStartIndex
```

**Description:** Index of the first valid byte in the send buffer.

###### mCapacityUsed (heading level 9)

```
size_t otTcpCircularSendBuffer::mCapacityUsed
```

**Description:** Number of bytes stored in the send buffer.

###### mSendLinks (heading level 9)

```
otLinkedBuffer otTcpCircularSendBuffer::mSendLinks[2]
```

###### mFirstSendLinkIndex (heading level 9)

```
uint8_t otTcpCircularSendBuffer::mFirstSendLinkIndex
```

Context structure to use with mbedtls_ssl_set_bio. 

###### Public Attributes (heading level 8)

###### mEndpoint (heading level 9)

```
otTcpEndpoint* otTcpEndpointAndCircularSendBuffer::mEndpoint
```

###### mSendBuffer (heading level 9)

```
otTcpCircularSendBuffer* otTcpEndpointAndCircularSendBuffer::mSendBuffer
```

##### UDP

###### Modules

[UDP](api-udp)

[UDP Forward](api-udp-forward)

###### UDP Forward

This module includes functions for UDP forward feature. 

The functions in this module are available when udp-forward feature (`OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE`) is enabled. 

###### Typedefs (heading level 7)

###### otUdpForwarder (heading level 8)

`typedef void(* otUdpForwarder) (otMessage *aMessage, uint16_t aPeerPort, otIp6Address *aPeerAddr, uint16_t aSockPort, void *aContext)`

**Description:**

Pointer delivers the UDP packet to host and host should send the packet through its own network stack.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aMessage|A pointer to the UDP Message.|
||[in]|aPeerPort|The destination UDP port.|
||[in]|aPeerAddr|A pointer to the destination IPv6 address.|
||[in]|aSockPort|The source UDP port.|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### Functions (heading level 7)

###### otUdpForwardSetForwarder (heading level 8)

`void otUdpForwardSetForwarder(otInstance *aInstance, otUdpForwarder aForwarder, void *aContext)`

**Description:** Set UDP forward callback to deliver UDP packets to host.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otUdpForwarder](api-udp-forward#ot-udp-forwarder)|[in]|aForwarder|A pointer to a function called to forward UDP packet to host.|
|void *|[in]|aContext|A pointer to application-specific context.|

###### otUdpForwardReceive (heading level 8)

`void otUdpForwardReceive(otInstance *aInstance, otMessage *aMessage, uint16_t aPeerPort, const otIp6Address *aPeerAddr, uint16_t aSockPort)`

**Description:** Handle a UDP packet received from host.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the UDP Message.|
|uint16_t|[in]|aPeerPort|The source UDP port.|
|const [otIp6Address](ot-ip6-address) *|[in]|aPeerAddr|A pointer to the source address.|
|uint16_t|[in]|aSockPort|The destination UDP port.|

**Warnings**

- No matter the call success or fail, the message is freed.

###### otUdpIsPortInUse (heading level 8)

`bool otUdpIsPortInUse(otInstance *aInstance, uint16_t port)`

**Description:** Determines if the given UDP port is exclusively opened by OpenThread API.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|port|UDP port number to verify.|

###### UDP

This module includes functions that control UDP communication. 

###### Modules (heading level 7)

[otUdpReceiver](ot-udp-receiver)

[otUdpSocket](ot-udp-socket)

###### Enumerations (heading level 7)

###### otNetifIdentifier (heading level 8)

```
enum otNetifIdentifier {
    OT_NETIF_UNSPECIFIED = 0
    OT_NETIF_THREAD_HOST
    OT_NETIF_THREAD_INTERNAL
    OT_NETIF_BACKBONE
}
```

**Description:**

Defines the OpenThread network interface identifiers.

**Enumerator:**

|   |   |
|---|---|
|OT_NETIF_UNSPECIFIED|Unspecified network interface.|
|OT_NETIF_THREAD_HOST|The host Thread interface - allow use of platform UDP.|
|OT_NETIF_THREAD_INTERNAL|The internal Thread interface (within OpenThread) - do not use platform UDP.|
|OT_NETIF_BACKBONE|The Backbone interface.|

###### Typedefs (heading level 7)

###### otUdpHandler (heading level 8)

`typedef bool(* otUdpHandler) (void *aContext, const otMessage *aMessage, const otMessageInfo *aMessageInfo)`

**Description:**

Represents a callback to handle a received UDP message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to the application-specific context.|
||[in]|aMessage|A pointer to the received UDP message.|
||[in]|aMessageInfo|A pointer to the IPv6 message info structure.|

**Details:**

This callback is used by a UDP receiver (see `otUdpAddReceiver()`) to process an incoming UDP message.

This callback does not transfer ownership of `aMessage`. The callback implementation must not modify the message content. The message is guaranteed to be valid only within the context of the callback.

###### otUdpReceiver (heading level 8)

`typedef struct otUdpReceiver otUdpReceiver`

**Description:**

Represents a UDP receiver.

###### otUdpReceive (heading level 8)

`typedef void(* otUdpReceive) (void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)`

**Description:**

Callback function pointer to notify the application of a received UDP message on a UDP socket.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to the application-specific context.|
||[in]|aMessage|A pointer to the received UDP message.|
||[in]|aMessageInfo|A pointer to the IPv6 message info structure.|

**Details:**

This callback does not transfer ownership of `aMessage`. The callback implementation must not modify the message content. The message is guaranteed to be valid only within the context of the callback.

###### otNetifIdentifier (heading level 8)

`typedef enum otNetifIdentifier otNetifIdentifier`

**Description:**

Defines the OpenThread network interface identifiers.

###### otUdpSocket (heading level 8)

`typedef struct otUdpSocket otUdpSocket`

**Description:**

Represents a UDP socket.

###### Functions (heading level 7)

###### otUdpAddReceiver (heading level 8)

`otError otUdpAddReceiver(otInstance *aInstance, otUdpReceiver *aUdpReceiver)`

**Description:** Adds a UDP receiver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otUdpReceiver](ot-udp-receiver) *|[in]|aUdpReceiver|A pointer to the UDP receiver.|

###### otUdpRemoveReceiver (heading level 8)

`otError otUdpRemoveReceiver(otInstance *aInstance, otUdpReceiver *aUdpReceiver)`

**Description:** Removes a UDP receiver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otUdpReceiver](ot-udp-receiver) *|[in]|aUdpReceiver|A pointer to the UDP receiver.|

###### otUdpSendDatagram (heading level 8)

`otError otUdpSendDatagram(otInstance *aInstance, otMessage *aMessage, otMessageInfo *aMessageInfo)`

**Description:** Sends a UDP message without socket.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to a message without UDP header.|
|[otMessageInfo](ot-message-info) *|[in]|aMessageInfo|A pointer to a message info associated with `aMessage`.|

###### otUdpNewMessage (heading level 8)

`otMessage * otUdpNewMessage(otInstance *aInstance, const otMessageSettings *aSettings)`

**Description:** Allocate a new message buffer for sending a UDP message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otMessageSettings](ot-message-settings) *|[in]|aSettings|A pointer to the message settings or NULL to use default settings.|

**Note**

- If `aSettings` is 'NULL', the link layer security is enabled and the message priority is set to OT_MESSAGE_PRIORITY_NORMAL by default.

**Returns**

- A pointer to the message buffer or NULL if no message buffers are available or parameters are invalid.

**See Also**

- [otMessageFree](api-message#ot-message-free)

###### otUdpOpen (heading level 8)

`otError otUdpOpen(otInstance *aInstance, otUdpSocket *aSocket, otUdpReceive aCallback, void *aContext)`

**Description:** Open a UDP/IPv6 socket.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otUdpSocket](ot-udp-socket) *|[in]|aSocket|A pointer to a UDP socket structure.|
|[otUdpReceive](api-udp#ot-udp-receive)|[in]|aCallback|A pointer to the application callback function.|
|void *|[in]|aContext|A pointer to application-specific context.|

###### otUdpIsOpen (heading level 8)

`bool otUdpIsOpen(otInstance *aInstance, const otUdpSocket *aSocket)`

**Description:** Check if a UDP socket is open.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otUdpSocket](ot-udp-socket) *|[in]|aSocket|A pointer to a UDP socket structure.|

**Returns**

- Whether the UDP socket is open.

###### otUdpClose (heading level 8)

`otError otUdpClose(otInstance *aInstance, otUdpSocket *aSocket)`

**Description:** Close a UDP/IPv6 socket.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otUdpSocket](ot-udp-socket) *|[in]|aSocket|A pointer to a UDP socket structure.|

###### otUdpBind (heading level 8)

`otError otUdpBind(otInstance *aInstance, otUdpSocket *aSocket, const otSockAddr *aSockName, otNetifIdentifier aNetif)`

**Description:** Bind a UDP/IPv6 socket.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otUdpSocket](ot-udp-socket) *|[in]|aSocket|A pointer to a UDP socket structure.|
|const [otSockAddr](ot-sock-addr) *|[in]|aSockName|A pointer to an IPv6 socket address structure.|
|[otNetifIdentifier](api-udp#ot-netif-identifier)|[in]|aNetif|The network interface to bind.|

###### otUdpConnect (heading level 8)

`otError otUdpConnect(otInstance *aInstance, otUdpSocket *aSocket, const otSockAddr *aSockName)`

**Description:** Connect a UDP/IPv6 socket.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otUdpSocket](ot-udp-socket) *|[in]|aSocket|A pointer to a UDP socket structure.|
|const [otSockAddr](ot-sock-addr) *|[in]|aSockName|A pointer to an IPv6 socket address structure.|

###### otUdpSend (heading level 8)

`otError otUdpSend(otInstance *aInstance, otUdpSocket *aSocket, otMessage *aMessage, const otMessageInfo *aMessageInfo)`

**Description:** Send a UDP/IPv6 message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otUdpSocket](ot-udp-socket) *|[in]|aSocket|A pointer to a UDP socket structure.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to a message buffer.|
|const [otMessageInfo](ot-message-info) *|[in]|aMessageInfo|A pointer to a message info structure.|

If the return value is OT_ERROR_NONE, OpenThread takes ownership of `aMessage`, and the caller should no longer reference `aMessage`. If the return value is not OT_ERROR_NONE, the caller retains ownership of `aMessage`, including freeing `aMessage` if the message buffer is no longer needed.

###### otUdpGetSockets (heading level 8)

`otUdpSocket * otUdpGetSockets(otInstance *aInstance)`

**Description:** Gets the head of linked list of UDP Sockets.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- A pointer to the head of UDP Socket linked list.

Represents a UDP receiver. 

###### Public Attributes (heading level 8)

###### mNext (heading level 9)

```
struct otUdpReceiver* otUdpReceiver::mNext
```

**Description:** A pointer to the next UDP receiver (internal use only).

###### mHandler (heading level 9)

```
otUdpHandler otUdpReceiver::mHandler
```

**Description:** A function pointer to the receiver callback.

###### mContext (heading level 9)

```
void* otUdpReceiver::mContext
```

**Description:** A pointer to application-specific context.

Represents a UDP socket. 

###### Public Attributes (heading level 8)

###### mSockName (heading level 9)

```
otSockAddr otUdpSocket::mSockName
```

**Description:** The local IPv6 socket address.

###### mPeerName (heading level 9)

```
otSockAddr otUdpSocket::mPeerName
```

**Description:** The peer IPv6 socket address.

###### mHandler (heading level 9)

```
otUdpReceive otUdpSocket::mHandler
```

**Description:** A function pointer to the application callback.

###### mContext (heading level 9)

```
void* otUdpSocket::mContext
```

**Description:** A pointer to application-specific context.

###### mHandle (heading level 9)

```
void* otUdpSocket::mHandle
```

**Description:** A handle to platform's UDP.

###### mNext (heading level 9)

```
struct otUdpSocket* otUdpSocket::mNext
```

**Description:** A pointer to the next UDP socket (internal use only).

###### mNetifId (heading level 9)

```
otNetifIdentifier otUdpSocket::mNetifId
```

**Description:** The network interface identifier.

#### Link

##### Modules

[Link](api-link-link)

[Link Metrics](api-link-metrics)

[Raw Link](api-link-raw)

##### Raw Link

This module includes functions that control the raw link-layer configuration. 

###### Typedefs

###### otLinkRawReceiveDone (heading level 7)

`typedef void(* otLinkRawReceiveDone) (otInstance *aInstance, otRadioFrame *aFrame, otError aError)`

**Description:**

Pointer on receipt of a IEEE 802.15.4 frame.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aInstance|A pointer to an OpenThread instance.|
||[in]|aFrame|A pointer to the received frame or NULL if the receive operation was aborted.|
||[in]|aError|OT_ERROR_NONE when successfully received a frame. OT_ERROR_ABORT when reception was aborted and a frame was not received.|

**Details:**

###### otLinkRawTransmitDone (heading level 7)

`typedef void(* otLinkRawTransmitDone) (otInstance *aInstance, otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError)`

**Description:**

Pointer on receipt of a IEEE 802.15.4 frame.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aInstance|A pointer to an OpenThread instance.|
||[in]|aFrame|A pointer to the frame that was transmitted.|
||[in]|aAckFrame|A pointer to the ACK frame.|
||[in]|aError|OT_ERROR_NONE when the frame was transmitted. OT_ERROR_NO_ACK when the frame was transmitted but no ACK was received OT_ERROR_CHANNEL_ACCESS_FAILURE when the transmission could not take place due to activity on the channel. OT_ERROR_ABORT when transmission was aborted for other reasons.|

**Details:**

###### otLinkRawEnergyScanDone (heading level 7)

`typedef void(* otLinkRawEnergyScanDone) (otInstance *aInstance, int8_t aEnergyScanMaxRssi)`

**Description:**

Pointer on receipt of a IEEE 802.15.4 frame.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aInstance|A pointer to an OpenThread instance.|
||[in]|aEnergyScanMaxRssi|The maximum RSSI encountered on the scanned channel.|

**Details:**

###### Functions

###### otLinkRawSetReceiveDone (heading level 7)

`otError otLinkRawSetReceiveDone(otInstance *aInstance, otLinkRawReceiveDone aCallback)`

**Description:** Enables/disables the raw link-layer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otLinkRawReceiveDone](api-link-raw#ot-link-raw-receive-done)|[in]|aCallback|A pointer to a function called on receipt of a IEEE 802.15.4 frame. NULL to disable the raw-link layer.|

###### otLinkRawIsEnabled (heading level 7)

`bool otLinkRawIsEnabled(otInstance *aInstance)`

**Description:** Indicates whether or not the raw link-layer is enabled.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otLinkRawGetPromiscuous (heading level 7)

`bool otLinkRawGetPromiscuous(otInstance *aInstance)`

**Description:** Gets the status of promiscuous mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otLinkRawSetPromiscuous (heading level 7)

`otError otLinkRawSetPromiscuous(otInstance *aInstance, bool aEnable)`

**Description:** Enables or disables promiscuous mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnable|A value to enable or disable promiscuous mode.|

###### otLinkRawSetShortAddress (heading level 7)

`otError otLinkRawSetShortAddress(otInstance *aInstance, uint16_t aShortAddress)`

**Description:** Set the Short Address for address filtering.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aShortAddress|The IEEE 802.15.4 Short Address.|

###### otLinkRawSetAlternateShortAddress (heading level 7)

`otError otLinkRawSetAlternateShortAddress(otInstance *aInstance, otShortAddress aShortAddress)`

**Description:** Set the alternate short address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otShortAddress](radio-types#ot-short-address)|[in]|aShortAddress|The alternate short address. `OT_RADIO_INVALID_SHORT_ADDR` to clear.|

This is an optional API. Support for this is indicated by including the capability `OT_RADIO_CAPS_ALT_SHORT_ADDR` in `otLinkRawGetCaps()`.

When supported, the radio will accept received frames destined to the specified alternate short address in addition to the short address provided in `otLinkRawSetShortAddress()`.

The `aShortAddress` can be set to `OT_RADIO_INVALID_SHORT_ADDR` (0xfffe) to clear any previously set alternate short address.

###### otLinkRawSleep (heading level 7)

`otError otLinkRawSleep(otInstance *aInstance)`

**Description:** Transition the radio from Receive to Sleep.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Turn off the radio.

###### otLinkRawReceive (heading level 7)

`otError otLinkRawReceive(otInstance *aInstance)`

**Description:** Transitioning the radio from Sleep to Receive.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Turn on the radio.

###### otLinkRawGetTransmitBuffer (heading level 7)

`otRadioFrame * otLinkRawGetTransmitBuffer(otInstance *aInstance)`

**Description:** The radio transitions from Transmit to Receive.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Returns a pointer to the transmit buffer.

The caller forms the IEEE 802.15.4 frame in this buffer then calls [otLinkRawTransmit()](api-link-raw#ot-link-raw-transmit) to request transmission.

**Returns**

- A pointer to the transmit buffer or NULL if the raw link-layer isn't enabled.

###### otLinkRawTransmit (heading level 7)

`otError otLinkRawTransmit(otInstance *aInstance, otLinkRawTransmitDone aCallback)`

**Description:** Begins the transmit sequence on the radio.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otLinkRawTransmitDone](api-link-raw#ot-link-raw-transmit-done)|[in]|aCallback|A pointer to a function called on completion of the transmission.|

The caller must form the IEEE 802.15.4 frame in the buffer provided by [otLinkRawGetTransmitBuffer()](api-link-raw#ot-link-raw-get-transmit-buffer) before requesting transmission. The channel and transmit power are also included in the [otRadioFrame](ot-radio-frame) structure.

The transmit sequence consists of:

1. Transitioning the radio to Transmit from Receive.
2. Transmits the PSDU on the given channel and at the given transmit power.

###### otLinkRawGetRssi (heading level 7)

`int8_t otLinkRawGetRssi(otInstance *aInstance)`

**Description:** Get the most recent RSSI measurement.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The RSSI in dBm when it is valid. 127 when RSSI is invalid.

###### otLinkRawGetCaps (heading level 7)

`otRadioCaps otLinkRawGetCaps(otInstance *aInstance)`

**Description:** Get the radio capabilities.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The radio capability bit vector. The stack enables or disables some functions based on this value.

###### otLinkRawEnergyScan (heading level 7)

`otError otLinkRawEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration, otLinkRawEnergyScanDone aCallback)`

**Description:** Begins the energy scan sequence on the radio.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aScanChannel|The channel to perform the energy scan on.|
|uint16_t|[in]|aScanDuration|The duration, in milliseconds, for the channel to be scanned.|
|[otLinkRawEnergyScanDone](api-link-raw#ot-link-raw-energy-scan-done)|[in]|aCallback|A pointer to a function called on completion of a scanned channel.|

###### otLinkRawSrcMatchEnable (heading level 7)

`otError otLinkRawSrcMatchEnable(otInstance *aInstance, bool aEnable)`

**Description:** Enable/Disable source match for frame pending.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnable|Enable/disable source match for frame pending.|

###### otLinkRawSrcMatchAddShortEntry (heading level 7)

`otError otLinkRawSrcMatchAddShortEntry(otInstance *aInstance, uint16_t aShortAddress)`

**Description:** Adding short address to the source match table.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aShortAddress|The short address to be added.|

###### otLinkRawSrcMatchAddExtEntry (heading level 7)

`otError otLinkRawSrcMatchAddExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress)`

**Description:** Adding extended address to the source match table.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otExtAddress](ot-ext-address) *|[in]|aExtAddress|The extended address to be added.|

###### otLinkRawSrcMatchClearShortEntry (heading level 7)

`otError otLinkRawSrcMatchClearShortEntry(otInstance *aInstance, uint16_t aShortAddress)`

**Description:** Removing short address to the source match table.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aShortAddress|The short address to be removed.|

###### otLinkRawSrcMatchClearExtEntry (heading level 7)

`otError otLinkRawSrcMatchClearExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress)`

**Description:** Removing extended address to the source match table of the radio.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otExtAddress](ot-ext-address) *|[in]|aExtAddress|The extended address to be removed.|

###### otLinkRawSrcMatchClearShortEntries (heading level 7)

`otError otLinkRawSrcMatchClearShortEntries(otInstance *aInstance)`

**Description:** Removing all the short addresses from the source match table.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otLinkRawSrcMatchClearExtEntries (heading level 7)

`otError otLinkRawSrcMatchClearExtEntries(otInstance *aInstance)`

**Description:** Removing all the extended addresses from the source match table.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otLinkRawSetMacKey (heading level 7)

`otError otLinkRawSetMacKey(otInstance *aInstance, uint8_t aKeyIdMode, uint8_t aKeyId, const otMacKey *aPrevKey, const otMacKey *aCurrKey, const otMacKey *aNextKey)`

**Description:** Update MAC keys and key index.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aKeyIdMode|The key ID mode.|
|uint8_t|[in]|aKeyId|The key index.|
|const [otMacKey](ot-mac-key) *|[in]|aPrevKey|The previous MAC key.|
|const [otMacKey](ot-mac-key) *|[in]|aCurrKey|The current MAC key.|
|const [otMacKey](ot-mac-key) *|[in]|aNextKey|The next MAC key.|

###### otLinkRawSetMacFrameCounter (heading level 7)

`otError otLinkRawSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCounter)`

**Description:** Sets the current MAC frame counter value.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aMacFrameCounter|The MAC frame counter value.|

Always sets the MAC counter to the new given value `aMacFrameCounter` independent of the current value.

###### otLinkRawSetMacFrameCounterIfLarger (heading level 7)

`otError otLinkRawSetMacFrameCounterIfLarger(otInstance *aInstance, uint32_t aMacFrameCounter)`

**Description:** Sets the current MAC frame counter value only if the new value is larger than the current one.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aMacFrameCounter|The MAC frame counter value.|

###### otLinkRawGetRadioTime (heading level 7)

`uint64_t otLinkRawGetRadioTime(otInstance *aInstance)`

**Description:** Get current platform time (64bits width) of the radio chip.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The current radio time in microseconds.

##### Link

This module includes functions that control link-layer configuration. 

###### Modules

[otMacFilterEntry](ot-mac-filter-entry)

[otMacCounters](ot-mac-counters)

[otActiveScanResult](ot-active-scan-result)

[otEnergyScanResult](ot-energy-scan-result)

###### Enumerations

###### otMacFilterAddressMode (heading level 7)

```
enum otMacFilterAddressMode {
    OT_MAC_FILTER_ADDRESS_MODE_DISABLED
    OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST
    OT_MAC_FILTER_ADDRESS_MODE_DENYLIST
}
```

**Description:**

Defines address mode of the mac filter.

**Enumerator:**

|   |   |
|---|---|
|OT_MAC_FILTER_ADDRESS_MODE_DISABLED|Address filter is disabled.|
|OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST|Allowlist address filter mode is enabled.|
|OT_MAC_FILTER_ADDRESS_MODE_DENYLIST|Denylist address filter mode is enabled.|

###### Typedefs

###### otMacFilterIterator (heading level 7)

`typedef uint8_t otMacFilterIterator`

**Description:**

Used to iterate through mac filter entries.

###### otMacFilterAddressMode (heading level 7)

`typedef enum otMacFilterAddressMode otMacFilterAddressMode`

**Description:**

Defines address mode of the mac filter.

###### otMacFilterEntry (heading level 7)

`typedef struct otMacFilterEntry otMacFilterEntry`

**Description:**

Represents a Mac Filter entry.

###### otMacCounters (heading level 7)

`typedef struct otMacCounters otMacCounters`

**Description:**

Represents the MAC layer counters.

###### otActiveScanResult (heading level 7)

`typedef struct otActiveScanResult otActiveScanResult`

**Description:**

Represents a received IEEE 802.15.4 Beacon.

###### otEnergyScanResult (heading level 7)

`typedef struct otEnergyScanResult otEnergyScanResult`

**Description:**

Represents an energy scan result.

###### otHandleActiveScanResult (heading level 7)

`typedef void(* otHandleActiveScanResult) (otActiveScanResult *aResult, void *aContext)`

**Description:**

Pointer is called during an IEEE 802.15.4 Active Scan when an IEEE 802.15.4 Beacon is received or the scan completes.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aResult|A valid pointer to the beacon information or NULL when the active scan completes.|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### otHandleEnergyScanResult (heading level 7)

`typedef void(* otHandleEnergyScanResult) (otEnergyScanResult *aResult, void *aContext)`

**Description:**

Pointer is called during an IEEE 802.15.4 Energy Scan when the result for a channel is ready or the scan completes.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aResult|A valid pointer to the energy scan result information or NULL when the energy scan completes.|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### otLinkPcapCallback (heading level 7)

`typedef void(* otLinkPcapCallback) (const otRadioFrame *aFrame, bool aIsTx, void *aContext)`

**Description:**

Pointer is called when an IEEE 802.15.4 frame is received.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aFrame|A pointer to the received IEEE 802.15.4 frame.|
||[in]|aIsTx|Whether this frame is transmitted, not received.|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

**Note**

- This callback is called after FCS processing and `aFrame` may not contain the actual FCS that was received.
- This callback is called before IEEE 802.15.4 security processing.

###### Functions

###### otLinkActiveScan (heading level 7)

`otError otLinkActiveScan(otInstance *aInstance, uint32_t aScanChannels, uint16_t aScanDuration, otHandleActiveScanResult aCallback, void *aCallbackContext)`

**Description:** Starts an IEEE 802.15.4 Active Scan.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aScanChannels|A bit vector indicating which channels to scan (e.g. OT_CHANNEL_11_MASK).|
|uint16_t|[in]|aScanDuration|The time in milliseconds to spend scanning each channel.|
|[otHandleActiveScanResult](api-link-link#ot-handle-active-scan-result)|[in]|aCallback|A pointer to a function called on receiving a beacon or scan completes.|
|void *|[in]|aCallbackContext|A pointer to application-specific context.|

###### otLinkIsActiveScanInProgress (heading level 7)

`bool otLinkIsActiveScanInProgress(otInstance *aInstance)`

**Description:** Indicates whether or not an IEEE 802.15.4 Active Scan is currently in progress.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- true if an IEEE 802.15.4 Active Scan is in progress, false otherwise.

###### otLinkEnergyScan (heading level 7)

`otError otLinkEnergyScan(otInstance *aInstance, uint32_t aScanChannels, uint16_t aScanDuration, otHandleEnergyScanResult aCallback, void *aCallbackContext)`

**Description:** Starts an IEEE 802.15.4 Energy Scan.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aScanChannels|A bit vector indicating on which channels to perform energy scan.|
|uint16_t|[in]|aScanDuration|The time in milliseconds to spend scanning each channel.|
|[otHandleEnergyScanResult](api-link-link#ot-handle-energy-scan-result)|[in]|aCallback|A pointer to a function called to pass on scan result on indicate scan completion.|
|void *|[in]|aCallbackContext|A pointer to application-specific context.|

###### otLinkIsEnergyScanInProgress (heading level 7)

`bool otLinkIsEnergyScanInProgress(otInstance *aInstance)`

**Description:** Indicates whether or not an IEEE 802.15.4 Energy Scan is currently in progress.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- true if an IEEE 802.15.4 Energy Scan is in progress, false otherwise.

###### otLinkSendDataRequest (heading level 7)

`otError otLinkSendDataRequest(otInstance *aInstance)`

**Description:** Enqueues an IEEE 802.15.4 Data Request message for transmission.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otLinkIsInTransmitState (heading level 7)

`bool otLinkIsInTransmitState(otInstance *aInstance)`

**Description:** Indicates whether or not an IEEE 802.15.4 MAC is in the transmit state.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

MAC module is in the transmit state during CSMA/CA procedure, CCA, Data, Beacon or Data Request frame transmission and receiving an ACK of a transmitted frame. MAC module is not in the transmit state during transmission of an ACK frame or a Beacon Request frame.

**Returns**

- true if an IEEE 802.15.4 MAC is in the transmit state, false otherwise.

###### otLinkGetChannel (heading level 7)

`uint8_t otLinkGetChannel(otInstance *aInstance)`

**Description:** Get the IEEE 802.15.4 channel.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The IEEE 802.15.4 channel.

**See Also**

- [otLinkSetChannel](api-link-link#ot-link-set-channel)

###### otLinkSetChannel (heading level 7)

`otError otLinkSetChannel(otInstance *aInstance, uint8_t aChannel)`

**Description:** Set the IEEE 802.15.4 channel.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aChannel|The IEEE 802.15.4 channel.|

Succeeds only when Thread protocols are disabled. A successful call to this function invalidates the Active and Pending Operational Datasets in non-volatile memory.

**See Also**

- [otLinkGetChannel](api-link-link#ot-link-get-channel)

###### otLinkGetSupportedChannelMask (heading level 7)

`uint32_t otLinkGetSupportedChannelMask(otInstance *aInstance)`

**Description:** Get the supported channel mask of MAC layer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The supported channel mask as `uint32_t` with bit 0 (lsb) mapping to channel 0, bit 1 to channel 1, so on.

###### otLinkSetSupportedChannelMask (heading level 7)

`otError otLinkSetSupportedChannelMask(otInstance *aInstance, uint32_t aChannelMask)`

**Description:** Set the supported channel mask of MAC layer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aChannelMask|The supported channel mask (bit 0 or lsb mapping to channel 0, and so on).|

Succeeds only when Thread protocols are disabled.

###### otLinkGetExtendedAddress (heading level 7)

`const otExtAddress * otLinkGetExtendedAddress(otInstance *aInstance)`

**Description:** Gets the IEEE 802.15.4 Extended Address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- A pointer to the IEEE 802.15.4 Extended Address.

###### otLinkSetExtendedAddress (heading level 7)

`otError otLinkSetExtendedAddress(otInstance *aInstance, const otExtAddress *aExtAddress)`

**Description:** Sets the IEEE 802.15.4 Extended Address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otExtAddress](ot-ext-address) *|[in]|aExtAddress|A pointer to the IEEE 802.15.4 Extended Address.|

**Note**

- Only succeeds when Thread protocols are disabled.

###### otLinkGetFactoryAssignedIeeeEui64 (heading level 7)

`void otLinkGetFactoryAssignedIeeeEui64(otInstance *aInstance, otExtAddress *aEui64)`

**Description:** Get the factory-assigned IEEE EUI-64.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otExtAddress](ot-ext-address) *|[out]|aEui64|A pointer to where the factory-assigned IEEE EUI-64 is placed.|

###### otLinkGetPanId (heading level 7)

`otPanId otLinkGetPanId(otInstance *aInstance)`

**Description:** Get the IEEE 802.15.4 PAN ID.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The IEEE 802.15.4 PAN ID.

**See Also**

- [otLinkSetPanId](api-link-link#ot-link-set-pan-id)

###### otLinkSetPanId (heading level 7)

`otError otLinkSetPanId(otInstance *aInstance, otPanId aPanId)`

**Description:** Set the IEEE 802.15.4 PAN ID.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otPanId](radio-types#ot-pan-id)|[in]|aPanId|The IEEE 802.15.4 PAN ID.|

Succeeds only when Thread protocols are disabled. A successful call to this function also invalidates the Active and Pending Operational Datasets in non-volatile memory.

**See Also**

- [otLinkGetPanId](api-link-link#ot-link-get-pan-id)

###### otLinkGetPollPeriod (heading level 7)

`uint32_t otLinkGetPollPeriod(otInstance *aInstance)`

**Description:** Get the data poll period of sleepy end device.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The data poll period of sleepy end device in milliseconds.

**See Also**

- [otLinkSetPollPeriod](api-link-link#ot-link-set-poll-period)

###### otLinkSetPollPeriod (heading level 7)

`otError otLinkSetPollPeriod(otInstance *aInstance, uint32_t aPollPeriod)`

**Description:** Set/clear user-specified/external data poll period for sleepy end device.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aPollPeriod|data poll period in milliseconds.|

**Note**

- This function updates only poll period of sleepy end device. To update child timeout the function `otThreadSetChildTimeout()` shall be called.
- Minimal non-zero value should be `OPENTHREAD_CONFIG_MAC_MINIMUM_POLL_PERIOD` (10ms). Or zero to clear user-specified poll period.
- User-specified value should be no more than the maximal value 0x3FFFFFF ((1 << 26) - 1) allowed, otherwise it would be clipped by the maximal value.

**See Also**

- [otLinkGetPollPeriod](api-link-link#ot-link-get-poll-period)

###### otLinkGetShortAddress (heading level 7)

`otShortAddress otLinkGetShortAddress(otInstance *aInstance)`

**Description:** Get the IEEE 802.15.4 Short Address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The IEEE 802.15.4 Short Address.

###### otLinkGetAlternateShortAddress (heading level 7)

`otShortAddress otLinkGetAlternateShortAddress(otInstance *aInstance)`

**Description:** Get the IEEE 802.15.4 alternate short address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The alternate short address, or `OT_RADIO_INVALID_SHORT_ADDR` (0xfffe) if there is no alternate address.

###### otLinkGetMaxFrameRetriesDirect (heading level 7)

`uint8_t otLinkGetMaxFrameRetriesDirect(otInstance *aInstance)`

**Description:** Returns the maximum number of frame retries during direct transmission.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The maximum number of retries during direct transmission.

###### otLinkSetMaxFrameRetriesDirect (heading level 7)

`void otLinkSetMaxFrameRetriesDirect(otInstance *aInstance, uint8_t aMaxFrameRetriesDirect)`

**Description:** Sets the maximum number of frame retries during direct transmission.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aMaxFrameRetriesDirect|The maximum number of retries during direct transmission.|

###### otLinkGetMaxFrameRetriesIndirect (heading level 7)

`uint8_t otLinkGetMaxFrameRetriesIndirect(otInstance *aInstance)`

**Description:** Returns the maximum number of frame retries during indirect transmission.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The maximum number of retries during indirect transmission.

###### otLinkSetMaxFrameRetriesIndirect (heading level 7)

`void otLinkSetMaxFrameRetriesIndirect(otInstance *aInstance, uint8_t aMaxFrameRetriesIndirect)`

**Description:** Sets the maximum number of frame retries during indirect transmission.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aMaxFrameRetriesIndirect|The maximum number of retries during indirect transmission.|

###### otLinkGetFrameCounter (heading level 7)

`uint32_t otLinkGetFrameCounter(otInstance *aInstance)`

**Description:** Gets the current MAC frame counter value.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

**Returns**

- The current MAC frame counter value.

###### otLinkFilterGetAddressMode (heading level 7)

`otMacFilterAddressMode otLinkFilterGetAddressMode(otInstance *aInstance)`

**Description:** Gets the address mode of MAC filter.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Is available when `OPENTHREAD_CONFIG_MAC_FILTER_ENABLE` configuration is enabled.

**Returns**

- the address mode.

###### otLinkFilterSetAddressMode (heading level 7)

`void otLinkFilterSetAddressMode(otInstance *aInstance, otMacFilterAddressMode aMode)`

**Description:** Sets the address mode of MAC filter.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMacFilterAddressMode](api-link-link#ot-mac-filter-address-mode)|[in]|aMode|The address mode to set.|

Is available when `OPENTHREAD_CONFIG_MAC_FILTER_ENABLE` configuration is enabled.

###### otLinkFilterAddAddress (heading level 7)

`otError otLinkFilterAddAddress(otInstance *aInstance, const otExtAddress *aExtAddress)`

**Description:** Adds an Extended Address to MAC filter.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otExtAddress](ot-ext-address) *|[in]|aExtAddress|A pointer to the Extended Address (MUST NOT be NULL).|

Is available when `OPENTHREAD_CONFIG_MAC_FILTER_ENABLE` configuration is enabled.

###### otLinkFilterRemoveAddress (heading level 7)

`void otLinkFilterRemoveAddress(otInstance *aInstance, const otExtAddress *aExtAddress)`

**Description:** Removes an Extended Address from MAC filter.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otExtAddress](ot-ext-address) *|[in]|aExtAddress|A pointer to the Extended Address (MUST NOT be NULL).|

Is available when `OPENTHREAD_CONFIG_MAC_FILTER_ENABLE` configuration is enabled.

No action is performed if there is no existing entry in Filter matching the given Extended Address.

###### otLinkFilterClearAddresses (heading level 7)

`void otLinkFilterClearAddresses(otInstance *aInstance)`

**Description:** Clears all the Extended Addresses from MAC filter.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Is available when `OPENTHREAD_CONFIG_MAC_FILTER_ENABLE` configuration is enabled.

###### otLinkFilterGetNextAddress (heading level 7)

`otError otLinkFilterGetNextAddress(otInstance *aInstance, otMacFilterIterator *aIterator, otMacFilterEntry *aEntry)`

**Description:** Gets an in-use address filter entry.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMacFilterIterator](api-link-link#ot-mac-filter-iterator) *|[inout]|aIterator|A pointer to the MAC filter iterator context. To get the first in-use address filter entry, it should be set to OT_MAC_FILTER_ITERATOR_INIT. MUST NOT be NULL.|
|[otMacFilterEntry](ot-mac-filter-entry) *|[out]|aEntry|A pointer to where the information is placed. MUST NOT be NULL.|

Is available when `OPENTHREAD_CONFIG_MAC_FILTER_ENABLE` configuration is enabled.

###### otLinkFilterAddRssIn (heading level 7)

`otError otLinkFilterAddRssIn(otInstance *aInstance, const otExtAddress *aExtAddress, int8_t aRss)`

**Description:** Adds the specified Extended Address to the `RssIn` list (or modifies an existing address in the `RssIn` list) and sets the received signal strength (in dBm) entry for messages from that address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otExtAddress](ot-ext-address) *|[in]|aExtAddress|A pointer to the IEEE 802.15.4 Extended Address. MUST NOT be NULL.|
|int8_t|[in]|aRss|A received signal strength (in dBm).|

The Extended Address does not necessarily have to be in the `address allowlist/denylist` filter to set the `rss`. **Note**

- The `RssIn` list contains Extended Addresses whose `rss` or link quality indicator (`lqi`) values have been set to be different from the defaults.

Is available when `OPENTHREAD_CONFIG_MAC_FILTER_ENABLE` configuration is enabled.

###### otLinkFilterRemoveRssIn (heading level 7)

`void otLinkFilterRemoveRssIn(otInstance *aInstance, const otExtAddress *aExtAddress)`

**Description:** Removes the specified Extended Address from the `RssIn` list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otExtAddress](ot-ext-address) *|[in]|aExtAddress|A pointer to the IEEE 802.15.4 Extended Address. MUST NOT be NULL.|

Once removed from the `RssIn` list, this MAC address will instead use the default `rss` and `lqi` settings, assuming defaults have been set. (If no defaults have been set, the over-air signal is used.)

Is available when `OPENTHREAD_CONFIG_MAC_FILTER_ENABLE` configuration is enabled.

No action is performed if there is no existing entry in the `RssIn` list matching the specified Extended Address.

###### otLinkFilterSetDefaultRssIn (heading level 7)

`void otLinkFilterSetDefaultRssIn(otInstance *aInstance, int8_t aRss)`

**Description:** Sets the default received signal strength (in dBm) on MAC Filter.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|int8_t|[in]|aRss|The default received signal strength (in dBm) to set.|

Is available when `OPENTHREAD_CONFIG_MAC_FILTER_ENABLE` configuration is enabled.

The default RSS value is used for all received frames from addresses for which there is no explicit RSS-IN entry in the Filter list (added using `otLinkFilterAddRssIn()`).

###### otLinkFilterClearDefaultRssIn (heading level 7)

`void otLinkFilterClearDefaultRssIn(otInstance *aInstance)`

**Description:** Clears any previously set default received signal strength (in dBm) on MAC Filter.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Is available when `OPENTHREAD_CONFIG_MAC_FILTER_ENABLE` configuration is enabled.

###### otLinkFilterClearAllRssIn (heading level 7)

`void otLinkFilterClearAllRssIn(otInstance *aInstance)`

**Description:** Clears all the received signal strength (`rss`) and link quality indicator (`lqi`) entries (including defaults) from the `RssIn` list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Performing this action means that all Extended Addresses will use the on-air signal.

Is available when `OPENTHREAD_CONFIG_MAC_FILTER_ENABLE` configuration is enabled.

###### otLinkFilterGetNextRssIn (heading level 7)

`otError otLinkFilterGetNextRssIn(otInstance *aInstance, otMacFilterIterator *aIterator, otMacFilterEntry *aEntry)`

**Description:** Gets an in-use RssIn filter entry.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMacFilterIterator](api-link-link#ot-mac-filter-iterator) *|[inout]|aIterator|A pointer to the MAC filter iterator context. MUST NOT be NULL. To get the first entry, it should be set to OT_MAC_FILTER_ITERATOR_INIT.|
|[otMacFilterEntry](ot-mac-filter-entry) *|[out]|aEntry|A pointer to where the information is placed. The last entry would have the extended address as all 0xff to indicate the default received signal strength if it was set. `aEntry` MUST NOT be NULL.|

Is available when `OPENTHREAD_CONFIG_MAC_FILTER_ENABLE` configuration is enabled.

###### otLinkSetRadioFilterEnabled (heading level 7)

`void otLinkSetRadioFilterEnabled(otInstance *aInstance, bool aFilterEnabled)`

**Description:** Enables/disables IEEE 802.15.4 radio filter mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aFilterEnabled|TRUE to enable radio filter, FALSE to disable|

Is available when `OPENTHREAD_CONFIG_MAC_FILTER_ENABLE` configuration is enabled.

The radio filter is mainly intended for testing. It can be used to temporarily block all tx/rx on the 802.15.4 radio. When radio filter is enabled, radio is put to sleep instead of receive (to ensure device does not receive any frame and/or potentially send ack). Also the frame transmission requests return immediately without sending the frame over the air (return "no ack" error if ack is requested, otherwise return success).

###### otLinkIsRadioFilterEnabled (heading level 7)

`bool otLinkIsRadioFilterEnabled(otInstance *aInstance)`

**Description:** Indicates whether the IEEE 802.15.4 radio filter is enabled or not.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

Is available when `OPENTHREAD_CONFIG_MAC_FILTER_ENABLE` configuration is enabled.

###### otLinkConvertRssToLinkQuality (heading level 7)

`uint8_t otLinkConvertRssToLinkQuality(otInstance *aInstance, int8_t aRss)`

**Description:** Converts received signal strength to link quality.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|int8_t|[in]|aRss|The received signal strength value to be converted.|

**Returns**

- Link quality value mapping to `aRss`.

###### otLinkConvertLinkQualityToRss (heading level 7)

`int8_t otLinkConvertLinkQualityToRss(otInstance *aInstance, uint8_t aLinkQuality)`

**Description:** Converts link quality to typical received signal strength.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aLinkQuality|LinkQuality value, should be in range [0,3].|

**Returns**

- Typical platform received signal strength mapping to `aLinkQuality`.

###### otLinkGetTxDirectRetrySuccessHistogram (heading level 7)

`const uint32_t * otLinkGetTxDirectRetrySuccessHistogram(otInstance *aInstance, uint8_t *aNumberOfEntries)`

**Description:** Gets histogram of retries for a single direct packet until success.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t *|[out]|aNumberOfEntries|A pointer to where the size of returned histogram array is placed.|

Is valid when OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE configuration is enabled.

**Returns**

- A pointer to the histogram of retries (in a form of an array). The n-th element indicates that the packet has been sent with n-th retry.

###### otLinkGetTxIndirectRetrySuccessHistogram (heading level 7)

`const uint32_t * otLinkGetTxIndirectRetrySuccessHistogram(otInstance *aInstance, uint8_t *aNumberOfEntries)`

**Description:** Gets histogram of retries for a single indirect packet until success.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t *|[out]|aNumberOfEntries|A pointer to where the size of returned histogram array is placed.|

Is valid when OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE configuration is enabled.

**Returns**

- A pointer to the histogram of retries (in a form of an array). The n-th element indicates that the packet has been sent with n-th retry.

###### otLinkResetTxRetrySuccessHistogram (heading level 7)

`void otLinkResetTxRetrySuccessHistogram(otInstance *aInstance)`

**Description:** Clears histogram statistics for direct and indirect transmissions.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Is valid when OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE configuration is enabled.

###### otLinkGetCounters (heading level 7)

`const otMacCounters * otLinkGetCounters(otInstance *aInstance)`

**Description:** Get the MAC layer counters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- A pointer to the MAC layer counters.

###### otLinkResetCounters (heading level 7)

`void otLinkResetCounters(otInstance *aInstance)`

**Description:** Resets the MAC layer counters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otLinkSetPcapCallback (heading level 7)

`void otLinkSetPcapCallback(otInstance *aInstance, otLinkPcapCallback aPcapCallback, void *aCallbackContext)`

**Description:** Registers a callback to provide received raw IEEE 802.15.4 frames.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otLinkPcapCallback](api-link-link#ot-link-pcap-callback)|[in]|aPcapCallback|A pointer to a function that is called when receiving an IEEE 802.15.4 link frame or NULL to disable the callback.|
|void *|[in]|aCallbackContext|A pointer to application-specific context.|

###### otLinkIsPromiscuous (heading level 7)

`bool otLinkIsPromiscuous(otInstance *aInstance)`

**Description:** Indicates whether or not promiscuous mode is enabled at the link layer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otLinkSetPromiscuous (heading level 7)

`otError otLinkSetPromiscuous(otInstance *aInstance, bool aPromiscuous)`

**Description:** Enables or disables the link layer promiscuous mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aPromiscuous|true to enable promiscuous mode, or false otherwise.|

**Note**

- Promiscuous mode may only be enabled when the Thread interface is disabled.

###### otLinkGetCslChannel (heading level 7)

`uint8_t otLinkGetCslChannel(otInstance *aInstance)`

**Description:** Gets the CSL channel.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The CSL channel.

###### otLinkSetCslChannel (heading level 7)

`otError otLinkSetCslChannel(otInstance *aInstance, uint8_t aChannel)`

**Description:** Sets the CSL channel.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aChannel|The CSL sample channel. Channel value should be `0` (Set CSL Channel unspecified) or within the range [1, 10] (if 915-MHz supported) and [11, 26] (if 2.4 GHz supported).|

###### otLinkGetCslPeriod (heading level 7)

`uint32_t otLinkGetCslPeriod(otInstance *aInstance)`

**Description:** Gets the CSL period in microseconds.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The CSL period in microseconds.

###### otLinkSetCslPeriod (heading level 7)

`otError otLinkSetCslPeriod(otInstance *aInstance, uint32_t aPeriod)`

**Description:** Sets the CSL period in microseconds.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aPeriod|The CSL period in microseconds.|

Disable CSL by setting this parameter to `0`.

The CSL period MUST be a multiple of `OT_LINK_CSL_PERIOD_TEN_SYMBOLS_UNIT_IN_USEC`, otherwise `OT_ERROR_INVALID_ARGS` is returned.

###### otLinkGetCslTimeout (heading level 7)

`uint32_t otLinkGetCslTimeout(otInstance *aInstance)`

**Description:** Gets the CSL timeout.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The CSL timeout in seconds.

###### otLinkSetCslTimeout (heading level 7)

`otError otLinkSetCslTimeout(otInstance *aInstance, uint32_t aTimeout)`

**Description:** Sets the CSL timeout in seconds.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aTimeout|The CSL timeout in seconds.|

###### otLinkGetCcaFailureRate (heading level 7)

`uint16_t otLinkGetCcaFailureRate(otInstance *aInstance)`

**Description:** Returns the current CCA (Clear Channel Assessment) failure rate.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

The rate is maintained over a window of (roughly) last `OPENTHREAD_CONFIG_CCA_FAILURE_RATE_AVERAGING_WINDOW` frame transmissions.

**Returns**

- The CCA failure rate with maximum value `0xffff` corresponding to 100% failure rate.

###### otLinkSetEnabled (heading level 7)

`otError otLinkSetEnabled(otInstance *aInstance, bool aEnable)`

**Description:** Enables or disables the link layer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnable|true to enable the link layer, or false otherwise.|

**Note**

- The link layer may only be enabled / disabled when the Thread Interface is disabled.

###### otLinkIsEnabled (heading level 7)

`bool otLinkIsEnabled(otInstance *aInstance)`

**Description:** Indicates whether or not the link layer is enabled.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otLinkIsCslEnabled (heading level 7)

`bool otLinkIsCslEnabled(otInstance *aInstance)`

**Description:** Indicates whether or not CSL is enabled.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otLinkIsCslSupported (heading level 7)

`bool otLinkIsCslSupported(otInstance *aInstance)`

**Description:** Indicates whether the device is connected to a parent which supports CSL.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

###### otLinkSendEmptyData (heading level 7)

`otError otLinkSendEmptyData(otInstance *aInstance)`

**Description:** Instructs the device to send an empty IEEE 802.15.4 data frame.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Is only supported on an Rx-Off-When-Idle device to send an empty data frame to its parent. Note: available only when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.

###### otLinkSetRegion (heading level 7)

`otError otLinkSetRegion(otInstance *aInstance, uint16_t aRegionCode)`

**Description:** Sets the region code.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint16_t|[in]|aRegionCode|The radio region code. The `aRegionCode >> 8` is first ascii char and the `aRegionCode & 0xff` is the second ascii char.|

The radio region format is the 2-bytes ascii representation of the ISO 3166 alpha-2 code.

###### otLinkGetRegion (heading level 7)

`otError otLinkGetRegion(otInstance *aInstance, uint16_t *aRegionCode)`

**Description:** Get the region code.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint16_t *|[out]|aRegionCode|The radio region code. The `aRegionCode >> 8` is first ascii char and the `aRegionCode & 0xff` is the second ascii char.|

The radio region format is the 2-bytes ascii representation of the ISO 3166 alpha-2 code.

###### otLinkGetWakeupChannel (heading level 7)

`uint8_t otLinkGetWakeupChannel(otInstance *aInstance)`

**Description:** Gets the Wake-up channel.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Requires `OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE` or `OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE`.

**Returns**

- The Wake-up channel.

###### otLinkSetWakeupChannel (heading level 7)

`otError otLinkSetWakeupChannel(otInstance *aInstance, uint8_t aChannel)`

**Description:** Sets the Wake-up channel.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aChannel|The Wake-up sample channel. Channel value should be `0` (Set Wake-up Channel unspecified, which means the device will use the PAN channel) or within the range [1, 10] (if 915-MHz supported) and [11, 26] (if 2.4 GHz supported).|

Requires `OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE` or `OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE`.

###### otLinkSetWakeUpListenEnabled (heading level 7)

`otError otLinkSetWakeUpListenEnabled(otInstance *aInstance, bool aEnable)`

**Description:** Enables or disables listening for wake-up frames.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnable|true to enable listening for wake-up frames, or false otherwise.|

Requires `OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE`.

###### otLinkIsWakeupListenEnabled (heading level 7)

`bool otLinkIsWakeupListenEnabled(otInstance *aInstance)`

**Description:** Returns whether listening for wake-up frames is enabled.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Requires `OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE`.

###### otLinkGetWakeupListenParameters (heading level 7)

`void otLinkGetWakeupListenParameters(otInstance *aInstance, uint32_t *aInterval, uint32_t *aDuration)`

**Description:** Get the wake-up listen parameters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t *|[out]|aInterval|A pointer to return the wake-up listen interval in microseconds.|
|uint32_t *|[out]|aDuration|A pointer to return the wake-up listen duration in microseconds.|

Requires `OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE`.

###### otLinkSetWakeupListenParameters (heading level 7)

`otError otLinkSetWakeupListenParameters(otInstance *aInstance, uint32_t aInterval, uint32_t aDuration)`

**Description:** Set the wake-up listen parameters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aInterval|The wake-up listen interval in microseconds.|
|uint32_t|[in]|aDuration|The wake-up listen duration in microseconds.|

The listen interval must be greater than the listen duration. The listen duration must be greater or equal than the minimum supported.

Requires `OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE`.

###### otLinkSetRxOnWhenIdle (heading level 7)

`otError otLinkSetRxOnWhenIdle(otInstance *aInstance, bool aRxOnWhenIdle)`

**Description:** Sets the rx-on-when-idle state.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aRxOnWhenIdle|TRUE to keep radio in Receive state, FALSE to put to Sleep state during idle periods.|

###### Macros

`#define OT_US_PER_TEN_SYMBOLS OT_RADIO_TEN_SYMBOLS_TIME`

**Description**: Time for 10 symbols in units of microseconds.

`#define OT_MAC_FILTER_FIXED_RSS_DISABLED 127`

**Description**: Used to indicate no fixed received signal strength was set.

`#define OT_MAC_FILTER_ITERATOR_INIT 0`

**Description**: Initializer for otMacFilterIterator.

`#define OT_LINK_CSL_PERIOD_TEN_SYMBOLS_UNIT_IN_USEC (160)`

**Description**: Represents CSL period ten symbols unit in microseconds.

Represents a Mac Filter entry. 

###### Public Attributes (heading level 7)

###### mExtAddress (heading level 8)

```
otExtAddress otMacFilterEntry::mExtAddress
```

**Description:** IEEE 802.15.4 Extended Address.

###### mRssIn (heading level 8)

```
int8_t otMacFilterEntry::mRssIn
```

**Description:** Received signal strength.

Represents the MAC layer counters. 

###### Public Attributes (heading level 7)

###### mTxTotal (heading level 8)

```
uint32_t otMacCounters::mTxTotal
```

**Description:** The total number of unique MAC frame transmission requests.

**Details:** Note that this counter is incremented for each MAC transmission request only by one, regardless of the amount of CCA failures, CSMA-CA attempts, or retransmissions.

This increment rule applies to the following counters:

- `mTxUnicast`
- `mTxBroadcast`
- `mTxAckRequested`
- `mTxNoAckRequested`
- `mTxData`
- `mTxDataPoll`
- `mTxBeacon`
- `mTxBeaconRequest`
- `mTxOther`
- `mTxErrAbort`
- `mTxErrBusyChannel`

The following equations are valid:

- `mTxTotal` = `mTxUnicast` + `mTxBroadcast`
- `mTxTotal` = `mTxAckRequested` + `mTxNoAckRequested`
- `mTxTotal` = `mTxData` + `mTxDataPoll` + `mTxBeacon` + `mTxBeaconRequest` + `mTxOther`

###### mTxUnicast (heading level 8)

```
uint32_t otMacCounters::mTxUnicast
```

**Description:** The total number of unique unicast MAC frame transmission requests.

###### mTxBroadcast (heading level 8)

```
uint32_t otMacCounters::mTxBroadcast
```

**Description:** The total number of unique broadcast MAC frame transmission requests.

###### mTxAckRequested (heading level 8)

```
uint32_t otMacCounters::mTxAckRequested
```

**Description:** The total number of unique MAC frame transmission requests with requested acknowledgment.

###### mTxAcked (heading level 8)

```
uint32_t otMacCounters::mTxAcked
```

**Description:** The total number of unique MAC frame transmission requests that were acked.

###### mTxNoAckRequested (heading level 8)

```
uint32_t otMacCounters::mTxNoAckRequested
```

**Description:** The total number of unique MAC frame transmission requests without requested acknowledgment.

###### mTxData (heading level 8)

```
uint32_t otMacCounters::mTxData
```

**Description:** The total number of unique MAC Data frame transmission requests.

###### mTxDataPoll (heading level 8)

```
uint32_t otMacCounters::mTxDataPoll
```

**Description:** The total number of unique MAC Data Poll frame transmission requests.

###### mTxBeacon (heading level 8)

```
uint32_t otMacCounters::mTxBeacon
```

**Description:** The total number of unique MAC Beacon frame transmission requests.

###### mTxBeaconRequest (heading level 8)

```
uint32_t otMacCounters::mTxBeaconRequest
```

**Description:** The total number of unique MAC Beacon Request frame transmission requests.

###### mTxOther (heading level 8)

```
uint32_t otMacCounters::mTxOther
```

**Description:** The total number of unique other MAC frame transmission requests.

**Details:** This counter is currently used for counting out-of-band frames.

###### mTxRetry (heading level 8)

```
uint32_t otMacCounters::mTxRetry
```

**Description:** The total number of MAC retransmission attempts.

**Details:** Note that this counter is incremented by one for each retransmission attempt that may be triggered by lack of acknowledgement, CSMA/CA failure, or other type of transmission error. The `mTxRetry` counter is incremented both for unicast and broadcast MAC frames.

Modify the following configuration parameters to control the amount of retransmissions in the system:

- OPENTHREAD_CONFIG_MAC_DEFAULT_MAX_FRAME_RETRIES_DIRECT
- OPENTHREAD_CONFIG_MAC_DEFAULT_MAX_FRAME_RETRIES_INDIRECT
- OPENTHREAD_CONFIG_MAC_TX_NUM_BCAST
- OPENTHREAD_CONFIG_MAC_MAX_CSMA_BACKOFFS_DIRECT
- OPENTHREAD_CONFIG_MAC_MAX_CSMA_BACKOFFS_INDIRECT

Currently, this counter is invalid if the platform's radio driver capability includes [OT_RADIO_CAPS_TRANSMIT_RETRIES](radio-types#ot-radio-caps-transmit-retries).

###### mTxDirectMaxRetryExpiry (heading level 8)

```
uint32_t otMacCounters::mTxDirectMaxRetryExpiry
```

**Description:** The total number of unique MAC transmission packets that meet maximal retry limit for direct packets.

###### mTxIndirectMaxRetryExpiry (heading level 8)

```
uint32_t otMacCounters::mTxIndirectMaxRetryExpiry
```

**Description:** The total number of unique MAC transmission packets that meet maximal retry limit for indirect packets.

###### mTxErrCca (heading level 8)

```
uint32_t otMacCounters::mTxErrCca
```

**Description:** The total number of CCA failures.

**Details:** The meaning of this counter can be different and it depends on the platform's radio driver capabilities.

If [OT_RADIO_CAPS_CSMA_BACKOFF](radio-types#ot-radio-caps-csma-backoff) is enabled, this counter represents the total number of full CSMA/CA failed attempts and it is incremented by one also for each retransmission (in case of a CSMA/CA fail).

If [OT_RADIO_CAPS_TRANSMIT_RETRIES](radio-types#ot-radio-caps-transmit-retries) is enabled, this counter represents the total number of full CSMA/CA failed attempts and it is incremented by one for each individual data frame request (regardless of the amount of retransmissions).

###### mTxErrAbort (heading level 8)

```
uint32_t otMacCounters::mTxErrAbort
```

**Description:** The total number of unique MAC transmission request failures cause by an abort error.

###### mTxErrBusyChannel (heading level 8)

```
uint32_t otMacCounters::mTxErrBusyChannel
```

**Description:** The total number of unique MAC transmission requests failures caused by a busy channel (a CSMA/CA fail).

###### mRxTotal (heading level 8)

```
uint32_t otMacCounters::mRxTotal
```

**Description:** The total number of received frames.

**Details:** This counter counts all frames reported by the platform's radio driver, including frames that were dropped, for example because of an FCS error.

###### mRxUnicast (heading level 8)

```
uint32_t otMacCounters::mRxUnicast
```

**Description:** The total number of unicast frames received.

###### mRxBroadcast (heading level 8)

```
uint32_t otMacCounters::mRxBroadcast
```

**Description:** The total number of broadcast frames received.

###### mRxData (heading level 8)

```
uint32_t otMacCounters::mRxData
```

**Description:** The total number of MAC Data frames received.

###### mRxDataPoll (heading level 8)

```
uint32_t otMacCounters::mRxDataPoll
```

**Description:** The total number of MAC Data Poll frames received.

###### mRxBeacon (heading level 8)

```
uint32_t otMacCounters::mRxBeacon
```

**Description:** The total number of MAC Beacon frames received.

###### mRxBeaconRequest (heading level 8)

```
uint32_t otMacCounters::mRxBeaconRequest
```

**Description:** The total number of MAC Beacon Request frames received.

###### mRxOther (heading level 8)

```
uint32_t otMacCounters::mRxOther
```

**Description:** The total number of other types of frames received.

###### mRxAddressFiltered (heading level 8)

```
uint32_t otMacCounters::mRxAddressFiltered
```

**Description:** The total number of frames dropped by MAC Filter module, for example received from denylisted node.

###### mRxDestAddrFiltered (heading level 8)

```
uint32_t otMacCounters::mRxDestAddrFiltered
```

**Description:** The total number of frames dropped by destination address check, for example received frame for other node.

###### mRxDuplicated (heading level 8)

```
uint32_t otMacCounters::mRxDuplicated
```

**Description:** The total number of frames dropped due to duplication, that is when the frame has been already received.

**Details:** This counter may be incremented, for example when ACK frame generated by the receiver hasn't reached transmitter node which performed retransmission.

###### mRxErrNoFrame (heading level 8)

```
uint32_t otMacCounters::mRxErrNoFrame
```

**Description:** The total number of frames dropped because of missing or malformed content.

###### mRxErrUnknownNeighbor (heading level 8)

```
uint32_t otMacCounters::mRxErrUnknownNeighbor
```

**Description:** The total number of frames dropped due to unknown neighbor.

###### mRxErrInvalidSrcAddr (heading level 8)

```
uint32_t otMacCounters::mRxErrInvalidSrcAddr
```

**Description:** The total number of frames dropped due to invalid source address.

###### mRxErrSec (heading level 8)

```
uint32_t otMacCounters::mRxErrSec
```

**Description:** The total number of frames dropped due to security error.

**Details:** This counter may be incremented, for example when lower than expected Frame Counter is used to encrypt the frame.

###### mRxErrFcs (heading level 8)

```
uint32_t otMacCounters::mRxErrFcs
```

**Description:** The total number of frames dropped due to invalid FCS.

###### mRxErrOther (heading level 8)

```
uint32_t otMacCounters::mRxErrOther
```

**Description:** The total number of frames dropped due to other error.

Represents a received IEEE 802.15.4 Beacon. 

###### Public Attributes (heading level 7)

###### mExtAddress (heading level 8)

```
otExtAddress otActiveScanResult::mExtAddress
```

**Description:** IEEE 802.15.4 Extended Address.

###### mNetworkName (heading level 8)

```
otNetworkName otActiveScanResult::mNetworkName
```

**Description:** Thread Network Name.

###### mExtendedPanId (heading level 8)

```
otExtendedPanId otActiveScanResult::mExtendedPanId
```

**Description:** Thread Extended PAN ID.

###### mSteeringData (heading level 8)

```
otSteeringData otActiveScanResult::mSteeringData
```

**Description:** Steering Data.

###### mPanId (heading level 8)

```
uint16_t otActiveScanResult::mPanId
```

**Description:** IEEE 802.15.4 PAN ID.

###### mJoinerUdpPort (heading level 8)

```
uint16_t otActiveScanResult::mJoinerUdpPort
```

**Description:** Joiner UDP Port.

###### mChannel (heading level 8)

```
uint8_t otActiveScanResult::mChannel
```

**Description:** IEEE 802.15.4 Channel.

###### mRssi (heading level 8)

```
int8_t otActiveScanResult::mRssi
```

**Description:** RSSI (dBm)

###### mLqi (heading level 8)

```
uint8_t otActiveScanResult::mLqi
```

**Description:** LQI.

###### mVersion (heading level 8)

```
unsigned int otActiveScanResult::mVersion
```

**Description:** Version.

###### mIsNative (heading level 8)

```
bool otActiveScanResult::mIsNative
```

**Description:** Native Commissioner flag.

###### mDiscover (heading level 8)

```
bool otActiveScanResult::mDiscover
```

**Description:** Result from MLE Discovery.

###### mIsJoinable (heading level 8)

```
bool otActiveScanResult::mIsJoinable
```

**Description:** Joining Permitted flag.

Represents an energy scan result. 

###### Public Attributes (heading level 7)

###### mChannel (heading level 8)

```
uint8_t otEnergyScanResult::mChannel
```

**Description:** IEEE 802.15.4 Channel.

###### mMaxRssi (heading level 8)

```
int8_t otEnergyScanResult::mMaxRssi
```

**Description:** The max RSSI (dBm)

##### Link Metrics

This module includes functions that control the Link Metrics protocol. 

###### Modules

[otLinkMetricsValues](ot-link-metrics-values)

[otLinkMetricsSeriesFlags](ot-link-metrics-series-flags)

###### Enumerations

###### otLinkMetricsEnhAckFlags (heading level 7)

```
enum otLinkMetricsEnhAckFlags {
    OT_LINK_METRICS_ENH_ACK_CLEAR = 0
    OT_LINK_METRICS_ENH_ACK_REGISTER = 1
}
```

**Description:**

Enhanced-ACK Flags.

**Details:**

These are used in Enhanced-ACK Based Probing to indicate whether to register or clear the probing.

**Enumerator:**

|   |   |
|---|---|
|OT_LINK_METRICS_ENH_ACK_CLEAR|Clear.|
|OT_LINK_METRICS_ENH_ACK_REGISTER|Register.|

###### otLinkMetricsStatus (heading level 7)

```
enum otLinkMetricsStatus {
    OT_LINK_METRICS_STATUS_SUCCESS = 0
    OT_LINK_METRICS_STATUS_CANNOT_SUPPORT_NEW_SERIES = 1
    OT_LINK_METRICS_STATUS_SERIESID_ALREADY_REGISTERED = 2
    OT_LINK_METRICS_STATUS_SERIESID_NOT_RECOGNIZED = 3
    OT_LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED = 4
    OT_LINK_METRICS_STATUS_OTHER_ERROR = 254
}
```

**Description:**

Link Metrics Status values.

**Enumerator:**

|   |   |
|---|---|
|OT_LINK_METRICS_STATUS_SUCCESS||
|OT_LINK_METRICS_STATUS_CANNOT_SUPPORT_NEW_SERIES||
|OT_LINK_METRICS_STATUS_SERIESID_ALREADY_REGISTERED||
|OT_LINK_METRICS_STATUS_SERIESID_NOT_RECOGNIZED||
|OT_LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED||
|OT_LINK_METRICS_STATUS_OTHER_ERROR||

###### Typedefs

###### otLinkMetricsValues (heading level 7)

`typedef struct otLinkMetricsValues otLinkMetricsValues`

**Description:**

Represents the result (value) for a Link Metrics query.

###### otLinkMetricsSeriesFlags (heading level 7)

`typedef struct otLinkMetricsSeriesFlags otLinkMetricsSeriesFlags`

**Description:**

Represents which frames are accounted in a Forward Tracking Series.

###### otLinkMetricsEnhAckFlags (heading level 7)

`typedef enum otLinkMetricsEnhAckFlags otLinkMetricsEnhAckFlags`

**Description:**

Enhanced-ACK Flags.

**Details:**

These are used in Enhanced-ACK Based Probing to indicate whether to register or clear the probing.

###### otLinkMetricsStatus (heading level 7)

`typedef enum otLinkMetricsStatus otLinkMetricsStatus`

**Description:**

Link Metrics Status values.

###### otLinkMetricsReportCallback (heading level 7)

`typedef void(* otLinkMetricsReportCallback) (const otIp6Address *aSource, const otLinkMetricsValues *aMetricsValues, otLinkMetricsStatus aStatus, void *aContext)`

**Description:**

Pointer is called when a Link Metrics report is received.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aSource|A pointer to the source address.|
||[in]|aMetricsValues|A pointer to the Link Metrics values (the query result).|
||[in]|aStatus|The status code in the report (only useful when `aMetricsValues` is NULL).|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### otLinkMetricsMgmtResponseCallback (heading level 7)

`typedef void(* otLinkMetricsMgmtResponseCallback) (const otIp6Address *aSource, otLinkMetricsStatus aStatus, void *aContext)`

**Description:**

Pointer is called when a Link Metrics Management Response is received.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aSource|A pointer to the source address.|
||[in]|aStatus|The status code in the response.|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### otLinkMetricsEnhAckProbingIeReportCallback (heading level 7)

`typedef void(* otLinkMetricsEnhAckProbingIeReportCallback) (otShortAddress aShortAddress, const otExtAddress *aExtAddress, const otLinkMetricsValues *aMetricsValues, void *aContext)`

**Description:**

Pointer is called when Enh-ACK Probing IE is received.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aShortAddress|The Mac short address of the Probing Subject.|
||[in]|aExtAddress|A pointer to the Mac extended address of the Probing Subject.|
||[in]|aMetricsValues|A pointer to the Link Metrics values obtained from the IE.|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### Functions

###### otLinkMetricsQuery (heading level 7)

`otError otLinkMetricsQuery(otInstance *aInstance, const otIp6Address *aDestination, uint8_t aSeriesId, const otLinkMetrics *aLinkMetricsFlags, otLinkMetricsReportCallback aCallback, void *aCallbackContext)`

**Description:** Sends an MLE Data Request to query Link Metrics.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Address](ot-ip6-address) *|[in]|aDestination|A pointer to the destination address.|
|uint8_t|[in]|aSeriesId|The Series ID to query about, 0 for Single Probe.|
|const [otLinkMetrics](ot-link-metrics) *|[in]|aLinkMetricsFlags|A pointer to flags specifying what metrics to query.|
|[otLinkMetricsReportCallback](api-link-metrics#ot-link-metrics-report-callback)|[in]|aCallback|A pointer to a function that is called when Link Metrics report is received.|
|void *|[in]|aCallbackContext|A pointer to application-specific context.|

It could be either Single Probe or Forward Tracking Series.

###### otLinkMetricsConfigForwardTrackingSeries (heading level 7)

`otError otLinkMetricsConfigForwardTrackingSeries(otInstance *aInstance, const otIp6Address *aDestination, uint8_t aSeriesId, otLinkMetricsSeriesFlags aSeriesFlags, const otLinkMetrics *aLinkMetricsFlags, otLinkMetricsMgmtResponseCallback aCallback, void *aCallbackContext)`

**Description:** Sends an MLE Link Metrics Management Request to configure or clear a Forward Tracking Series.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Address](ot-ip6-address) *|[in]|aDestination|A pointer to the destination address.|
|uint8_t|[in]|aSeriesId|The Series ID to operate with.|
|[otLinkMetricsSeriesFlags](ot-link-metrics-series-flags)|[in]|aSeriesFlags|The Series Flags that specifies which frames are to be accounted.|
|const [otLinkMetrics](ot-link-metrics) *|[in]|aLinkMetricsFlags|A pointer to flags specifying what metrics to query. Should be `NULL` when `aSeriesFlags` is `0`.|
|[otLinkMetricsMgmtResponseCallback](api-link-metrics#ot-link-metrics-mgmt-response-callback)|[in]|aCallback|A pointer to a function that is called when Link Metrics Management Response is received.|
|void *|[in]|aCallbackContext|A pointer to application-specific context.|

###### otLinkMetricsConfigEnhAckProbing (heading level 7)

`otError otLinkMetricsConfigEnhAckProbing(otInstance *aInstance, const otIp6Address *aDestination, otLinkMetricsEnhAckFlags aEnhAckFlags, const otLinkMetrics *aLinkMetricsFlags, otLinkMetricsMgmtResponseCallback aCallback, void *aCallbackContext, otLinkMetricsEnhAckProbingIeReportCallback aEnhAckCallback, void *aEnhAckCallbackContext)`

**Description:** Sends an MLE Link Metrics Management Request to configure/clear an Enhanced-ACK Based Probing.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Address](ot-ip6-address) *|[in]|aDestination|A pointer to the destination address.|
|[otLinkMetricsEnhAckFlags](api-link-metrics#ot-link-metrics-enh-ack-flags)|[in]|aEnhAckFlags|Enh-ACK Flags to indicate whether to register or clear the probing. `0` to clear and `1` to register. Other values are reserved.|
|const [otLinkMetrics](ot-link-metrics) *|[in]|aLinkMetricsFlags|A pointer to flags specifying what metrics to query. Should be `NULL` when `aEnhAckFlags` is `0`.|
|[otLinkMetricsMgmtResponseCallback](api-link-metrics#ot-link-metrics-mgmt-response-callback)|[in]|aCallback|A pointer to a function that is called when an Enhanced Ack with Link Metrics is received.|
|void *|[in]|aCallbackContext|A pointer to application-specific context.|
|[otLinkMetricsEnhAckProbingIeReportCallback](api-link-metrics#ot-link-metrics-enh-ack-probing-ie-report-callback)|N/A|aEnhAckCallback||
|void *|N/A|aEnhAckCallbackContext||

This functionality requires OT_LINK_METRICS_INITIATOR feature enabled.

###### otLinkMetricsSendLinkProbe (heading level 7)

`otError otLinkMetricsSendLinkProbe(otInstance *aInstance, const otIp6Address *aDestination, uint8_t aSeriesId, uint8_t aLength)`

**Description:** Sends an MLE Link Probe message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Address](ot-ip6-address) *|[in]|aDestination|A pointer to the destination address.|
|uint8_t|[in]|aSeriesId|The Series ID [1, 254] which the Probe message aims at.|
|uint8_t|[in]|aLength|The length of the data payload in Link Probe TLV, [0, 64] (per Thread 1.2 spec, 4.4.37).|

###### otLinkMetricsManagerIsEnabled (heading level 7)

`bool otLinkMetricsManagerIsEnabled(otInstance *aInstance)`

**Description:** If Link Metrics Manager is enabled.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otLinkMetricsManagerSetEnabled (heading level 7)

`void otLinkMetricsManagerSetEnabled(otInstance *aInstance, bool aEnable)`

**Description:** Enable or disable Link Metrics Manager.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnable|A boolean indicating to enable or disable.|

###### otLinkMetricsManagerGetMetricsValueByExtAddr (heading level 7)

`otError otLinkMetricsManagerGetMetricsValueByExtAddr(otInstance *aInstance, const otExtAddress *aExtAddress, otLinkMetricsValues *aLinkMetricsValues)`

**Description:** Get Link Metrics data of a neighbor by its extended address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otExtAddress](ot-ext-address) *|[in]|aExtAddress|A pointer to the Mac extended address of the Probing Subject.|
|[otLinkMetricsValues](ot-link-metrics-values) *|[out]|aLinkMetricsValues|A pointer to the Link Metrics values of the subject.|

Represents the result (value) for a Link Metrics query. 

###### Public Attributes (heading level 7)

###### mMetrics (heading level 8)

```
otLinkMetrics otLinkMetricsValues::mMetrics
```

**Description:** Specifies which metrics values are present/included.

###### mPduCountValue (heading level 8)

```
uint32_t otLinkMetricsValues::mPduCountValue
```

**Description:** The value of Pdu Count.

###### mLqiValue (heading level 8)

```
uint8_t otLinkMetricsValues::mLqiValue
```

**Description:** The value LQI.

###### mLinkMarginValue (heading level 8)

```
uint8_t otLinkMetricsValues::mLinkMarginValue
```

**Description:** The value of Link Margin.

###### mRssiValue (heading level 8)

```
int8_t otLinkMetricsValues::mRssiValue
```

**Description:** The value of Rssi.

Represents which frames are accounted in a Forward Tracking Series. 

###### Public Attributes (heading level 7)

###### mLinkProbe (heading level 8)

```
bool otLinkMetricsSeriesFlags::mLinkProbe
```

**Description:** MLE Link Probe.

###### mMacData (heading level 8)

```
bool otLinkMetricsSeriesFlags::mMacData
```

**Description:** MAC Data frame.

###### mMacDataRequest (heading level 8)

```
bool otLinkMetricsSeriesFlags::mMacDataRequest
```

**Description:** MAC Data Request.

###### mMacAck (heading level 8)

```
bool otLinkMetricsSeriesFlags::mMacAck
```

**Description:** MAC Ack.

#### Message

This module includes functions that manipulate OpenThread message buffers. 

##### Modules

[otMessageSettings](ot-message-settings)

[otThreadLinkInfo](ot-thread-link-info)

[otMessageQueue](ot-message-queue)

[otMessageQueueInfo](ot-message-queue-info)

[otBufferInfo](ot-buffer-info)

##### Enumerations

###### otMessagePriority

```
enum otMessagePriority {
    OT_MESSAGE_PRIORITY_LOW = 0
    OT_MESSAGE_PRIORITY_NORMAL = 1
    OT_MESSAGE_PRIORITY_HIGH = 2
}
```

**Description:**

Defines the OpenThread message priority levels.

**Enumerator:**

|   |   |
|---|---|
|OT_MESSAGE_PRIORITY_LOW|Low priority level.|
|OT_MESSAGE_PRIORITY_NORMAL|Normal priority level.|
|OT_MESSAGE_PRIORITY_HIGH|High priority level.|

###### otMessageOrigin

```
enum otMessageOrigin {
    OT_MESSAGE_ORIGIN_THREAD_NETIF = 0
    OT_MESSAGE_ORIGIN_HOST_TRUSTED = 1
    OT_MESSAGE_ORIGIN_HOST_UNTRUSTED = 2
}
```

**Description:**

Defines the OpenThread message origins.

**Enumerator:**

|   |   |
|---|---|
|OT_MESSAGE_ORIGIN_THREAD_NETIF|Message from Thread Netif.|
|OT_MESSAGE_ORIGIN_HOST_TRUSTED|Message from a trusted source on host.|
|OT_MESSAGE_ORIGIN_HOST_UNTRUSTED|Message from an untrusted source on host.|

##### Typedefs

###### otMessage

`typedef struct otMessage otMessage`

**Description:**

An opaque representation of an OpenThread message buffer.

###### otMessagePriority

`typedef enum otMessagePriority otMessagePriority`

**Description:**

Defines the OpenThread message priority levels.

###### otMessageOrigin

`typedef enum otMessageOrigin otMessageOrigin`

**Description:**

Defines the OpenThread message origins.

###### otMessageSettings

`typedef struct otMessageSettings otMessageSettings`

**Description:**

Represents a message settings.

###### otThreadLinkInfo

`typedef struct otThreadLinkInfo otThreadLinkInfo`

**Description:**

Represents link-specific information for messages received from the Thread radio.

###### otMessageTxCallback

`typedef void(* otMessageTxCallback) (const otMessage *aMessage, otError aError, void *aContext)`

**Description:**

Represents the callback function pointer to notify the transmission outcome (success or failure) of a message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aMessage|A pointer to the message.|
||[in]|aError|The TX error when sending the message.|
||[in]|aContext|A pointer to the user-provided context when the callback was registered.|

**Details:**

The error indicates the transmission status of the IPv6 message from this device to an immediate neighbor (one-hop transmission). It doesn't indicate that the message is received by its final intended destination (multi-hop away).

For a unicast IPv6 message, an `OT_ERROR_NONE` error indicates that the message (all its corresponding fragment frames if the message is larger and requires fragmentation) was successfully delivered to the immediate neighbor, and a MAC layer acknowledgment was received for all fragments. This is reported regardless of whether the message is sent using direct TX or indirect TX (to a sleepy child using CSL or data poll triggered TX).

For a multicast message, an `OT_ERROR_NONE` status indicates that the message (all its fragment frames) was successfully broadcast. Note that no MAC-level acknowledgment is required for broadcast frame TX.

The OpenThread stack may alter the content of the message as it is prepared for transmission (e.g., IPv6 headers may be prepended, or additional metadata appended at the end). So, the content of `aMessage` when this callback is invoked may differ from its original content (e.g., when it was given as input in `otIp6Send()` for transmission).

###### otMessageQueueInfo

`typedef struct otMessageQueueInfo otMessageQueueInfo`

**Description:**

Represents information about a message queue.

###### otBufferInfo

`typedef struct otBufferInfo otBufferInfo`

**Description:**

Represents the message buffer information for different queues used by OpenThread stack.

##### Functions

###### otMessageGetInstance

`otInstance * otMessageGetInstance(const otMessage *aMessage)`

**Description:** Gets the `otInstance` associated with a given message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otMessage](api-message#ot-message) *|[in]|aMessage|A message.|

**Returns**

- The `otInstance` associated with `aMessage`.

###### otMessageFree

`void otMessageFree(otMessage *aMessage)`

**Description:** Free an allocated message buffer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to a message buffer.|

**See Also**

- [otMessageAppend](api-message#ot-message-append)
- [otMessageGetLength](api-message#ot-message-get-length)
- [otMessageSetLength](api-message#ot-message-set-length)
- [otMessageGetOffset](api-message#ot-message-get-offset)
- [otMessageSetOffset](api-message#ot-message-set-offset)
- [otMessageRead](api-message#ot-message-read)
- [otMessageWrite](api-message#ot-message-write)

###### otMessageGetLength

`uint16_t otMessageGetLength(const otMessage *aMessage)`

**Description:** Get the message length in bytes.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to a message buffer.|

**Returns**

- The message length in bytes.

**See Also**

- [otMessageFree](api-message#ot-message-free)
- [otMessageAppend](api-message#ot-message-append)
- [otMessageSetLength](api-message#ot-message-set-length)
- [otMessageGetOffset](api-message#ot-message-get-offset)
- [otMessageSetOffset](api-message#ot-message-set-offset)
- [otMessageRead](api-message#ot-message-read)
- [otMessageWrite](api-message#ot-message-write)
- [otMessageSetLength](api-message#ot-message-set-length)

###### otMessageSetLength

`otError otMessageSetLength(otMessage *aMessage, uint16_t aLength)`

**Description:** Set the message length in bytes.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to a message buffer.|
|uint16_t|[in]|aLength|A length in bytes.|

**See Also**

- [otMessageFree](api-message#ot-message-free)
- [otMessageAppend](api-message#ot-message-append)
- [otMessageGetLength](api-message#ot-message-get-length)
- [otMessageGetOffset](api-message#ot-message-get-offset)
- [otMessageSetOffset](api-message#ot-message-set-offset)
- [otMessageRead](api-message#ot-message-read)
- [otMessageWrite](api-message#ot-message-write)

###### otMessageGetOffset

`uint16_t otMessageGetOffset(const otMessage *aMessage)`

**Description:** Get the message offset in bytes.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to a message buffer.|

**Returns**

- The message offset value.

**See Also**

- [otMessageFree](api-message#ot-message-free)
- [otMessageAppend](api-message#ot-message-append)
- [otMessageGetLength](api-message#ot-message-get-length)
- [otMessageSetLength](api-message#ot-message-set-length)
- [otMessageSetOffset](api-message#ot-message-set-offset)
- [otMessageRead](api-message#ot-message-read)
- [otMessageWrite](api-message#ot-message-write)

###### otMessageSetOffset

`void otMessageSetOffset(otMessage *aMessage, uint16_t aOffset)`

**Description:** Set the message offset in bytes.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to a message buffer.|
|uint16_t|[in]|aOffset|An offset in bytes.|

**See Also**

- [otMessageFree](api-message#ot-message-free)
- [otMessageAppend](api-message#ot-message-append)
- [otMessageGetLength](api-message#ot-message-get-length)
- [otMessageSetLength](api-message#ot-message-set-length)
- [otMessageGetOffset](api-message#ot-message-get-offset)
- [otMessageRead](api-message#ot-message-read)
- [otMessageWrite](api-message#ot-message-write)

###### otMessageIsLinkSecurityEnabled

`bool otMessageIsLinkSecurityEnabled(const otMessage *aMessage)`

**Description:** Indicates whether or not link security is enabled for the message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to a message buffer.|

###### otMessageIsLoopbackToHostAllowed

`bool otMessageIsLoopbackToHostAllowed(const otMessage *aMessage)`

**Description:** Indicates whether or not the message is allowed to be looped back to host.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to a message buffer.|

###### otMessageSetLoopbackToHostAllowed

`void otMessageSetLoopbackToHostAllowed(otMessage *aMessage, bool aAllowLoopbackToHost)`

**Description:** Sets whether or not the message is allowed to be looped back to host.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to a message buffer.|
|bool|[in]|aAllowLoopbackToHost|Whether to allow the message to be looped back to host.|

###### otMessageIsMulticastLoopEnabled

`bool otMessageIsMulticastLoopEnabled(otMessage *aMessage)`

**Description:** Indicates whether the given message may be looped back in a case of a multicast destination address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the message.|

If `aMessage` is used along with an `otMessageInfo`, the `mMulticastLoop` field from `otMessageInfo` structure takes precedence and will be used instead of the the value set on `aMessage`.

This API is mainly intended for use along with `otIp6Send()` which expects an already prepared IPv6 message.

###### otMessageSetMulticastLoopEnabled

`void otMessageSetMulticastLoopEnabled(otMessage *aMessage, bool aEnabled)`

**Description:** Controls whether the given message may be looped back in a case of a multicast destination address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the message.|
|bool|[in]|aEnabled|The configuration value.|

###### otMessageGetOrigin

`otMessageOrigin otMessageGetOrigin(const otMessage *aMessage)`

**Description:** Gets the message origin.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to a message buffer.|

**Returns**

- The message origin.

###### otMessageSetOrigin

`void otMessageSetOrigin(otMessage *aMessage, otMessageOrigin aOrigin)`

**Description:** Sets the message origin.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to a message buffer.|
|[otMessageOrigin](api-message#ot-message-origin)|[in]|aOrigin|The message origin.|

###### otMessageSetDirectTransmission

`void otMessageSetDirectTransmission(otMessage *aMessage, bool aEnabled)`

**Description:** Sets/forces the message to be forwarded using direct transmission.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to a message buffer.|
|bool|[in]|aEnabled|If `true`, the message is forced to use direct transmission. If `false`, the message follows the normal procedure.|

Default setting for a new message is `false`.

###### otMessageGetRss

`int8_t otMessageGetRss(const otMessage *aMessage)`

**Description:** Returns the average RSS (received signal strength) associated with the message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to a message buffer.|

**Returns**

- The average RSS value (in dBm) or OT_RADIO_RSSI_INVALID if no average RSS is available.

###### otMessageGetThreadLinkInfo

`otError otMessageGetThreadLinkInfo(const otMessage *aMessage, otThreadLinkInfo *aLinkInfo)`

**Description:** Retrieves the link-specific information for a message received over Thread radio.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otMessage](api-message#ot-message) *|[in]|aMessage|The message from which to retrieve `otThreadLinkInfo`. @pram[out] aLinkInfo A pointer to an `otThreadLinkInfo` to populate.|
|[otThreadLinkInfo](ot-thread-link-info) *|N/A|aLinkInfo||

###### otMessageRegisterTxCallback

`void otMessageRegisterTxCallback(otMessage *aMessage, otMessageTxCallback aCallback, void *aContext)`

**Description:** Registers a callback to be notified of a message's transmission outcome.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[in]|aMessage|The message to register the callback with.|
|[otMessageTxCallback](api-message#ot-message-tx-callback)|[in]|aCallback|The TX callback.|
|void *|[in]|aContext|A pointer to a user-provided arbitrary context for the callback.|

Calling this function again for the same message will replace any previously registered callback.

If the message is never actually sent (e.g., it's not passed to `otIp6Send()` or other send APIs), the callback will still be invoked when the message is freed. In this case, `OT_ERROR_DROP` will be passed as the error.

###### otMessageAppend

`otError otMessageAppend(otMessage *aMessage, const void *aBuf, uint16_t aLength)`

**Description:** Append bytes to a message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to a message buffer.|
|const void *|[in]|aBuf|A pointer to the data to append.|
|uint16_t|[in]|aLength|Number of bytes to append.|

**See Also**

- [otMessageFree](api-message#ot-message-free)
- [otMessageGetLength](api-message#ot-message-get-length)
- [otMessageSetLength](api-message#ot-message-set-length)
- [otMessageGetOffset](api-message#ot-message-get-offset)
- [otMessageSetOffset](api-message#ot-message-set-offset)
- [otMessageRead](api-message#ot-message-read)
- [otMessageWrite](api-message#ot-message-write)

###### otMessageRead

`uint16_t otMessageRead(const otMessage *aMessage, uint16_t aOffset, void *aBuf, uint16_t aLength)`

**Description:** Read bytes from a message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to a message buffer.|
|uint16_t|[in]|aOffset|An offset in bytes.|
|void *|[in]|aBuf|A pointer to a buffer that message bytes are read to.|
|uint16_t|[in]|aLength|Number of bytes to read.|

**Returns**

- The number of bytes read.

**See Also**

- [otMessageFree](api-message#ot-message-free)
- [otMessageAppend](api-message#ot-message-append)
- [otMessageGetLength](api-message#ot-message-get-length)
- [otMessageSetLength](api-message#ot-message-set-length)
- [otMessageGetOffset](api-message#ot-message-get-offset)
- [otMessageSetOffset](api-message#ot-message-set-offset)
- [otMessageWrite](api-message#ot-message-write)

###### otMessageWrite

`int otMessageWrite(otMessage *aMessage, uint16_t aOffset, const void *aBuf, uint16_t aLength)`

**Description:** Write bytes to a message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to a message buffer.|
|uint16_t|[in]|aOffset|An offset in bytes.|
|const void *|[in]|aBuf|A pointer to a buffer that message bytes are written from.|
|uint16_t|[in]|aLength|Number of bytes to write.|

**Returns**

- The number of bytes written.

**See Also**

- [otMessageFree](api-message#ot-message-free)
- [otMessageAppend](api-message#ot-message-append)
- [otMessageGetLength](api-message#ot-message-get-length)
- [otMessageSetLength](api-message#ot-message-set-length)
- [otMessageGetOffset](api-message#ot-message-get-offset)
- [otMessageSetOffset](api-message#ot-message-set-offset)
- [otMessageRead](api-message#ot-message-read)

###### otMessageClone

`otMessage * otMessageClone(const otMessage *aMessage)`

**Description:** Creates a clone of a given message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the message to clone.|

The new message is allocated from the same message pool as `aMessage`. The entire message content from `aMessage` is copied to the new message.

The caller takes ownership of the returned message and must free it by calling `otMessageFree()` when it is no longer needed.

**Returns**

- A pointer to the new message clone, or `nullptr` if no message buffers are available.

###### otMessageQueueInit

`void otMessageQueueInit(otMessageQueue *aQueue)`

**Description:** Initialize the message queue.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessageQueue](ot-message-queue) *|[in]|aQueue|A pointer to a message queue.|

MUST be called once and only once for a `otMessageQueue` instance before any other `otMessageQueue` functions. The behavior is undefined if other queue APIs are used with an `otMessageQueue` before it being initialized or if it is initialized more than once.

###### otMessageQueueEnqueue

`void otMessageQueueEnqueue(otMessageQueue *aQueue, otMessage *aMessage)`

**Description:** Adds a message to the end of the given message queue.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessageQueue](ot-message-queue) *|[in]|aQueue|A pointer to the message queue.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|The message to add.|

###### otMessageQueueEnqueueAtHead

`void otMessageQueueEnqueueAtHead(otMessageQueue *aQueue, otMessage *aMessage)`

**Description:** Adds a message at the head/front of the given message queue.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessageQueue](ot-message-queue) *|[in]|aQueue|A pointer to the message queue.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|The message to add.|

###### otMessageQueueDequeue

`void otMessageQueueDequeue(otMessageQueue *aQueue, otMessage *aMessage)`

**Description:** Removes a message from the given message queue.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessageQueue](ot-message-queue) *|[in]|aQueue|A pointer to the message queue.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|The message to remove.|

###### otMessageQueueGetHead

`otMessage * otMessageQueueGetHead(otMessageQueue *aQueue)`

**Description:** Returns a pointer to the message at the head of the queue.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessageQueue](ot-message-queue) *|[in]|aQueue|A pointer to a message queue.|

**Returns**

- A pointer to the message at the head of queue or NULL if queue is empty.

###### otMessageQueueGetNext

`otMessage * otMessageQueueGetNext(otMessageQueue *aQueue, const otMessage *aMessage)`

**Description:** Returns a pointer to the next message in the queue by iterating forward (from head to tail).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessageQueue](ot-message-queue) *|[in]|aQueue|A pointer to a message queue.|
|const [otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to current message buffer.|

**Returns**

- A pointer to the next message in the queue after `aMessage` or NULL if `aMessage is the tail of queue. NULL is returned if`aMessage`is not in the queue`aQueue`.

###### otMessageGetBufferInfo

`void otMessageGetBufferInfo(otInstance *aInstance, otBufferInfo *aBufferInfo)`

**Description:** Get the Message Buffer information.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otBufferInfo](ot-buffer-info) *|[out]|aBufferInfo|A pointer where the message buffer information is written.|

###### otMessageResetBufferInfo

`void otMessageResetBufferInfo(otInstance *aInstance)`

**Description:** Reset the Message Buffer information counter tracking the maximum number buffers in use at the same time.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

This resets `mMaxUsedBuffers` in `otBufferInfo`.

Represents a message settings. 

###### Public Attributes

###### mLinkSecurityEnabled (heading level 7)

```
bool otMessageSettings::mLinkSecurityEnabled
```

**Description:** TRUE if the message should be secured at Layer 2.

###### mPriority (heading level 7)

```
uint8_t otMessageSettings::mPriority
```

**Description:** Priority level (MUST be a `OT_MESSAGE_PRIORITY_*` from `otMessagePriority`).

Represents link-specific information for messages received from the Thread radio. 

###### Public Attributes

###### mPanId (heading level 7)

```
uint16_t otThreadLinkInfo::mPanId
```

**Description:** Source PAN ID.

###### mChannel (heading level 7)

```
uint8_t otThreadLinkInfo::mChannel
```

**Description:** 802.15.4 Channel

###### mRss (heading level 7)

```
int8_t otThreadLinkInfo::mRss
```

**Description:** Received Signal Strength in dBm (averaged over fragments)

###### mLqi (heading level 7)

```
uint8_t otThreadLinkInfo::mLqi
```

**Description:** Average Link Quality Indicator (averaged over fragments)

###### mLinkSecurity (heading level 7)

```
bool otThreadLinkInfo::mLinkSecurity
```

**Description:** Indicates whether or not link security is enabled.

###### mIsDstPanIdBroadcast (heading level 7)

```
bool otThreadLinkInfo::mIsDstPanIdBroadcast
```

**Description:** Indicates whether or not destination PAN ID is broadcast.

###### mTimeSyncSeq (heading level 7)

```
uint8_t otThreadLinkInfo::mTimeSyncSeq
```

**Description:** The time sync sequence.

###### mNetworkTimeOffset (heading level 7)

```
int64_t otThreadLinkInfo::mNetworkTimeOffset
```

**Description:** The time offset to the Thread network time, in microseconds.

###### mRadioType (heading level 7)

```
uint8_t otThreadLinkInfo::mRadioType
```

**Description:** Radio link type.

Represents an OpenThread message queue. 

###### Public Attributes

###### mData (heading level 7)

```
void* otMessageQueue::mData
```

**Description:** Opaque data used by the implementation.

###### mData2 (heading level 7)

```
void* otMessageQueue::mData2
```

**Description:** Opaque data used by the implementation.

Represents information about a message queue. 

###### Public Attributes

###### mNumMessages (heading level 7)

```
uint16_t otMessageQueueInfo::mNumMessages
```

**Description:** Number of messages in the queue.

###### mNumBuffers (heading level 7)

```
uint16_t otMessageQueueInfo::mNumBuffers
```

**Description:** Number of data buffers used by messages in the queue.

###### mTotalBytes (heading level 7)

```
uint32_t otMessageQueueInfo::mTotalBytes
```

**Description:** Total number of bytes used by all messages in the queue.

Represents the message buffer information for different queues used by OpenThread stack. 

###### Public Attributes

###### mTotalBuffers (heading level 7)

```
uint16_t otBufferInfo::mTotalBuffers
```

**Description:** The total number of buffers in the messages pool (0xffff if unknown).

###### mFreeBuffers (heading level 7)

```
uint16_t otBufferInfo::mFreeBuffers
```

**Description:** The number of free buffers (0xffff if unknown).

###### mMaxUsedBuffers (heading level 7)

```
uint16_t otBufferInfo::mMaxUsedBuffers
```

**Description:** The maximum number of used buffers at the same time since OT stack initialization or last call to `otMessageResetBufferInfo()`.

###### m6loSendQueue (heading level 7)

```
otMessageQueueInfo otBufferInfo::m6loSendQueue
```

**Description:** Info about 6LoWPAN send queue.

###### m6loReassemblyQueue (heading level 7)

```
otMessageQueueInfo otBufferInfo::m6loReassemblyQueue
```

**Description:** Info about 6LoWPAN reassembly queue.

###### mIp6Queue (heading level 7)

```
otMessageQueueInfo otBufferInfo::mIp6Queue
```

**Description:** Info about IPv6 send queue.

###### mMplQueue (heading level 7)

```
otMessageQueueInfo otBufferInfo::mMplQueue
```

**Description:** Info about MPL send queue.

###### mMleQueue (heading level 7)

```
otMessageQueueInfo otBufferInfo::mMleQueue
```

**Description:** Info about MLE delayed message queue.

###### mCoapQueue (heading level 7)

```
otMessageQueueInfo otBufferInfo::mCoapQueue
```

**Description:** Info about CoAP/TMF send queue.

###### mCoapSecureQueue (heading level 7)

```
otMessageQueueInfo otBufferInfo::mCoapSecureQueue
```

**Description:** Info about CoAP secure send queue.

###### mApplicationCoapQueue (heading level 7)

```
otMessageQueueInfo otBufferInfo::mApplicationCoapQueue
```

**Description:** Info about application CoAP send queue.

#### Multi Radio Link

This module includes definitions and functions for multi radio link. 

##### Modules

[otRadioLinkInfo](ot-radio-link-info)

[otMultiRadioNeighborInfo](ot-multi-radio-neighbor-info)

##### Typedefs

###### otRadioLinkInfo

`typedef struct otRadioLinkInfo otRadioLinkInfo`

**Description:**

Represents information associated with a radio link.

###### otMultiRadioNeighborInfo

`typedef struct otMultiRadioNeighborInfo otMultiRadioNeighborInfo`

**Description:**

Represents multi radio link information associated with a neighbor.

##### Functions

###### otMultiRadioGetNeighborInfo

`otError otMultiRadioGetNeighborInfo(otInstance *aInstance, const otExtAddress *aExtAddress, otMultiRadioNeighborInfo *aNeighborInfo)`

**Description:** Gets the multi radio link information associated with a neighbor with a given Extended Address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otExtAddress](ot-ext-address) *|[in]|aExtAddress|The Extended Address of neighbor.|
|[otMultiRadioNeighborInfo](ot-multi-radio-neighbor-info) *|[out]|aNeighborInfo|A pointer to `otMultiRadioNeighborInfo` to output the neighbor info (on success).|

`OPENTHREAD_CONFIG_MULTI_RADIO` must be enabled.

Represents information associated with a radio link. 

###### Public Attributes

###### mPreference (heading level 7)

```
uint8_t otRadioLinkInfo::mPreference
```

**Description:** Preference level of radio link.

Represents multi radio link information associated with a neighbor. 

###### Public Attributes

###### mSupportsIeee802154 (heading level 7)

```
bool otMultiRadioNeighborInfo::mSupportsIeee802154
```

**Description:** Neighbor supports IEEE 802.15.4 radio link.

###### mSupportsTrelUdp6 (heading level 7)

```
bool otMultiRadioNeighborInfo::mSupportsTrelUdp6
```

**Description:** Neighbor supports Thread Radio Encapsulation Link (TREL) radio link.

###### mIeee802154Info (heading level 7)

```
otRadioLinkInfo otMultiRadioNeighborInfo::mIeee802154Info
```

**Description:** Additional info for 15.4 radio link (applicable when supported).

###### mTrelUdp6Info (heading level 7)

```
otRadioLinkInfo otMultiRadioNeighborInfo::mTrelUdp6Info
```

**Description:** Additional info for TREL radio link (applicable when supported).

#### TREL - Thread Stack

This module defines Thread Radio Encapsulation Link (TREL) APIs for Thread Over Infrastructure. 

The functions in this module require `OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE` to be enabled. 

##### Modules

[otTrelPeer](ot-trel-peer)

##### Typedefs

###### otTrelPeer

`typedef struct otTrelPeer otTrelPeer`

**Description:**

Represents a TREL peer.

###### otTrelPeerIterator

`typedef const void* otTrelPeerIterator`

**Description:**

Represents an iterator for iterating over TREL peer table entries.

###### otTrelCounters

`typedef otPlatTrelCounters otTrelCounters`

**Description:**

Represents a group of TREL related counters.

###### otTrelStateChangeCallback

`typedef void(* otTrelStateChangeCallback) (void *aContext)`

**Description:**

Callback function pointer to signal state changes to the TREL.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to an arbitrary context (provided when callback is set).|

**Details:**

This callback is invoked whenever TREL state or TREL UDP port gets changed.

##### Functions

###### otTrelSetEnabled

`void otTrelSetEnabled(otInstance *aInstance, bool aEnable)`

**Description:** Sets the user's preference to enable or disable the TREL operation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnable|A boolean to enable/disable the TREL operation.|

The TREL interface's operational state is determined by two factors: the user's preference (set by this function) and the OpenThread stack's internal state. The TREL interface is enabled only when both the user and the OpenThread stack have it enabled. Otherwise, it is disabled.

Upon OpenThread initialization, the user's preference is set to enabled by default. This allows the stack to control the TREL interface state automatically (e.g., enabling it when radio links are enabled and disabling it when radio links are disabled).

If the user explicitly disables the TREL operation by calling this function with `aEnable` as `false`, it will remain disabled until the user explicitly re-enables it by calling this function with `aEnable` as `true`. This ensures the user's 'disable' request persists across other OpenThread stack state changes (which may trigger disabling/enabling of all radio links, including the TREL link).

###### otTrelIsEnabled

`bool otTrelIsEnabled(otInstance *aInstance)`

**Description:** Indicates whether the TREL operation is enabled.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|

The TREL operation is enabled if and only if it is enabled by both the user (see `otTrelSetEnabled()`) and the OpenThread stack.

###### otTrelInitPeerIterator

`void otTrelInitPeerIterator(otInstance *aInstance, otTrelPeerIterator *aIterator)`

**Description:** Initializes a peer table iterator.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otTrelPeerIterator](api-trel#ot-trel-peer-iterator) *|[in]|aIterator|The iterator to initialize.|

###### otTrelGetNextPeer

`const otTrelPeer * otTrelGetNextPeer(otInstance *aInstance, otTrelPeerIterator *aIterator)`

**Description:** Iterates over the peer table entries and get the next entry from the table.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otTrelPeerIterator](api-trel#ot-trel-peer-iterator) *|[in]|aIterator|The iterator. MUST be initialized.|

**Returns**

- A pointer to the next `otTrelPeer` entry or `NULL` if no more entries in the table.

###### otTrelGetNumberOfPeers

`uint16_t otTrelGetNumberOfPeers(otInstance *aInstance)`

**Description:** Returns the number of TREL peers.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The number of TREL peers.

###### otTrelSetFilterEnabled

`void otTrelSetFilterEnabled(otInstance *aInstance, bool aEnable)`

**Description:** Sets the filter mode (enables/disables filtering).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|bool|[in]|aEnable|TRUE to enable filter mode, FALSE to disable filter mode.|

When filter mode is enabled, any rx and tx traffic through TREL interface is silently dropped. This is mainly intended for use during testing.

Unlike `otTrel{Enable/Disable}()` which fully starts/stops the TREL operation, when filter mode is enabled the TREL interface continues to be enabled.

###### otTrelIsFilterEnabled

`bool otTrelIsFilterEnabled(otInstance *aInstance)`

**Description:** Indicates whether or not the filter mode is enabled.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|

###### otTrelGetCounters

`const otTrelCounters * otTrelGetCounters(otInstance *aInstance)`

**Description:** Gets the TREL counters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- A pointer to the TREL counters.

###### otTrelResetCounters

`void otTrelResetCounters(otInstance *aInstance)`

**Description:** Resets the TREL counters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otTrelGetUdpPort

`uint16_t otTrelGetUdpPort(otInstance *aInstance)`

**Description:** Gets the UDP port of the TREL interface.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- UDP port of the TREL interface.

###### otTrelSetStateChangedCallback

`void otTrelSetStateChangedCallback(otInstance *aInstance, otTrelStateChangeCallback aCallback, void *aContext)`

**Description:** Sets the callback function to notify state changes of TREL.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otTrelStateChangeCallback](api-trel#ot-trel-state-change-callback)|[in]|aCallback|The callback function pointer.|
|void *|[in]|aContext|The arbitrary context to use with callback.|

Represents a TREL peer. 

###### Public Attributes

###### mExtAddress (heading level 7)

```
otExtAddress otTrelPeer::mExtAddress
```

**Description:** The Extended MAC Address of TREL peer.

###### mExtPanId (heading level 7)

```
otExtendedPanId otTrelPeer::mExtPanId
```

**Description:** The Extended PAN Identifier of TREL peer.

###### mSockAddr (heading level 7)

```
otSockAddr otTrelPeer::mSockAddr
```

**Description:** The IPv6 socket address of TREL peer.

#### Thread

##### Modules

[Backbone Router](api-backbone-router)

[Border Agent](api-border-agent)

[Border Agent Tracker](api-border-agent-tracker)

[Border Agent TXT Data Parser](api-border-agent-txt-data)

[Border Router](api-border-router)

[Border Routing Manager](api-border-routing)

[Border Router Multi AIL Detection](api-multi-ail-detection)

[Commissioner](api-commissioner)

[General](api-thread-general)

[Joiner](api-joiner)

[Operational Dataset](api-operational-dataset)

[Router/Leader](api-thread-router)

[Seeker](api-seeker)

[Server](api-server)

[Steering Data](api-steering-data)

##### Border Router

This module includes functions to manage local network data with the OpenThread Border Router. 

###### Typedefs

###### otBorderRouterNetDataFullCallback (heading level 7)

`typedef void(* otBorderRouterNetDataFullCallback) (void *aContext)`

**Description:**

Function pointer callback which is invoked when Network Data (local or leader) gets full.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to arbitrary context information.|

**Details:**

###### Functions

###### otBorderRouterGetNetData (heading level 7)

`otError otBorderRouterGetNetData(otInstance *aInstance, bool aStable, uint8_t *aData, uint8_t *aDataLength)`

**Description:** Provides a full or stable copy of the local Thread Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aStable|TRUE when copying the stable version, FALSE when copying the full version.|
|uint8_t *|[out]|aData|A pointer to the data buffer.|
|uint8_t *|[inout]|aDataLength|On entry, size of the data buffer pointed to by `aData`. On exit, number of copied bytes.|

###### otBorderRouterAddOnMeshPrefix (heading level 7)

`otError otBorderRouterAddOnMeshPrefix(otInstance *aInstance, const otBorderRouterConfig *aConfig)`

**Description:** Add a border router configuration to the local network data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otBorderRouterConfig](ot-border-router-config) *|[in]|aConfig|A pointer to the border router configuration.|

**See Also**

- [otBorderRouterRemoveOnMeshPrefix](api-border-router#ot-border-router-remove-on-mesh-prefix)
- [otBorderRouterRegister](api-border-router#ot-border-router-register)

###### otBorderRouterRemoveOnMeshPrefix (heading level 7)

`otError otBorderRouterRemoveOnMeshPrefix(otInstance *aInstance, const otIp6Prefix *aPrefix)`

**Description:** Remove a border router configuration from the local network data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Prefix](ot-ip6-prefix) *|[in]|aPrefix|A pointer to an IPv6 prefix.|

**See Also**

- [otBorderRouterAddOnMeshPrefix](api-border-router#ot-border-router-add-on-mesh-prefix)
- [otBorderRouterRegister](api-border-router#ot-border-router-register)

###### otBorderRouterGetNextOnMeshPrefix (heading level 7)

`otError otBorderRouterGetNextOnMeshPrefix(otInstance *aInstance, otNetworkDataIterator *aIterator, otBorderRouterConfig *aConfig)`

**Description:** Gets the next On Mesh Prefix in the local Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otNetworkDataIterator](api-thread-general#ot-network-data-iterator) *|[inout]|aIterator|A pointer to the Network Data iterator context. To get the first on-mesh entry it should be set to OT_NETWORK_DATA_ITERATOR_INIT.|
|[otBorderRouterConfig](ot-border-router-config) *|[out]|aConfig|A pointer to the On Mesh Prefix information.|

###### otBorderRouterAddRoute (heading level 7)

`otError otBorderRouterAddRoute(otInstance *aInstance, const otExternalRouteConfig *aConfig)`

**Description:** Add an external route configuration to the local network data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otExternalRouteConfig](ot-external-route-config) *|[in]|aConfig|A pointer to the external route configuration.|

**See Also**

- [otBorderRouterRemoveRoute](api-border-router#ot-border-router-remove-route)
- [otBorderRouterRegister](api-border-router#ot-border-router-register)

###### otBorderRouterRemoveRoute (heading level 7)

`otError otBorderRouterRemoveRoute(otInstance *aInstance, const otIp6Prefix *aPrefix)`

**Description:** Remove an external route configuration from the local network data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Prefix](ot-ip6-prefix) *|[in]|aPrefix|A pointer to an IPv6 prefix.|

**See Also**

- [otBorderRouterAddRoute](api-border-router#ot-border-router-add-route)
- [otBorderRouterRegister](api-border-router#ot-border-router-register)

###### otBorderRouterGetNextRoute (heading level 7)

`otError otBorderRouterGetNextRoute(otInstance *aInstance, otNetworkDataIterator *aIterator, otExternalRouteConfig *aConfig)`

**Description:** Gets the next external route in the local Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otNetworkDataIterator](api-thread-general#ot-network-data-iterator) *|[inout]|aIterator|A pointer to the Network Data iterator context. To get the first external route entry it should be set to OT_NETWORK_DATA_ITERATOR_INIT.|
|[otExternalRouteConfig](ot-external-route-config) *|[out]|aConfig|A pointer to the External Route information.|

###### otBorderRouterRegister (heading level 7)

`otError otBorderRouterRegister(otInstance *aInstance)`

**Description:** Immediately register the local network data with the Leader.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**See Also**

- [otBorderRouterAddOnMeshPrefix](api-border-router#ot-border-router-add-on-mesh-prefix)
- [otBorderRouterRemoveOnMeshPrefix](api-border-router#ot-border-router-remove-on-mesh-prefix)
- [otBorderRouterAddRoute](api-border-router#ot-border-router-add-route)
- [otBorderRouterRemoveRoute](api-border-router#ot-border-router-remove-route)

###### otBorderRouterSetNetDataFullCallback (heading level 7)

`void otBorderRouterSetNetDataFullCallback(otInstance *aInstance, otBorderRouterNetDataFullCallback aCallback, void *aContext)`

**Description:** Sets the callback to indicate when Network Data gets full.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otBorderRouterNetDataFullCallback](api-border-router#ot-border-router-net-data-full-callback)|[in]|aCallback|The callback.|
|void *|[in]|aContext|A pointer to arbitrary context information used with `aCallback`.|

Requires `OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL`.

The callback is invoked whenever:

- The device is acting as a leader and receives a Network Data registration from a Border Router (BR) that it cannot add to Network Data (running out of space).
- The device is acting as a BR and new entries cannot be added to its local Network Data.
- The device is acting as a BR and tries to register its local Network Data entries with the leader, but determines that its local entries will not fit.

##### Border Router Multi AIL Detection

This module includes functions for the OpenThread Multi-AIL Detection feature. 

All the functions in this module require both the `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` and `OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE` to be enabled. 

###### Typedefs

###### otBorderRoutingMultiAilCallback (heading level 7)

`typedef void(* otBorderRoutingMultiAilCallback) (bool aDetected, void *aContext)`

**Description:**

A callback function pointer called when the multi-AIL detection state changes.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aDetected|`TRUE` if multiple AILs are now detected, `FALSE` otherwise.|
||[in]|aContext|A pointer to arbitrary context information provided when the callback was registered using `otBorderRoutingSetMultiAilCallback()`.|

**Details:**

This callback function is invoked by the OpenThread stack whenever the detector determines a change in whether Border Routers on the Thread mesh might be connected to different Adjacent Infrastructure Links (AILs).

See `otBorderRoutingIsMultiAilDetected()` for more details.

###### Functions

###### otBorderRoutingSetMultiAilDetectionEnabled (heading level 7)

`void otBorderRoutingSetMultiAilDetectionEnabled(otInstance *aInstance, bool aEnable)`

**Description:** Enables or disables the Multi-AIL Detector.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|bool|[in]|aEnable|TRUE to enable the detector, FALSE to disable.|

If `OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_AUTO_ENABLE_MODE` is enabled, the detector is enabled by default and starts running when the infra-if network is initialized and becomes active (running).

###### otBorderRoutingIsMultiAilDetectionEnabled (heading level 7)

`bool otBorderRoutingIsMultiAilDetectionEnabled(otInstance *aInstance)`

**Description:** Checks if the Multi-AIL Detector is enabled.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

###### otBorderRoutingIsMultiAilDetectionRunning (heading level 7)

`bool otBorderRoutingIsMultiAilDetectionRunning(otInstance *aInstance)`

**Description:** Checks if the Multi-AIL Detector is running.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

The detector runs when it is enabled and the infrastructure interface is also active.

###### otBorderRoutingIsMultiAilDetected (heading level 7)

`bool otBorderRoutingIsMultiAilDetected(otInstance *aInstance)`

**Description:** Gets the current detected state regarding multiple Adjacent Infrastructure Links (AILs).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

It returns whether the detector currently believes that Border Routers (BRs) on the Thread mesh may be connected to different AILs.

The detection mechanism operates as follows: The detector monitors the number of peer BRs listed in the Thread Network Data (see `otBorderRoutingCountPeerBrs()`) and compares this count with the number of peer BRs discovered by processing received Router Advertisement (RA) messages on its connected AIL. If the count derived from Network Data consistently exceeds the count derived from RAs for a detection duration of 10 minutes, it concludes that BRs are likely connected to different AILs. To clear state a shorter window of 1 minute is used.

The detection window of 10 minutes helps to avoid false positives due to transient changes. The detector uses 200 seconds for reachability checks of peer BRs (sending Neighbor Solicitation). Stale Network Data entries are also expected to age out within a few minutes. So a 10-minute detection time accommodates both cases.

While generally effective, this detection mechanism may get less reliable in scenarios with a large number of BRs, particularly exceeding ten. This is related to the "Network Data Publisher" mechanism, where BRs might refrain from publishing their external route information in the Network Data to conserve its limited size, potentially skewing the Network Data BR count.

###### otBorderRoutingSetMultiAilCallback (heading level 7)

`void otBorderRoutingSetMultiAilCallback(otInstance *aInstance, otBorderRoutingMultiAilCallback aCallback, void *aContext)`

**Description:** Sets a callback function to be notified of changes in the multi-AIL detection state.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otBorderRoutingMultiAilCallback](api-multi-ail-detection#ot-border-routing-multi-ail-callback)|[in]|aCallback|A pointer to the function (`otBorderRoutingMultiAilCallback`) to be called upon state changes, or `NULL` to unregister a previously set callback.|
|void *|[in]|aContext|A pointer to application-specific context that will be passed back in the `aCallback` function. This can be `NULL` if no context is needed.|

Subsequent calls to this function will overwrite the previous callback setting. Using `NULL` for `aCallback` will disable the callback.

##### Seeker

This module includes functions for the Thread Seeker role. 

**Note**

- The functions in this module require `OPENTHREAD_CONFIG_SEEKER_ENABLE`.

###### Enumerations

###### otSeekerVerdict (heading level 7)

```
enum otSeekerVerdict {
    OT_SEEKER_ACCEPT
    OT_SEEKER_ACCEPT_PREFERRED
    OT_SEEKER_IGNORE
}
```

**Description:**

Represents a verdict returned from the `otSeekerScanEvaluator` callback when evaluating a Discover Scan result.

**Enumerator:**

|   |   |
|---|---|
|OT_SEEKER_ACCEPT|The scan result is acceptable.|
|OT_SEEKER_ACCEPT_PREFERRED|The scan result is acceptable and preferred.|
|OT_SEEKER_IGNORE|The scan result should be ignored.|

###### Typedefs

###### otSeekerScanResult (heading level 7)

`typedef otActiveScanResult otSeekerScanResult`

**Description:**

Represents a Discover Scan result.

###### otSeekerVerdict (heading level 7)

`typedef enum otSeekerVerdict otSeekerVerdict`

**Description:**

Represents a verdict returned from the `otSeekerScanEvaluator` callback when evaluating a Discover Scan result.

###### otSeekerScanEvaluator (heading level 7)

`typedef otSeekerVerdict(* otSeekerScanEvaluator) (void *aContext, const otSeekerScanResult *aResult)`

**Description:**

Represents the callback function type used to evaluate a scan result or report the end of a scan.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to the callback context.|
||[in]|aResult|A pointer to the scan result to evaluate, or `NULL` to indicate scan completion.|

**Details:**

**Returns**

- The verdict for the scan result (Accept, AcceptPreferred, or Ignore). If `aResult` is `NULL` (scan complete), the return value is ignored.

###### Functions

###### otSeekerStart (heading level 7)

`otError otSeekerStart(otInstance *aInstance, otSeekerScanEvaluator aScanEvaluator, void *aContext)`

**Description:** Starts the Seeker operation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otSeekerScanEvaluator](api-seeker#ot-seeker-scan-evaluator)|[in]|aScanEvaluator|The callback function to evaluate scan results.|
|void *|[in]|aContext|An arbitrary context pointer to use with `aScanEvaluator`.|

The Seeker generates and sets a random MAC address for anonymity, then initiates an MLE Discover Scan to find Joiner Router candidates.

Found candidates are reported to the `aScanEvaluator` callback. Based on the returned `otSeekerVerdict`, the Seeker maintains a prioritized list of candidates for future connection attempts.

###### otSeekerStop (heading level 7)

`void otSeekerStop(otInstance *aInstance)`

**Description:** Stops the Seeker operation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|

This function stops any ongoing discovery or connection process, unregisters the unsecure Joiner/Seeker UDP port, and clears internal state. If the Seeker is already stopped, this method has no effect.

If the join process succeeds after a call to `otSeekerSetUpNextConnection()`, the caller MUST call this method to stop the Seeker and, importantly, unregister the Seeker UDP port as an unsecure port.

**Note**

- If `otSeekerSetUpNextConnection()` returns `OT_ERROR_NOT_FOUND` (indicating the candidate list is exhausted), the Seeker stops automatically.

###### otSeekerIsRunning (heading level 7)

`bool otSeekerIsRunning(otInstance *aInstance)`

**Description:** Indicates whether or not the Seeker is running.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|

###### otSeekerGetUdpPort (heading level 7)

`uint16_t otSeekerGetUdpPort(otInstance *aInstance)`

**Description:** Gets the Seeker UDP port (unsecure port).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|

**Returns**

- The Seeker UDP port.

###### otSeekerSetUdpPort (heading level 7)

`otError otSeekerSetUdpPort(otInstance *aInstance, uint16_t aUdpPort)`

**Description:** Sets the Seeker UDP port (unsecure port).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|uint16_t|[in]|aUdpPort|The Seeker UDP port.|

This UDP port can only be changed when the Seeker is not running.

###### otSeekerSetUpNextConnection (heading level 7)

`otError otSeekerSetUpNextConnection(otInstance *aInstance, otSockAddr *aSockAddr)`

**Description:** Selects the next best candidate and prepares the connection.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[out]|aInstance|A pointer to a socket address to output the candidate's address.|
|[otSockAddr](ot-sock-addr) *|N/A|aSockAddr||

This function MUST be called after the discovery scan has completed (indicated by the `otSeekerScanEvaluator` callback receiving `NULL`). Calling it before scan completion will result in `OT_ERROR_INVALID_STATE`.

This function iterates through the discovered Joiner Router candidates in order of priority. For the selected candidate, it configures the radio channel and PAN ID, and populates `aSockAddr` with the candidate's address. It also registers the Seeker UDP port `otSeekerGetUdpPort()` as an unsecure port to allow a UDP connection to the candidate. The next layer code can start sending UDP messages to the given `otSockAddr` ensuring to use the unsecure Seeker UDP port as the source port. These messages are then forwarded by the Joiner Router onward to a Commissioner/Enroller connected via a Border Agent/Admitter.

If the list is exhausted, this function returns `OT_ERROR_NOT_FOUND` and automatically calls `otSeekerStop()`, which removes the unsecure port and clears internal state.

##### Server

This module includes functions to manage local network data with the OpenThread Server. 

###### Functions

###### otServerGetNetDataLocal (heading level 7)

`otError otServerGetNetDataLocal(otInstance *aInstance, bool aStable, uint8_t *aData, uint8_t *aDataLength)`

**Description:** Provides a full or stable copy of the local Thread Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aStable|TRUE when copying the stable version, FALSE when copying the full version.|
|uint8_t *|[out]|aData|A pointer to the data buffer.|
|uint8_t *|[inout]|aDataLength|On entry, size of the data buffer pointed to by `aData`. On exit, number of copied bytes.|

###### otServerAddService (heading level 7)

`otError otServerAddService(otInstance *aInstance, const otServiceConfig *aConfig)`

**Description:** Add a service configuration to the local network data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otServiceConfig](ot-service-config) *|[in]|aConfig|A pointer to the service configuration.|

**See Also**

- [otServerRemoveService](api-server#ot-server-remove-service)
- [otServerRegister](api-server#ot-server-register)

###### otServerRemoveService (heading level 7)

`otError otServerRemoveService(otInstance *aInstance, uint32_t aEnterpriseNumber, const uint8_t *aServiceData, uint8_t aServiceDataLength)`

**Description:** Remove a service configuration from the local network data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aEnterpriseNumber|Enterprise Number of the service entry to be deleted.|
|const uint8_t *|[in]|aServiceData|A pointer to an Service Data to look for during deletion.|
|uint8_t|[in]|aServiceDataLength|The length of `aServiceData` in bytes.|

**See Also**

- [otServerAddService](api-server#ot-server-add-service)
- [otServerRegister](api-server#ot-server-register)

###### otServerGetNextService (heading level 7)

`otError otServerGetNextService(otInstance *aInstance, otNetworkDataIterator *aIterator, otServiceConfig *aConfig)`

**Description:** Gets the next service in the local Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otNetworkDataIterator](api-thread-general#ot-network-data-iterator) *|[inout]|aIterator|A pointer to the Network Data iterator context. To get the first service entry it should be set to OT_NETWORK_DATA_ITERATOR_INIT.|
|[otServiceConfig](ot-service-config) *|[out]|aConfig|A pointer to where the service information will be placed.|

###### otServerRegister (heading level 7)

`otError otServerRegister(otInstance *aInstance)`

**Description:** Immediately register the local network data with the Leader.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**See Also**

- [otServerAddService](api-server#ot-server-add-service)
- [otServerRemoveService](api-server#ot-server-remove-service)

##### Backbone Router

This module includes functions for the OpenThread Backbone Router Service. 

###### Modules

[otBackboneRouterConfig](ot-backbone-router-config)

[otBackboneRouterMulticastListenerInfo](ot-backbone-router-multicast-listener-info)

[otBackboneRouterNdProxyInfo](ot-backbone-router-nd-proxy-info)

###### Enumerations

###### otBackboneRouterState (heading level 7)

```
enum otBackboneRouterState {
    OT_BACKBONE_ROUTER_STATE_DISABLED = 0
    OT_BACKBONE_ROUTER_STATE_SECONDARY = 1
    OT_BACKBONE_ROUTER_STATE_PRIMARY = 2
}
```

**Description:**

Represents the Backbone Router Status.

**Enumerator:**

|   |   |
|---|---|
|OT_BACKBONE_ROUTER_STATE_DISABLED|Backbone function is disabled.|
|OT_BACKBONE_ROUTER_STATE_SECONDARY|Secondary Backbone Router.|
|OT_BACKBONE_ROUTER_STATE_PRIMARY|The Primary Backbone Router.|

###### otBackboneRouterMulticastListenerEvent (heading level 7)

```
enum otBackboneRouterMulticastListenerEvent {
    OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ADDED = 0
    OT_BACKBONE_ROUTER_MULTICAST_LISTENER_REMOVED = 1
}
```

**Description:**

Represents the Multicast Listener events.

**Enumerator:**

|   |   |
|---|---|
|OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ADDED|Multicast Listener was added.|
|OT_BACKBONE_ROUTER_MULTICAST_LISTENER_REMOVED|Multicast Listener was removed or expired.|

###### otBackboneRouterNdProxyEvent (heading level 7)

```
enum otBackboneRouterNdProxyEvent {
    OT_BACKBONE_ROUTER_NDPROXY_ADDED = 0
    OT_BACKBONE_ROUTER_NDPROXY_REMOVED = 1
    OT_BACKBONE_ROUTER_NDPROXY_RENEWED = 2
    OT_BACKBONE_ROUTER_NDPROXY_CLEARED = 3
}
```

**Description:**

Represents the ND Proxy events.

**Enumerator:**

|   |   |
|---|---|
|OT_BACKBONE_ROUTER_NDPROXY_ADDED|ND Proxy was added.|
|OT_BACKBONE_ROUTER_NDPROXY_REMOVED|ND Proxy was removed.|
|OT_BACKBONE_ROUTER_NDPROXY_RENEWED|ND Proxy was renewed.|
|OT_BACKBONE_ROUTER_NDPROXY_CLEARED|All ND Proxies were cleared.|

###### otBackboneRouterDomainPrefixEvent (heading level 7)

```
enum otBackboneRouterDomainPrefixEvent {
    OT_BACKBONE_ROUTER_DOMAIN_PREFIX_ADDED = 0
    OT_BACKBONE_ROUTER_DOMAIN_PREFIX_REMOVED = 1
    OT_BACKBONE_ROUTER_DOMAIN_PREFIX_CHANGED = 2
}
```

**Description:**

Represents the Domain Prefix events.

**Enumerator:**

|   |   |
|---|---|
|OT_BACKBONE_ROUTER_DOMAIN_PREFIX_ADDED|Domain Prefix was added.|
|OT_BACKBONE_ROUTER_DOMAIN_PREFIX_REMOVED|Domain Prefix was removed.|
|OT_BACKBONE_ROUTER_DOMAIN_PREFIX_CHANGED|Domain Prefix was changed.|

###### Typedefs

###### otBackboneRouterConfig (heading level 7)

`typedef struct otBackboneRouterConfig otBackboneRouterConfig`

**Description:**

Represents Backbone Router configuration.

###### otBackboneRouterMulticastListenerCallback (heading level 7)

`typedef void(* otBackboneRouterMulticastListenerCallback) (void *aContext, otBackboneRouterMulticastListenerEvent aEvent, const otIp6Address *aAddress)`

**Description:**

Pointer is called whenever the Multicast Listeners change.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|The user context pointer.|
||[in]|aEvent|The Multicast Listener event.|
||[in]|aAddress|The IPv6 multicast address of the Multicast Listener.|

**Details:**

###### otBackboneRouterMulticastListenerIterator (heading level 7)

`typedef uint16_t otBackboneRouterMulticastListenerIterator`

**Description:**

Used to iterate through Multicast Listeners.

###### otBackboneRouterMulticastListenerInfo (heading level 7)

`typedef struct otBackboneRouterMulticastListenerInfo otBackboneRouterMulticastListenerInfo`

**Description:**

Represents a Backbone Router Multicast Listener info.

###### otBackboneRouterNdProxyCallback (heading level 7)

`typedef void(* otBackboneRouterNdProxyCallback) (void *aContext, otBackboneRouterNdProxyEvent aEvent, const otIp6Address *aDua)`

**Description:**

Pointer is called whenever the Nd Proxy changed.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|The user context pointer.|
||[in]|aEvent|The ND Proxy event.|
||[in]|aDua|The Domain Unicast Address of the ND Proxy, or NULL if `aEvent` is `OT_BACKBONE_ROUTER_NDPROXY_CLEARED`.|

**Details:**

###### otBackboneRouterNdProxyInfo (heading level 7)

`typedef struct otBackboneRouterNdProxyInfo otBackboneRouterNdProxyInfo`

**Description:**

Represents the Backbone Router ND Proxy info.

###### otBackboneRouterDomainPrefixCallback (heading level 7)

`typedef void(* otBackboneRouterDomainPrefixCallback) (void *aContext, otBackboneRouterDomainPrefixEvent aEvent, const otIp6Prefix *aDomainPrefix)`

**Description:**

Pointer is called whenever the Domain Prefix changed.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|The user context pointer.|
||[in]|aEvent|The Domain Prefix event.|
||[in]|aDomainPrefix|The new Domain Prefix if added or changed, NULL otherwise.|

**Details:**

###### Functions

###### otBackboneRouterGetPrimary (heading level 7)

`otError otBackboneRouterGetPrimary(otInstance *aInstance, otBackboneRouterConfig *aConfig)`

**Description:** Gets the Primary Backbone Router information in the Thread Network.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otBackboneRouterConfig](ot-backbone-router-config) *|[out]|aConfig|A pointer to where to put Primary Backbone Router information.|

###### otBackboneRouterSetEnabled (heading level 7)

`void otBackboneRouterSetEnabled(otInstance *aInstance, bool aEnable)`

**Description:** Enables or disables Backbone functionality.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnable|TRUE to enable Backbone functionality, FALSE otherwise.|

If enabled, a Server Data Request message `SRV_DATA.ntf` is triggered for the attached device if there is no Backbone Router Service in the Thread Network Data.

If disabled, `SRV_DATA.ntf` is triggered if the Backbone Router is in the Primary state.

Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is enabled.

**See Also**

- [otBackboneRouterGetState](api-backbone-router#ot-backbone-router-get-state)
- [otBackboneRouterGetConfig](api-backbone-router#ot-backbone-router-get-config)
- [otBackboneRouterSetConfig](api-backbone-router#ot-backbone-router-set-config)
- [otBackboneRouterRegister](api-backbone-router#ot-backbone-router-register)

###### otBackboneRouterGetState (heading level 7)

`otBackboneRouterState otBackboneRouterGetState(otInstance *aInstance)`

**Description:** Gets the Backbone Router [otBackboneRouterState](api-backbone-router#ot-backbone-router-state).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**See Also**

- [otBackboneRouterSetEnabled](api-backbone-router#ot-backbone-router-set-enabled)
- [otBackboneRouterGetConfig](api-backbone-router#ot-backbone-router-get-config)
- [otBackboneRouterSetConfig](api-backbone-router#ot-backbone-router-set-config)
- [otBackboneRouterRegister](api-backbone-router#ot-backbone-router-register)

###### otBackboneRouterGetConfig (heading level 7)

`void otBackboneRouterGetConfig(otInstance *aInstance, otBackboneRouterConfig *aConfig)`

**Description:** Gets the local Backbone Router configuration.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otBackboneRouterConfig](ot-backbone-router-config) *|[out]|aConfig|A pointer where to put local Backbone Router configuration.|

Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is enabled.

**See Also**

- [otBackboneRouterSetEnabled](api-backbone-router#ot-backbone-router-set-enabled)
- [otBackboneRouterGetState](api-backbone-router#ot-backbone-router-get-state)
- [otBackboneRouterSetConfig](api-backbone-router#ot-backbone-router-set-config)
- [otBackboneRouterRegister](api-backbone-router#ot-backbone-router-register)

###### otBackboneRouterSetConfig (heading level 7)

`otError otBackboneRouterSetConfig(otInstance *aInstance, const otBackboneRouterConfig *aConfig)`

**Description:** Sets the local Backbone Router configuration [otBackboneRouterConfig](ot-backbone-router-config).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otBackboneRouterConfig](ot-backbone-router-config) *|[in]|aConfig|A pointer to the Backbone Router configuration to take effect.|

A Server Data Request message `SRV_DATA.ntf` is initiated automatically if BBR Dataset changes for Primary Backbone Router.

Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is enabled.

**See Also**

- [otBackboneRouterSetEnabled](api-backbone-router#ot-backbone-router-set-enabled)
- [otBackboneRouterGetState](api-backbone-router#ot-backbone-router-get-state)
- [otBackboneRouterGetConfig](api-backbone-router#ot-backbone-router-get-config)
- [otBackboneRouterRegister](api-backbone-router#ot-backbone-router-register)

###### otBackboneRouterRegister (heading level 7)

`otError otBackboneRouterRegister(otInstance *aInstance)`

**Description:** Explicitly registers local Backbone Router configuration.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

A Server Data Request message `SRV_DATA.ntf` is triggered for the attached device.

Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is enabled.

**See Also**

- [otBackboneRouterSetEnabled](api-backbone-router#ot-backbone-router-set-enabled)
- [otBackboneRouterGetState](api-backbone-router#ot-backbone-router-get-state)
- [otBackboneRouterGetConfig](api-backbone-router#ot-backbone-router-get-config)
- [otBackboneRouterSetConfig](api-backbone-router#ot-backbone-router-set-config)

###### otBackboneRouterGetRegistrationJitter (heading level 7)

`uint8_t otBackboneRouterGetRegistrationJitter(otInstance *aInstance)`

**Description:** Returns the Backbone Router registration jitter value.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

**Returns**

- The Backbone Router registration jitter value.

**See Also**

- [otBackboneRouterSetRegistrationJitter](api-backbone-router#ot-backbone-router-set-registration-jitter)

###### otBackboneRouterSetRegistrationJitter (heading level 7)

`void otBackboneRouterSetRegistrationJitter(otInstance *aInstance, uint8_t aJitter)`

**Description:** Sets the Backbone Router registration jitter value.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|the Backbone Router registration jitter value to set.|
|uint8_t|N/A|aJitter||

**See Also**

- [otBackboneRouterGetRegistrationJitter](api-backbone-router#ot-backbone-router-get-registration-jitter)

###### otBackboneRouterGetDomainPrefix (heading level 7)

`otError otBackboneRouterGetDomainPrefix(otInstance *aInstance, otBorderRouterConfig *aConfig)`

**Description:** Gets the local Domain Prefix configuration.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otBorderRouterConfig](ot-border-router-config) *|[out]|aConfig|A pointer to the Domain Prefix configuration.|

###### otBackboneRouterConfigNextDuaRegistrationResponse (heading level 7)

`void otBackboneRouterConfigNextDuaRegistrationResponse(otInstance *aInstance, const otIp6InterfaceIdentifier *aMlIid, uint8_t aStatus)`

**Description:** Configures response status for next DUA registration.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6InterfaceIdentifier](ot-ip6-interface-identifier) *|[in]|aMlIid|A pointer to the Mesh Local IID. If NULL, respond with `aStatus` for any coming DUA.req, otherwise only respond the one with matching `aMlIid`.|
|uint8_t|[in]|aStatus|The status to respond.|

Note: available only when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled. Only used for test and certification.

TODO: (DUA) support coap error code and corresponding process for certification purpose.

###### otBackboneRouterConfigNextMulticastListenerRegistrationResponse (heading level 7)

`void otBackboneRouterConfigNextMulticastListenerRegistrationResponse(otInstance *aInstance, uint8_t aStatus)`

**Description:** Configures the response status for the next Multicast Listener Registration.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aStatus|The status to respond.|

Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE`, `OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE`, and `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` are enabled.

###### otBackboneRouterSetMulticastListenerCallback (heading level 7)

`void otBackboneRouterSetMulticastListenerCallback(otInstance *aInstance, otBackboneRouterMulticastListenerCallback aCallback, void *aContext)`

**Description:** Sets the Backbone Router Multicast Listener callback.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otBackboneRouterMulticastListenerCallback](api-backbone-router#ot-backbone-router-multicast-listener-callback)|[in]|aCallback|A pointer to the Multicast Listener callback.|
|void *|[in]|aContext|A user context pointer.|

###### otBackboneRouterMulticastListenerClear (heading level 7)

`void otBackboneRouterMulticastListenerClear(otInstance *aInstance)`

**Description:** Clears the Multicast Listeners.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE`, `OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE`, and `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` are enabled.

**See Also**

- [otBackboneRouterMulticastListenerAdd](api-backbone-router#ot-backbone-router-multicast-listener-add)
- [otBackboneRouterMulticastListenerGetNext](api-backbone-router#ot-backbone-router-multicast-listener-get-next)

###### otBackboneRouterMulticastListenerAdd (heading level 7)

`otError otBackboneRouterMulticastListenerAdd(otInstance *aInstance, const otIp6Address *aAddress, uint32_t aTimeout)`

**Description:** Adds a Multicast Listener with a timeout value, in seconds.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Address](ot-ip6-address) *|[in]|aAddress|The Multicast Listener address.|
|uint32_t|[in]|aTimeout|The timeout (in seconds) of the Multicast Listener, or 0 to use the default MLR timeout.|

Pass `0` to use the default MLR timeout.

Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE`, `OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE`, and `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` are enabled.

**See Also**

- [otBackboneRouterMulticastListenerClear](api-backbone-router#ot-backbone-router-multicast-listener-clear)
- [otBackboneRouterMulticastListenerGetNext](api-backbone-router#ot-backbone-router-multicast-listener-get-next)

###### otBackboneRouterMulticastListenerGetNext (heading level 7)

`otError otBackboneRouterMulticastListenerGetNext(otInstance *aInstance, otBackboneRouterMulticastListenerIterator *aIterator, otBackboneRouterMulticastListenerInfo *aListenerInfo)`

**Description:** Gets the next Multicast Listener info (using an iterator).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otBackboneRouterMulticastListenerIterator](api-backbone-router#ot-backbone-router-multicast-listener-iterator) *|[inout]|aIterator|A pointer to the iterator. On success the iterator will be updated to point to next Multicast Listener. To get the first entry the iterator should be set to OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT.|
|[otBackboneRouterMulticastListenerInfo](ot-backbone-router-multicast-listener-info) *|[out]|aListenerInfo|A pointer to an `otBackboneRouterMulticastListenerInfo` where information of next Multicast Listener is placed (on success).|

**See Also**

- [otBackboneRouterMulticastListenerClear](api-backbone-router#ot-backbone-router-multicast-listener-clear)
- [otBackboneRouterMulticastListenerAdd](api-backbone-router#ot-backbone-router-multicast-listener-add)

###### otBackboneRouterSetNdProxyCallback (heading level 7)

`void otBackboneRouterSetNdProxyCallback(otInstance *aInstance, otBackboneRouterNdProxyCallback aCallback, void *aContext)`

**Description:** Sets the Backbone Router ND Proxy callback.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otBackboneRouterNdProxyCallback](api-backbone-router#ot-backbone-router-nd-proxy-callback)|[in]|aCallback|A pointer to the ND Proxy callback.|
|void *|[in]|aContext|A user context pointer.|

###### otBackboneRouterGetNdProxyInfo (heading level 7)

`otError otBackboneRouterGetNdProxyInfo(otInstance *aInstance, const otIp6Address *aDua, otBackboneRouterNdProxyInfo *aNdProxyInfo)`

**Description:** Gets the Backbone Router ND Proxy info.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Address](ot-ip6-address) *|[in]|aDua|The Domain Unicast Address.|
|[otBackboneRouterNdProxyInfo](ot-backbone-router-nd-proxy-info) *|[out]|aNdProxyInfo|A pointer to the ND Proxy info.|

###### otBackboneRouterSetDomainPrefixCallback (heading level 7)

`void otBackboneRouterSetDomainPrefixCallback(otInstance *aInstance, otBackboneRouterDomainPrefixCallback aCallback, void *aContext)`

**Description:** Sets the Backbone Router Domain Prefix callback.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otBackboneRouterDomainPrefixCallback](api-backbone-router#ot-backbone-router-domain-prefix-callback)|[in]|aCallback|A pointer to the Domain Prefix callback.|
|void *|[in]|aContext|A user context pointer.|

###### Macros

`#define OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT     0`

**Description**: Initializer for otBackboneRouterMulticastListenerIterator.

Represents Backbone Router configuration. 

###### Public Attributes (heading level 7)

###### mServer16 (heading level 8)

```
uint16_t otBackboneRouterConfig::mServer16
```

**Description:** Only used when get Primary Backbone Router information in the Thread Network.

###### mReregistrationDelay (heading level 8)

```
uint16_t otBackboneRouterConfig::mReregistrationDelay
```

**Description:** Reregistration Delay (in seconds)

###### mMlrTimeout (heading level 8)

```
uint32_t otBackboneRouterConfig::mMlrTimeout
```

**Description:** Multicast Listener Registration Timeout (in seconds)

###### mSequenceNumber (heading level 8)

```
uint8_t otBackboneRouterConfig::mSequenceNumber
```

**Description:** Sequence Number.

Represents a Backbone Router Multicast Listener info. 

###### Public Attributes (heading level 7)

###### mAddress (heading level 8)

```
otIp6Address otBackboneRouterMulticastListenerInfo::mAddress
```

###### mTimeout (heading level 8)

```
uint32_t otBackboneRouterMulticastListenerInfo::mTimeout
```

Represents the Backbone Router ND Proxy info. 

###### Public Attributes (heading level 7)

###### mMeshLocalIid (heading level 8)

```
otIp6InterfaceIdentifier* otBackboneRouterNdProxyInfo::mMeshLocalIid
```

**Description:** Mesh-local IID.

###### mTimeSinceLastTransaction (heading level 8)

```
uint32_t otBackboneRouterNdProxyInfo::mTimeSinceLastTransaction
```

**Description:** Time since last transaction (Seconds)

###### mRloc16 (heading level 8)

```
uint16_t otBackboneRouterNdProxyInfo::mRloc16
```

**Description:** RLOC16.

##### Border Agent

This module includes functions for the Thread Border Agent role. 

This module includes functions for the Thread Border Agent Admitter role.

All APIs in this module require both `OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE` and `OPENTHREAD_CONFIG_BORDER_AGENT_ADMITTER_ENABLE` features to be enabled. 

###### Modules

[otBorderAgentId](ot-border-agent-id)

[otBorderAgentCounters](ot-border-agent-counters)

[otBorderAgentSessionInfo](ot-border-agent-session-info)

[otBorderAgentSessionIterator](ot-border-agent-session-iterator)

[otBorderAgentMeshCoPServiceTxtData](ot-border-agent-mesh-co-p-service-txt-data)

[otBorderAdmitterIterator](ot-border-admitter-iterator)

[otBorderAdmitterEnrollerInfo](ot-border-admitter-enroller-info)

[otBorderAdmitterJoinerInfo](ot-border-admitter-joiner-info)

[otBorderAgentEphemeralKeyTap](ot-border-agent-ephemeral-key-tap)

###### Enumerations

###### otBorderAgentEphemeralKeyState (heading level 7)

```
enum otBorderAgentEphemeralKeyState {
    OT_BORDER_AGENT_STATE_DISABLED = 0
    OT_BORDER_AGENT_STATE_STOPPED = 1
    OT_BORDER_AGENT_STATE_STARTED = 2
    OT_BORDER_AGENT_STATE_CONNECTED = 3
    OT_BORDER_AGENT_STATE_ACCEPTED = 4
}
```

**Description:**

Represents Border Agent's Ephemeral Key Manager state.

**Enumerator:**

|   |   |
|---|---|
|OT_BORDER_AGENT_STATE_DISABLED|Ephemeral Key Manager is disabled.|
|OT_BORDER_AGENT_STATE_STOPPED|Enabled, but no ephemeral key is in use (not set or started).|
|OT_BORDER_AGENT_STATE_STARTED|Ephemeral key is set. Listening to accept secure connections.|
|OT_BORDER_AGENT_STATE_CONNECTED|Session is established with an external commissioner candidate.|
|OT_BORDER_AGENT_STATE_ACCEPTED|Session is established and candidate is accepted as full commissioner.|

###### Typedefs

###### otBorderAgentId (heading level 7)

`typedef struct otBorderAgentId otBorderAgentId`

**Description:**

Represents a Border Agent Identifier.

###### otBorderAgentCounters (heading level 7)

`typedef struct otBorderAgentCounters otBorderAgentCounters`

**Description:**

Defines Border Agent counters.

**Details:**

The `mEpskc` related counters require `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.

###### otBorderAgentSessionInfo (heading level 7)

`typedef struct otBorderAgentSessionInfo otBorderAgentSessionInfo`

**Description:**

Represents information about a Border Agent session.

**Details:**

This structure is populated by `otBorderAgentGetNextSessionInfo()` during iteration over the list of sessions using an `otBorderAgentSessionIterator`.

To ensure consistent `mLifetime` calculations, the iterator's initialization time is stored within the iterator, and each session's `mLifetime` is calculated relative to this time.

###### otBorderAgentSessionIterator (heading level 7)

`typedef struct otBorderAgentSessionIterator otBorderAgentSessionIterator`

**Description:**

Represents an iterator for Border Agent sessions.

**Details:**

The caller MUST NOT access or update the fields in this struct. It is intended for OpenThread internal use only.

###### otBorderAgentMeshCoPServiceTxtData (heading level 7)

`typedef struct otBorderAgentMeshCoPServiceTxtData otBorderAgentMeshCoPServiceTxtData`

**Description:**

Represents the Border Agent MeshCoP Service TXT data.

###### otBorderAgentMeshCoPServiceChangedCallback (heading level 7)

`typedef void(* otBorderAgentMeshCoPServiceChangedCallback) (void *aContext)`

**Description:**

This callback informs the application of the changes in the state of the MeshCoP service.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

In specific, the 'state' includes the MeshCoP TXT data originated from the Thread network and whether the Border Agent is Active (which can be obtained by `otBorderAgentIsActive`).

###### otBorderAdmitterIterator (heading level 7)

`typedef struct otBorderAdmitterIterator otBorderAdmitterIterator`

**Description:**

Represents an iterator for Border Admitter enroller.

**Details:**

The caller MUST NOT access or update the fields in this struct. It is intended for OpenThread internal use only.

###### otBorderAdmitterEnrollerInfo (heading level 7)

`typedef struct otBorderAdmitterEnrollerInfo otBorderAdmitterEnrollerInfo`

**Description:**

Represents information about an enroller.

**Details:**

To ensure consistent `mRegisterDuration` calculations, the iterator's initialization time is stored within the iterator, and each enroller `mRegisterDuration` is calculated relative to this time.

###### otBorderAdmitterJoinerInfo (heading level 7)

`typedef struct otBorderAdmitterJoinerInfo otBorderAdmitterJoinerInfo`

**Description:**

Represents information about a joiner accepted by an enroller.

**Details:**

To ensure consistent duration calculations, the iterator's initialization time is stored within the iterator, and the `mMsecSinceAccept` is calculated relative to this time.

###### otBorderAgentEphemeralKeyState (heading level 7)

`typedef enum otBorderAgentEphemeralKeyState otBorderAgentEphemeralKeyState`

**Description:**

Represents Border Agent's Ephemeral Key Manager state.

###### otBorderAgentEphemeralKeyTap (heading level 7)

`typedef struct otBorderAgentEphemeralKeyTap otBorderAgentEphemeralKeyTap`

**Description:**

Represents a Thread Administration One-Time Passcode (TAP).

###### otBorderAgentEphemeralKeyCallback (heading level 7)

`typedef void(* otBorderAgentEphemeralKeyCallback) (void *aContext)`

**Description:**

Callback function pointer to signal state changes to the Border Agent's Ephemeral Key Manager.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to an arbitrary context (provided when callback is set).|

**Details:**

This callback is invoked whenever the `otBorderAgentEphemeralKeyGetState()` gets changed.

Any OpenThread API, including `otBorderAgent` APIs, can be safely called from this callback.

###### Functions

###### otBorderAgentSetEnabled (heading level 7)

`void otBorderAgentSetEnabled(otInstance *aInstance, bool aEnabled)`

**Description:** Enables or disables the Border Agent service on the device.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|bool|[in]|aEnabled|A boolean to indicate whether to to enable (TRUE), or disable (FALSE).|

By default, the Border Agent service is enabled when the `OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE` feature is used. This function allows higher-layer code to explicitly control its state. This can be useful in scenarios such as:

- The higher-layer code wishes to delay the start of the Border Agent service (and its mDNS advertisement of the `_meshcop._udp` service on the infrastructure link). This allows time to prepare or determine vendor-specific TXT data entries for inclusion.
- Unit tests or test scripts might disable the Border Agent service to prevent it from interfering with specific test steps. For example, tests validating mDNS or DNS-SD functionality may disable the Border Agent to prevent its registration of the MeshCoP service.

###### otBorderAgentIsEnabled (heading level 7)

`bool otBorderAgentIsEnabled(otInstance *aInstance)`

**Description:** Indicates whether or not the Border Agent service is enabled.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|

###### otBorderAgentIsActive (heading level 7)

`bool otBorderAgentIsActive(otInstance *aInstance)`

**Description:** Indicates whether or not the Border Agent service is enabled and also active.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

While the Border Agent is active, external commissioner candidates can try to connect to and establish secure DTLS sessions with the Border Agent using PSKc. A connected commissioner can then petition to become a full commissioner.

If `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE` is enabled, independent and separate DTLS transport and sessions are used for the ephemeral key. Therefore, the ephemeral key and Border Agent service can be enabled and used in parallel.

###### otBorderAgentGetUdpPort (heading level 7)

`uint16_t otBorderAgentGetUdpPort(otInstance *aInstance)`

**Description:** Gets the UDP port of the Thread Border Agent service.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- UDP port of the Border Agent.

###### otBorderAgentSetMeshCoPServiceChangedCallback (heading level 7)

`void otBorderAgentSetMeshCoPServiceChangedCallback(otInstance *aInstance, otBorderAgentMeshCoPServiceChangedCallback aCallback, void *aContext)`

**Description:** Sets the callback function used by the Border Agent to notify of any changes to the state of the MeshCoP service.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otBorderAgentMeshCoPServiceChangedCallback](api-border-agent#ot-border-agent-mesh-co-p-service-changed-callback)|[in]|aCallback|The callback to be invoked when there are any changes of the MeshCoP service.|
|void *|[in]|aContext|A pointer to application-specific context.|

The callback is invoked when the 'Is Active' state of the Border Agent or the MeshCoP service TXT data values change. For example, it is invoked when the network name or the extended PAN ID changes and passes the updated encoded TXT data to the application layer.

This callback is invoked once right after this API is called to provide initial states of the MeshCoP service.

###### otBorderAgentGetMeshCoPServiceTxtData (heading level 7)

`otError otBorderAgentGetMeshCoPServiceTxtData(otInstance *aInstance, otBorderAgentMeshCoPServiceTxtData *aTxtData)`

**Description:** Gets the MeshCoP service TXT data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otBorderAgentMeshCoPServiceTxtData](ot-border-agent-mesh-co-p-service-txt-data) *|[out]|aTxtData|A pointer to a MeshCoP Service TXT data struct to get the data.|

The generated TXT data includes a subset of keys (depending on the device's current state and whether features are enabled) as specified in the documentation of the `OT_BORDER_AGENT_MESHCOP_SERVICE_TXT_DATA_MAX_LENGTH` constant. Notably, if `OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE` is enabled and `otBorderAgentSetVendorTxtData()` was used to set extra vendor-specific TXT data bytes, those vendor-specified TXT data bytes are NOT included in the TXT data returned by this function.

###### otBorderAgentSetMeshCoPServiceBaseName (heading level 7)

`otError otBorderAgentSetMeshCoPServiceBaseName(otInstance *aInstance, const char *aBaseName)`

**Description:** Sets the base name to construct the service instance name used when advertising the mDNS `_meshcop._udp` service by the Border Agent.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const char *|[in]|aBaseName|The base name to use (MUST not be NULL).|

Requires the `OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE` feature.

The name can also be configured using the `OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_BASE_NAME` configuration option (which is the recommended way to specify this name). This API is provided for projects where the name needs to be set after device initialization and at run-time.

Per the Thread specification, the service instance should be a user-friendly name identifying the device model or product. A recommended format is "VendorName ProductName".

To construct the full name and ensure name uniqueness, the OpenThread Border Agent module appends a suffix (e.g., " #XXXX" where "XXXX" represents the last two bytes of the device's Extended Address in hex) to the given base name. If a name conflict is detected on the network, an additional index may be appended (e.g., " #XXXX (1)").

Note that the same name will be used for the ephemeral key service `_meshcop-e._udp` when the ephemeral key feature is enabled and used.

###### otBorderAgentSetVendorTxtData (heading level 7)

`void otBorderAgentSetVendorTxtData(otInstance *aInstance, const uint8_t *aVendorData, uint16_t aVendorDataLength)`

**Description:** Sets the vendor extra TXT data to be included when the Border Agent advertises the mDNS `_meshcop._udp` service.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const uint8_t *|[in]|aVendorData|A pointer to the buffer containing the vendor TXT data.|
|uint16_t|[in]|aVendorDataLength|The length of `aVendorData` in bytes.|

Requires the `OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE` feature.

The provided `aVendorData` bytes are appended as they appear in the buffer to the end of the TXT data generated by the Border Agent itself, and are then included in the advertised mDNS `_meshcop._udp` service.

This function itself does not perform any validation of the format of the provided `aVendorData`. Therefore, the caller MUST ensure it is formatted properly. Per the Thread specification, vendor-specific Key-Value TXT data pairs use TXT keys starting with 'v'. For example, `vn` for vendor name and generally `v*`.

The OpenThread stack will create and retain its own copy of the bytes in `aVendorData`. So, the buffer passed to this function does not need to persist beyond the scope of the call.

The vendor TXT data can be set at any time while the Border Agent is in any state. If there is a change from the previously set value, it will trigger an update of the registered mDNS service to advertise the new TXT data.

###### otBorderAgentGetId (heading level 7)

`otError otBorderAgentGetId(otInstance *aInstance, otBorderAgentId *aId)`

**Description:** Gets the randomly generated Border Agent ID.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otBorderAgentId](ot-border-agent-id) *|[out]|aId|A pointer to buffer to receive the ID.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE`.

The ID is saved in persistent storage and survives reboots. The typical use case of the ID is to be published in the MeshCoP mDNS service as the `id` TXT value for the client to identify this Border Router/Agent device.

**See Also**

- [otBorderAgentSetId](api-border-agent#ot-border-agent-set-id)

###### otBorderAgentSetId (heading level 7)

`otError otBorderAgentSetId(otInstance *aInstance, const otBorderAgentId *aId)`

**Description:** Sets the Border Agent ID.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otBorderAgentId](ot-border-agent-id) *|[in]|aId|A pointer to the Border Agent ID.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE`.

The Border Agent ID will be saved in persistent storage and survive reboots. It's required to set the ID only once after factory reset. If the ID has never been set by calling this function, a random ID will be generated and returned when `otBorderAgentGetId` is called.

**See Also**

- [otBorderAgentGetId](api-border-agent#ot-border-agent-get-id)

###### otBorderAgentInitSessionIterator (heading level 7)

`void otBorderAgentInitSessionIterator(otInstance *aInstance, otBorderAgentSessionIterator *aIterator)`

**Description:** Initializes a session iterator.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otBorderAgentSessionIterator](ot-border-agent-session-iterator) *|[in]|aIterator|The iterator to initialize.|

An iterator MUST be initialized before being used in `otBorderAgentGetNextSessionInfo()`. A previously initialized iterator can be re-initialized to start from the beginning of the session list.

###### otBorderAgentGetNextSessionInfo (heading level 7)

`otError otBorderAgentGetNextSessionInfo(otBorderAgentSessionIterator *aIterator, otBorderAgentSessionInfo *aSessionInfo)`

**Description:** Retrieves the next Border Agent session information.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otBorderAgentSessionIterator](ot-border-agent-session-iterator) *|[in]|aIterator|The iterator to use.|
|[otBorderAgentSessionInfo](ot-border-agent-session-info) *|[out]|aSessionInfo|A pointer to an `otBorderAgentSessionInfo` to populate.|

###### otBorderAgentGetCounters (heading level 7)

`const otBorderAgentCounters * otBorderAgentGetCounters(otInstance *aInstance)`

**Description:** Gets the counters of the Thread Border Agent.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- A pointer to the Border Agent counters.

###### otBorderAgentEvictActiveCommissioner (heading level 7)

`otError otBorderAgentEvictActiveCommissioner(otInstance *aInstance)`

**Description:** Forcefully evicts the current active Thread Commissioner.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_COMMISSIONER_EVICTION_API_ENABLE`.

This is intended as an administrator tool to address a misbehaving or stale commissioner session that may be connected through a different Border Agent. It provides a mechanism to clear the single Active Commissioner role within the Thread network, allowing a new candidate to be selected as the Active commissioner.

###### otBorderAdmitterSetEnabled (heading level 7)

`void otBorderAdmitterSetEnabled(otInstance *aInstance, bool aEnable)`

**Description:** Enables or disables the Border Agent Admitter.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|bool|[in]|aEnable|A boolean to indicate whether to enable (TRUE) or disable (FALSE) the Border Agent Admitter.|

The default enable/disable state of Border Admitter (after OpenThread stack initialization) is determined by the OpenThread config `OPENTHREAD_CONFIG_BORDER_AGENT_ADMITTER_ENABLED_BY_DEFAULT`.

###### otBorderAdmitterIsEnabled (heading level 7)

`bool otBorderAdmitterIsEnabled(otInstance *aInstance)`

**Description:** Indicates whether the Border Agent Admitter is enabled.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|

###### otBorderAdmitterIsPrimeAdmitter (heading level 7)

`bool otBorderAdmitterIsPrimeAdmitter(otInstance *aInstance)`

**Description:** Indicates whether the device is currently the Prime Admitter.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|

The Prime Admitter is the device that wins the election among all Admitters within the Thread mesh network. The election algorithm ensures convergence on a single Prime Admitter within the mesh.

###### otBorderAdmitterIsActiveCommissioner (heading level 7)

`bool otBorderAdmitterIsActiveCommissioner(otInstance *aInstance)`

**Description:** Indicates whether the Prime Admitter is currently the active commissioner.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|

After becoming the Prime Admitter and having at least one enroller register, the Admitter petitions the Leader to be granted the commissioner role.

###### otBorderAdmitterIsPetitionRejected (heading level 7)

`bool otBorderAdmitterIsPetitionRejected(otInstance *aInstance)`

**Description:** Indicates whether the Prime Admitter's petition to become the native mesh commissioner was rejected.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|

A rejection typically occurs if there is already another active commissioner in the Thread network.

The Admitter will automatically retry petitioning. It monitors the Thread Network Data to see when the other commissioner is removed and retry its own petition.

###### otBorderAdmitterGetJoinerUdpPort (heading level 7)

`uint16_t otBorderAdmitterGetJoinerUdpPort(otInstance *aInstance)`

**Description:** Gets the Joiner UDP port.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|

A zero value indicates the Joiner UDP port is not specified/fixed by the Admitter (Joiner Routers can pick).

**Returns**

- The joiner UDP port number.

###### otBorderAdmitterSetJoinerUdpPort (heading level 7)

`void otBorderAdmitterSetJoinerUdpPort(otInstance *aInstance, uint16_t aUdpPort)`

**Description:** Sets the joiner UDP port.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|uint16_t|[in]|aUdpPort|The joiner UDP port number.|

A zero value indicates the Joiner UDP port is not specified/fixed by the Admitter (Joiner Routers can pick).

###### otBorderAdmitterInitIterator (heading level 7)

`void otBorderAdmitterInitIterator(otInstance *aInstance, otBorderAdmitterIterator *aIterator)`

**Description:** Initializes an `otBorderAdmitterIterator`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otBorderAdmitterIterator](ot-border-admitter-iterator) *|[in]|aIterator|A pointer to the iterator to initialize.|

An iterator MUST be initialized before it is used.

An iterator can be initialized again to restart from the beginning of the list.

When iterating over enrollers, the initialization time is recorded and used to calculate a consistent `mRegisterDuration` for each enroller.

###### otBorderAdmitterGetNextEnrollerInfo (heading level 7)

`otError otBorderAdmitterGetNextEnrollerInfo(otBorderAdmitterIterator *aIterator, otBorderAdmitterEnrollerInfo *aEnrollerInfo)`

**Description:** Retrieves the information about the next Enroller registered with the Admitter.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otBorderAdmitterIterator](ot-border-admitter-iterator) *|[in]|aIterator|The iterator to use.|
|[otBorderAdmitterEnrollerInfo](ot-border-admitter-enroller-info) *|[out]|aEnrollerInfo|A pointer to an `otBorderAdmitterEnrollerInfo` to populate.|

###### otBorderAdmitterGetNextJoinerInfo (heading level 7)

`otError otBorderAdmitterGetNextJoinerInfo(otBorderAdmitterIterator *aIterator, otBorderAdmitterJoinerInfo *aJoinerInfo)`

**Description:** Retrieves the information about the next accepted joiner by the latest retrieved enroller during iteration.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otBorderAdmitterIterator](ot-border-admitter-iterator) *|[in]|aIterator|The iterator to use.|
|[otBorderAdmitterJoinerInfo](ot-border-admitter-joiner-info) *|[out]|aJoinerInfo|A pointer to an `otBorderAdmitterJoinerInfo` to populate.|

Iterates over all joiners which are accepted by the latest enroller, i.e., the last enroller which was retrieved using the `aIterator` along with `otBorderAdmitterGetNextEnrollerInfo()`.

###### otBorderAgentEphemeralKeyGetState (heading level 7)

`otBorderAgentEphemeralKeyState otBorderAgentEphemeralKeyGetState(otInstance *aInstance)`

**Description:** Gets the state of Border Agent's Ephemeral Key Manager.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.

**Returns**

- The current state of Ephemeral Key Manager.

###### otBorderAgentEphemeralKeySetEnabled (heading level 7)

`void otBorderAgentEphemeralKeySetEnabled(otInstance *aInstance, bool aEnabled)`

**Description:** Enables/disables the Border Agent's Ephemeral Key Manager.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|bool|[in]|aEnabled|Whether to enable or disable the Ephemeral Key Manager.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.

If this function is called to disable, while an an ephemeral key is in use, the ephemeral key use will be stopped (as if `otBorderAgentEphemeralKeyStop()` is called).

###### otBorderAgentEphemeralKeyStart (heading level 7)

`otError otBorderAgentEphemeralKeyStart(otInstance *aInstance, const char *aKeyString, uint32_t aTimeout, uint16_t aUdpPort)`

**Description:** Starts using an ephemeral key for a given timeout duration.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const char *|[in]|aKeyString|The ephemeral key.|
|uint32_t|[in]|aTimeout|The timeout duration, in milliseconds, to use the ephemeral key. If zero, the default `OT_BORDER_AGENT_DEFAULT_EPHEMERAL_KEY_TIMEOUT` value is used. If the timeout value is larger than `OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_TIMEOUT`, the maximum value is used instead.|
|uint16_t|[in]|aUdpPort|The UDP port to use with the ephemeral key. If the UDP port is zero, an ephemeral port will be used. `otBorderAgentEphemeralKeyGetUdpPort()` returns the current UDP port being used.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.

An ephemeral key can only be set when `otBorderAgentEphemeralKeyGetState()` is `OT_BORDER_AGENT_STATE_STOPPED`, i.e., enabled but not yet started. Otherwise, `OT_ERROR_INVALID_STATE` is returned. This means that setting the ephemeral key again while a previously set key is still in use will fail. Callers can stop the previous key by calling `otBorderAgentEphemeralKeyStop()` before starting with a new key.

The Ephemeral Key Manager and the Border Agent service (which uses PSKc) can be enabled and used in parallel, as they use independent and separate DTLS transport and sessions.

The given `aKeyString` is used directly as the ephemeral PSK (excluding the trailing null `\0` character). Its length must be between `OT_BORDER_AGENT_MIN_EPHEMERAL_KEY_LENGTH` and `OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_LENGTH`, inclusive. Otherwise `OT_ERROR_INVALID_ARGS` is returned.

When successfully set, the ephemeral key can be used only once by an external commissioner candidate to establish a secure session. After the commissioner candidate disconnects, the use of the ephemeral key is stopped. If the timeout expires, the use of the ephemeral key is stopped, and any connected session using the key is immediately disconnected.

The Ephemeral Key Manager limits the number of failed DTLS connections to 10 attempts. After the 10th failed attempt, the use of the ephemeral key is automatically stopped (even if the timeout has not yet expired).

###### otBorderAgentEphemeralKeyStop (heading level 7)

`void otBorderAgentEphemeralKeyStop(otInstance *aInstance)`

**Description:** Stops the ephemeral key use and disconnects any session using it.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.

If there is no ephemeral key in use, calling this function has no effect.

###### otBorderAgentEphemeralKeyGetUdpPort (heading level 7)

`uint16_t otBorderAgentEphemeralKeyGetUdpPort(otInstance *aInstance)`

**Description:** Gets the UDP port used by Border Agent's Ephemeral Key Manager.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.

The port is applicable if an ephemeral key is in use, i.e., the state is not `OT_BORDER_AGENT_STATE_DISABLED` or `OT_BORDER_AGENT_STATE_STOPPED`.

**Returns**

- The UDP port being used by Border Agent's Ephemeral Key Manager (when active).

###### otBorderAgentEphemeralKeySetCallback (heading level 7)

`void otBorderAgentEphemeralKeySetCallback(otInstance *aInstance, otBorderAgentEphemeralKeyCallback aCallback, void *aContext)`

**Description:** Sets the callback function to notify state changes of Border Agent's Ephemeral Key Manager.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otBorderAgentEphemeralKeyCallback](api-border-agent#ot-border-agent-ephemeral-key-callback)|[in]|aCallback|The callback function pointer.|
|void *|[in]|aContext|The arbitrary context to use with callback.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.

A subsequent call to this function will replace any previously set callback.

###### otBorderAgentEphemeralKeyStateToString (heading level 7)

`const char * otBorderAgentEphemeralKeyStateToString(otBorderAgentEphemeralKeyState aState)`

**Description:** Converts a given `otBorderAgentEphemeralKeyState` to a human-readable string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otBorderAgentEphemeralKeyState](api-border-agent#ot-border-agent-ephemeral-key-state)|[in]|aState|The state to convert.|

**Returns**

- Human-readable string corresponding to `aState`.

###### otBorderAgentEphemeralKeyGenerateTap (heading level 7)

`otError otBorderAgentEphemeralKeyGenerateTap(otBorderAgentEphemeralKeyTap *aTap)`

**Description:** Generates a cryptographically secure random Thread Administration One-Time Passcode (TAP) string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otBorderAgentEphemeralKeyTap](ot-border-agent-ephemeral-key-tap) *|[out]|aTap|A pointer to an `otBorderAgentEphemeralKeyTap` to output the generated TAP.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE` and `OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE`.

The TAP is a string of 9 characters, generated as a sequence of eight cryptographically secure random numeric digits [`0`-`9`] followed by a single check digit determined using the Verhoeff algorithm.

###### otBorderAgentEphemeralKeyValidateTap (heading level 7)

`otError otBorderAgentEphemeralKeyValidateTap(const otBorderAgentEphemeralKeyTap *aTap)`

**Description:** Validates a given Thread Administration One-Time Passcode (TAP) string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otBorderAgentEphemeralKeyTap](ot-border-agent-ephemeral-key-tap) *|[in]|aTap|The `otBorderAgentEphemeralKeyTap` to validate.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE` and `OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE`.

Validates that the TAP string has the proper length, contains digit characters [`0`-`9`], and validates the Verhoeff checksum.

###### Macros

`#define OT_BORDER_AGENT_ID_LENGTH (16)`

**Description**: The length of Border Agent/Router ID in bytes.

`#define OT_BORDER_AGENT_MESHCOP_SERVICE_TXT_DATA_MAX_LENGTH 256`

**Description**: Maximum length of the OT core generated MeshCoP Service TXT data.

`#define OT_BORDER_AGENT_MESHCOP_SERVICE_BASE_NAME_MAX_LENGTH (OT_DNS_MAX_LABEL_SIZE - 13)`

**Description**: Maximum string length of base name used in `otBorderAgentSetMeshCoPServiceBaseName()`.

`#define OT_BORDER_AGENT_MIN_EPHEMERAL_KEY_LENGTH (6)`

**Description**: Minimum length of the ephemeral key string.

`#define OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_LENGTH (32)`

**Description**: Maximum length of the ephemeral key string.

`#define OT_BORDER_AGENT_DEFAULT_EPHEMERAL_KEY_TIMEOUT (2 * 60 * 1000u)`

**Description**: Default ephemeral key timeout interval in milliseconds.

`#define OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_TIMEOUT (10 * 60 * 1000u)`

**Description**: Maximum ephemeral key timeout interval in milliseconds.

`#define OT_BORDER_AGENT_EPHEMERAL_KEY_TAP_STRING_LENGTH 9`

**Description**: The string length of Thread Administration One-Time Passcode (TAP).

Represents a Border Agent Identifier. 

###### Public Attributes (heading level 7)

###### mId (heading level 8)

```
uint8_t otBorderAgentId::mId[OT_BORDER_AGENT_ID_LENGTH]
```

**Description:** Border Agent ID bytes.

Defines Border Agent counters. 

The `mEpskc` related counters require `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`. 

###### Public Attributes (heading level 7)

###### mEpskcActivations (heading level 8)

```
uint32_t otBorderAgentCounters::mEpskcActivations
```

**Description:** The number of ePSKc activations.

###### mEpskcDeactivationClears (heading level 8)

```
uint32_t otBorderAgentCounters::mEpskcDeactivationClears
```

**Description:** The number of ePSKc deactivations via API.

###### mEpskcDeactivationTimeouts (heading level 8)

```
uint32_t otBorderAgentCounters::mEpskcDeactivationTimeouts
```

**Description:** The number of ePSKc deactivations due to timeout.

###### mEpskcDeactivationMaxAttempts (heading level 8)

```
uint32_t otBorderAgentCounters::mEpskcDeactivationMaxAttempts
```

**Description:** The number of ePSKc deactivations due to reached max attempts.

###### mEpskcDeactivationDisconnects (heading level 8)

```
uint32_t otBorderAgentCounters::mEpskcDeactivationDisconnects
```

**Description:** The number of ePSKc deactivations due to commissioner disconnected.

###### mEpskcInvalidBaStateErrors (heading level 8)

```
uint32_t otBorderAgentCounters::mEpskcInvalidBaStateErrors
```

**Description:** The number of invalid border agent state errors at ePSKc activation.

###### mEpskcInvalidArgsErrors (heading level 8)

```
uint32_t otBorderAgentCounters::mEpskcInvalidArgsErrors
```

**Description:** The number of invalid args errors at ePSKc activation.

###### mEpskcStartSecureSessionErrors (heading level 8)

```
uint32_t otBorderAgentCounters::mEpskcStartSecureSessionErrors
```

**Description:** The number of start secure session errors at ePSKc activation.

###### mEpskcSecureSessionSuccesses (heading level 8)

```
uint32_t otBorderAgentCounters::mEpskcSecureSessionSuccesses
```

**Description:** The number of established secure sessions with ePSKc.

###### mEpskcSecureSessionFailures (heading level 8)

```
uint32_t otBorderAgentCounters::mEpskcSecureSessionFailures
```

**Description:** The number of failed secure sessions with ePSKc.

###### mEpskcCommissionerPetitions (heading level 8)

```
uint32_t otBorderAgentCounters::mEpskcCommissionerPetitions
```

**Description:** The number of successful commissioner petitions with ePSKc.

###### mPskcSecureSessionSuccesses (heading level 8)

```
uint32_t otBorderAgentCounters::mPskcSecureSessionSuccesses
```

**Description:** The number of established secure sessions with PSKc.

###### mPskcSecureSessionFailures (heading level 8)

```
uint32_t otBorderAgentCounters::mPskcSecureSessionFailures
```

**Description:** The number of failed secure sessions with PSKc.

###### mPskcCommissionerPetitions (heading level 8)

```
uint32_t otBorderAgentCounters::mPskcCommissionerPetitions
```

**Description:** The number of successful commissioner petitions with PSKc.

###### mMgmtActiveGets (heading level 8)

```
uint32_t otBorderAgentCounters::mMgmtActiveGets
```

**Description:** The number of MGMT_ACTIVE_GET.req sent over secure sessions.

###### mMgmtPendingGets (heading level 8)

```
uint32_t otBorderAgentCounters::mMgmtPendingGets
```

**Description:** The number of MGMT_PENDING_GET.req sent over secure sessions.

Represents information about a Border Agent session. 

This structure is populated by `otBorderAgentGetNextSessionInfo()` during iteration over the list of sessions using an `otBorderAgentSessionIterator`.

To ensure consistent `mLifetime` calculations, the iterator's initialization time is stored within the iterator, and each session's `mLifetime` is calculated relative to this time. 

###### Public Attributes (heading level 7)

###### mPeerSockAddr (heading level 8)

```
otSockAddr otBorderAgentSessionInfo::mPeerSockAddr
```

**Description:** Socket address (IPv6 address and port number) of session peer.

###### mIsConnected (heading level 8)

```
bool otBorderAgentSessionInfo::mIsConnected
```

**Description:** Indicates whether the session is connected.

###### mIsCommissioner (heading level 8)

```
bool otBorderAgentSessionInfo::mIsCommissioner
```

**Description:** Indicates whether the session is accepted as full commissioner.

###### mLifetime (heading level 8)

```
uint64_t otBorderAgentSessionInfo::mLifetime
```

**Description:** Milliseconds since the session was first established.

Represents an iterator for Border Agent sessions. 

The caller MUST NOT access or update the fields in this struct. It is intended for OpenThread internal use only. 

###### Public Attributes (heading level 7)

###### mPtr (heading level 8)

```
void* otBorderAgentSessionIterator::mPtr
```

###### mData (heading level 8)

```
uint64_t otBorderAgentSessionIterator::mData
```

Represents the Border Agent MeshCoP Service TXT data. 

###### Public Attributes (heading level 7)

###### mData (heading level 8)

```
uint8_t otBorderAgentMeshCoPServiceTxtData::mData[OT_BORDER_AGENT_MESHCOP_SERVICE_TXT_DATA_MAX_LENGTH]
```

###### mLength (heading level 8)

```
uint16_t otBorderAgentMeshCoPServiceTxtData::mLength
```

Represents an iterator for Border Admitter enroller. 

The caller MUST NOT access or update the fields in this struct. It is intended for OpenThread internal use only. 

###### Public Attributes (heading level 7)

###### mPtr1 (heading level 8)

```
void* otBorderAdmitterIterator::mPtr1
```

###### mPtr2 (heading level 8)

```
void* otBorderAdmitterIterator::mPtr2
```

###### mData1 (heading level 8)

```
uint64_t otBorderAdmitterIterator::mData1
```

###### mData2 (heading level 8)

```
uint32_t otBorderAdmitterIterator::mData2
```

Represents information about an enroller. 

To ensure consistent `mRegisterDuration` calculations, the iterator's initialization time is stored within the iterator, and each enroller `mRegisterDuration` is calculated relative to this time. 

###### Public Attributes (heading level 7)

###### mSessionInfo (heading level 8)

```
otBorderAgentSessionInfo otBorderAdmitterEnrollerInfo::mSessionInfo
```

**Description:** The session information.

###### mId (heading level 8)

```
const char* otBorderAdmitterEnrollerInfo::mId
```

**Description:** The enroller ID string.

###### mSteeringData (heading level 8)

```
otSteeringData otBorderAdmitterEnrollerInfo::mSteeringData
```

**Description:** The steering data.

###### mMode (heading level 8)

```
uint8_t otBorderAdmitterEnrollerInfo::mMode
```

**Description:** The enroller's mode.

###### mRegisterDuration (heading level 8)

```
uint64_t otBorderAdmitterEnrollerInfo::mRegisterDuration
```

**Description:** Milliseconds since the enroller registered.

Represents information about a joiner accepted by an enroller. 

To ensure consistent duration calculations, the iterator's initialization time is stored within the iterator, and the `mMsecSinceAccept` is calculated relative to this time. 

###### Public Attributes (heading level 7)

###### mIid (heading level 8)

```
otIp6InterfaceIdentifier otBorderAdmitterJoinerInfo::mIid
```

**Description:** Joiner IID.

###### mMsecSinceAccept (heading level 8)

```
uint64_t otBorderAdmitterJoinerInfo::mMsecSinceAccept
```

**Description:** Milliseconds since the joiner was accepted by the enroller.

###### mMsecTillExpiration (heading level 8)

```
uint32_t otBorderAdmitterJoinerInfo::mMsecTillExpiration
```

**Description:** Milliseconds till the joiner will be expired and removed.

Represents a Thread Administration One-Time Passcode (TAP). 

###### Public Attributes (heading level 7)

###### mTap (heading level 8)

```
char otBorderAgentEphemeralKeyTap::mTap[OT_BORDER_AGENT_EPHEMERAL_KEY_TAP_STRING_LENGTH+1]
```

**Description:** TAP string buffer (including `\0` character).

##### Border Agent Tracker

This module includes APIs for the Border Agent Tracker. 

The Border Agent Tracker discovers and tracks Border Agents on the infrastructure link by browsing for the `_meshcop._udp` mDNS service. 

###### Modules

[otBorderAgentTrackerIterator](ot-border-agent-tracker-iterator)

[otBorderAgentTrackerAgentInfo](ot-border-agent-tracker-agent-info)

###### Typedefs

###### otBorderAgentTrackerIterator (heading level 7)

`typedef struct otBorderAgentTrackerIterator otBorderAgentTrackerIterator`

**Description:**

Represents an iterator to iterate through the discovered Border Agents.

**Details:**

The fields in this struct are for OpenThread internal use only and MUST NOT be accessed or modified by the caller.

An iterator MUST be initialized using `otBorderAgentTrackerInitIterator()` before it is used.

###### otBorderAgentTrackerAgentInfo (heading level 7)

`typedef struct otBorderAgentTrackerAgentInfo otBorderAgentTrackerAgentInfo`

**Description:**

Represents information about a discovered Border Agent.

**Details:**

To ensure consistent `mMsecSinceDiscovered` and `mMsecSinceLastChange` time calculations, the iterator's initialization time is stored within the iterator when `otBorderAgentTrackerInitIterator()` is called. The time values in this struct are calculated relative to the iterator's initialization time.

###### Functions

###### otBorderAgentTrackerSetEnabled (heading level 7)

`void otBorderAgentTrackerSetEnabled(otInstance *aInstance, bool aEnable)`

**Description:** Enables or disables the Border Agent Tracker.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnable|TRUE to enable the Border Agent Tracker, FALSE to disable it.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE`.

When enabled, the tracker browses for the `_meshcop._udp` mDNS service to discover and track Border Agents on the infra-if network.

###### otBorderAgentTrackerIsRunning (heading level 7)

`bool otBorderAgentTrackerIsRunning(otInstance *aInstance)`

**Description:** Indicates whether the Border Agent Tracker is running.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE`.

The tracker can be enabled by the user (via `otBorderAgentTrackerSetEnabled()`) or by the OpenThread stack itself. The tracker is considered running if it is enabled by either entity AND the underlying DNS-SD (mDNS) is ready. This means that `otBorderAgentTrackerIsRunning()` may not return `TRUE` immediately after a call to `otBorderAgentTrackerSetEnabled(true)`.

###### otBorderAgentTrackerInitIterator (heading level 7)

`void otBorderAgentTrackerInitIterator(otInstance *aInstance, otBorderAgentTrackerIterator *aIterator)`

**Description:** Initializes a Border Agent Tracker iterator.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otBorderAgentTrackerIterator](ot-border-agent-tracker-iterator) *|[in]|aIterator|A pointer to the iterator to initialize.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE`.

An iterator MUST be initialized before being used.

###### otBorderAgentTrackerGetNextAgent (heading level 7)

`otError otBorderAgentTrackerGetNextAgent(otInstance *aInstance, otBorderAgentTrackerIterator *aIterator, otBorderAgentTrackerAgentInfo *aAgentInfo)`

**Description:** Gets the information for the next discovered Border Agent.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otBorderAgentTrackerIterator](ot-border-agent-tracker-iterator) *|[inout]|aIterator|A pointer to the iterator. An iterator MUST be initialized using `otBorderAgentTrackerInitIterator()` before it is used.|
|[otBorderAgentTrackerAgentInfo](ot-border-agent-tracker-agent-info) *|[out]|aAgentInfo|A pointer to an `otBorderAgentTrackerAgentInfo` struct to populate.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_TRACKER_ENABLE`.

The iterator initialization time is used to determine the `mMsecSinceDiscovered` and `mMsecSinceLastChange` in the `otBorderAgentTrackerAgentInfo`.

Represents an iterator to iterate through the discovered Border Agents. 

The fields in this struct are for OpenThread internal use only and MUST NOT be accessed or modified by the caller.

An iterator MUST be initialized using `otBorderAgentTrackerInitIterator()` before it is used. 

###### Public Attributes (heading level 7)

###### mPtr (heading level 8)

```
const void* otBorderAgentTrackerIterator::mPtr
```

###### mData (heading level 8)

```
uint64_t otBorderAgentTrackerIterator::mData
```

Represents information about a discovered Border Agent. 

To ensure consistent `mMsecSinceDiscovered` and `mMsecSinceLastChange` time calculations, the iterator's initialization time is stored within the iterator when `otBorderAgentTrackerInitIterator()` is called. The time values in this struct are calculated relative to the iterator's initialization time. 

###### Public Attributes (heading level 7)

###### mServiceName (heading level 8)

```
const char* otBorderAgentTrackerAgentInfo::mServiceName
```

**Description:** The service name.

###### mHostName (heading level 8)

```
const char* otBorderAgentTrackerAgentInfo::mHostName
```

**Description:** The host name. May be NULL if not known yet.

###### mPort (heading level 8)

```
uint16_t otBorderAgentTrackerAgentInfo::mPort
```

**Description:** The port number. Can be zero if not known yet.

###### mTxtData (heading level 8)

```
const uint8_t* otBorderAgentTrackerAgentInfo::mTxtData
```

**Description:** The TXT data. May be NULL if not known yet.

###### mTxtDataLength (heading level 8)

```
uint16_t otBorderAgentTrackerAgentInfo::mTxtDataLength
```

**Description:** The TXT data length.

###### mAddresses (heading level 8)

```
const otIp6Address* otBorderAgentTrackerAgentInfo::mAddresses
```

**Description:** Array of IPv6 addresses of the host. May be NULL if not known yet.

###### mNumAddresses (heading level 8)

```
uint16_t otBorderAgentTrackerAgentInfo::mNumAddresses
```

**Description:** Number of addresses in the `mAddresses` array.

###### mMsecSinceDiscovered (heading level 8)

```
uint64_t otBorderAgentTrackerAgentInfo::mMsecSinceDiscovered
```

**Description:** Milliseconds since the service was discovered.

###### mMsecSinceLastChange (heading level 8)

```
uint64_t otBorderAgentTrackerAgentInfo::mMsecSinceLastChange
```

**Description:** Milliseconds since the last change (port, TXT, or addresses).

##### Border Agent TXT Data Parser

This module includes APIs for parsing the MeshCoP service TXT data of a Border Agent. 

###### Modules

[otBorderAgentStateBitmap](ot-border-agent-state-bitmap)

[otBorderAgentTxtDataInfo](ot-border-agent-txt-data-info)

###### Enumerations

###### otBorderAgentConnMode (heading level 7)

```
enum otBorderAgentConnMode {
    OT_BORDER_AGENT_CONN_MODE_DISABLED = 0
    OT_BORDER_AGENT_CONN_MODE_PSKC = 1
    OT_BORDER_AGENT_CONN_MODE_PSKD = 2
    OT_BORDER_AGENT_CONN_MODE_VENDOR = 3
    OT_BORDER_AGENT_CONN_MODE_X509 = 4
}
```

**Description:**

Represents the Connection Mode in a Border Agent State Bitmap.

**Enumerator:**

|   |   |
|---|---|
|OT_BORDER_AGENT_CONN_MODE_DISABLED|DTLS connection not allowed.|
|OT_BORDER_AGENT_CONN_MODE_PSKC|DTLS connection with PSKc.|
|OT_BORDER_AGENT_CONN_MODE_PSKD|DTLS connection with PSKd.|
|OT_BORDER_AGENT_CONN_MODE_VENDOR|DTLS with vendor defined credential.|
|OT_BORDER_AGENT_CONN_MODE_X509|DTLS with X.509 certificate.|

###### otBorderAgentThreadIfState (heading level 7)

```
enum otBorderAgentThreadIfState {
    OT_BORDER_AGENT_THREAD_IF_NOT_INITIALIZED = 0
    OT_BORDER_AGENT_THREAD_IF_INITIALIZED = 1
    OT_BORDER_AGENT_THREAD_IF_ACTIVE = 2
}
```

**Description:**

Represents the Thread Interface Status in a Border Agent State Bitmap.

**Enumerator:**

|   |   |
|---|---|
|OT_BORDER_AGENT_THREAD_IF_NOT_INITIALIZED|Thread interface is not initialized.|
|OT_BORDER_AGENT_THREAD_IF_INITIALIZED|Thread interface is initialized but is not yet active.|
|OT_BORDER_AGENT_THREAD_IF_ACTIVE|Thread interface is initialized and active.|

###### otBorderAgentAvailability (heading level 7)

```
enum otBorderAgentAvailability {
    OT_BORDER_AGENT_AVAILABILITY_INFREQUENT = 0
    OT_BORDER_AGENT_AVAILABILITY_HIGH = 1
}
```

**Description:**

Represents the Availability Status in a Border Agent State Bitmap.

**Enumerator:**

|   |   |
|---|---|
|OT_BORDER_AGENT_AVAILABILITY_INFREQUENT|Infrequent availability.|
|OT_BORDER_AGENT_AVAILABILITY_HIGH|High availability.|

###### otBorderAgentThreadRole (heading level 7)

```
enum otBorderAgentThreadRole {
    OT_BORDER_AGENT_THREAD_ROLE_DISABLED_OR_DETACHED = 0
    OT_BORDER_AGENT_THREAD_ROLE_CHILD = 1
    OT_BORDER_AGENT_THREAD_ROLE_ROUTER = 2
    OT_BORDER_AGENT_THREAD_ROLE_LEADER = 3
}
```

**Description:**

Represents the Thread Role in a Border Agent State Bitmap.

**Enumerator:**

|   |   |
|---|---|
|OT_BORDER_AGENT_THREAD_ROLE_DISABLED_OR_DETACHED|Detached or disabled.|
|OT_BORDER_AGENT_THREAD_ROLE_CHILD|End device (child).|
|OT_BORDER_AGENT_THREAD_ROLE_ROUTER|Router.|
|OT_BORDER_AGENT_THREAD_ROLE_LEADER|Leader.|

###### otBorderAgentMultiAilState (heading level 7)

```
enum otBorderAgentMultiAilState {
    OT_BORDER_AGENT_MULTI_AIL_STATE_DISABLED = 0
    OT_BORDER_AGENT_MULTI_AIL_STATE_NOT_DETECTED = 1
    OT_BORDER_AGENT_MULTI_AIL_STATE_DETECTED = 2
}
```

**Description:**

Represents the Multi-AIL (Adjacent Infrastructure Link) detection state in a Border Agent State Bitmap.

**Enumerator:**

|   |   |
|---|---|
|OT_BORDER_AGENT_MULTI_AIL_STATE_DISABLED|Multi-AIL detection is disabled.|
|OT_BORDER_AGENT_MULTI_AIL_STATE_NOT_DETECTED|Multi-AIL detection is enabled, not detected.|
|OT_BORDER_AGENT_MULTI_AIL_STATE_DETECTED|Multi-AIL detection is enabled, detected.|

###### Typedefs

###### otBorderAgentConnMode (heading level 7)

`typedef enum otBorderAgentConnMode otBorderAgentConnMode`

**Description:**

Represents the Connection Mode in a Border Agent State Bitmap.

###### otBorderAgentThreadIfState (heading level 7)

`typedef enum otBorderAgentThreadIfState otBorderAgentThreadIfState`

**Description:**

Represents the Thread Interface Status in a Border Agent State Bitmap.

###### otBorderAgentAvailability (heading level 7)

`typedef enum otBorderAgentAvailability otBorderAgentAvailability`

**Description:**

Represents the Availability Status in a Border Agent State Bitmap.

###### otBorderAgentThreadRole (heading level 7)

`typedef enum otBorderAgentThreadRole otBorderAgentThreadRole`

**Description:**

Represents the Thread Role in a Border Agent State Bitmap.

###### otBorderAgentMultiAilState (heading level 7)

`typedef enum otBorderAgentMultiAilState otBorderAgentMultiAilState`

**Description:**

Represents the Multi-AIL (Adjacent Infrastructure Link) detection state in a Border Agent State Bitmap.

###### otBorderAgentStateBitmap (heading level 7)

`typedef struct otBorderAgentStateBitmap otBorderAgentStateBitmap`

**Description:**

Represents Border Agent State Bitmap information.

###### otBorderAgentTxtDataInfo (heading level 7)

`typedef struct otBorderAgentTxtDataInfo otBorderAgentTxtDataInfo`

**Description:**

Represents parsed Border Agent TXT data.

**Details:**

The boolean flags indicate whether a specific field is present in the parsed TXT data.

###### Functions

###### otBorderAgentTxtDataParse (heading level 7)

`otError otBorderAgentTxtDataParse(const uint8_t *aTxtData, uint16_t aTxtDataLength, otBorderAgentTxtDataInfo *aInfo)`

**Description:** Parses a Border Agent's MeshCoP service TXT data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const uint8_t *|[in]|aTxtData|A pointer to the buffer containing the TXT data.|
|uint16_t|[in]|aTxtDataLength|The length of the TXT data in bytes.|
|[otBorderAgentTxtDataInfo](ot-border-agent-txt-data-info) *|[out]|aInfo|A pointer to a structure to output the parsed information.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_TXT_DATA_PARSER_ENABLE`.

###### otBorderAgentConnModeToString (heading level 7)

`const char * otBorderAgentConnModeToString(otBorderAgentConnMode aConnMode)`

**Description:** Converts a given Connection Mode in a Border Agent State Bitmap to a human-readable string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otBorderAgentConnMode](api-border-agent-txt-data#ot-border-agent-conn-mode)|[in]|aConnMode|The Connection Mode to convert.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_TXT_DATA_PARSER_ENABLE`.

**Returns**

- The string representation of `aConnMode`.

###### otBorderAgentIfStateToString (heading level 7)

`const char * otBorderAgentIfStateToString(otBorderAgentThreadIfState aIfState)`

**Description:** Converts a given Thread Interface State in a Border Agent State Bitmap to a human-readable string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otBorderAgentThreadIfState](api-border-agent-txt-data#ot-border-agent-thread-if-state)|[in]|aIfState|The Thread Interface State to convert.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_TXT_DATA_PARSER_ENABLE`.

**Returns**

- The string representation of `aIfState`.

###### otBorderAgentAvailabilityToString (heading level 7)

`const char * otBorderAgentAvailabilityToString(otBorderAgentAvailability aAvailability)`

**Description:** Converts a given Availability Status in a Border Agent State Bitmap to a human-readable string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otBorderAgentAvailability](api-border-agent-txt-data#ot-border-agent-availability)|[in]|aAvailability|The Availability Status to convert.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_TXT_DATA_PARSER_ENABLE`.

**Returns**

- The string representation of `aAvailability`.

###### otBorderAgentThreadRoleToString (heading level 7)

`const char * otBorderAgentThreadRoleToString(otBorderAgentThreadRole aRole)`

**Description:** Converts a given Thread Role in a Border Agent State Bitmap to a human-readable string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otBorderAgentThreadRole](api-border-agent-txt-data#ot-border-agent-thread-role)|[in]|aRole|The Thread Role to convert.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_TXT_DATA_PARSER_ENABLE`.

**Returns**

- The string representation of `aRole`.

###### otBorderAgentMultiAilStateToString (heading level 7)

`const char * otBorderAgentMultiAilStateToString(otBorderAgentMultiAilState aState)`

**Description:** Converts a given Multi-AIL State in a Border Agent State Bitmap to a human-readable string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otBorderAgentMultiAilState](api-border-agent-txt-data#ot-border-agent-multi-ail-state)|[in]|aState|The Multi-AIL State to convert.|

Requires `OPENTHREAD_CONFIG_BORDER_AGENT_TXT_DATA_PARSER_ENABLE`.

**Returns**

- The string representation of `aState`.

###### Macros

`#define OT_BORDER_AGENT_RECORD_VERSION_SIZE (8)`

**Description**: Max size of Record Version string in `otBorderAgentTxtDataInfo`.

`#define OT_BORDER_AGENT_THREAD_VERSION_SIZE (16)`

**Description**: Max size of Thread Version string in `otBorderAgentTxtDataInfo`.

`#define OT_BORDER_AGENT_VENDOR_NAME_SIZE (32)`

**Description**: Max size of Vendor Name string in `otBorderAgentTxtDataInfo`.

`#define OT_BORDER_AGENT_MODEL_NAME_SIZE (32)`

**Description**: Max size of Model Name string in `otBorderAgentTxtDataInfo`.

`#define OT_BORDER_AGENT_VENDOR_OUI_SIZE (3)`

**Description**: Size of Vendor OUI (in bytes) in `otBorderAgentTxtDataInfo`.

Represents Border Agent State Bitmap information. 

###### Public Attributes (heading level 7)

###### mConnMode (heading level 8)

```
otBorderAgentConnMode otBorderAgentStateBitmap::mConnMode
```

**Description:** Connection Mode.

###### mThreadIfState (heading level 8)

```
otBorderAgentThreadIfState otBorderAgentStateBitmap::mThreadIfState
```

**Description:** Thread Interface Status.

###### mAvailability (heading level 8)

```
otBorderAgentAvailability otBorderAgentStateBitmap::mAvailability
```

**Description:** Availability.

###### mThreadRole (heading level 8)

```
otBorderAgentThreadRole otBorderAgentStateBitmap::mThreadRole
```

**Description:** Thread Role.

###### mBbrIsActive (heading level 8)

```
bool otBorderAgentStateBitmap::mBbrIsActive
```

**Description:** Backbone Router function is active.

###### mBbrIsPrimary (heading level 8)

```
bool otBorderAgentStateBitmap::mBbrIsPrimary
```

**Description:** Device is the Primary Backbone Router.

###### mEpskcSupported (heading level 8)

```
bool otBorderAgentStateBitmap::mEpskcSupported
```

**Description:** ePSKc Mode is supported.

###### mMultiAilState (heading level 8)

```
otBorderAgentMultiAilState otBorderAgentStateBitmap::mMultiAilState
```

**Description:** Multi-AIL detection state.

###### mAdmitterSupported (heading level 8)

```
bool otBorderAgentStateBitmap::mAdmitterSupported
```

**Description:** Border Admitter function is supported.

Represents parsed Border Agent TXT data. 

The boolean flags indicate whether a specific field is present in the parsed TXT data. 

###### Public Attributes (heading level 7)

###### mHasRecordVersion (heading level 8)

```
bool otBorderAgentTxtDataInfo::mHasRecordVersion
```

**Description:** Indicates whether Record Version is present.

###### mHasAgentId (heading level 8)

```
bool otBorderAgentTxtDataInfo::mHasAgentId
```

**Description:** Indicates whether Agent ID is present.

###### mHasThreadVersion (heading level 8)

```
bool otBorderAgentTxtDataInfo::mHasThreadVersion
```

**Description:** Indicates whether Thread Version is present.

###### mHasStateBitmap (heading level 8)

```
bool otBorderAgentTxtDataInfo::mHasStateBitmap
```

**Description:** Indicates whether State Bitmap is present.

###### mHasNetworkName (heading level 8)

```
bool otBorderAgentTxtDataInfo::mHasNetworkName
```

**Description:** Indicates whether Network Name is present.

###### mHasExtendedPanId (heading level 8)

```
bool otBorderAgentTxtDataInfo::mHasExtendedPanId
```

**Description:** Indicates whether Extended PAN ID is present.

###### mHasActiveTimestamp (heading level 8)

```
bool otBorderAgentTxtDataInfo::mHasActiveTimestamp
```

**Description:** Indicates whether Active Timestamp is present.

###### mHasPartitionId (heading level 8)

```
bool otBorderAgentTxtDataInfo::mHasPartitionId
```

**Description:** Indicates whether Partition ID is present.

###### mHasDomainName (heading level 8)

```
bool otBorderAgentTxtDataInfo::mHasDomainName
```

**Description:** Indicates whether Domain Name is present.

###### mHasBbrSeqNum (heading level 8)

```
bool otBorderAgentTxtDataInfo::mHasBbrSeqNum
```

**Description:** Indicates whether BBR Sequence Number is present.

###### mHasBbrPort (heading level 8)

```
bool otBorderAgentTxtDataInfo::mHasBbrPort
```

**Description:** Indicates whether BBR Port is present.

###### mHasOmrPrefix (heading level 8)

```
bool otBorderAgentTxtDataInfo::mHasOmrPrefix
```

**Description:** Indicates whether OMR Prefix is present.

###### mHasExtAddress (heading level 8)

```
bool otBorderAgentTxtDataInfo::mHasExtAddress
```

**Description:** Indicates whether Extended Address is present.

###### mHasVendorName (heading level 8)

```
bool otBorderAgentTxtDataInfo::mHasVendorName
```

**Description:** Indicates whether Vendor Name is present.

###### mHasModelName (heading level 8)

```
bool otBorderAgentTxtDataInfo::mHasModelName
```

**Description:** Indicates whether Model Name is present.

###### mHasVendorOui (heading level 8)

```
bool otBorderAgentTxtDataInfo::mHasVendorOui
```

**Description:** Indicates whether Vendor OUI is present.

###### mRecordVersion (heading level 8)

```
char otBorderAgentTxtDataInfo::mRecordVersion[OT_BORDER_AGENT_RECORD_VERSION_SIZE]
```

**Description:** Record Version string.

###### mAgentId (heading level 8)

```
otBorderAgentId otBorderAgentTxtDataInfo::mAgentId
```

**Description:** Agent ID.

###### mThreadVersion (heading level 8)

```
char otBorderAgentTxtDataInfo::mThreadVersion[OT_BORDER_AGENT_THREAD_VERSION_SIZE]
```

**Description:** Thread Version string.

###### mStateBitmap (heading level 8)

```
otBorderAgentStateBitmap otBorderAgentTxtDataInfo::mStateBitmap
```

**Description:** State Bitmap.

###### mNetworkName (heading level 8)

```
otNetworkName otBorderAgentTxtDataInfo::mNetworkName
```

**Description:** Network Name.

###### mExtendedPanId (heading level 8)

```
otExtendedPanId otBorderAgentTxtDataInfo::mExtendedPanId
```

**Description:** Extended PAN ID.

###### mActiveTimestamp (heading level 8)

```
otTimestamp otBorderAgentTxtDataInfo::mActiveTimestamp
```

**Description:** Active Timestamp.

###### mPartitionId (heading level 8)

```
uint32_t otBorderAgentTxtDataInfo::mPartitionId
```

**Description:** Partition ID.

###### mDomainName (heading level 8)

```
otNetworkName otBorderAgentTxtDataInfo::mDomainName
```

**Description:** Domain Name.

###### mBbrSeqNum (heading level 8)

```
uint8_t otBorderAgentTxtDataInfo::mBbrSeqNum
```

**Description:** BBR Sequence Number.

###### mBbrPort (heading level 8)

```
uint16_t otBorderAgentTxtDataInfo::mBbrPort
```

**Description:** BBR Port.

###### mOmrPrefix (heading level 8)

```
otIp6Prefix otBorderAgentTxtDataInfo::mOmrPrefix
```

**Description:** OMR Prefix.

###### mExtAddress (heading level 8)

```
otExtAddress otBorderAgentTxtDataInfo::mExtAddress
```

**Description:** Extended Address.

###### mVendorName (heading level 8)

```
char otBorderAgentTxtDataInfo::mVendorName[OT_BORDER_AGENT_VENDOR_NAME_SIZE]
```

**Description:** Vendor Name string.

###### mModelName (heading level 8)

```
char otBorderAgentTxtDataInfo::mModelName[OT_BORDER_AGENT_MODEL_NAME_SIZE]
```

**Description:** Model Name string.

###### mVendorOui (heading level 8)

```
uint8_t otBorderAgentTxtDataInfo::mVendorOui[OT_BORDER_AGENT_VENDOR_OUI_SIZE]
```

**Description:** Vendor OUI (24-bit).

##### Border Routing Manager

This module includes definitions related to Border Routing Manager. 

All the functions in this module require `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` to be enabled.

Border Routing Manager handles bi-directional routing between Thread network and adjacent infrastructure link (AIL).

It emits ICMRv6 ND Router Advertisement (RA) messages on AIL to advertise on-link and route prefixes. It also processes received RA messages from infrastructure and mirrors the discovered prefixes on the Thread Network Data to ensure devices on Thread mesh can reach AIL through the Border Router.

Routing Manager manages the Off-Mesh Routable (OMR) prefix on the Thread Network data which configures Thread devices with a suitable Off-Mesh Routable IPv6 address. It announces the reachability of this prefix on AIL by including it in the emitted RA messages as an IPv6 Route Information Option (RIO).

Routing Manager also monitors and adds on-link prefix on the infrastructure network. If a router on AIL is already providing RA messages containing an IPv6 Prefix Information Option (PIO) that enables IPv6 devices on the link to self-configure their own routable unicast IPv6 address, this address can be used by Thread devices to reach AIL. If Border Router finds no such RA message on AIL, it generates a ULA on-link prefix which it then advertises on AIL in the emitted RA messages. 

###### Modules

[otBorderRoutingPrefixTableIterator](ot-border-routing-prefix-table-iterator)

[otBorderRoutingRouterEntry](ot-border-routing-router-entry)

[otBorderRoutingPrefixTableEntry](ot-border-routing-prefix-table-entry)

[otBorderRoutingNat64PrefixEntry](ot-border-routing-nat64-prefix-entry)

[otBorderRoutingRdnssAddrEntry](ot-border-routing-rdnss-addr-entry)

[otBorderRoutingPeerBorderRouterEntry](ot-border-routing-peer-border-router-entry)

[otBorderRoutingIfAddrEntry](ot-border-routing-if-addr-entry)

[otPdProcessedRaInfo](ot-pd-processed-ra-info)

###### Enumerations

###### otBorderRoutingOmrConfig (heading level 7)

```
enum otBorderRoutingOmrConfig {
    OT_BORDER_ROUTING_OMR_CONFIG_AUTO
    OT_BORDER_ROUTING_OMR_CONFIG_CUSTOM
    OT_BORDER_ROUTING_OMR_CONFIG_DISABLED
}
```

**Description:**

Represents the configuration options related to the OMR prefix.

**Details:**

This is used in `otBorderRoutingSetOmrConfig()` to offer manual administration options to explicitly configure the OMR prefix or to disable it.

**Enumerator:**

|   |   |
|---|---|
|OT_BORDER_ROUTING_OMR_CONFIG_AUTO|BR auto-generates the local OMR prefix.|
|OT_BORDER_ROUTING_OMR_CONFIG_CUSTOM|BR uses a given custom OMR prefix.|
|OT_BORDER_ROUTING_OMR_CONFIG_DISABLED|BR does not add local/PD OMR prefix in Network Data.|

###### otBorderRoutingState (heading level 7)

```
enum otBorderRoutingState {
    OT_BORDER_ROUTING_STATE_UNINITIALIZED = 0
    OT_BORDER_ROUTING_STATE_DISABLED = 1
    OT_BORDER_ROUTING_STATE_STOPPED = 2
    OT_BORDER_ROUTING_STATE_RUNNING = 3
}
```

**Description:**

Represents the state of Border Routing Manager.

**Enumerator:**

|   |   |
|---|---|
|OT_BORDER_ROUTING_STATE_UNINITIALIZED|Routing Manager is uninitialized.|
|OT_BORDER_ROUTING_STATE_DISABLED|Routing Manager is initialized but disabled.|
|OT_BORDER_ROUTING_STATE_STOPPED|Routing Manager in initialized and enabled but currently stopped.|
|OT_BORDER_ROUTING_STATE_RUNNING|Routing Manager is initialized, enabled, and running.|

###### otBorderRoutingDhcp6PdState (heading level 7)

```
enum otBorderRoutingDhcp6PdState {
    OT_BORDER_ROUTING_DHCP6_PD_STATE_DISABLED
    OT_BORDER_ROUTING_DHCP6_PD_STATE_STOPPED
    OT_BORDER_ROUTING_DHCP6_PD_STATE_RUNNING
    OT_BORDER_ROUTING_DHCP6_PD_STATE_IDLE
}
```

**Description:**

This enumeration represents the state of DHCPv6 Prefix Delegation State.

**Enumerator:**

|   |   |
|---|---|
|OT_BORDER_ROUTING_DHCP6_PD_STATE_DISABLED|DHCPv6 PD is disabled on the border router.|
|OT_BORDER_ROUTING_DHCP6_PD_STATE_STOPPED|DHCPv6 PD in enabled but won't try to request and publish a prefix.|
|OT_BORDER_ROUTING_DHCP6_PD_STATE_RUNNING|DHCPv6 PD is enabled and will try to request and publish a prefix.|
|OT_BORDER_ROUTING_DHCP6_PD_STATE_IDLE|DHCPv6 PD is idle; Higher-prf prefix published by other BRs.|

###### Typedefs

###### otBorderRoutingPrefixTableIterator (heading level 7)

`typedef struct otBorderRoutingPrefixTableIterator otBorderRoutingPrefixTableIterator`

**Description:**

Represents an iterator to iterate through the Border Router's discovered prefix table.

**Details:**

The fields in this type are opaque (intended for use by OpenThread core only) and therefore should not be accessed or used by caller.

Before using an iterator, it MUST be initialized using `otBorderRoutingPrefixTableInitIterator()`.

###### otBorderRoutingRouterEntry (heading level 7)

`typedef struct otBorderRoutingRouterEntry otBorderRoutingRouterEntry`

**Description:**

Represents a discovered router on the infrastructure link.

**Details:**

The `mIsPeerBr` field requires `OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE`. Routing Manager determines whether the router is a peer BR (connected to the same Thread mesh network) by comparing its advertised PIO/RIO prefixes with the entries in the Thread Network Data. While this method is generally effective, it may not be 100% accurate in all scenarios, so the `mIsPeerBr` flag should be used with caution.

###### otBorderRoutingPrefixTableEntry (heading level 7)

`typedef struct otBorderRoutingPrefixTableEntry otBorderRoutingPrefixTableEntry`

**Description:**

Represents an entry from the discovered prefix table.

**Details:**

The entries in the discovered table track the Prefix/Route Info Options in the received Router Advertisement messages from other routers on the infrastructure link.

###### otBorderRoutingNat64PrefixEntry (heading level 7)

`typedef struct otBorderRoutingNat64PrefixEntry otBorderRoutingNat64PrefixEntry`

**Description:**

Represents an entry from the discovered NAT64 prefix table.

**Details:**

The entries in the discovered table track the NAT64 Prefix Options in the received Router Advertisement messages from other routers on the infrastructure link.

###### otBorderRoutingRdnssAddrEntry (heading level 7)

`typedef struct otBorderRoutingRdnssAddrEntry otBorderRoutingRdnssAddrEntry`

**Description:**

Represents a discovered Recursive DNS Server (RDNSS) address entry.

**Details:**

Address entries are discovered by processing the RDNSS options within received Router Advertisement messages from routers on infrastructure link.

###### otBorderRoutingPeerBorderRouterEntry (heading level 7)

`typedef struct otBorderRoutingPeerBorderRouterEntry otBorderRoutingPeerBorderRouterEntry`

**Description:**

Represents information about a peer Border Router found in the Network Data.

###### otBorderRoutingIfAddrEntry (heading level 7)

`typedef struct otBorderRoutingIfAddrEntry otBorderRoutingIfAddrEntry`

**Description:**

Represents an infra-if IPv6 address entry (an address used by this BR itself on the AIL).

###### otPdProcessedRaInfo (heading level 7)

`typedef struct otPdProcessedRaInfo otPdProcessedRaInfo`

**Description:**

Represents a group of data of platform-generated RA messages processed.

###### otBorderRoutingRdnssAddrCallback (heading level 7)

`typedef void(* otBorderRoutingRdnssAddrCallback) (void *aContext)`

**Description:**

Callback function pointer to notify of changes to discovered Recursive DNS Server (RDNSS) address entries.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to arbitrary context information.|

**Details:**

Address entries are discovered by processing the RDNSS options within received Router Advertisement messages from routers on infrastructure link.

The `otBorderRoutingGetNextRdnssAddrEntry()` function can be used to iterate over the discovered RDNSS address entries.

This callback is invoked when any of the following changes occur to the address entries associated with a discovered router:

- A new RDNSS address is advertised by the router.
- A previously discovered address is removed due to the router advertising it with a zero lifetime.
- A previously discovered address has aged out (its lifetime expired without being re-advertised).
- We determine that the router that advertised the address is now unreachable, and therefore all its associated entries are removed.

###### otBorderRoutingRequestDhcp6PdCallback (heading level 7)

`typedef void(* otBorderRoutingRequestDhcp6PdCallback) (otBorderRoutingDhcp6PdState aState, void *aContext)`

**Description:**

When the state of a DHCPv6 Prefix Delegation (PD) on the Thread interface changes, this callback notifies processes in the OS of this changed state.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aState|The state of DHCPv6 Prefix Delegation State.|
||[in]|aContext|A pointer to arbitrary context information.|

**Details:**

###### Functions

###### otBorderRoutingInit (heading level 7)

`otError otBorderRoutingInit(otInstance *aInstance, uint32_t aInfraIfIndex, bool aInfraIfIsRunning)`

**Description:** Initializes the Border Routing Manager on given infrastructure interface.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aInfraIfIndex|The infrastructure interface index.|
|bool|[in]|aInfraIfIsRunning|A boolean that indicates whether the infrastructure|

This function MUST be called before any other otBorderRouting* APIs.

This function can also be used to re-initialize and switch the infrastructure interface index to a new one. Switching the interface index will trigger all components running on the previous interface (Border Routing, mDNS, etc) to be stopped (as if the previous if-index is no longer running) before restarting operations on the new interface.

**See Also**

- [otPlatInfraIfStateChanged](plat-infra-if#ot-plat-infra-if-state-changed).
- [otBorderRoutingSetEnabled](api-border-routing#ot-border-routing-set-enabled).

###### otBorderRoutingGetInfraIfInfo (heading level 7)

`otError otBorderRoutingGetInfraIfInfo(otInstance *aInstance, uint32_t *aInfraIfIndex, bool *aInfraIfIsRunning)`

**Description:** Gets the interface index and running state of the configured infrastructure interface.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t *|[out]|aInfraIfIndex|A pointer to output the interface index. MUST NOT be NULL.|
|bool *|[out]|aInfraIfIsRunning|A pointer to output whether the interface is running. Can be NULL if not needed.|

**Note**

- The running state in `aInfraIfIsRunning` reflects the Border Routing Manager's perspective. This state is set when `otBorderRoutingInit()` is called and is subsequently updated by the platform signaling changes via `otPlatInfraIfStateChanged()`.

**See Also**

- [otBorderRoutingInit](api-border-routing#ot-border-routing-init)
- [otPlatInfraIfStateChanged](plat-infra-if#ot-plat-infra-if-state-changed)

###### otBorderRoutingSetEnabled (heading level 7)

`otError otBorderRoutingSetEnabled(otInstance *aInstance, bool aEnabled)`

**Description:** Enables or disables the Border Routing Manager.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnabled|A boolean to enable/disable the routing manager.|

**Note**

- The Border Routing Manager is disabled by default.

###### otBorderRoutingGetState (heading level 7)

`otBorderRoutingState otBorderRoutingGetState(otInstance *aInstance)`

**Description:** Gets the current state of Border Routing Manager.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The current state of Border Routing Manager.

###### otBorderRoutingSetOmrConfig (heading level 7)

`otError otBorderRoutingSetOmrConfig(otInstance *aInstance, otBorderRoutingOmrConfig aConfig, const otIp6Prefix *aOmrPrefix, otRoutePreference aPreference)`

**Description:** Configures the OMR prefix handling in the Border Routing Manager.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otBorderRoutingOmrConfig](api-border-routing#ot-border-routing-omr-config)|[in]|aConfig|The desired OMR configuration.|
|const [otIp6Prefix](ot-ip6-prefix) *|[in]|aOmrPrefix|A pointer to the custom OMR prefix. Required only when `aConfig` is `OT_BORDER_ROUTING_OMR_CONFIG_CUSTOM`. Otherwise, it can be `NULL`.|
|[otRoutePreference](api-thread-general#ot-route-preference)|[in]|aPreference|The preference associated with the custom OMR prefix.|

This function offers manual administration options to explicitly configure the OMR prefix or to disable it.

By default, `OT_BORDER_ROUTING_OMR_CONFIG_AUTO` is used. In this mode, the Border Routing Manager automatically selects and manages the OMR prefix. This can involve auto-generating a local prefix or utilizing a prefix obtained through DHCPv6 PD (Prefix Delegation), if the feature is enabled.

The `OT_BORDER_ROUTING_OMR_CONFIG_CUSTOM` option enables the use of a user-specified OMR prefix. When this option is selected, the `aOmrPrefix` and `aPreference` parameters are used to define the custom OMR prefix and its associated preference. These parameters are ignored for other configuration modes, and `aOmrPrefix` can be `NULL`.

The `OT_BORDER_ROUTING_OMR_CONFIG_DISABLED` option disables the Border Routing Manager's management of the OMR prefix. The Routing Manager module itself will not add any local or DHCPv6 PD OMR prefixes to the Network Data.

###### otBorderRoutingGetOmrConfig (heading level 7)

`otBorderRoutingOmrConfig otBorderRoutingGetOmrConfig(otInstance *aInstance, otIp6Prefix *aOmrPrefix, otRoutePreference *aPreference)`

**Description:** Gets the current OMR prefix configuration mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otIp6Prefix](ot-ip6-prefix) *|[out]|aOmrPrefix|A pointer to an `otIp6Prefix` to return the custom OMR prefix, if the configuration is `OT_BORDER_ROUTING_OMR_CONFIG_CUSTOM`.|
|[otRoutePreference](api-thread-general#ot-route-preference) *|[out]|aPreference|A pointer to return the preference associated with the custom OMR prefix.|

This function retrieves the current OMR configuration and, if a custom OMR prefix is configured, the custom prefix and its associated preference.

If the caller does not require the custom OMR prefix and preference, the `aOmrPrefix` and `aPreference` parameters can be set to `NULL`.

**Returns**

- The current OMR prefix configuration mode.

###### otBorderRoutingGetRouteInfoOptionPreference (heading level 7)

`otRoutePreference otBorderRoutingGetRouteInfoOptionPreference(otInstance *aInstance)`

**Description:** Gets the current preference used when advertising Route Info Options (RIO) in Router Advertisement messages sent over the infrastructure link.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

The RIO preference is determined as follows:

- If explicitly set by user by calling `otBorderRoutingSetRouteInfoOptionPreference()`, the given preference is used.
- Otherwise, it is determined based on device's current role: Medium preference when in router/leader role and low preference when in child role.

**Returns**

- The current Route Info Option preference.

###### otBorderRoutingSetRouteInfoOptionPreference (heading level 7)

`void otBorderRoutingSetRouteInfoOptionPreference(otInstance *aInstance, otRoutePreference aPreference)`

**Description:** Explicitly sets the preference to use when advertising Route Info Options (RIO) in Router Advertisement messages sent over the infrastructure link.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otRoutePreference](api-thread-general#ot-route-preference)|[in]|aPreference|The route preference to use.|

After a call to this function, BR will use the given preference for all its advertised RIOs. The preference can be cleared by calling `otBorderRoutingClearRouteInfoOptionPreference()`.

###### otBorderRoutingClearRouteInfoOptionPreference (heading level 7)

`void otBorderRoutingClearRouteInfoOptionPreference(otInstance *aInstance)`

**Description:** Clears a previously set preference value for advertised Route Info Options.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

After a call to this function, BR will use device's role to determine the RIO preference: Medium preference when in router/leader role and low preference when in child role.

###### otBorderRoutingSetExtraRouterAdvertOptions (heading level 7)

`otError otBorderRoutingSetExtraRouterAdvertOptions(otInstance *aInstance, const uint8_t *aOptions, uint16_t aLength)`

**Description:** Sets additional options to append at the end of emitted Router Advertisement (RA) messages.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the encoded options. Can be `NULL` to clear.|
|const uint8_t *|[in]|aOptions|Number of bytes in `aOptions`.|
|uint16_t|N/A|aLength||

The content of `aOptions` is copied internally, so it can be a temporary buffer (e.g., a stack allocated array).

Subsequent calls to this function overwrite the previously set value.

###### otBorderRoutingGetRoutePreference (heading level 7)

`otRoutePreference otBorderRoutingGetRoutePreference(otInstance *aInstance)`

**Description:** Gets the current preference used for published routes in Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

The preference is determined as follows:

- If explicitly set by user by calling `otBorderRoutingSetRoutePreference()`, the given preference is used.
- Otherwise, it is determined automatically by `RoutingManager` based on the device's role and link quality.

**Returns**

- The current published route preference.

###### otBorderRoutingSetRoutePreference (heading level 7)

`void otBorderRoutingSetRoutePreference(otInstance *aInstance, otRoutePreference aPreference)`

**Description:** Explicitly sets the preference of published routes in Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otRoutePreference](api-thread-general#ot-route-preference)|[in]|aPreference|The route preference to use.|

After a call to this function, BR will use the given preference. The preference can be cleared by calling `otBorderRoutingClearRoutePreference()`.

###### otBorderRoutingClearRoutePreference (heading level 7)

`void otBorderRoutingClearRoutePreference(otInstance *aInstance)`

**Description:** Clears a previously set preference value for published routes in Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

After a call to this function, BR will determine the preference automatically based on the device's role and link quality (to the parent when acting as end-device).

###### otBorderRoutingGetOmrPrefix (heading level 7)

`otError otBorderRoutingGetOmrPrefix(otInstance *aInstance, otIp6Prefix *aPrefix)`

**Description:** Gets the local Off-Mesh-Routable (OMR) Prefix, for example `fdfc:1ff5:1512:5622::/64`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otIp6Prefix](ot-ip6-prefix) *|[out]|aPrefix|A pointer to where the prefix will be output to.|

An OMR Prefix is a randomly generated 64-bit prefix that's published in the Thread network if there isn't already an OMR prefix. This prefix can be reached from the local Wi-Fi or Ethernet network.

Note: When DHCPv6 PD is enabled, the border router may publish the prefix from DHCPv6 PD.

**See Also**

- [otBorderRoutingGetPdOmrPrefix](api-border-routing#ot-border-routing-get-pd-omr-prefix)

###### otBorderRoutingGetPdOmrPrefix (heading level 7)

`otError otBorderRoutingGetPdOmrPrefix(otInstance *aInstance, otBorderRoutingPrefixTableEntry *aPrefixInfo)`

**Description:** Gets the DHCPv6 Prefix Delegation (PD) provided off-mesh-routable (OMR) prefix.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otBorderRoutingPrefixTableEntry](ot-border-routing-prefix-table-entry) *|[out]|aPrefixInfo|A pointer to where the prefix info will be output to.|

Only mPrefix, mValidLifetime and mPreferredLifetime fields are used in the returned prefix info.

`OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE` must be enabled.

**See Also**

- [otBorderRoutingGetOmrPrefix](api-border-routing#ot-border-routing-get-omr-prefix)
- [otPlatBorderRoutingProcessIcmp6Ra](border-routing-h-1#ot-plat-border-routing-process-icmp6-ra)

###### otBorderRoutingGetPdProcessedRaInfo (heading level 7)

`otError otBorderRoutingGetPdProcessedRaInfo(otInstance *aInstance, otPdProcessedRaInfo *aPdProcessedRaInfo)`

**Description:** Gets the data of platform generated RA message processed.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otPdProcessedRaInfo](ot-pd-processed-ra-info) *|[out]|aPdProcessedRaInfo|A pointer to where the prefix info will be output to.|

`OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE` must be enabled.

###### otBorderRoutingGetFavoredOmrPrefix (heading level 7)

`otError otBorderRoutingGetFavoredOmrPrefix(otInstance *aInstance, otIp6Prefix *aPrefix, otRoutePreference *aPreference)`

**Description:** Gets the currently favored Off-Mesh-Routable (OMR) Prefix.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otIp6Prefix](ot-ip6-prefix) *|[out]|aPrefix|A pointer to output the favored OMR prefix.|
|[otRoutePreference](api-thread-general#ot-route-preference) *|[out]|aPreference|A pointer to output the preference associated the favored prefix.|

The favored OMR prefix can be discovered from Network Data or can be this device's local OMR prefix.

###### otBorderRoutingGetOnLinkPrefix (heading level 7)

`otError otBorderRoutingGetOnLinkPrefix(otInstance *aInstance, otIp6Prefix *aPrefix)`

**Description:** Gets the local On-Link Prefix for the adjacent infrastructure link.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otIp6Prefix](ot-ip6-prefix) *|[out]|aPrefix|A pointer to where the prefix will be output to.|

The local On-Link Prefix is a 64-bit prefix that's advertised on the infrastructure link if there isn't already a usable on-link prefix being advertised on the link.

###### otBorderRoutingGetFavoredOnLinkPrefix (heading level 7)

`otError otBorderRoutingGetFavoredOnLinkPrefix(otInstance *aInstance, otIp6Prefix *aPrefix)`

**Description:** Gets the currently favored On-Link Prefix.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otIp6Prefix](ot-ip6-prefix) *|[out]|aPrefix|A pointer to where the prefix will be output to.|

The favored prefix is either a discovered on-link prefix on the infrastructure link or the local on-link prefix.

###### otBorderRoutingGetNat64Prefix (heading level 7)

`otError otBorderRoutingGetNat64Prefix(otInstance *aInstance, otIp6Prefix *aPrefix)`

**Description:** Gets the local NAT64 Prefix of the Border Router.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otIp6Prefix](ot-ip6-prefix) *|[out]|aPrefix|A pointer to where the prefix will be output to.|

NAT64 Prefix might not be advertised in the Thread network.

`OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` must be enabled.

###### otBorderRoutingGetFavoredNat64Prefix (heading level 7)

`otError otBorderRoutingGetFavoredNat64Prefix(otInstance *aInstance, otIp6Prefix *aPrefix, otRoutePreference *aPreference)`

**Description:** Gets the currently favored NAT64 prefix.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otIp6Prefix](ot-ip6-prefix) *|[out]|aPrefix|A pointer to output the favored NAT64 prefix.|
|[otRoutePreference](api-thread-general#ot-route-preference) *|[out]|aPreference|A pointer to output the preference associated the favored prefix.|

The favored NAT64 prefix can be discovered from infrastructure link or can be this device's local NAT64 prefix.

###### otBorderRoutingPrefixTableInitIterator (heading level 7)

`void otBorderRoutingPrefixTableInitIterator(otInstance *aInstance, otBorderRoutingPrefixTableIterator *aIterator)`

**Description:** Initializes an `otBorderRoutingPrefixTableIterator`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otBorderRoutingPrefixTableIterator](ot-border-routing-prefix-table-iterator) *|[out]|aIterator|A pointer to the iterator to initialize.|

An iterator MUST be initialized before it is used.

An iterator can be initialized again to restart from the beginning of the table.

When iterating over entries in the table, to ensure the update times `mMsecSinceLastUpdate` of entries are consistent, they are given relative to the time the iterator was initialized.

###### otBorderRoutingGetNextPrefixTableEntry (heading level 7)

`otError otBorderRoutingGetNextPrefixTableEntry(otInstance *aInstance, otBorderRoutingPrefixTableIterator *aIterator, otBorderRoutingPrefixTableEntry *aEntry)`

**Description:** Iterates over the entries in the Border Router's discovered prefix table.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otBorderRoutingPrefixTableIterator](ot-border-routing-prefix-table-iterator) *|[inout]|aIterator|A pointer to the iterator.|
|[otBorderRoutingPrefixTableEntry](ot-border-routing-prefix-table-entry) *|[out]|aEntry|A pointer to the entry to populate.|

Prefix entries associated with the same discovered router on an infrastructure link are guaranteed to be grouped together (retrieved back-to-back).

###### otBorderRoutingGetNextRouterEntry (heading level 7)

`otError otBorderRoutingGetNextRouterEntry(otInstance *aInstance, otBorderRoutingPrefixTableIterator *aIterator, otBorderRoutingRouterEntry *aEntry)`

**Description:** Iterates over the discovered router entries on the infrastructure link.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otBorderRoutingPrefixTableIterator](ot-border-routing-prefix-table-iterator) *|[inout]|aIterator|A pointer to the iterator.|
|[otBorderRoutingRouterEntry](ot-border-routing-router-entry) *|[out]|aEntry|A pointer to the entry to populate.|

###### otBorderRoutingGetNextPeerBrEntry (heading level 7)

`otError otBorderRoutingGetNextPeerBrEntry(otInstance *aInstance, otBorderRoutingPrefixTableIterator *aIterator, otBorderRoutingPeerBorderRouterEntry *aEntry)`

**Description:** Iterates over the peer BRs found in the Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otBorderRoutingPrefixTableIterator](ot-border-routing-prefix-table-iterator) *|[inout]|aIterator|A pointer to the iterator.|
|[otBorderRoutingPeerBorderRouterEntry](ot-border-routing-peer-border-router-entry) *|[out]|aEntry|A pointer to the entry to populate.|

Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE`.

Peer BRs are other devices within the Thread mesh that provide external IP connectivity. A device is considered to provide external IP connectivity if at least one of the following conditions is met regarding its Network Data entries:

- It has added at least one external route entry.
- It has added at least one prefix entry with both the default-route and on-mesh flags set.
- It has added at least one domain prefix (with both the domain and on-mesh flags set).

The list of peer BRs specifically excludes the current device, even if it is itself acting as a BR.

###### otBorderRoutingCountPeerBrs (heading level 7)

`uint16_t otBorderRoutingCountPeerBrs(otInstance *aInstance, uint32_t *aMinAge)`

**Description:** Returns the number of peer BRs found in the Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|uint32_t *|[out]|aMinAge|Pointer to an `uint32_t` to return the minimum age among all peer BRs. Can be NULL if the caller does not need this information. Age is represented as seconds since appearance of the BR entry in the Network Data.|

Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE`.

Peer BRs are other devices within the Thread mesh that provide external IP connectivity. A device is considered to provide external IP connectivity if at least one of the following conditions is met regarding its Network Data entries:

- It has added at least one external route entry.
- It has added at least one prefix entry with both the default-route and on-mesh flags set.
- It has added at least one domain prefix (with both the domain and on-mesh flags set).

The list of peer BRs specifically excludes the current device, even if it is itself acting as a BR.

**Returns**

- The number of peer BRs.

###### otBorderRoutingGetNextRdnssAddrEntry (heading level 7)

`otError otBorderRoutingGetNextRdnssAddrEntry(otInstance *aInstance, otBorderRoutingPrefixTableIterator *aIterator, otBorderRoutingRdnssAddrEntry *aEntry)`

**Description:** Iterates over the Recursive DNS Server (RDNSS) address entries.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otBorderRoutingPrefixTableIterator](ot-border-routing-prefix-table-iterator) *|[inout]|aIterator|A pointer to the iterator.|
|[otBorderRoutingRdnssAddrEntry](ot-border-routing-rdnss-addr-entry) *|[out]|aEntry|A pointer to the entry to populate.|

Address entries are discovered by processing the RDNSS options within received Router Advertisement messages from routers on infrastructure link.

Address entries associated with the same discovered router on an infrastructure link are guaranteed to be grouped together (retrieved back-to-back).

###### otBorderRoutingGetNextNat64PrefixEntry (heading level 7)

`otError otBorderRoutingGetNextNat64PrefixEntry(otInstance *aInstance, otBorderRoutingPrefixTableIterator *aIterator, otBorderRoutingNat64PrefixEntry *aEntry)`

**Description:** Iterates through the RA-discovered NAT64 prefix table.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otBorderRoutingPrefixTableIterator](ot-border-routing-prefix-table-iterator) *|[inout]|aIterator|A pointer to the iterator.|
|[otBorderRoutingNat64PrefixEntry](ot-border-routing-nat64-prefix-entry) *|[out]|aEntry|A pointer to the entry to populate.|

Requires `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE`.

###### otBorderRoutingSetRdnssAddrCallback (heading level 7)

`void otBorderRoutingSetRdnssAddrCallback(otInstance *aInstance, otBorderRoutingRdnssAddrCallback aCallback, void *aContext)`

**Description:** Sets the callback to be notified of changes to discovered Recursive DNS Server (RDNSS) address entries.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otBorderRoutingRdnssAddrCallback](api-border-routing#ot-border-routing-rdnss-addr-callback)|[in]|aCallback|The callback function pointer. Can be `NULL` if no callback is required.|
|void *|[in]|aContext|An arbitrary context information (used when invoking the callback).|

A subsequent call to this function, replaces a previously set callback.

###### otBorderRoutingGetNextIfAddrEntry (heading level 7)

`otError otBorderRoutingGetNextIfAddrEntry(otInstance *aInstance, otBorderRoutingPrefixTableIterator *aIterator, otBorderRoutingIfAddrEntry *aEntry)`

**Description:** Iterates over the infrastructure interface address entries.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otBorderRoutingPrefixTableIterator](ot-border-routing-prefix-table-iterator) *|[inout]|aIterator|A pointer to the iterator.|
|[otBorderRoutingIfAddrEntry](ot-border-routing-if-addr-entry) *|[out]|aEntry|A pointer to the entry to populate.|

These are addresses used by the BR itself, for example, when sending Router Advertisements.

###### otBorderRoutingDhcp6PdSetEnabled (heading level 7)

`void otBorderRoutingDhcp6PdSetEnabled(otInstance *aInstance, bool aEnabled)`

**Description:** Enables / Disables DHCPv6 Prefix Delegation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnabled|Whether to accept platform generated RA messages.|

`OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE` must be enabled.

###### otBorderRoutingDhcp6PdGetState (heading level 7)

`otBorderRoutingDhcp6PdState otBorderRoutingDhcp6PdGetState(otInstance *aInstance)`

**Description:** Gets the current state of DHCPv6 Prefix Delegation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE` to be enabled.

**Returns**

- The current state of DHCPv6 Prefix Delegation.

###### otBorderRoutingDhcp6PdSetRequestCallback (heading level 7)

`void otBorderRoutingDhcp6PdSetRequestCallback(otInstance *aInstance, otBorderRoutingRequestDhcp6PdCallback aCallback, void *aContext)`

**Description:** Sets the callback whenever the DHCPv6 PD state changes on the Thread interface.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otBorderRoutingRequestDhcp6PdCallback](api-border-routing#ot-border-routing-request-dhcp6-pd-callback)|[in]|aCallback|A pointer to a function that is called whenever the DHCPv6 PD state changes.|
|void *|[in]|aContext|A pointer to arbitrary context information.|

Subsequent calls to this function replace the previously set callback.

###### otBorderRoutingSetOnLinkPrefix (heading level 7)

`void otBorderRoutingSetOnLinkPrefix(otInstance *aInstance, const otIp6Prefix *aPrefix)`

**Description:** Sets the local on-link prefix.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The on-link prefix to use.|
|const [otIp6Prefix](ot-ip6-prefix) *|N/A|aPrefix||

Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE`.

This is intended for testing only and using it will make the BR non-compliant with the Thread Specification.

Represents an iterator to iterate through the Border Router's discovered prefix table. 

The fields in this type are opaque (intended for use by OpenThread core only) and therefore should not be accessed or used by caller.

Before using an iterator, it MUST be initialized using `otBorderRoutingPrefixTableInitIterator()`. 

###### Public Attributes (heading level 7)

###### mPtr1 (heading level 8)

```
const void* otBorderRoutingPrefixTableIterator::mPtr1
```

###### mPtr2 (heading level 8)

```
const void* otBorderRoutingPrefixTableIterator::mPtr2
```

###### mData0 (heading level 8)

```
uint32_t otBorderRoutingPrefixTableIterator::mData0
```

###### mData1 (heading level 8)

```
uint32_t otBorderRoutingPrefixTableIterator::mData1
```

###### mData2 (heading level 8)

```
uint8_t otBorderRoutingPrefixTableIterator::mData2
```

###### mData3 (heading level 8)

```
uint8_t otBorderRoutingPrefixTableIterator::mData3
```

Represents a discovered router on the infrastructure link. 

The `mIsPeerBr` field requires `OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE`. Routing Manager determines whether the router is a peer BR (connected to the same Thread mesh network) by comparing its advertised PIO/RIO prefixes with the entries in the Thread Network Data. While this method is generally effective, it may not be 100% accurate in all scenarios, so the `mIsPeerBr` flag should be used with caution. 

###### Public Attributes (heading level 7)

###### mAddress (heading level 8)

```
otIp6Address otBorderRoutingRouterEntry::mAddress
```

**Description:** IPv6 address of the router.

###### mMsecSinceLastUpdate (heading level 8)

```
uint32_t otBorderRoutingRouterEntry::mMsecSinceLastUpdate
```

**Description:** Milliseconds since last update (any message rx) from this router.

###### mAge (heading level 8)

```
uint32_t otBorderRoutingRouterEntry::mAge
```

**Description:** The router's age in seconds (duration since its first discovery).

###### mManagedAddressConfigFlag (heading level 8)

```
bool otBorderRoutingRouterEntry::mManagedAddressConfigFlag
```

**Description:** The router's Managed Address Config flag (`M` flag).

###### mOtherConfigFlag (heading level 8)

```
bool otBorderRoutingRouterEntry::mOtherConfigFlag
```

**Description:** The router's Other Config flag (`O` flag).

###### mSnacRouterFlag (heading level 8)

```
bool otBorderRoutingRouterEntry::mSnacRouterFlag
```

**Description:** The router's SNAC Router flag (`S` flag).

###### mIsLocalDevice (heading level 8)

```
bool otBorderRoutingRouterEntry::mIsLocalDevice
```

**Description:** This router is the local device (this BR).

###### mIsReachable (heading level 8)

```
bool otBorderRoutingRouterEntry::mIsReachable
```

**Description:** This router is reachable.

###### mIsPeerBr (heading level 8)

```
bool otBorderRoutingRouterEntry::mIsPeerBr
```

**Description:** This router is (likely) a peer BR.

Represents an entry from the discovered prefix table. 

The entries in the discovered table track the Prefix/Route Info Options in the received Router Advertisement messages from other routers on the infrastructure link. 

###### Public Attributes (heading level 7)

###### mRouter (heading level 8)

```
otBorderRoutingRouterEntry otBorderRoutingPrefixTableEntry::mRouter
```

**Description:** Information about the router advertising this prefix.

###### mPrefix (heading level 8)

```
otIp6Prefix otBorderRoutingPrefixTableEntry::mPrefix
```

**Description:** The discovered IPv6 prefix.

###### mIsOnLink (heading level 8)

```
bool otBorderRoutingPrefixTableEntry::mIsOnLink
```

**Description:** Indicates whether the prefix is on-link or route prefix.

###### mMsecSinceLastUpdate (heading level 8)

```
uint32_t otBorderRoutingPrefixTableEntry::mMsecSinceLastUpdate
```

**Description:** Milliseconds since last update of this prefix.

###### mValidLifetime (heading level 8)

```
uint32_t otBorderRoutingPrefixTableEntry::mValidLifetime
```

**Description:** Valid lifetime of the prefix (in seconds).

###### mRoutePreference (heading level 8)

```
otRoutePreference otBorderRoutingPrefixTableEntry::mRoutePreference
```

**Description:** Route preference when `mIsOnlink` is false.

###### mPreferredLifetime (heading level 8)

```
uint32_t otBorderRoutingPrefixTableEntry::mPreferredLifetime
```

**Description:** Preferred lifetime of the on-link prefix when `mIsOnLink`.

Represents an entry from the discovered NAT64 prefix table. 

The entries in the discovered table track the NAT64 Prefix Options in the received Router Advertisement messages from other routers on the infrastructure link. 

###### Public Attributes (heading level 7)

###### mRouter (heading level 8)

```
otBorderRoutingRouterEntry otBorderRoutingNat64PrefixEntry::mRouter
```

**Description:** Information about the router advertising this NAT64 prefix.

###### mPrefix (heading level 8)

```
otIp6Prefix otBorderRoutingNat64PrefixEntry::mPrefix
```

**Description:** The discovered IPv6 prefix.

###### mMsecSinceLastUpdate (heading level 8)

```
uint32_t otBorderRoutingNat64PrefixEntry::mMsecSinceLastUpdate
```

**Description:** Milliseconds since last update of this prefix.

###### mLifetime (heading level 8)

```
uint32_t otBorderRoutingNat64PrefixEntry::mLifetime
```

**Description:** Lifetime of the prefix (in seconds).

Represents a discovered Recursive DNS Server (RDNSS) address entry. 

Address entries are discovered by processing the RDNSS options within received Router Advertisement messages from routers on infrastructure link. 

###### Public Attributes (heading level 7)

###### mRouter (heading level 8)

```
otBorderRoutingRouterEntry otBorderRoutingRdnssAddrEntry::mRouter
```

**Description:** Information about the router advertising this address.

###### mAddress (heading level 8)

```
otIp6Address otBorderRoutingRdnssAddrEntry::mAddress
```

**Description:** The DNS Server IPv6 address.

###### mMsecSinceLastUpdate (heading level 8)

```
uint32_t otBorderRoutingRdnssAddrEntry::mMsecSinceLastUpdate
```

**Description:** Milliseconds since last update of this address.

###### mLifetime (heading level 8)

```
uint32_t otBorderRoutingRdnssAddrEntry::mLifetime
```

**Description:** Lifetime of the address (in seconds).

Represents information about a peer Border Router found in the Network Data. 

###### Public Attributes (heading level 7)

###### mRloc16 (heading level 8)

```
uint16_t otBorderRoutingPeerBorderRouterEntry::mRloc16
```

**Description:** The RLOC16 of BR.

###### mAge (heading level 8)

```
uint32_t otBorderRoutingPeerBorderRouterEntry::mAge
```

**Description:** Seconds since the BR appeared in the Network Data.

Represents an infra-if IPv6 address entry (an address used by this BR itself on the AIL). 

###### Public Attributes (heading level 7)

###### mAddress (heading level 8)

```
otIp6Address otBorderRoutingIfAddrEntry::mAddress
```

**Description:** The IPv6 address.

###### mSecSinceLastUse (heading level 8)

```
uint32_t otBorderRoutingIfAddrEntry::mSecSinceLastUse
```

**Description:** Seconds since the last RA was sent from this BR using this address.

Represents a group of data of platform-generated RA messages processed. 

###### Public Attributes (heading level 7)

###### mNumPlatformRaReceived (heading level 8)

```
uint32_t otPdProcessedRaInfo::mNumPlatformRaReceived
```

**Description:** The number of platform generated RA handled by ProcessPlatformGeneratedRa.

###### mNumPlatformPioProcessed (heading level 8)

```
uint32_t otPdProcessedRaInfo::mNumPlatformPioProcessed
```

**Description:** The number of PIO processed for adding OMR prefixes.

###### mLastPlatformRaMsec (heading level 8)

```
uint32_t otPdProcessedRaInfo::mLastPlatformRaMsec
```

**Description:** The timestamp of last processed RA message.

##### Commissioner

This module includes functions for the Thread Commissioner role. 

###### Modules

[otCommissioningDataset](ot-commissioning-dataset)

[otJoinerPskd](ot-joiner-pskd)

[otJoinerInfo](ot-joiner-info)

###### Enumerations

###### otCommissionerState (heading level 7)

```
enum otCommissionerState {
    OT_COMMISSIONER_STATE_DISABLED = 0
    OT_COMMISSIONER_STATE_PETITION = 1
    OT_COMMISSIONER_STATE_ACTIVE = 2
}
```

**Description:**

Defines the Commissioner State.

**Enumerator:**

|   |   |
|---|---|
|OT_COMMISSIONER_STATE_DISABLED|Commissioner role is disabled.|
|OT_COMMISSIONER_STATE_PETITION|Currently petitioning to become a Commissioner.|
|OT_COMMISSIONER_STATE_ACTIVE|Commissioner role is active.|

###### otCommissionerJoinerEvent (heading level 7)

```
enum otCommissionerJoinerEvent {
    OT_COMMISSIONER_JOINER_START = 0
    OT_COMMISSIONER_JOINER_CONNECTED = 1
    OT_COMMISSIONER_JOINER_FINALIZE = 2
    OT_COMMISSIONER_JOINER_END = 3
    OT_COMMISSIONER_JOINER_REMOVED = 4
}
```

**Description:**

Defines a Joiner Event on the Commissioner.

**Enumerator:**

|   |   |
|---|---|
|OT_COMMISSIONER_JOINER_START||
|OT_COMMISSIONER_JOINER_CONNECTED||
|OT_COMMISSIONER_JOINER_FINALIZE||
|OT_COMMISSIONER_JOINER_END||
|OT_COMMISSIONER_JOINER_REMOVED||

###### otJoinerInfoType (heading level 7)

```
enum otJoinerInfoType {
    OT_JOINER_INFO_TYPE_ANY = 0
    OT_JOINER_INFO_TYPE_EUI64 = 1
    OT_JOINER_INFO_TYPE_DISCERNER = 2
}
```

**Description:**

Defines a Joiner Info Type.

**Enumerator:**

|   |   |
|---|---|
|OT_JOINER_INFO_TYPE_ANY|Accept any Joiner (no EUI64 or Discerner is specified).|
|OT_JOINER_INFO_TYPE_EUI64|Joiner EUI-64 is specified (`mSharedId.mEui64` in `otJoinerInfo`).|
|OT_JOINER_INFO_TYPE_DISCERNER|Joiner Discerner is specified (`mSharedId.mDiscerner` in `otJoinerInfo`).|

###### Typedefs

###### otCommissionerState (heading level 7)

`typedef enum otCommissionerState otCommissionerState`

**Description:**

Defines the Commissioner State.

###### otCommissionerJoinerEvent (heading level 7)

`typedef enum otCommissionerJoinerEvent otCommissionerJoinerEvent`

**Description:**

Defines a Joiner Event on the Commissioner.

###### otCommissioningDataset (heading level 7)

`typedef struct otCommissioningDataset otCommissioningDataset`

**Description:**

Represents a Commissioning Dataset.

###### otJoinerPskd (heading level 7)

`typedef struct otJoinerPskd otJoinerPskd`

**Description:**

Represents a Joiner PSKd.

###### otJoinerInfoType (heading level 7)

`typedef enum otJoinerInfoType otJoinerInfoType`

**Description:**

Defines a Joiner Info Type.

###### otJoinerInfo (heading level 7)

`typedef struct otJoinerInfo otJoinerInfo`

**Description:**

Represents a Joiner Info.

###### otCommissionerStateCallback (heading level 7)

`typedef void(* otCommissionerStateCallback) (otCommissionerState aState, void *aContext)`

**Description:**

Pointer is called whenever the commissioner state changes.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aState|The Commissioner state.|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### otCommissionerJoinerCallback (heading level 7)

`typedef void(* otCommissionerJoinerCallback) (otCommissionerJoinerEvent aEvent, const otJoinerInfo *aJoinerInfo, const otExtAddress *aJoinerId, void *aContext)`

**Description:**

Pointer is called whenever the joiner state changes.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aEvent|The joiner event type.|
||[in]|aJoinerInfo|A pointer to the Joiner Info.|
||[in]|aJoinerId|A pointer to the Joiner ID (if not known, it will be NULL).|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### otCommissionerEnergyReportCallback (heading level 7)

`typedef void(* otCommissionerEnergyReportCallback) (uint32_t aChannelMask, const uint8_t *aEnergyList, uint8_t aEnergyListLength, void *aContext)`

**Description:**

Pointer is called when the Commissioner receives an Energy Report.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aChannelMask|The channel mask value.|
||[in]|aEnergyList|A pointer to the energy measurement list.|
||[in]|aEnergyListLength|Number of entries in `aEnergyListLength`.|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### otCommissionerPanIdConflictCallback (heading level 7)

`typedef void(* otCommissionerPanIdConflictCallback) (uint16_t aPanId, uint32_t aChannelMask, void *aContext)`

**Description:**

Pointer is called when the Commissioner receives a PAN ID Conflict message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aPanId|The PAN ID value.|
||[in]|aChannelMask|The channel mask value.|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### Functions

###### otCommissionerStart (heading level 7)

`otError otCommissionerStart(otInstance *aInstance, otCommissionerStateCallback aStateCallback, otCommissionerJoinerCallback aJoinerCallback, void *aCallbackContext)`

**Description:** Enables the Thread Commissioner role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otCommissionerStateCallback](api-commissioner#ot-commissioner-state-callback)|[in]|aStateCallback|A pointer to a function that is called when the commissioner state changes.|
|[otCommissionerJoinerCallback](api-commissioner#ot-commissioner-joiner-callback)|[in]|aJoinerCallback|A pointer to a function that is called with a joiner event occurs.|
|void *|[in]|aCallbackContext|A pointer to application-specific context.|

###### otCommissionerStop (heading level 7)

`otError otCommissionerStop(otInstance *aInstance)`

**Description:** Disables the Thread Commissioner role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otCommissionerGetId (heading level 7)

`const char * otCommissionerGetId(otInstance *aInstance)`

**Description:** Returns the Commissioner Id.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The Commissioner Id.

###### otCommissionerSetId (heading level 7)

`otError otCommissionerSetId(otInstance *aInstance, const char *aId)`

**Description:** Sets the Commissioner Id.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const char *|[in]|aId|A pointer to a string character array. Must be null terminated.|

###### otCommissionerAddJoiner (heading level 7)

`otError otCommissionerAddJoiner(otInstance *aInstance, const otExtAddress *aEui64, const char *aPskd, uint32_t aTimeout)`

**Description:** Adds a Joiner entry.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otExtAddress](ot-ext-address) *|[in]|aEui64|A pointer to the Joiner's IEEE EUI-64 or NULL for any Joiner.|
|const char *|[in]|aPskd|A pointer to the PSKd.|
|uint32_t|[in]|aTimeout|A time after which a Joiner is automatically removed, in seconds.|

**Note**

- Only use this after successfully starting the Commissioner role with [otCommissionerStart()](api-commissioner#ot-commissioner-start).

###### otCommissionerAddJoinerWithDiscerner (heading level 7)

`otError otCommissionerAddJoinerWithDiscerner(otInstance *aInstance, const otJoinerDiscerner *aDiscerner, const char *aPskd, uint32_t aTimeout)`

**Description:** Adds a Joiner entry with a given Joiner Discerner value.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otJoinerDiscerner](ot-joiner-discerner) *|[in]|aDiscerner|A pointer to the Joiner Discerner.|
|const char *|[in]|aPskd|A pointer to the PSKd.|
|uint32_t|[in]|aTimeout|A time after which a Joiner is automatically removed, in seconds.|

**Note**

- Only use this after successfully starting the Commissioner role with [otCommissionerStart()](api-commissioner#ot-commissioner-start).

###### otCommissionerGetNextJoinerInfo (heading level 7)

`otError otCommissionerGetNextJoinerInfo(otInstance *aInstance, uint16_t *aIterator, otJoinerInfo *aJoiner)`

**Description:** Get joiner info at aIterator position.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to instance.|
|uint16_t *|[inout]|aIterator|A pointer to the Joiner Info iterator context.|
|[otJoinerInfo](ot-joiner-info) *|[out]|aJoiner|A reference to Joiner info.|

###### otCommissionerRemoveJoiner (heading level 7)

`otError otCommissionerRemoveJoiner(otInstance *aInstance, const otExtAddress *aEui64)`

**Description:** Removes a Joiner entry.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otExtAddress](ot-ext-address) *|[in]|aEui64|A pointer to the Joiner's IEEE EUI-64 or NULL for any Joiner.|

**Note**

- Only use this after successfully starting the Commissioner role with [otCommissionerStart()](api-commissioner#ot-commissioner-start).

###### otCommissionerRemoveJoinerWithDiscerner (heading level 7)

`otError otCommissionerRemoveJoinerWithDiscerner(otInstance *aInstance, const otJoinerDiscerner *aDiscerner)`

**Description:** Removes a Joiner entry.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otJoinerDiscerner](ot-joiner-discerner) *|[in]|aDiscerner|A pointer to the Joiner Discerner.|

**Note**

- Only use this after successfully starting the Commissioner role with [otCommissionerStart()](api-commissioner#ot-commissioner-start).

###### otCommissionerGetProvisioningUrl (heading level 7)

`const char * otCommissionerGetProvisioningUrl(otInstance *aInstance)`

**Description:** Gets the Provisioning URL.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- A pointer to the URL string.

###### otCommissionerSetProvisioningUrl (heading level 7)

`otError otCommissionerSetProvisioningUrl(otInstance *aInstance, const char *aProvisioningUrl)`

**Description:** Sets the Provisioning URL.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const char *|[in]|aProvisioningUrl|A pointer to the Provisioning URL (may be NULL to set as empty string).|

###### otCommissionerAnnounceBegin (heading level 7)

`otError otCommissionerAnnounceBegin(otInstance *aInstance, uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod, const otIp6Address *aAddress)`

**Description:** Sends an Announce Begin message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aChannelMask|The channel mask value.|
|uint8_t|[in]|aCount|The number of Announcement messages per channel.|
|uint16_t|[in]|aPeriod|The time between two successive MLE Announce transmissions (in milliseconds).|
|const [otIp6Address](ot-ip6-address) *|[in]|aAddress|A pointer to the IPv6 destination.|

**Note**

- Only use this after successfully starting the Commissioner role with [otCommissionerStart()](api-commissioner#ot-commissioner-start).

###### otCommissionerEnergyScan (heading level 7)

`otError otCommissionerEnergyScan(otInstance *aInstance, uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod, uint16_t aScanDuration, const otIp6Address *aAddress, otCommissionerEnergyReportCallback aCallback, void *aContext)`

**Description:** Sends an Energy Scan Query message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aChannelMask|The channel mask value.|
|uint8_t|[in]|aCount|The number of energy measurements per channel.|
|uint16_t|[in]|aPeriod|The time between energy measurements (milliseconds).|
|uint16_t|[in]|aScanDuration|The scan duration for each energy measurement (milliseconds).|
|const [otIp6Address](ot-ip6-address) *|[in]|aAddress|A pointer to the IPv6 destination.|
|[otCommissionerEnergyReportCallback](api-commissioner#ot-commissioner-energy-report-callback)|[in]|aCallback|A pointer to a function called on receiving an Energy Report message.|
|void *|[in]|aContext|A pointer to application-specific context.|

**Note**

- Only use this after successfully starting the Commissioner role with [otCommissionerStart()](api-commissioner#ot-commissioner-start).

###### otCommissionerPanIdQuery (heading level 7)

`otError otCommissionerPanIdQuery(otInstance *aInstance, uint16_t aPanId, uint32_t aChannelMask, const otIp6Address *aAddress, otCommissionerPanIdConflictCallback aCallback, void *aContext)`

**Description:** Sends a PAN ID Query message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aPanId|The PAN ID to query.|
|uint32_t|[in]|aChannelMask|The channel mask value.|
|const [otIp6Address](ot-ip6-address) *|[in]|aAddress|A pointer to the IPv6 destination.|
|[otCommissionerPanIdConflictCallback](api-commissioner#ot-commissioner-pan-id-conflict-callback)|[in]|aCallback|A pointer to a function called on receiving a PAN ID Conflict message.|
|void *|[in]|aContext|A pointer to application-specific context.|

**Note**

- Only use this after successfully starting the Commissioner role with [otCommissionerStart()](api-commissioner#ot-commissioner-start).

###### otCommissionerSendMgmtGet (heading level 7)

`otError otCommissionerSendMgmtGet(otInstance *aInstance, const uint8_t *aTlvs, uint8_t aLength)`

**Description:** Sends MGMT_COMMISSIONER_GET.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const uint8_t *|[in]|aTlvs|A pointer to TLVs.|
|uint8_t|[in]|aLength|The length of TLVs.|

###### otCommissionerSendMgmtSet (heading level 7)

`otError otCommissionerSendMgmtSet(otInstance *aInstance, const otCommissioningDataset *aDataset, const uint8_t *aTlvs, uint8_t aLength)`

**Description:** Sends MGMT_COMMISSIONER_SET.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otCommissioningDataset](ot-commissioning-dataset) *|[in]|aDataset|A pointer to commissioning dataset.|
|const uint8_t *|[in]|aTlvs|A pointer to TLVs.|
|uint8_t|[in]|aLength|The length of TLVs.|

###### otCommissionerGetSessionId (heading level 7)

`uint16_t otCommissionerGetSessionId(otInstance *aInstance)`

**Description:** Returns the Commissioner Session ID.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The current commissioner session id.

###### otCommissionerGetState (heading level 7)

`otCommissionerState otCommissionerGetState(otInstance *aInstance)`

**Description:** Returns the Commissioner State.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### Macros

`#define OT_COMMISSIONING_PASSPHRASE_MIN_SIZE 6`

**Description**: Minimum size of the Commissioning Passphrase.

`#define OT_COMMISSIONING_PASSPHRASE_MAX_SIZE 255`

**Description**: Maximum size of the Commissioning Passphrase.

`#define OT_PROVISIONING_URL_MAX_SIZE 64`

**Description**: Max size (number of chars) in Provisioning URL string (excludes null char).

`#define OT_JOINER_MAX_PSKD_LENGTH 32`

**Description**: Maximum string length of a Joiner PSKd (does not include null char).

Represents a Commissioning Dataset. 

###### Public Attributes (heading level 7)

###### mLocator (heading level 8)

```
uint16_t otCommissioningDataset::mLocator
```

**Description:** Border Router RLOC16.

###### mSessionId (heading level 8)

```
uint16_t otCommissioningDataset::mSessionId
```

**Description:** Commissioner Session Id.

###### mSteeringData (heading level 8)

```
otSteeringData otCommissioningDataset::mSteeringData
```

**Description:** Steering Data.

###### mJoinerUdpPort (heading level 8)

```
uint16_t otCommissioningDataset::mJoinerUdpPort
```

**Description:** Joiner UDP Port.

###### mIsLocatorSet (heading level 8)

```
bool otCommissioningDataset::mIsLocatorSet
```

**Description:** TRUE if Border Router RLOC16 is set, FALSE otherwise.

###### mIsSessionIdSet (heading level 8)

```
bool otCommissioningDataset::mIsSessionIdSet
```

**Description:** TRUE if Commissioner Session Id is set, FALSE otherwise.

###### mIsSteeringDataSet (heading level 8)

```
bool otCommissioningDataset::mIsSteeringDataSet
```

**Description:** TRUE if Steering Data is set, FALSE otherwise.

###### mIsJoinerUdpPortSet (heading level 8)

```
bool otCommissioningDataset::mIsJoinerUdpPortSet
```

**Description:** TRUE if Joiner UDP Port is set, FALSE otherwise.

###### mHasExtraTlv (heading level 8)

```
bool otCommissioningDataset::mHasExtraTlv
```

**Description:** TRUE if the Dataset contains any extra unknown sub-TLV, FALSE otherwise.

Represents a Joiner PSKd. 

###### Public Attributes (heading level 7)

###### m8 (heading level 8)

```
char otJoinerPskd::m8[OT_JOINER_MAX_PSKD_LENGTH+1]
```

**Description:** Char string array (must be null terminated - +1 is for null char).

Represents a Joiner Info. 

###### Public Attributes (heading level 7)

###### mType (heading level 8)

```
otJoinerInfoType otJoinerInfo::mType
```

**Description:** Joiner type.

###### mEui64 (heading level 8)

```
otExtAddress otJoinerInfo::mEui64
```

**Description:** Joiner EUI64 (when `mType` is `OT_JOINER_INFO_TYPE_EUI64`)

###### mDiscerner (heading level 8)

```
otJoinerDiscerner otJoinerInfo::mDiscerner
```

**Description:** Joiner Discerner (when `mType` is `OT_JOINER_INFO_TYPE_DISCERNER`)

###### mSharedId (heading level 8)

```
union otJoinerInfo::@0 otJoinerInfo::mSharedId
```

**Description:** Shared fields.

###### mPskd (heading level 8)

```
otJoinerPskd otJoinerInfo::mPskd
```

**Description:** Joiner PSKd.

###### mExpirationTime (heading level 8)

```
uint32_t otJoinerInfo::mExpirationTime
```

**Description:** Joiner expiration time in msec.

##### General

This module includes functions for all Thread roles. 

The Network Data Publisher provides mechanisms to limit the number of similar Service and/or Prefix (on-mesh prefix or external route) entries in the Thread Network Data by monitoring the Network Data and managing if or when to add or remove entries.

All the functions in this module require `OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE` to be enabled.

**Note**

- The functions in this module require `OPENTHREAD_FTD=1` or `OPENTHREAD_MTD=1`.

###### Modules

[otBorderRouterConfig](ot-border-router-config)

[otLowpanContextInfo](ot-lowpan-context-info)

[otExternalRouteConfig](ot-external-route-config)

[otServerConfig](ot-server-config)

[otServiceConfig](ot-service-config)

[otNetworkDiagIp6AddrList](ot-network-diag-ip6-addr-list)

[otNetworkDiagData](ot-network-diag-data)

[otNetworkDiagConnectivity](ot-network-diag-connectivity)

[otNetworkDiagRouteData](ot-network-diag-route-data)

[otNetworkDiagRoute](ot-network-diag-route)

[otNetworkDiagEnhRouteData](ot-network-diag-enh-route-data)

[otNetworkDiagEnhRoute](ot-network-diag-enh-route)

[otNetworkDiagMacCounters](ot-network-diag-mac-counters)

[otNetworkDiagMleCounters](ot-network-diag-mle-counters)

[otNetworkDiagChildEntry](ot-network-diag-child-entry)

[otNetworkDiagChildTable](ot-network-diag-child-table)

[otNetworkDiagTlv](ot-network-diag-tlv)

[otLinkModeConfig](ot-link-mode-config)

[otNeighborInfo](ot-neighbor-info)

[otLeaderData](ot-leader-data)

[otRouterInfo](ot-router-info)

[otIpCounters](ot-ip-counters)

[otMleCounters](ot-mle-counters)

[otThreadParentResponseInfo](ot-thread-parent-response-info)

[otThreadDiscoveryRequestInfo](ot-thread-discovery-request-info)

###### Enumerations

###### otRoutePreference (heading level 7)

```
enum otRoutePreference {
    OT_ROUTE_PREFERENCE_LOW = -1
    OT_ROUTE_PREFERENCE_MED = 0
    OT_ROUTE_PREFERENCE_HIGH = 1
}
```

**Description:**

Defines valid values for `mPreference` in `otExternalRouteConfig` and `otBorderRouterConfig`.

**Enumerator:**

|   |   |
|---|---|
|OT_ROUTE_PREFERENCE_LOW|Low route preference.|
|OT_ROUTE_PREFERENCE_MED|Medium route preference.|
|OT_ROUTE_PREFERENCE_HIGH|High route preference.|

###### otNetDataPublisherEvent (heading level 7)

```
enum otNetDataPublisherEvent {
    OT_NETDATA_PUBLISHER_EVENT_ENTRY_ADDED = 0
    OT_NETDATA_PUBLISHER_EVENT_ENTRY_REMOVED = 1
}
```

**Description:**

Represents the events reported from the Publisher callbacks.

**Enumerator:**

|   |   |
|---|---|
|OT_NETDATA_PUBLISHER_EVENT_ENTRY_ADDED|Published entry is added to the Thread Network Data.|
|OT_NETDATA_PUBLISHER_EVENT_ENTRY_REMOVED|Published entry is removed from the Thread Network Data.|

###### otDeviceRole (heading level 7)

```
enum otDeviceRole {
    OT_DEVICE_ROLE_DISABLED = 0
    OT_DEVICE_ROLE_DETACHED = 1
    OT_DEVICE_ROLE_CHILD = 2
    OT_DEVICE_ROLE_ROUTER = 3
    OT_DEVICE_ROLE_LEADER = 4
}
```

**Description:**

Represents a Thread device role.

**Enumerator:**

|   |   |
|---|---|
|OT_DEVICE_ROLE_DISABLED|The Thread stack is disabled.|
|OT_DEVICE_ROLE_DETACHED|Not currently participating in a Thread network/partition.|
|OT_DEVICE_ROLE_CHILD|The Thread Child role.|
|OT_DEVICE_ROLE_ROUTER|The Thread Router role.|
|OT_DEVICE_ROLE_LEADER|The Thread Leader role.|

###### Typedefs

###### otNetworkDataIterator (heading level 7)

`typedef uint32_t otNetworkDataIterator`

**Description:**

Used to iterate through Network Data information.

###### otBorderRouterConfig (heading level 7)

`typedef struct otBorderRouterConfig otBorderRouterConfig`

**Description:**

Represents a Border Router configuration.

###### otLowpanContextInfo (heading level 7)

`typedef struct otLowpanContextInfo otLowpanContextInfo`

**Description:**

Represents 6LoWPAN Context ID information associated with a prefix in Network Data.

###### otExternalRouteConfig (heading level 7)

`typedef struct otExternalRouteConfig otExternalRouteConfig`

**Description:**

Represents an External Route configuration.

###### otRoutePreference (heading level 7)

`typedef enum otRoutePreference otRoutePreference`

**Description:**

Defines valid values for `mPreference` in `otExternalRouteConfig` and `otBorderRouterConfig`.

###### otServerConfig (heading level 7)

`typedef struct otServerConfig otServerConfig`

**Description:**

Represents a Server configuration.

###### otServiceConfig (heading level 7)

`typedef struct otServiceConfig otServiceConfig`

**Description:**

Represents a Service configuration.

###### otNetDataPublisherEvent (heading level 7)

`typedef enum otNetDataPublisherEvent otNetDataPublisherEvent`

**Description:**

Represents the events reported from the Publisher callbacks.

###### otNetDataDnsSrpServicePublisherCallback (heading level 7)

`typedef void(* otNetDataDnsSrpServicePublisherCallback) (otNetDataPublisherEvent aEvent, void *aContext)`

**Description:**

Pointer type defines the callback used to notify when a "DNS/SRP Service" entry is added to or removed from the Thread Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aEvent|Indicates the event (whether the entry was added or removed).|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

On remove the callback is invoked independent of whether the entry is removed by `Publisher` (e.g., when there are too many similar entries already present in the Network Data) or through an explicit call to unpublish the entry (i.e., a call to `otNetDataUnpublishDnsSrpService()`).

###### otNetDataPrefixPublisherCallback (heading level 7)

`typedef void(* otNetDataPrefixPublisherCallback) (otNetDataPublisherEvent aEvent, const otIp6Prefix *aPrefix, void *aContext)`

**Description:**

Pointer type defines the callback used to notify when a prefix (on-mesh or external route) entry is added to or removed from the Thread Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aEvent|Indicates the event (whether the entry was added or removed).|
||[in]|aPrefix|A pointer to the prefix entry.|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

On remove the callback is invoked independent of whether the entry is removed by `Publisher` (e.g., when there are too many similar entries already present in the Network Data) or through an explicit call to unpublish the entry.

###### otNetworkDiagIterator (heading level 7)

`typedef uint16_t otNetworkDiagIterator`

**Description:**

Used to iterate through Network Diagnostic TLV.

###### otNetworkDiagIp6AddrList (heading level 7)

`typedef struct otNetworkDiagIp6AddrList otNetworkDiagIp6AddrList`

**Description:**

Represents a Network Diagnostics IPv6 Address List TLV value.

###### otNetworkDiagData (heading level 7)

`typedef struct otNetworkDiagData otNetworkDiagData`

**Description:**

Represents a Network Diagnostic TLV Data.

###### otNetworkDiagConnectivity (heading level 7)

`typedef struct otNetworkDiagConnectivity otNetworkDiagConnectivity`

**Description:**

Represents a Network Diagnostic Connectivity value.

###### otNetworkDiagRouteData (heading level 7)

`typedef struct otNetworkDiagRouteData otNetworkDiagRouteData`

**Description:**

Represents a Network Diagnostic Route data.

###### otNetworkDiagRoute (heading level 7)

`typedef struct otNetworkDiagRoute otNetworkDiagRoute`

**Description:**

Represents a Network Diagnostic Route64 TLV value.

###### otNetworkDiagEnhRouteData (heading level 7)

`typedef struct otNetworkDiagEnhRouteData otNetworkDiagEnhRouteData`

**Description:**

Represents a Network Diagnostic Enhanced Route data.

###### otNetworkDiagEnhRoute (heading level 7)

`typedef struct otNetworkDiagEnhRoute otNetworkDiagEnhRoute`

**Description:**

Represents a Network Diagnostic Enhanced Route TLV value.

###### otNetworkDiagMacCounters (heading level 7)

`typedef struct otNetworkDiagMacCounters otNetworkDiagMacCounters`

**Description:**

Represents a Network Diagnostic Mac Counters value.

**Details:**

See [RFC 2863](https://www.ietf.org/rfc/rfc2863) for definitions of member fields.

###### otNetworkDiagMleCounters (heading level 7)

`typedef struct otNetworkDiagMleCounters otNetworkDiagMleCounters`

**Description:**

Represents a Network Diagnostics MLE Counters value.

###### otNetworkDiagChildEntry (heading level 7)

`typedef struct otNetworkDiagChildEntry otNetworkDiagChildEntry`

**Description:**

Represents a Network Diagnostic Child Table Entry.

###### otNetworkDiagChildTable (heading level 7)

`typedef struct otNetworkDiagChildTable otNetworkDiagChildTable`

**Description:**

Represents a Network Diagnostic Child Table TLV value.

###### otNetworkDiagBrState (heading level 7)

`typedef otBorderRoutingState otNetworkDiagBrState`

**Description:**

Represents a Border Router State TLV value.

###### otNetworkDiagTlv (heading level 7)

`typedef struct otNetworkDiagTlv otNetworkDiagTlv`

**Description:**

Represents a Network Diagnostic TLV.

###### otReceiveDiagnosticGetCallback (heading level 7)

`typedef void(* otReceiveDiagnosticGetCallback) (otError aError, otMessage *aMessage, const otMessageInfo *aMessageInfo, void *aContext)`

**Description:**

Pointer is called when Network Diagnostic Get response is received.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aError|The error when failed to get the response.|
||[in]|aMessage|A pointer to the message buffer containing the received Network Diagnostic Get response payload. Available only when `aError` is `OT_ERROR_NONE`.|
||[in]|aMessageInfo|A pointer to the message info for `aMessage`. Available only when `aError` is `OT_ERROR_NONE`.|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### otThreadNonPreferredChannelsResetCallback (heading level 7)

`typedef void(* otThreadNonPreferredChannelsResetCallback) (void *aContext)`

**Description:**

Callback function pointer to notify when a Network Diagnostic Reset request message is received for the `OT_NETWORK_DIAGNOSTIC_TLV_NON_PREFERRED_CHANNELS` TLV.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

This is used to inform the device to reevaluate the channels that are presently included in the non-preferred channels list and update it if needed based on the reevaluation.

###### otLinkModeConfig (heading level 7)

`typedef struct otLinkModeConfig otLinkModeConfig`

**Description:**

Represents an MLE Link Mode configuration.

###### otNeighborInfoIterator (heading level 7)

`typedef int16_t otNeighborInfoIterator`

**Description:**

Used to iterate through neighbor table.

###### otLeaderData (heading level 7)

`typedef struct otLeaderData otLeaderData`

**Description:**

Represents the Thread Leader Data.

###### otIpCounters (heading level 7)

`typedef struct otIpCounters otIpCounters`

**Description:**

Represents the IP level counters.

###### otMleCounters (heading level 7)

`typedef struct otMleCounters otMleCounters`

**Description:**

Represents the Thread MLE counters.

###### otThreadParentResponseInfo (heading level 7)

`typedef struct otThreadParentResponseInfo otThreadParentResponseInfo`

**Description:**

Represents the MLE Parent Response data.

###### otDetachGracefullyCallback (heading level 7)

`typedef void(* otDetachGracefullyCallback) (void *aContext)`

**Description:**

This callback informs the application that the detaching process has finished.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### otWakeupCallback (heading level 7)

`typedef void(* otWakeupCallback) (otError aError, void *aContext)`

**Description:**

Informs the application about the result of waking a Wake-up End Device.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aError|OT_ERROR_NONE Indicates that the Wake-up End Device has been added as a neighbor. OT_ERROR_FAILED Indicates that the Wake-up End Device has not received a wake-up frame, or it has failed the MLE procedure.|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### otThreadParentResponseCallback (heading level 7)

`typedef void(* otThreadParentResponseCallback) (otThreadParentResponseInfo *aInfo, void *aContext)`

**Description:**

Pointer is called every time an MLE Parent Response message is received.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aInfo|A pointer to a location on stack holding the stats data.|
||[in]|aContext|A pointer to callback client-specific context.|

**Details:**

This is used in `otThreadRegisterParentResponseCallback()`.

###### otThreadDiscoveryRequestInfo (heading level 7)

`typedef struct otThreadDiscoveryRequestInfo otThreadDiscoveryRequestInfo`

**Description:**

Represents the Thread Discovery Request data.

###### otThreadDiscoveryRequestCallback (heading level 7)

`typedef void(* otThreadDiscoveryRequestCallback) (const otThreadDiscoveryRequestInfo *aInfo, void *aContext)`

**Description:**

Pointer is called every time an MLE Discovery Request message is received.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aInfo|A pointer to the Discovery Request info data.|
||[in]|aContext|A pointer to callback application-specific context.|

**Details:**

###### otThreadAnycastLocatorCallback (heading level 7)

`typedef void(* otThreadAnycastLocatorCallback) (void *aContext, otError aError, const otIp6Address *aMeshLocalAddress, uint16_t aRloc16)`

**Description:**

Pointer type defines the callback to notify the outcome of a `otThreadLocateAnycastDestination()` request.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to an arbitrary context (provided when callback is registered).|
||[in]|aError|The error when handling the request. OT_ERROR_NONE indicates success. OT_ERROR_RESPONSE_TIMEOUT indicates a destination could not be found. OT_ERROR_ABORT indicates the request was aborted.|
||[in]|aMeshLocalAddress|A pointer to the mesh-local EID of the closest destination of the anycast address when `aError` is OT_ERROR_NONE, NULL otherwise.|
||[in]|aRloc16|The RLOC16 of the destination if found, otherwise invalid RLOC16 (0xfffe).|

**Details:**

###### Functions

###### otNetDataGet (heading level 7)

`otError otNetDataGet(otInstance *aInstance, bool aStable, uint8_t *aData, uint8_t *aDataLength)`

**Description:** Provide full or stable copy of the Partition's Thread Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aStable|TRUE when copying the stable version, FALSE when copying the full version.|
|uint8_t *|[out]|aData|A pointer to the data buffer.|
|uint8_t *|[inout]|aDataLength|On entry, size of the data buffer pointed to by `aData`. On exit, number of copied bytes.|

###### otNetDataGetLength (heading level 7)

`uint8_t otNetDataGetLength(otInstance *aInstance)`

**Description:** Get the current length (number of bytes) of Partition's Thread Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The length of the Network Data.

###### otNetDataGetMaxLength (heading level 7)

`uint8_t otNetDataGetMaxLength(otInstance *aInstance)`

**Description:** Get the maximum observed length of the Thread Network Data since OT stack initialization or since the last call to `otNetDataResetMaxLength()`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The maximum length of the Network Data (high water mark for Network Data length).

###### otNetDataResetMaxLength (heading level 7)

`void otNetDataResetMaxLength(otInstance *aInstance)`

**Description:** Reset the tracked maximum length of the Thread Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**See Also**

- [otNetDataGetMaxLength](api-thread-general#ot-net-data-get-max-length)

###### otNetDataGetNextOnMeshPrefix (heading level 7)

`otError otNetDataGetNextOnMeshPrefix(otInstance *aInstance, otNetworkDataIterator *aIterator, otBorderRouterConfig *aConfig)`

**Description:** Get the next On Mesh Prefix in the partition's Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otNetworkDataIterator](api-thread-general#ot-network-data-iterator) *|[inout]|aIterator|A pointer to the Network Data iterator context. To get the first on-mesh entry it should be set to OT_NETWORK_DATA_ITERATOR_INIT.|
|[otBorderRouterConfig](ot-border-router-config) *|[out]|aConfig|A pointer to where the On Mesh Prefix information will be placed.|

###### otNetDataGetNextRoute (heading level 7)

`otError otNetDataGetNextRoute(otInstance *aInstance, otNetworkDataIterator *aIterator, otExternalRouteConfig *aConfig)`

**Description:** Get the next external route in the partition's Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otNetworkDataIterator](api-thread-general#ot-network-data-iterator) *|[inout]|aIterator|A pointer to the Network Data iterator context. To get the first external route entry it should be set to OT_NETWORK_DATA_ITERATOR_INIT.|
|[otExternalRouteConfig](ot-external-route-config) *|[out]|aConfig|A pointer to where the External Route information will be placed.|

###### otNetDataGetNextService (heading level 7)

`otError otNetDataGetNextService(otInstance *aInstance, otNetworkDataIterator *aIterator, otServiceConfig *aConfig)`

**Description:** Get the next service in the partition's Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otNetworkDataIterator](api-thread-general#ot-network-data-iterator) *|[inout]|aIterator|A pointer to the Network Data iterator context. To get the first service entry it should be set to OT_NETWORK_DATA_ITERATOR_INIT.|
|[otServiceConfig](ot-service-config) *|[out]|aConfig|A pointer to where the service information will be placed.|

###### otNetDataGetNextLowpanContextInfo (heading level 7)

`otError otNetDataGetNextLowpanContextInfo(otInstance *aInstance, otNetworkDataIterator *aIterator, otLowpanContextInfo *aContextInfo)`

**Description:** Get the next 6LoWPAN Context ID info in the partition's Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otNetworkDataIterator](api-thread-general#ot-network-data-iterator) *|[inout]|aIterator|A pointer to the Network Data iterator. To get the first service entry it should be set to OT_NETWORK_DATA_ITERATOR_INIT.|
|[otLowpanContextInfo](ot-lowpan-context-info) *|[out]|aContextInfo|A pointer to where the retrieved 6LoWPAN Context ID information will be placed.|

###### otNetDataGetCommissioningDataset (heading level 7)

`void otNetDataGetCommissioningDataset(otInstance *aInstance, otCommissioningDataset *aDataset)`

**Description:** Gets the Commissioning Dataset from the partition's Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otCommissioningDataset](ot-commissioning-dataset) *|[out]|aDataset|A pointer to a `otCommissioningDataset` to populate.|

###### otNetDataGetVersion (heading level 7)

`uint8_t otNetDataGetVersion(otInstance *aInstance)`

**Description:** Get the Network Data Version.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The Network Data Version.

###### otNetDataGetStableVersion (heading level 7)

`uint8_t otNetDataGetStableVersion(otInstance *aInstance)`

**Description:** Get the Stable Network Data Version.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The Stable Network Data Version.

###### otNetDataSteeringDataCheckJoiner (heading level 7)

`otError otNetDataSteeringDataCheckJoiner(otInstance *aInstance, const otExtAddress *aEui64)`

**Description:** Check if the steering data includes a Joiner.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otExtAddress](ot-ext-address) *|[in]|aEui64|A pointer to the Joiner's IEEE EUI-64.|

###### otNetDataSteeringDataCheckJoinerWithDiscerner (heading level 7)

`otError otNetDataSteeringDataCheckJoinerWithDiscerner(otInstance *aInstance, const struct otJoinerDiscerner *aDiscerner)`

**Description:** Check if the steering data includes a Joiner with a given discerner value.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const struct [otJoinerDiscerner](ot-joiner-discerner) *|[in]|aDiscerner|A pointer to the Joiner Discerner.|

###### otNetDataContainsOmrPrefix (heading level 7)

`bool otNetDataContainsOmrPrefix(otInstance *aInstance, const otIp6Prefix *aPrefix)`

**Description:** Check whether a given Prefix can act as a valid OMR prefix and also the Leader's Network Data contains this prefix.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Prefix](ot-ip6-prefix) *|[in]|aPrefix|A pointer to the IPv6 prefix.|

**Returns**

- Whether `aPrefix` is a valid OMR prefix and Leader's Network Data contains the OMR prefix `aPrefix`.

**Note**

- This API is only available when `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` is used.

###### otNetDataPublishDnsSrpServiceAnycast (heading level 7)

`void otNetDataPublishDnsSrpServiceAnycast(otInstance *aInstance, uint8_t aSequenceNUmber, uint8_t aVersion)`

**Description:** Requests "DNS/SRP Service Anycast Address" to be published in the Thread Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aSequenceNUmber|The sequence number of DNS/SRP Anycast Service.|
|uint8_t|[in]|aVersion|The version number to publish.|

Requires the feature `OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE` to be enabled.

A call to this function will remove and replace any previous "DNS/SRP Service" entry that was being published (from earlier call to any of `otNetDataPublishDnsSrpService{Type}()` functions).

###### otNetDataPublishDnsSrpServiceUnicast (heading level 7)

`void otNetDataPublishDnsSrpServiceUnicast(otInstance *aInstance, const otIp6Address *aAddress, uint16_t aPort, uint8_t aVersion)`

**Description:** Requests "DNS/SRP Service Unicast Address" to be published in the Thread Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Address](ot-ip6-address) *|[in]|aAddress|The DNS/SRP server address to publish (MUST NOT be NULL).|
|uint16_t|[in]|aPort|The SRP server port number to publish.|
|uint8_t|[in]|aVersion|The version number to publish.|

Requires the feature `OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE` to be enabled.

A call to this function will remove and replace any previous "DNS/SRP Service" entry that was being published (from earlier call to any of `otNetDataPublishDnsSrpService{Type}()` functions).

Publishes the "DNS/SRP Service Unicast Address" by including the address and port info in the Service TLV data.

###### otNetDataPublishDnsSrpServiceUnicastMeshLocalEid (heading level 7)

`void otNetDataPublishDnsSrpServiceUnicastMeshLocalEid(otInstance *aInstance, uint16_t aPort, uint8_t aVersion)`

**Description:** Requests "DNS/SRP Service Unicast Address" to be published in the Thread Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aPort|The SRP server port number to publish.|
|uint8_t|[in]|aVersion|The version number to publish.|

Requires the feature `OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE` to be enabled.

A call to this function will remove and replace any previous "DNS/SRP Service" entry that was being published (from earlier call to any of `otNetDataPublishDnsSrpService{Type}()` functions).

Unlike `otNetDataPublishDnsSrpServiceUnicast()` which requires the published address to be given and includes the info in the Service TLV data, this function uses the device's mesh-local EID and includes the info in the Server TLV data.

###### otNetDataIsDnsSrpServiceAdded (heading level 7)

`bool otNetDataIsDnsSrpServiceAdded(otInstance *aInstance)`

**Description:** Indicates whether or not currently the "DNS/SRP Service" entry is added to the Thread Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Requires the feature `OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE` to be enabled.

###### otNetDataSetDnsSrpServicePublisherCallback (heading level 7)

`void otNetDataSetDnsSrpServicePublisherCallback(otInstance *aInstance, otNetDataDnsSrpServicePublisherCallback aCallback, void *aContext)`

**Description:** Sets a callback for notifying when a published "DNS/SRP Service" is actually added to or removed from the Thread Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otNetDataDnsSrpServicePublisherCallback](api-thread-general#ot-net-data-dns-srp-service-publisher-callback)|[in]|aCallback|The callback function pointer (can be NULL if not needed).|
|void *|[in]|aContext|A pointer to application-specific context (used when `aCallback` is invoked).|

A subsequent call to this function replaces any previously set callback function.

Requires the feature `OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE` to be enabled.

###### otNetDataUnpublishDnsSrpService (heading level 7)

`void otNetDataUnpublishDnsSrpService(otInstance *aInstance)`

**Description:** Unpublishes any previously added DNS/SRP (Anycast or Unicast) Service entry from the Thread Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

`OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE` must be enabled.

###### otNetDataPublishOnMeshPrefix (heading level 7)

`otError otNetDataPublishOnMeshPrefix(otInstance *aInstance, const otBorderRouterConfig *aConfig)`

**Description:** Requests an on-mesh prefix to be published in the Thread Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otBorderRouterConfig](ot-border-router-config) *|[in]|aConfig|The on-mesh prefix config to publish (MUST NOT be NULL).|

Requires the feature `OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE` to be enabled.

Only stable entries can be published (i.e.,`aConfig.mStable` MUST be TRUE).

A subsequent call to this method will replace a previous request for the same prefix. In particular, if the new call only changes the flags (e.g., preference level) and the prefix is already added in the Network Data, the change to flags is immediately reflected in the Network Data. This ensures that existing entries in the Network Data are not abruptly removed. Note that a change in the preference level can potentially later cause the entry to be removed from the Network Data after determining there are other nodes that are publishing the same prefix with the same or higher preference.

###### otNetDataPublishExternalRoute (heading level 7)

`otError otNetDataPublishExternalRoute(otInstance *aInstance, const otExternalRouteConfig *aConfig)`

**Description:** Requests an external route prefix to be published in the Thread Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otExternalRouteConfig](ot-external-route-config) *|[in]|aConfig|The external route config to publish (MUST NOT be NULL).|

Requires the feature `OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE` to be enabled.

Only stable entries can be published (i.e.,`aConfig.mStable` MUST be TRUE).

A subsequent call to this method will replace a previous request for the same prefix. In particular, if the new call only changes the flags (e.g., preference level) and the prefix is already added in the Network Data, the change to flags is immediately reflected in the Network Data. This ensures that existing entries in the Network Data are not abruptly removed. Note that a change in the preference level can potentially later cause the entry to be removed from the Network Data after determining there are other nodes that are publishing the same prefix with the same or higher preference.

###### otNetDataReplacePublishedExternalRoute (heading level 7)

`otError otNetDataReplacePublishedExternalRoute(otInstance *aInstance, const otIp6Prefix *aPrefix, const otExternalRouteConfig *aConfig)`

**Description:** Replaces a previously published external route in the Thread Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Prefix](ot-ip6-prefix) *|[in]|aPrefix|The previously published external route prefix to replace.|
|const [otExternalRouteConfig](ot-external-route-config) *|[in]|aConfig|The external route config to publish.|

Requires the feature `OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE` to be enabled.

If there is no previously published external route matching `aPrefix`, this function behaves similarly to `otNetDataPublishExternalRoute()`, i.e., it will start the process of publishing **aConfig** as an external route in the Thread Network Data.

If there is a previously published route entry matching `aPrefix`, it will be replaced with the new prefix from `aConfig`.

- If the `aPrefix` was already added in the Network Data, the change to the new prefix in `aConfig` is immediately reflected in the Network Data. This ensures that route entries in the Network Data are not abruptly removed and the transition from aPrefix to the new prefix is smooth.
- If the old published `aPrefix` was not added in the Network Data, it will be replaced with the new `aConfig` prefix but it will not be immediately added. Instead, it will start the process of publishing it in the Network Data (monitoring the Network Data to determine when/if to add the prefix, depending on the number of similar prefixes present in the Network Data).

###### otNetDataIsPrefixAdded (heading level 7)

`bool otNetDataIsPrefixAdded(otInstance *aInstance, const otIp6Prefix *aPrefix)`

**Description:** Indicates whether or not currently a published prefix entry (on-mesh or external route) is added to the Thread Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Prefix](ot-ip6-prefix) *|[in]|aPrefix|A pointer to the prefix (MUST NOT be NULL).|

Requires the feature `OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE` to be enabled.

###### otNetDataSetPrefixPublisherCallback (heading level 7)

`void otNetDataSetPrefixPublisherCallback(otInstance *aInstance, otNetDataPrefixPublisherCallback aCallback, void *aContext)`

**Description:** Sets a callback for notifying when a published prefix entry is actually added to or removed from the Thread Network Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otNetDataPrefixPublisherCallback](api-thread-general#ot-net-data-prefix-publisher-callback)|[in]|aCallback|The callback function pointer (can be NULL if not needed).|
|void *|[in]|aContext|A pointer to application-specific context (used when `aCallback` is invoked).|

A subsequent call to this function replaces any previously set callback function.

Requires the feature `OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE` to be enabled.

###### otNetDataUnpublishPrefix (heading level 7)

`otError otNetDataUnpublishPrefix(otInstance *aInstance, const otIp6Prefix *aPrefix)`

**Description:** Unpublishes a previously published On-Mesh or External Route Prefix.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Prefix](ot-ip6-prefix) *|[in]|aPrefix|The prefix to unpublish (MUST NOT be NULL).|

`OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE` must be enabled.

###### otThreadGetNextDiagnosticTlv (heading level 7)

`otError otThreadGetNextDiagnosticTlv(const otMessage *aMessage, otNetworkDiagIterator *aIterator, otNetworkDiagTlv *aNetworkDiagTlv)`

**Description:** Gets the next Network Diagnostic TLV in the message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to a message.|
|[otNetworkDiagIterator](api-thread-general#ot-network-diag-iterator) *|[inout]|aIterator|A pointer to the Network Diagnostic iterator context. To get the first Network Diagnostic TLV it should be set to OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT.|
|[otNetworkDiagTlv](ot-network-diag-tlv) *|[out]|aNetworkDiagTlv|A pointer to where the Network Diagnostic TLV information will be placed.|

Requires `OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE`.

@Note A subsequent call to this function is allowed only when current return value is OT_ERROR_NONE. 

###### otThreadSendDiagnosticGet (heading level 7)

`otError otThreadSendDiagnosticGet(otInstance *aInstance, const otIp6Address *aDestination, const uint8_t aTlvTypes[], uint8_t aCount, otReceiveDiagnosticGetCallback aCallback, void *aCallbackContext)`

**Description:** Send a Network Diagnostic Get request.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Address](ot-ip6-address) *|[in]|aDestination|A pointer to destination address.|
|const uint8_t|[in]|aTlvTypes|An array of Network Diagnostic TLV types.|
|uint8_t|[in]|aCount|Number of types in aTlvTypes.|
|[otReceiveDiagnosticGetCallback](api-thread-general#ot-receive-diagnostic-get-callback)|[in]|aCallback|A pointer to a function that is called when Network Diagnostic Get response is received or NULL to disable the callback.|
|void *|[in]|aCallbackContext|A pointer to application-specific context.|

Requires `OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE`.

###### otThreadSendDiagnosticReset (heading level 7)

`otError otThreadSendDiagnosticReset(otInstance *aInstance, const otIp6Address *aDestination, const uint8_t aTlvTypes[], uint8_t aCount)`

**Description:** Send a Network Diagnostic Reset request.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Address](ot-ip6-address) *|[in]|aDestination|A pointer to destination address.|
|const uint8_t|[in]|aTlvTypes|An array of Network Diagnostic TLV types. Currently only Type 9 is allowed.|
|uint8_t|[in]|aCount|Number of types in aTlvTypes|

Requires `OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE`.

###### otThreadGetVendorName (heading level 7)

`const char * otThreadGetVendorName(otInstance *aInstance)`

**Description:** Get the vendor name string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The vendor name string.

###### otThreadGetVendorModel (heading level 7)

`const char * otThreadGetVendorModel(otInstance *aInstance)`

**Description:** Get the vendor model string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The vendor model string.

###### otThreadGetVendorSwVersion (heading level 7)

`const char * otThreadGetVendorSwVersion(otInstance *aInstance)`

**Description:** Get the vendor software version string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The vendor software version string.

###### otThreadGetVendorAppUrl (heading level 7)

`const char * otThreadGetVendorAppUrl(otInstance *aInstance)`

**Description:** Get the vendor app URL string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The vendor app URL string.

###### otThreadGetVendorOui (heading level 7)

`uint32_t otThreadGetVendorOui(otInstance *aInstance)`

**Description:** Get the vendor OUI-24.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The vendor OUI-24 value in hex format, or `OT_THREAD_UNSPECIFIED_VENDOR_OUI` is not specified.

###### otThreadSetVendorName (heading level 7)

`otError otThreadSetVendorName(otInstance *aInstance, const char *aVendorName)`

**Description:** Set the vendor name string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const char *|[in]|aVendorName|The vendor name string.|

Requires `OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE`.

`aVendorName` should be UTF8 with max length of 32 chars (`MAX_VENDOR_NAME_TLV_LENGTH`). Maximum length does not include the null `\0` character.

If `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled, `aVendorName` must start with the "RD:" prefix. This is enforced to ensure reference devices are identifiable. If `aVendorName` does not follow this pattern, the name is rejected, and `OT_ERROR_INVALID_ARGS` is returned.

###### otThreadSetVendorModel (heading level 7)

`otError otThreadSetVendorModel(otInstance *aInstance, const char *aVendorModel)`

**Description:** Set the vendor model string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const char *|[in]|aVendorModel|The vendor model string.|

Requires `OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE`.

`aVendorModel` should be UTF8 with max length of 32 chars (`MAX_VENDOR_MODEL_TLV_LENGTH`). Maximum length does not include the null `\0` character.

###### otThreadSetVendorSwVersion (heading level 7)

`otError otThreadSetVendorSwVersion(otInstance *aInstance, const char *aVendorSwVersion)`

**Description:** Set the vendor software version string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const char *|[in]|aVendorSwVersion|The vendor software version string.|

Requires `OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE`.

`aVendorSwVersion` should be UTF8 with max length of 16 chars(`MAX_VENDOR_SW_VERSION_TLV_LENGTH`). Maximum length does not include the null `\0` character.

###### otThreadSetVendorAppUrl (heading level 7)

`otError otThreadSetVendorAppUrl(otInstance *aInstance, const char *aVendorAppUrl)`

**Description:** Set the vendor app URL string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const char *|[in]|aVendorAppUrl|The vendor app URL string.|

Requires `OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE`.

`aVendorAppUrl` should be UTF8 with max length of 64 chars (`MAX_VENDOR_APPL_URL_TLV_LENGTH`). Maximum length does not include the null `\0` character.

###### otThreadSetVendorOui (heading level 7)

`otError otThreadSetVendorOui(otInstance *aInstance, uint32_t aVendorOui)`

**Description:** Set the vendor OUI-24.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aVendorOui|The vendor OUI-24 value in Hexadecimal representation (e.g., OUI 64-16-66 is represented as `0x641666`). Must be a 24-bit value.|

Requires `OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE`.

###### otThreadSetNonPreferredChannels (heading level 7)

`void otThreadSetNonPreferredChannels(otInstance *aInstance, otChannelMask aChannelMask)`

**Description:** Sets the non-preferred channels value for `OT_NETWORK_DIAGNOSTIC_TLV_NON_PREFERRED_CHANNELS` TLV.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otChannelMask](api-operational-dataset#ot-channel-mask)|[in]|aChannelMask|A channel mask specifying the non-preferred channels.|

This value is used to respond to a Network Diagnostic Get request for this TLV.

###### otThreadGetNonPreferredChannels (heading level 7)

`otChannelMask otThreadGetNonPreferredChannels(otInstance *aInstance)`

**Description:** Gets the non-preferred channels for `OT_NETWORK_DIAGNOSTIC_TLV_NON_PREFERRED_CHANNELS` TLV.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

**Returns**

- The non-preferred channels as a channel mask.

###### otThreadSetNonPreferredChannelsResetCallback (heading level 7)

`void otThreadSetNonPreferredChannelsResetCallback(otInstance *aInstance, otThreadNonPreferredChannelsResetCallback aCallback, void *aContext)`

**Description:** Sets the callback to notify when a Network Diagnostic Reset request message is received for the `OT_NETWORK_DIAGNOSTIC_TLV_NON_PREFERRED_CHANNELS` TLV.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otThreadNonPreferredChannelsResetCallback](api-thread-general#ot-thread-non-preferred-channels-reset-callback)|[in]|aCallback|The callback function pointer. Can be NULL.|
|void *|[in]|aContext|A pointer to application-specific context used with `aCallback`.|

A subsequent call to this function will replace the previously set callback.

###### otThreadSetEnabled (heading level 7)

`otError otThreadSetEnabled(otInstance *aInstance, bool aEnabled)`

**Description:** Starts Thread protocol operation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnabled|TRUE if Thread is enabled, FALSE otherwise.|

The interface must be up when calling this function.

Calling this function with `aEnabled` set to FALSE stops any ongoing processes of detaching started by [otThreadDetachGracefully()](api-thread-general#ot-thread-detach-gracefully). Its callback will be called.

###### otThreadGetVersion (heading level 7)

`uint16_t otThreadGetVersion(void)`

**Description:** Gets the Thread protocol version.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

The constants `OT_THREAD_VERSION_*` define the numerical version values.

**Returns**

- the Thread protocol version.

###### otThreadIsSingleton (heading level 7)

`bool otThreadIsSingleton(otInstance *aInstance)`

**Description:** Indicates whether a node is the only router on the network.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otThreadDiscover (heading level 7)

`otError otThreadDiscover(otInstance *aInstance, uint32_t aScanChannels, uint16_t aPanId, bool aJoiner, bool aEnableEui64Filtering, otHandleActiveScanResult aCallback, void *aCallbackContext)`

**Description:** Starts a Thread Discovery scan.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aScanChannels|A bit vector indicating which channels to scan (e.g. OT_CHANNEL_11_MASK).|
|uint16_t|[in]|aPanId|The PAN ID filter (set to Broadcast PAN to disable filter).|
|bool|[in]|aJoiner|Value of the Joiner Flag in the Discovery Request TLV.|
|bool|[in]|aEnableEui64Filtering|TRUE to filter responses on EUI-64, FALSE otherwise.|
|[otHandleActiveScanResult](api-link-link#ot-handle-active-scan-result)|[in]|aCallback|A pointer to a function called on receiving an MLE Discovery Response or scan completes.|
|void *|[in]|aCallbackContext|A pointer to application-specific context.|

**Note**

- A successful call to this function enables the rx-on-when-idle mode for the entire scan procedure.

###### otThreadIsDiscoverInProgress (heading level 7)

`bool otThreadIsDiscoverInProgress(otInstance *aInstance)`

**Description:** Determines if an MLE Thread Discovery is currently in progress.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otThreadSetJoinerAdvertisement (heading level 7)

`otError otThreadSetJoinerAdvertisement(otInstance *aInstance, uint32_t aOui, const uint8_t *aAdvData, uint8_t aAdvDataLength)`

**Description:** Sets the Thread Joiner Advertisement used when discovering a Thread network.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aOui|The Vendor IEEE OUI value that will be included in the Joiner Advertisement. Only the least significant 3 bytes will be used, and the most significant byte will be ignored.|
|const uint8_t *|[in]|aAdvData|A pointer to the AdvData that will be included in the Joiner Advertisement.|
|uint8_t|[in]|aAdvDataLength|The length of AdvData in bytes. Must not exceed `OT_JOINER_ADVDATA_MAX_LENGTH`.|

Requires `OPENTHREAD_CONFIG_JOINER_ADV_EXPERIMENTAL_ENABLE`.

**Note**

- This is an experimental feature and is not part of the Thread specification. OpenThread's implementation is partial: it provides the mechanism for a Joiner to include a new Joiner Adv TLV in its emitted Discovery Scan Request messages, but does not include the corresponding logic for the receiver of Scan Request to read or parse this TLV.

A Joiner can use this to advertise its own application-specific information (such as Vendor ID, Product ID, Discriminator, etc.) using a newly proposed Joiner Advertisement TLV (`OT_MESHCOP_TLV_JOINERADVERTISEMENT`). This TLV is appended as a sub-TLV within the MLE Discovery TLV in an MLE Discovery Scan Request message.

###### otThreadGetChildTimeout (heading level 7)

`uint32_t otThreadGetChildTimeout(otInstance *aInstance)`

**Description:** Gets the Thread Child Timeout (in seconds) used when operating in the Child role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The Thread Child Timeout value in seconds.

**See Also**

- [otThreadSetChildTimeout](api-thread-general#ot-thread-set-child-timeout)

###### otThreadSetChildTimeout (heading level 7)

`void otThreadSetChildTimeout(otInstance *aInstance, uint32_t aTimeout)`

**Description:** Sets the Thread Child Timeout (in seconds) used when operating in the Child role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aTimeout|The timeout value in seconds.|

**See Also**

- [otThreadGetChildTimeout](api-thread-general#ot-thread-get-child-timeout)

###### otThreadGetExtendedPanId (heading level 7)

`const otExtendedPanId * otThreadGetExtendedPanId(otInstance *aInstance)`

**Description:** Gets the IEEE 802.15.4 Extended PAN ID.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- A pointer to the IEEE 802.15.4 Extended PAN ID.

**See Also**

- [otThreadSetExtendedPanId](api-thread-general#ot-thread-set-extended-pan-id)

###### otThreadSetExtendedPanId (heading level 7)

`otError otThreadSetExtendedPanId(otInstance *aInstance, const otExtendedPanId *aExtendedPanId)`

**Description:** Sets the IEEE 802.15.4 Extended PAN ID.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otExtendedPanId](ot-extended-pan-id) *|[in]|aExtendedPanId|A pointer to the IEEE 802.15.4 Extended PAN ID.|

**Note**

- Can only be called while Thread protocols are disabled. A successful call to this function invalidates the Active and Pending Operational Datasets in non-volatile memory.

**See Also**

- [otThreadGetExtendedPanId](api-thread-general#ot-thread-get-extended-pan-id)

###### otThreadGetLeaderRloc (heading level 7)

`otError otThreadGetLeaderRloc(otInstance *aInstance, otIp6Address *aLeaderRloc)`

**Description:** Returns a pointer to the Leader's RLOC.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otIp6Address](ot-ip6-address) *|[out]|aLeaderRloc|A pointer to the Leader's RLOC.|

###### otThreadGetLinkMode (heading level 7)

`otLinkModeConfig otThreadGetLinkMode(otInstance *aInstance)`

**Description:** Get the MLE Link Mode configuration.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The MLE Link Mode configuration.

**See Also**

- [otThreadSetLinkMode](api-thread-general#ot-thread-set-link-mode)

###### otThreadSetLinkMode (heading level 7)

`otError otThreadSetLinkMode(otInstance *aInstance, otLinkModeConfig aConfig)`

**Description:** Set the MLE Link Mode configuration.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otLinkModeConfig](ot-link-mode-config)|[in]|aConfig|A pointer to the Link Mode configuration.|

**See Also**

- [otThreadGetLinkMode](api-thread-general#ot-thread-get-link-mode)

###### otThreadGetNetworkKey (heading level 7)

`void otThreadGetNetworkKey(otInstance *aInstance, otNetworkKey *aNetworkKey)`

**Description:** Get the Thread Network Key.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otNetworkKey](ot-network-key) *|[out]|aNetworkKey|A pointer to an `otNetworkKey` to return the Thread Network Key.|

**See Also**

- [otThreadSetNetworkKey](api-thread-general#ot-thread-set-network-key)

###### otThreadGetNetworkKeyRef (heading level 7)

`otNetworkKeyRef otThreadGetNetworkKeyRef(otInstance *aInstance)`

**Description:** Get the `otNetworkKeyRef` for Thread Network Key.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Requires the build-time feature `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` to be enabled.

**Returns**

- Reference to the Thread Network Key stored in memory.

**See Also**

- [otThreadSetNetworkKeyRef](api-thread-general#ot-thread-set-network-key-ref)

###### otThreadSetNetworkKey (heading level 7)

`otError otThreadSetNetworkKey(otInstance *aInstance, const otNetworkKey *aKey)`

**Description:** Set the Thread Network Key.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otNetworkKey](ot-network-key) *|[in]|aKey|A pointer to a buffer containing the Thread Network Key.|

Succeeds only when Thread protocols are disabled. A successful call to this function invalidates the Active and Pending Operational Datasets in non-volatile memory.

**See Also**

- [otThreadGetNetworkKey](api-thread-general#ot-thread-get-network-key)

###### otThreadSetNetworkKeyRef (heading level 7)

`otError otThreadSetNetworkKeyRef(otInstance *aInstance, otNetworkKeyRef aKeyRef)`

**Description:** Set the Thread Network Key as a `otNetworkKeyRef`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otNetworkKeyRef](api-operational-dataset#ot-network-key-ref)|[in]|aKeyRef|Reference to the Thread Network Key.|

Succeeds only when Thread protocols are disabled. A successful call to this function invalidates the Active and Pending Operational Datasets in non-volatile memory.

Requires the build-time feature `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` to be enabled.

**See Also**

- [otThreadGetNetworkKeyRef](api-thread-general#ot-thread-get-network-key-ref)

###### otThreadGetRloc (heading level 7)

`const otIp6Address * otThreadGetRloc(otInstance *aInstance)`

**Description:** Gets the Thread Routing Locator (RLOC) address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- A pointer to the Thread Routing Locator (RLOC) address.

###### otThreadGetMeshLocalEid (heading level 7)

`const otIp6Address * otThreadGetMeshLocalEid(otInstance *aInstance)`

**Description:** Gets the Mesh Local EID address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- A pointer to the Mesh Local EID address.

###### otThreadGetMeshLocalPrefix (heading level 7)

`const otMeshLocalPrefix * otThreadGetMeshLocalPrefix(otInstance *aInstance)`

**Description:** Returns a pointer to the Mesh Local Prefix.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- A pointer to the Mesh Local Prefix.

###### otThreadSetMeshLocalPrefix (heading level 7)

`otError otThreadSetMeshLocalPrefix(otInstance *aInstance, const otMeshLocalPrefix *aMeshLocalPrefix)`

**Description:** Sets the Mesh Local Prefix.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otMeshLocalPrefix](api-operational-dataset#ot-mesh-local-prefix) *|[in]|aMeshLocalPrefix|A pointer to the Mesh Local Prefix.|

Succeeds only when Thread protocols are disabled. A successful call to this function invalidates the Active and Pending Operational Datasets in non-volatile memory.

###### otThreadGetLinkLocalIp6Address (heading level 7)

`const otIp6Address * otThreadGetLinkLocalIp6Address(otInstance *aInstance)`

**Description:** Gets the Thread link-local IPv6 address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

The Thread link local address is derived using IEEE802.15.4 Extended Address as Interface Identifier.

**Returns**

- A pointer to Thread link-local IPv6 address.

###### otThreadGetLinkLocalAllThreadNodesMulticastAddress (heading level 7)

`const otIp6Address * otThreadGetLinkLocalAllThreadNodesMulticastAddress(otInstance *aInstance)`

**Description:** Gets the Thread Link-Local All Thread Nodes multicast address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

The address is a link-local Unicast Prefix-Based Multicast Address [RFC 3306], with:

- flgs set to 3 (P = 1 and T = 1)
- scop set to 2
- plen set to 64
- network prefix set to the Mesh Local Prefix
- group ID set to 1

**Returns**

- A pointer to Thread Link-Local All Thread Nodes multicast address.

###### otThreadGetRealmLocalAllThreadNodesMulticastAddress (heading level 7)

`const otIp6Address * otThreadGetRealmLocalAllThreadNodesMulticastAddress(otInstance *aInstance)`

**Description:** Gets the Thread Realm-Local All Thread Nodes multicast address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

The address is a realm-local Unicast Prefix-Based Multicast Address [RFC 3306], with:

- flgs set to 3 (P = 1 and T = 1)
- scop set to 3
- plen set to 64
- network prefix set to the Mesh Local Prefix
- group ID set to 1

**Returns**

- A pointer to Thread Realm-Local All Thread Nodes multicast address.

###### otThreadGetServiceAloc (heading level 7)

`otError otThreadGetServiceAloc(otInstance *aInstance, uint8_t aServiceId, otIp6Address *aServiceAloc)`

**Description:** Retrieves the Service ALOC for given Service ID.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aServiceId|Service ID to get ALOC for.|
|[otIp6Address](ot-ip6-address) *|[out]|aServiceAloc|A pointer to output the Service ALOC. MUST NOT BE NULL.|

###### otThreadGetNetworkName (heading level 7)

`const char * otThreadGetNetworkName(otInstance *aInstance)`

**Description:** Get the Thread Network Name.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- A pointer to the Thread Network Name.

**See Also**

- [otThreadSetNetworkName](api-thread-general#ot-thread-set-network-name)

###### otThreadSetNetworkName (heading level 7)

`otError otThreadSetNetworkName(otInstance *aInstance, const char *aNetworkName)`

**Description:** Set the Thread Network Name.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const char *|[in]|aNetworkName|A pointer to the Thread Network Name.|

Succeeds only when Thread protocols are disabled. A successful call to this function invalidates the Active and Pending Operational Datasets in non-volatile memory.

**See Also**

- [otThreadGetNetworkName](api-thread-general#ot-thread-get-network-name)

###### otThreadGetDomainName (heading level 7)

`const char * otThreadGetDomainName(otInstance *aInstance)`

**Description:** Gets the Thread Domain Name.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Note**

- Available since Thread 1.2.

**Returns**

- A pointer to the Thread Domain Name.

**See Also**

- [otThreadSetDomainName](api-thread-general#ot-thread-set-domain-name)

###### otThreadSetDomainName (heading level 7)

`otError otThreadSetDomainName(otInstance *aInstance, const char *aDomainName)`

**Description:** Sets the Thread Domain Name.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const char *|[in]|aDomainName|A pointer to the Thread Domain Name.|

Only succeeds when Thread protocols are disabled.

**Note**

- Available since Thread 1.2.

**See Also**

- [otThreadGetDomainName](api-thread-general#ot-thread-get-domain-name)

###### otThreadSetFixedDuaInterfaceIdentifier (heading level 7)

`otError otThreadSetFixedDuaInterfaceIdentifier(otInstance *aInstance, const otIp6InterfaceIdentifier *aIid)`

**Description:** Sets or clears the Interface Identifier manually specified for the Thread Domain Unicast Address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6InterfaceIdentifier](ot-ip6-interface-identifier) *|[in]|aIid|A pointer to the Interface Identifier to set or NULL to clear.|

Available when `OPENTHREAD_CONFIG_DUA_ENABLE` is enabled.

**Note**

- Only available since Thread 1.2.

**See Also**

- [otThreadGetFixedDuaInterfaceIdentifier](api-thread-general#ot-thread-get-fixed-dua-interface-identifier)

###### otThreadGetFixedDuaInterfaceIdentifier (heading level 7)

`const otIp6InterfaceIdentifier * otThreadGetFixedDuaInterfaceIdentifier(otInstance *aInstance)`

**Description:** Gets the Interface Identifier manually specified for the Thread Domain Unicast Address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Available when `OPENTHREAD_CONFIG_DUA_ENABLE` is enabled.

**Note**

- Only available since Thread 1.2.

**Returns**

- A pointer to the Interface Identifier which was set manually, or NULL if none was set.

**See Also**

- [otThreadSetFixedDuaInterfaceIdentifier](api-thread-general#ot-thread-set-fixed-dua-interface-identifier)

###### otThreadGetKeySequenceCounter (heading level 7)

`uint32_t otThreadGetKeySequenceCounter(otInstance *aInstance)`

**Description:** Gets the thrKeySequenceCounter.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The thrKeySequenceCounter value.

**See Also**

- [otThreadSetKeySequenceCounter](api-thread-general#ot-thread-set-key-sequence-counter)

###### otThreadSetKeySequenceCounter (heading level 7)

`void otThreadSetKeySequenceCounter(otInstance *aInstance, uint32_t aKeySequenceCounter)`

**Description:** Sets the thrKeySequenceCounter.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aKeySequenceCounter|The thrKeySequenceCounter value.|

**Note**

- This API is reserved for testing and demo purposes only. Changing settings with this API will render a production application non-compliant with the Thread Specification.

**See Also**

- [otThreadGetKeySequenceCounter](api-thread-general#ot-thread-get-key-sequence-counter)

###### otThreadGetKeySwitchGuardTime (heading level 7)

`uint16_t otThreadGetKeySwitchGuardTime(otInstance *aInstance)`

**Description:** Gets the thrKeySwitchGuardTime (in hours).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The thrKeySwitchGuardTime value (in hours).

**See Also**

- [otThreadSetKeySwitchGuardTime](api-thread-general#ot-thread-set-key-switch-guard-time)

###### otThreadSetKeySwitchGuardTime (heading level 7)

`void otThreadSetKeySwitchGuardTime(otInstance *aInstance, uint16_t aKeySwitchGuardTime)`

**Description:** Sets the thrKeySwitchGuardTime (in hours).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aKeySwitchGuardTime|The thrKeySwitchGuardTime value (in hours).|

**Note**

- This API is reserved for testing and demo purposes only. Changing settings with this API will render a production application non-compliant with the Thread Specification.

**See Also**

- [otThreadGetKeySwitchGuardTime](api-thread-general#ot-thread-get-key-switch-guard-time)

###### otThreadBecomeDetached (heading level 7)

`otError otThreadBecomeDetached(otInstance *aInstance)`

**Description:** Detach from the Thread network.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otThreadBecomeChild (heading level 7)

`otError otThreadBecomeChild(otInstance *aInstance)`

**Description:** Attempt to reattach as a child.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Note**

- This API is reserved for testing and demo purposes only. Changing settings with this API will render a production application non-compliant with the Thread Specification.

###### otThreadGetNextNeighborInfo (heading level 7)

`otError otThreadGetNextNeighborInfo(otInstance *aInstance, otNeighborInfoIterator *aIterator, otNeighborInfo *aInfo)`

**Description:** Gets the next neighbor information.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otNeighborInfoIterator](api-thread-general#ot-neighbor-info-iterator) *|[inout]|aIterator|A pointer to the iterator context. To get the first neighbor entry it should be set to OT_NEIGHBOR_INFO_ITERATOR_INIT.|
|[otNeighborInfo](ot-neighbor-info) *|[out]|aInfo|A pointer to the neighbor information.|

It is used to go through the entries of the neighbor table.

###### otThreadGetDeviceRole (heading level 7)

`otDeviceRole otThreadGetDeviceRole(otInstance *aInstance)`

**Description:** Get the device role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otThreadDeviceRoleToString (heading level 7)

`const char * otThreadDeviceRoleToString(otDeviceRole aRole)`

**Description:** Convert the device role to human-readable string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otDeviceRole](api-thread-general#ot-device-role)|[in]|aRole|The device role to convert.|

**Returns**

- A string representing `aRole`.

###### otThreadGetLeaderData (heading level 7)

`otError otThreadGetLeaderData(otInstance *aInstance, otLeaderData *aLeaderData)`

**Description:** Get the Thread Leader Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otLeaderData](ot-leader-data) *|[out]|aLeaderData|A pointer to where the leader data is placed.|

###### otThreadGetLeaderRouterId (heading level 7)

`uint8_t otThreadGetLeaderRouterId(otInstance *aInstance)`

**Description:** Get the Leader's Router ID.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The Leader's Router ID.

###### otThreadGetLeaderWeight (heading level 7)

`uint8_t otThreadGetLeaderWeight(otInstance *aInstance)`

**Description:** Get the Leader's Weight.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The Leader's Weight.

###### otThreadGetPartitionId (heading level 7)

`uint32_t otThreadGetPartitionId(otInstance *aInstance)`

**Description:** Get the Partition ID.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The Partition ID.

###### otThreadGetRloc16 (heading level 7)

`uint16_t otThreadGetRloc16(otInstance *aInstance)`

**Description:** Get the RLOC16.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The RLOC16.

###### otThreadGetParentInfo (heading level 7)

`otError otThreadGetParentInfo(otInstance *aInstance, otRouterInfo *aParentInfo)`

**Description:** The function retrieves diagnostic information for a Thread Router as parent.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otRouterInfo](ot-router-info) *|[out]|aParentInfo|A pointer to where the parent router information is placed.|

###### otThreadGetParentAverageRssi (heading level 7)

`otError otThreadGetParentAverageRssi(otInstance *aInstance, int8_t *aParentRssi)`

**Description:** The function retrieves the average RSSI for the Thread Parent.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|int8_t *|[out]|aParentRssi|A pointer to where the parent RSSI should be placed.|

###### otThreadGetParentLastRssi (heading level 7)

`otError otThreadGetParentLastRssi(otInstance *aInstance, int8_t *aLastRssi)`

**Description:** The function retrieves the RSSI of the last packet from the Thread Parent.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|int8_t *|[out]|aLastRssi|A pointer to where the last RSSI should be placed.|

###### otThreadSearchForBetterParent (heading level 7)

`otError otThreadSearchForBetterParent(otInstance *aInstance)`

**Description:** Starts the process for child to search for a better parent while staying attached to its current parent.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

Must be used when device is attached as a child.

###### otThreadGetIp6Counters (heading level 7)

`const otIpCounters * otThreadGetIp6Counters(otInstance *aInstance)`

**Description:** Gets the IPv6 counters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- A pointer to the IPv6 counters.

###### otThreadResetIp6Counters (heading level 7)

`void otThreadResetIp6Counters(otInstance *aInstance)`

**Description:** Resets the IPv6 counters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otThreadGetTimeInQueueHistogram (heading level 7)

`const uint32_t * otThreadGetTimeInQueueHistogram(otInstance *aInstance, uint16_t *aNumBins, uint32_t *aBinInterval)`

**Description:** Gets the time-in-queue histogram for messages in the TX queue.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t *|[out]|aNumBins|Pointer to return the number of bins in histogram (array length).|
|uint32_t *|[out]|aBinInterval|Pointer to return the histogram bin interval length in milliseconds.|

Requires `OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE`.

Histogram of the time-in-queue of messages in the transmit queue is collected. The time-in-queue is tracked for direct transmissions only and is measured as the duration from when a message is added to the transmit queue until it is passed to the MAC layer for transmission or dropped.

The histogram is returned as an array of `uint32_t` values with `aNumBins` entries. The first entry in the array (at index 0) represents the number of messages with a time-in-queue less than `aBinInterval`. The second entry represents the number of messages with a time-in-queue greater than or equal to `aBinInterval`, but less than `2 * aBinInterval`. And so on. The last entry represents the number of messages with time-in-queue greater than or equal to `(aNumBins - 1) * aBinInterval`.

The collected statistics can be reset by calling `otThreadResetTimeInQueueStat()`. The histogram information is collected since the OpenThread instance was initialized or since the last time statistics collection was reset by calling the `otThreadResetTimeInQueueStat()`.

Pointers `aNumBins` and `aBinInterval` MUST NOT be NULL.

**Returns**

- A pointer to an array of `aNumBins` entries representing the collected histogram info.

###### otThreadGetMaxTimeInQueue (heading level 7)

`uint32_t otThreadGetMaxTimeInQueue(otInstance *aInstance)`

**Description:** Gets the maximum time-in-queue for messages in the TX queue.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Requires `OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE`.

The time-in-queue is tracked for direct transmissions only and is measured as the duration from when a message is added to the transmit queue until it is passed to the MAC layer for transmission or dropped.

The collected statistics can be reset by calling `otThreadResetTimeInQueueStat()`.

**Returns**

- The maximum time-in-queue in milliseconds for all messages in the TX queue (so far).

###### otThreadResetTimeInQueueStat (heading level 7)

`void otThreadResetTimeInQueueStat(otInstance *aInstance)`

**Description:** Resets the TX queue time-in-queue statistics.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Requires `OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE`.

###### otThreadGetMleCounters (heading level 7)

`const otMleCounters * otThreadGetMleCounters(otInstance *aInstance)`

**Description:** Gets the Thread MLE counters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- A pointer to the Thread MLE counters.

###### otThreadResetMleCounters (heading level 7)

`void otThreadResetMleCounters(otInstance *aInstance)`

**Description:** Resets the Thread MLE counters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otThreadGetCurrentAttachDuration (heading level 7)

`uint32_t otThreadGetCurrentAttachDuration(otInstance *aInstance)`

**Description:** Gets the current attach duration (number of seconds since the device last attached).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

If the device is not currently attached, zero will be returned.

Unlike the role-tracking variables in `otMleCounters`, which track the cumulative time the device is in each role, this function tracks the time since the last successful attachment, indicating how long the device has been connected to the Thread mesh (regardless of its role, whether acting as a child, router, or leader).

**Returns**

- The number of seconds since last attached.

###### otThreadRegisterParentResponseCallback (heading level 7)

`void otThreadRegisterParentResponseCallback(otInstance *aInstance, otThreadParentResponseCallback aCallback, void *aContext)`

**Description:** Registers a callback to receive MLE Parent Response data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otThreadParentResponseCallback](api-thread-general#ot-thread-parent-response-callback)|[in]|aCallback|A pointer to a function that is called upon receiving an MLE Parent Response message.|
|void *|[in]|aContext|A pointer to callback client-specific context.|

Requires `OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE`.

###### otThreadSetDiscoveryRequestCallback (heading level 7)

`void otThreadSetDiscoveryRequestCallback(otInstance *aInstance, otThreadDiscoveryRequestCallback aCallback, void *aContext)`

**Description:** Sets a callback to receive MLE Discovery Request data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otThreadDiscoveryRequestCallback](api-thread-general#ot-thread-discovery-request-callback)|[in]|aCallback|A pointer to a function that is called upon receiving an MLE Discovery Request message.|
|void *|[in]|aContext|A pointer to callback application-specific context.|

Requires `OPENTHREAD_CONFIG_MLE_DISCOVERY_SCAN_REQUEST_CALLBACK_ENABLE`.

###### otThreadLocateAnycastDestination (heading level 7)

`otError otThreadLocateAnycastDestination(otInstance *aInstance, const otIp6Address *aAnycastAddress, otThreadAnycastLocatorCallback aCallback, void *aContext)`

**Description:** Requests the closest destination of a given anycast address to be located.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otIp6Address](ot-ip6-address) *|[in]|aAnycastAddress|The anycast address to locate. MUST NOT be NULL.|
|[otThreadAnycastLocatorCallback](api-thread-general#ot-thread-anycast-locator-callback)|[in]|aCallback|The callback function to report the result.|
|void *|[in]|aContext|An arbitrary context used with `aCallback`.|

Is only available when `OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE` is enabled.

If a previous request is ongoing, a subsequent call to this function will cancel and replace the earlier request.

###### otThreadIsAnycastLocateInProgress (heading level 7)

`bool otThreadIsAnycastLocateInProgress(otInstance *aInstance)`

**Description:** Indicates whether an anycast locate request is currently in progress.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Is only available when `OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE` is enabled.

**Returns**

- TRUE if an anycast locate request is currently in progress, FALSE otherwise.

###### otThreadSendAddressNotification (heading level 7)

`void otThreadSendAddressNotification(otInstance *aInstance, otIp6Address *aDestination, otIp6Address *aTarget, otIp6InterfaceIdentifier *aMlIid)`

**Description:** Sends a Proactive Address Notification (ADDR_NTF.ntf) message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otIp6Address](ot-ip6-address) *|[in]|aDestination|The destination to send the ADDR_NTF.ntf message.|
|[otIp6Address](ot-ip6-address) *|[in]|aTarget|The target address of the ADDR_NTF.ntf message.|
|[otIp6InterfaceIdentifier](ot-ip6-interface-identifier) *|[in]|aMlIid|The ML-IID of the ADDR_NTF.ntf message.|

Is only available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.

###### otThreadSendProactiveBackboneNotification (heading level 7)

`otError otThreadSendProactiveBackboneNotification(otInstance *aInstance, otIp6Address *aTarget, otIp6InterfaceIdentifier *aMlIid, uint32_t aTimeSinceLastTransaction)`

**Description:** Sends a Proactive Backbone Notification (PRO_BB.ntf) message on the Backbone link.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otIp6Address](ot-ip6-address) *|[in]|aTarget|The target address of the PRO_BB.ntf message.|
|[otIp6InterfaceIdentifier](ot-ip6-interface-identifier) *|[in]|aMlIid|The ML-IID of the PRO_BB.ntf message.|
|uint32_t|[in]|aTimeSinceLastTransaction|Time since last transaction (in seconds).|

Is only available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.

###### otThreadDetachGracefully (heading level 7)

`otError otThreadDetachGracefully(otInstance *aInstance, otDetachGracefullyCallback aCallback, void *aContext)`

**Description:** Notifies other nodes in the network (if any) and then stops Thread protocol operation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otDetachGracefullyCallback](api-thread-general#ot-detach-gracefully-callback)|[in]|aCallback|A pointer to a function that is called upon finishing detaching.|
|void *|[in]|aContext|A pointer to callback application-specific context.|

It sends an Address Release if it's a router, or sets its child timeout to 0 if it's a child.

###### otConvertDurationInSecondsToString (heading level 7)

`void otConvertDurationInSecondsToString(uint32_t aDuration, char *aBuffer, uint16_t aSize)`

**Description:** Converts an `uint32_t` duration (in seconds) to a human-readable string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint32_t|[in]|aDuration|A duration interval in seconds.|
|char *|[out]|aBuffer|A pointer to a char array to output the string.|
|uint16_t|[in]|aSize|The size of `aBuffer` (in bytes). Recommended to use `OT_DURATION_STRING_SIZE`.|

Requires `OPENTHREAD_CONFIG_UPTIME_ENABLE` to be enabled.

The string follows the format "<hh>:<mm>:<ss>" for hours, minutes, seconds (if duration is shorter than one day) or "<dd>d.<hh>:<mm>:<ss>" (if longer than a day).

If the resulting string does not fit in `aBuffer` (within its `aSize` characters), the string will be truncated but the outputted string is always null-terminated.

Is intended for use with `mAge` or `mConnectionTime` in `otNeighborInfo` or `otChildInfo` structures.

###### otThreadSetStoreFrameCounterAhead (heading level 7)

`void otThreadSetStoreFrameCounterAhead(otInstance *aInstance, uint32_t aStoreFrameCounterAhead)`

**Description:** Sets the store frame counter ahead.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aStoreFrameCounterAhead|The store frame counter ahead to set.|

Requires `OPENTHREAD_CONFIG_DYNAMIC_STORE_FRAME_AHEAD_COUNTER_ENABLE` to be enabled.

The OpenThread stack stores the MLE and MAC security frame counter values in non-volatile storage, ensuring they persist across device resets. These saved values are set to be ahead of their current values by the "frame counter ahead" value.

###### otThreadGetStoreFrameCounterAhead (heading level 7)

`uint32_t otThreadGetStoreFrameCounterAhead(otInstance *aInstance)`

**Description:** Gets the store frame counter ahead.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Requires `OPENTHREAD_CONFIG_DYNAMIC_STORE_FRAME_AHEAD_COUNTER_ENABLE` to be enabled.

**Returns**

- The current store frame counter ahead.

###### otThreadWakeup (heading level 7)

`otError otThreadWakeup(otInstance *aInstance, const otExtAddress *aWedAddress, uint16_t aWakeupIntervalUs, uint16_t aWakeupDurationMs, otWakeupCallback aCallback, void *aCallbackContext)`

**Description:** Attempts to wake a Wake-up End Device.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otExtAddress](ot-ext-address) *|[in]|aWedAddress|The extended address of the Wake-up End Device.|
|uint16_t|[in]|aWakeupIntervalUs|An interval between consecutive wake-up frames (in microseconds).|
|uint16_t|[in]|aWakeupDurationMs|Duration of the wake-up sequence (in milliseconds).|
|[otWakeupCallback](api-thread-general#ot-wakeup-callback)|[in]|aCallback|A pointer to function that is called when the wake-up succeeds or fails.|
|void *|[in]|aCallbackContext|A pointer to callback application-specific context.|

Requires `OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE` to be enabled.

The wake-up starts with transmitting a wake-up frame sequence to the Wake-up End Device. During the wake-up sequence, and for a short time after the last wake-up frame is sent, the Wake-up Coordinator keeps its receiver on to be able to receive an initial mesh link establishment message from the WED.

**Warnings**

- The functionality implemented by this function is still in the design phase. Consequently, the prototype and semantics of this function are subject to change.

###### Macros

`#define OT_NETWORK_DATA_ITERATOR_INIT 0`

**Description**: Value to initialize `otNetworkDataIterator`.

`#define OT_SERVICE_DATA_MAX_SIZE 252`

**Description**: Max size of Service Data in bytes.

`#define OT_SERVER_DATA_MAX_SIZE 248`

**Description**: Max size of Server Data in bytes. Theoretical limit, practically much lower.

`#define OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS 0`

**Description**: MAC Extended Address TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS 1`

**Description**: Address16 TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_MODE 2`

**Description**: Mode TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT 3`

**Description**: Timeout TLV (max polling time period for SEDs)

`#define OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY 4`

**Description**: Connectivity TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_ROUTE 5`

**Description**: Route64 TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_LEADER_DATA 6`

**Description**: Leader Data TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA 7`

**Description**: Network Data TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST 8`

**Description**: IPv6 Address List TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_MAC_COUNTERS 9`

**Description**: MAC Counters TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL 14`

**Description**: Battery Level TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_SUPPLY_VOLTAGE 15`

**Description**: Supply Voltage TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE 16`

**Description**: Child Table TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES 17`

**Description**: Channel Pages TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_TYPE_LIST 18`

**Description**: Type List TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT 19`

**Description**: Max Child Timeout TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_EUI64 23`

**Description**: EUI64 TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_VERSION 24`

**Description**: Thread Version TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_NAME 25`

**Description**: Vendor Name TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_MODEL 26`

**Description**: Vendor Model TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_SW_VERSION 27`

**Description**: Vendor SW Version TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_THREAD_STACK_VERSION 28`

**Description**: Thread Stack Version TLV (codebase/commit version)

`#define OT_NETWORK_DIAGNOSTIC_TLV_CHILD 29`

**Description**: Child TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_CHILD_IP6_ADDR_LIST 30`

**Description**: Child IPv6 Address List TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_ROUTER_NEIGHBOR 31`

**Description**: Router Neighbor TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_ANSWER 32`

**Description**: Answer TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_QUERY_ID 33`

**Description**: Query ID TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_MLE_COUNTERS 34`

**Description**: MLE Counters TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_APP_URL 35`

**Description**: Vendor App URL TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_NON_PREFERRED_CHANNELS 36`

**Description**: Non-Preferred Channels Mask TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_ENHANCED_ROUTE 37`

**Description**: Enhanced Route TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_BR_STATE 38`

**Description**: Border Router State TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_BR_IF_ADDRS 39`

**Description**: Border Router Infra Interface Addresses TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_BR_LOCAL_OMR_PREFIX 40`

**Description**: Border Router Local OMR Prefix TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_BR_DHCP6_PD_OMR_PREFIX 41`

**Description**: Border Router DHCPv6-PD OMR Prefix TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_BR_LOCAL_OL_PREFIX 42`

**Description**: Border Router Local On-link Prefix TLV.

`#define OT_NETWORK_DIAGNOSTIC_TLV_BR_FAVORED_OL_PREFIX 43`

**Description**: Border Router Favored On-link Prefix TLV.

`#define OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_NAME_TLV_LENGTH 32`

**Description**: Max length of Vendor Name TLV.

`#define OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_MODEL_TLV_LENGTH 32`

**Description**: Max length of Vendor Model TLV.

`#define OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_SW_VERSION_TLV_LENGTH 16`

**Description**: Max length of Vendor SW Version TLV.

`#define OT_NETWORK_DIAGNOSTIC_MAX_THREAD_STACK_VERSION_TLV_LENGTH 64`

**Description**: Max length of Thread Stack Version TLV.

`#define OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_APP_URL_TLV_LENGTH 96`

**Description**: Max length of Vendor App URL TLV.

`#define OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT 0`

**Description**: Initializer for `otNetworkDiagIterator`.

`#define OT_THREAD_UNSPECIFIED_VENDOR_OUI (0xffffffff)`

**Description**: Represents an unspecified Vendor OUI.

`#define OT_THREAD_VERSION_INVALID 0`

**Description**: Invalid Thread version.

`#define OT_THREAD_VERSION_1_1 2`

**Description**: Thread Version 1.1.

`#define OT_THREAD_VERSION_1_2 3`

**Description**: Thread Version 1.2.

`#define OT_THREAD_VERSION_1_3 4`

**Description**: Thread Version 1.3.

`#define OT_THREAD_VERSION_1_3_1 5`

**Description**: Thread Version 1.3.1 (alias for 1.4)

`#define OT_THREAD_VERSION_1_4 5`

**Description**: Thread Version 1.4.

`#define OT_NETWORK_BASE_TLV_MAX_LENGTH 254`

**Description**: Maximum value length of Thread Base TLV.

`#define OT_NETWORK_MAX_ROUTER_ID 62`

**Description**: Maximum Router ID.

`#define OT_NEIGHBOR_INFO_ITERATOR_INIT 0`

**Description**: Initializer for otNeighborInfoIterator.

`#define OT_JOINER_ADVDATA_MAX_LENGTH 64`

**Description**: Maximum AdvData Length of Joiner Advertisement.

`#define OT_DURATION_STRING_SIZE 21`

**Description**: Recommended size for string representation of `uint32_t` duration in seconds.

Represents a Border Router configuration. 

###### Public Attributes (heading level 7)

###### mPrefix (heading level 8)

```
otIp6Prefix otBorderRouterConfig::mPrefix
```

**Description:** The IPv6 prefix.

###### mPreference (heading level 8)

```
signed int otBorderRouterConfig::mPreference
```

**Description:** A 2-bit signed int preference (`OT_ROUTE_PREFERENCE_*` values).

###### mPreferred (heading level 8)

```
bool otBorderRouterConfig::mPreferred
```

**Description:** Whether prefix is preferred.

###### mSlaac (heading level 8)

```
bool otBorderRouterConfig::mSlaac
```

**Description:** Whether prefix can be used for address auto-configuration (SLAAC).

###### mDhcp (heading level 8)

```
bool otBorderRouterConfig::mDhcp
```

**Description:** Whether border router is DHCPv6 Agent.

###### mConfigure (heading level 8)

```
bool otBorderRouterConfig::mConfigure
```

**Description:** Whether DHCPv6 Agent supplying other config data.

###### mDefaultRoute (heading level 8)

```
bool otBorderRouterConfig::mDefaultRoute
```

**Description:** Whether border router is a default router for prefix.

###### mOnMesh (heading level 8)

```
bool otBorderRouterConfig::mOnMesh
```

**Description:** Whether this prefix is considered on-mesh.

###### mStable (heading level 8)

```
bool otBorderRouterConfig::mStable
```

**Description:** Whether this configuration is considered Stable Network Data.

###### mNdDns (heading level 8)

```
bool otBorderRouterConfig::mNdDns
```

**Description:** Whether this border router can supply DNS information via ND.

###### mDp (heading level 8)

```
bool otBorderRouterConfig::mDp
```

**Description:** Whether prefix is a Thread Domain Prefix (added since Thread 1.2).

###### mRloc16 (heading level 8)

```
uint16_t otBorderRouterConfig::mRloc16
```

**Description:** The border router's RLOC16 (value ignored on config add).

Represents 6LoWPAN Context ID information associated with a prefix in Network Data. 

###### Public Attributes (heading level 7)

###### mContextId (heading level 8)

```
uint8_t otLowpanContextInfo::mContextId
```

**Description:** The 6LoWPAN Context ID.

###### mCompressFlag (heading level 8)

```
bool otLowpanContextInfo::mCompressFlag
```

**Description:** The compress flag.

###### mStable (heading level 8)

```
bool otLowpanContextInfo::mStable
```

**Description:** Whether the Context TLV is marked as Stable Network Data.

###### mPrefix (heading level 8)

```
otIp6Prefix otLowpanContextInfo::mPrefix
```

**Description:** The associated IPv6 prefix.

Represents an External Route configuration. 

###### Public Attributes (heading level 7)

###### mPrefix (heading level 8)

```
otIp6Prefix otExternalRouteConfig::mPrefix
```

**Description:** The IPv6 prefix.

###### mRloc16 (heading level 8)

```
uint16_t otExternalRouteConfig::mRloc16
```

**Description:** The border router's RLOC16 (value ignored on config add).

###### mPreference (heading level 8)

```
signed int otExternalRouteConfig::mPreference
```

**Description:** A 2-bit signed int preference (`OT_ROUTE_PREFERENCE_*` values).

###### mNat64 (heading level 8)

```
bool otExternalRouteConfig::mNat64
```

**Description:** Whether this is a NAT64 prefix.

###### mStable (heading level 8)

```
bool otExternalRouteConfig::mStable
```

**Description:** Whether this configuration is considered Stable Network Data.

###### mNextHopIsThisDevice (heading level 8)

```
bool otExternalRouteConfig::mNextHopIsThisDevice
```

**Description:** Whether the next hop is this device (value ignored on config add).

###### mAdvPio (heading level 8)

```
bool otExternalRouteConfig::mAdvPio
```

**Description:** Whether or not BR is advertising a ULA prefix in PIO (AP flag).

Represents a Server configuration. 

###### Public Attributes (heading level 7)

###### mStable (heading level 8)

```
bool otServerConfig::mStable
```

**Description:** Whether this config is considered Stable Network Data.

###### mServerDataLength (heading level 8)

```
uint8_t otServerConfig::mServerDataLength
```

**Description:** Length of server data.

###### mServerData (heading level 8)

```
uint8_t otServerConfig::mServerData[OT_SERVER_DATA_MAX_SIZE]
```

**Description:** Server data bytes.

###### mRloc16 (heading level 8)

```
uint16_t otServerConfig::mRloc16
```

**Description:** The Server RLOC16.

Represents a Service configuration. 

###### Public Attributes (heading level 7)

###### mServiceId (heading level 8)

```
uint8_t otServiceConfig::mServiceId
```

**Description:** Service ID (when iterating over the Network Data).

###### mEnterpriseNumber (heading level 8)

```
uint32_t otServiceConfig::mEnterpriseNumber
```

**Description:** IANA Enterprise Number.

###### mServiceDataLength (heading level 8)

```
uint8_t otServiceConfig::mServiceDataLength
```

**Description:** Length of service data.

###### mServiceData (heading level 8)

```
uint8_t otServiceConfig::mServiceData[OT_SERVICE_DATA_MAX_SIZE]
```

**Description:** Service data bytes.

###### mServerConfig (heading level 8)

```
otServerConfig otServiceConfig::mServerConfig
```

**Description:** The Server configuration.

Represents a Network Diagnostics IPv6 Address List TLV value. 

###### Public Attributes (heading level 7)

###### mCount (heading level 8)

```
uint8_t otNetworkDiagIp6AddrList::mCount
```

**Description:** Number of IPv6 addresses.

###### mList (heading level 8)

```
otIp6Address otNetworkDiagIp6AddrList::mList[OT_NETWORK_BASE_TLV_MAX_LENGTH/sizeof(otIp6Address)]
```

**Description:** Array of IPv6 addresses.

Represents a Network Diagnostic TLV Data. 

###### Public Attributes (heading level 7)

###### mCount (heading level 8)

```
uint8_t otNetworkDiagData::mCount
```

**Description:** Number of bytes in the data.

###### m8 (heading level 8)

```
uint8_t otNetworkDiagData::m8[OT_NETWORK_BASE_TLV_MAX_LENGTH]
```

**Description:** Array containing the data bytes.

Represents a Network Diagnostic Connectivity value. 

###### Public Attributes (heading level 7)

###### mParentPriority (heading level 8)

```
int8_t otNetworkDiagConnectivity::mParentPriority
```

**Description:** The priority of the sender as a parent.

###### mLinkQuality3 (heading level 8)

```
uint8_t otNetworkDiagConnectivity::mLinkQuality3
```

**Description:** Number of neighbors with link of quality 3.

###### mLinkQuality2 (heading level 8)

```
uint8_t otNetworkDiagConnectivity::mLinkQuality2
```

**Description:** Number of neighbors with link of quality 2.

###### mLinkQuality1 (heading level 8)

```
uint8_t otNetworkDiagConnectivity::mLinkQuality1
```

**Description:** Number of neighbors with link of quality 1.

###### mLeaderCost (heading level 8)

```
uint8_t otNetworkDiagConnectivity::mLeaderCost
```

**Description:** Cost to the Leader.

###### mIdSequence (heading level 8)

```
uint8_t otNetworkDiagConnectivity::mIdSequence
```

**Description:** Most recent received ID seq number.

###### mActiveRouters (heading level 8)

```
uint8_t otNetworkDiagConnectivity::mActiveRouters
```

**Description:** Number of active routers.

###### mSedBufferSize (heading level 8)

```
uint16_t otNetworkDiagConnectivity::mSedBufferSize
```

**Description:** Buffer capacity in bytes for SEDs. Optional.

###### mSedDatagramCount (heading level 8)

```
uint8_t otNetworkDiagConnectivity::mSedDatagramCount
```

**Description:** Queue capacity (number of IPv6 datagrams) per SED. Optional.

Represents a Network Diagnostic Route data. 

###### Public Attributes (heading level 7)

###### mRouterId (heading level 8)

```
uint8_t otNetworkDiagRouteData::mRouterId
```

**Description:** The Assigned Router ID.

###### mLinkQualityOut (heading level 8)

```
uint8_t otNetworkDiagRouteData::mLinkQualityOut
```

**Description:** Link Quality Out.

###### mLinkQualityIn (heading level 8)

```
uint8_t otNetworkDiagRouteData::mLinkQualityIn
```

**Description:** Link Quality In.

###### mRouteCost (heading level 8)

```
uint8_t otNetworkDiagRouteData::mRouteCost
```

**Description:** Routing Cost. Infinite routing cost is represented by value 0.

Represents a Network Diagnostic Route64 TLV value. 

###### Public Attributes (heading level 7)

###### mIdSequence (heading level 8)

```
uint8_t otNetworkDiagRoute::mIdSequence
```

**Description:** The sequence number associated with the set of Router ID assignments in [mRouteData](ot-network-diag-route#m-route-data).

**Details:** Sequence number for Router ID assignments.

###### mRouteCount (heading level 8)

```
uint8_t otNetworkDiagRoute::mRouteCount
```

**Description:** Number of routes.

###### mRouteData (heading level 8)

```
otNetworkDiagRouteData otNetworkDiagRoute::mRouteData[OT_NETWORK_MAX_ROUTER_ID+1]
```

**Description:** Link Quality and Routing Cost data.

Represents a Network Diagnostic Enhanced Route data. 

###### Public Attributes (heading level 7)

###### mRouterId (heading level 8)

```
uint8_t otNetworkDiagEnhRouteData::mRouterId
```

**Description:** The Router ID.

###### mIsSelf (heading level 8)

```
bool otNetworkDiagEnhRouteData::mIsSelf
```

**Description:** This is the queried device itself. If set, the other fields should be ignored.

###### mHasLink (heading level 8)

```
bool otNetworkDiagEnhRouteData::mHasLink
```

**Description:** Indicates whether the queried device has a direct link with router.

###### mLinkQualityOut (heading level 8)

```
uint8_t otNetworkDiagEnhRouteData::mLinkQualityOut
```

**Description:** Link Quality Out (applicable when `mHasLink`).

###### mLinkQualityIn (heading level 8)

```
uint8_t otNetworkDiagEnhRouteData::mLinkQualityIn
```

**Description:** Link Quality In (applicable when `mHasLink`).

###### mNextHop (heading level 8)

```
uint8_t otNetworkDiagEnhRouteData::mNextHop
```

**Description:** The next hop Router ID tracked towards this router.

**Details:** This field indicates the next hop router towards `mRouterId` when using multi-hop forwarding.

If the device has no direct link with the router (`mHasLink == false`), this field indicates the next hop router that would be used to forward messages destined to `mRouterId`.

If the device has a direct link with the router (`mHasLink == true`), this field indicates the alternate multi-hop path that may be used. Note that whether the direct link or this alternate path through the next hop is used to forward messages depends on their associated total path costs.

If there is no next hop, then `OT_NETWORK_MAX_ROUTER_ID + 1` is used.

###### mNextHopCost (heading level 8)

```
uint8_t otNetworkDiagEnhRouteData::mNextHopCost
```

**Description:** The route cost associated with forwarding to `mRouterId` using `mNextHop` (when valid).

**Details:** This is the route cost `mNextHop` has claimed to have towards `mRouterId`. Importantly, it does not include the link cost to send to `mNextHop` itself.

Represents a Network Diagnostic Enhanced Route TLV value. 

###### Public Attributes (heading level 7)

###### mRouteCount (heading level 8)

```
uint8_t otNetworkDiagEnhRoute::mRouteCount
```

**Description:** Number of `mRouteData` entries.

###### mRouteData (heading level 8)

```
otNetworkDiagEnhRouteData otNetworkDiagEnhRoute::mRouteData[OT_NETWORK_MAX_ROUTER_ID+1]
```

**Description:** Route Data per router.

Represents a Network Diagnostic Mac Counters value. 

See [RFC 2863](https://www.ietf.org/rfc/rfc2863) for definitions of member fields. 

###### Public Attributes (heading level 7)

###### mIfInUnknownProtos (heading level 8)

```
uint32_t otNetworkDiagMacCounters::mIfInUnknownProtos
```

###### mIfInErrors (heading level 8)

```
uint32_t otNetworkDiagMacCounters::mIfInErrors
```

###### mIfOutErrors (heading level 8)

```
uint32_t otNetworkDiagMacCounters::mIfOutErrors
```

###### mIfInUcastPkts (heading level 8)

```
uint32_t otNetworkDiagMacCounters::mIfInUcastPkts
```

###### mIfInBroadcastPkts (heading level 8)

```
uint32_t otNetworkDiagMacCounters::mIfInBroadcastPkts
```

###### mIfInDiscards (heading level 8)

```
uint32_t otNetworkDiagMacCounters::mIfInDiscards
```

###### mIfOutUcastPkts (heading level 8)

```
uint32_t otNetworkDiagMacCounters::mIfOutUcastPkts
```

###### mIfOutBroadcastPkts (heading level 8)

```
uint32_t otNetworkDiagMacCounters::mIfOutBroadcastPkts
```

###### mIfOutDiscards (heading level 8)

```
uint32_t otNetworkDiagMacCounters::mIfOutDiscards
```

Represents a Network Diagnostics MLE Counters value. 

###### Public Attributes (heading level 7)

###### mDisabledRole (heading level 8)

```
uint16_t otNetworkDiagMleCounters::mDisabledRole
```

**Description:** Number of times device entered disabled role.

###### mDetachedRole (heading level 8)

```
uint16_t otNetworkDiagMleCounters::mDetachedRole
```

**Description:** Number of times device entered detached role.

###### mChildRole (heading level 8)

```
uint16_t otNetworkDiagMleCounters::mChildRole
```

**Description:** Number of times device entered child role.

###### mRouterRole (heading level 8)

```
uint16_t otNetworkDiagMleCounters::mRouterRole
```

**Description:** Number of times device entered router role.

###### mLeaderRole (heading level 8)

```
uint16_t otNetworkDiagMleCounters::mLeaderRole
```

**Description:** Number of times device entered leader role.

###### mAttachAttempts (heading level 8)

```
uint16_t otNetworkDiagMleCounters::mAttachAttempts
```

**Description:** Number of attach attempts while device was detached.

###### mPartitionIdChanges (heading level 8)

```
uint16_t otNetworkDiagMleCounters::mPartitionIdChanges
```

**Description:** Number of changes to partition ID.

###### mBetterPartitionAttachAttempts (heading level 8)

```
uint16_t otNetworkDiagMleCounters::mBetterPartitionAttachAttempts
```

**Description:** Number of attempts to attach to a better partition.

###### mParentChanges (heading level 8)

```
uint16_t otNetworkDiagMleCounters::mParentChanges
```

**Description:** Number of time device changed its parent.

###### mTrackedTime (heading level 8)

```
uint64_t otNetworkDiagMleCounters::mTrackedTime
```

**Description:** Milliseconds tracked by next counters (zero if not supported).

###### mDisabledTime (heading level 8)

```
uint64_t otNetworkDiagMleCounters::mDisabledTime
```

**Description:** Milliseconds device has been in disabled role.

###### mDetachedTime (heading level 8)

```
uint64_t otNetworkDiagMleCounters::mDetachedTime
```

**Description:** Milliseconds device has been in detached role.

###### mChildTime (heading level 8)

```
uint64_t otNetworkDiagMleCounters::mChildTime
```

**Description:** Milliseconds device has been in child role.

###### mRouterTime (heading level 8)

```
uint64_t otNetworkDiagMleCounters::mRouterTime
```

**Description:** Milliseconds device has been in router role.

###### mLeaderTime (heading level 8)

```
uint64_t otNetworkDiagMleCounters::mLeaderTime
```

**Description:** Milliseconds device has been in leader role.

Represents a Network Diagnostic Child Table Entry. 

###### Public Attributes (heading level 7)

###### mTimeout (heading level 8)

```
uint16_t otNetworkDiagChildEntry::mTimeout
```

**Description:** Expected poll timeout expressed as 2^(Timeout-4) seconds.

###### mLinkQuality (heading level 8)

```
uint8_t otNetworkDiagChildEntry::mLinkQuality
```

**Description:** Link Quality In value [0,3]. Zero indicate sender cannot provide link quality info.

###### mChildId (heading level 8)

```
uint16_t otNetworkDiagChildEntry::mChildId
```

**Description:** Child ID (derived from child RLOC)

###### mMode (heading level 8)

```
otLinkModeConfig otNetworkDiagChildEntry::mMode
```

**Description:** Link mode.

Represents a Network Diagnostic Child Table TLV value. 

###### Public Attributes (heading level 7)

###### mCount (heading level 8)

```
uint8_t otNetworkDiagChildTable::mCount
```

**Description:** Number of child entries in the table.

###### mTable (heading level 8)

```
otNetworkDiagChildEntry otNetworkDiagChildTable::mTable[OT_NETWORK_BASE_TLV_MAX_LENGTH/sizeof(otNetworkDiagChildEntry)]
```

**Description:** Child table.

Represents a Network Diagnostic TLV. 

###### Public Attributes (heading level 7)

###### mType (heading level 8)

```
uint8_t otNetworkDiagTlv::mType
```

**Description:** The Network Diagnostic TLV type.

###### mExtAddress (heading level 8)

```
otExtAddress otNetworkDiagTlv::mExtAddress
```

###### mEui64 (heading level 8)

```
otExtAddress otNetworkDiagTlv::mEui64
```

###### mAddr16 (heading level 8)

```
uint16_t otNetworkDiagTlv::mAddr16
```

###### mMode (heading level 8)

```
otLinkModeConfig otNetworkDiagTlv::mMode
```

###### mTimeout (heading level 8)

```
uint32_t otNetworkDiagTlv::mTimeout
```

###### mConnectivity (heading level 8)

```
otNetworkDiagConnectivity otNetworkDiagTlv::mConnectivity
```

###### mRoute (heading level 8)

```
otNetworkDiagRoute otNetworkDiagTlv::mRoute
```

###### mEnhRoute (heading level 8)

```
otNetworkDiagEnhRoute otNetworkDiagTlv::mEnhRoute
```

###### mLeaderData (heading level 8)

```
otLeaderData otNetworkDiagTlv::mLeaderData
```

###### mNetworkData (heading level 8)

```
otNetworkDiagData otNetworkDiagTlv::mNetworkData
```

###### mIp6AddrList (heading level 8)

```
otNetworkDiagIp6AddrList otNetworkDiagTlv::mIp6AddrList
```

###### mMacCounters (heading level 8)

```
otNetworkDiagMacCounters otNetworkDiagTlv::mMacCounters
```

###### mMleCounters (heading level 8)

```
otNetworkDiagMleCounters otNetworkDiagTlv::mMleCounters
```

###### mBatteryLevel (heading level 8)

```
uint8_t otNetworkDiagTlv::mBatteryLevel
```

###### mSupplyVoltage (heading level 8)

```
uint16_t otNetworkDiagTlv::mSupplyVoltage
```

###### mMaxChildTimeout (heading level 8)

```
uint32_t otNetworkDiagTlv::mMaxChildTimeout
```

###### mVersion (heading level 8)

```
uint16_t otNetworkDiagTlv::mVersion
```

###### mVendorName (heading level 8)

```
char otNetworkDiagTlv::mVendorName[OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_NAME_TLV_LENGTH+1]
```

###### mVendorModel (heading level 8)

```
char otNetworkDiagTlv::mVendorModel[OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_MODEL_TLV_LENGTH+1]
```

###### mVendorSwVersion (heading level 8)

```
char otNetworkDiagTlv::mVendorSwVersion[OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_SW_VERSION_TLV_LENGTH+1]
```

###### mThreadStackVersion (heading level 8)

```
char otNetworkDiagTlv::mThreadStackVersion[OT_NETWORK_DIAGNOSTIC_MAX_THREAD_STACK_VERSION_TLV_LENGTH+1]
```

###### mVendorAppUrl (heading level 8)

```
char otNetworkDiagTlv::mVendorAppUrl[OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_APP_URL_TLV_LENGTH+1]
```

###### mNonPreferredChannels (heading level 8)

```
otChannelMask otNetworkDiagTlv::mNonPreferredChannels
```

###### mChannelPages (heading level 8)

```
otNetworkDiagData otNetworkDiagTlv::mChannelPages
```

###### mChildTable (heading level 8)

```
otNetworkDiagChildTable otNetworkDiagTlv::mChildTable
```

###### mBrState (heading level 8)

```
otNetworkDiagBrState otNetworkDiagTlv::mBrState
```

###### mBrIfAddrList (heading level 8)

```
otNetworkDiagIp6AddrList otNetworkDiagTlv::mBrIfAddrList
```

###### mBrPrefix (heading level 8)

```
otIp6NetworkPrefix otNetworkDiagTlv::mBrPrefix
```

###### mData (heading level 8)

```
union otNetworkDiagTlv::@6 otNetworkDiagTlv::mData
```

Represents an MLE Link Mode configuration. 

###### Public Attributes (heading level 7)

###### mRxOnWhenIdle (heading level 8)

```
bool otLinkModeConfig::mRxOnWhenIdle
```

**Description:** 1, if the sender has its receiver on when not transmitting. 0, otherwise.

###### mDeviceType (heading level 8)

```
bool otLinkModeConfig::mDeviceType
```

**Description:** 1, if the sender is an FTD. 0, otherwise.

###### mNetworkData (heading level 8)

```
bool otLinkModeConfig::mNetworkData
```

**Description:** 1, if the sender requires the full Network Data. 0, otherwise.

Holds diagnostic information for a neighboring Thread node. 

###### Public Attributes (heading level 7)

###### mExtAddress (heading level 8)

```
otExtAddress otNeighborInfo::mExtAddress
```

**Description:** IEEE 802.15.4 Extended Address.

###### mAge (heading level 8)

```
uint32_t otNeighborInfo::mAge
```

**Description:** Seconds since last heard.

###### mConnectionTime (heading level 8)

```
uint32_t otNeighborInfo::mConnectionTime
```

**Description:** Seconds since link establishment (requires `CONFIG_UPTIME_ENABLE`)

###### mRloc16 (heading level 8)

```
uint16_t otNeighborInfo::mRloc16
```

**Description:** RLOC16.

###### mLinkFrameCounter (heading level 8)

```
uint32_t otNeighborInfo::mLinkFrameCounter
```

**Description:** Link Frame Counter.

###### mMleFrameCounter (heading level 8)

```
uint32_t otNeighborInfo::mMleFrameCounter
```

**Description:** MLE Frame Counter.

###### mLinkQualityIn (heading level 8)

```
uint8_t otNeighborInfo::mLinkQualityIn
```

**Description:** Link Quality In.

###### mAverageRssi (heading level 8)

```
int8_t otNeighborInfo::mAverageRssi
```

**Description:** Average RSSI.

###### mLastRssi (heading level 8)

```
int8_t otNeighborInfo::mLastRssi
```

**Description:** Last observed RSSI.

###### mLinkMargin (heading level 8)

```
uint8_t otNeighborInfo::mLinkMargin
```

**Description:** Link Margin.

###### mFrameErrorRate (heading level 8)

```
uint16_t otNeighborInfo::mFrameErrorRate
```

**Description:** Frame error rate (0xffff->100%). Requires error tracking feature.

###### mMessageErrorRate (heading level 8)

```
uint16_t otNeighborInfo::mMessageErrorRate
```

**Description:** (IPv6) msg error rate (0xffff->100%). Requires error tracking feature.

###### mVersion (heading level 8)

```
uint16_t otNeighborInfo::mVersion
```

**Description:** Thread version of the neighbor.

###### mRxOnWhenIdle (heading level 8)

```
bool otNeighborInfo::mRxOnWhenIdle
```

**Description:** rx-on-when-idle

###### mFullThreadDevice (heading level 8)

```
bool otNeighborInfo::mFullThreadDevice
```

**Description:** Full Thread Device.

###### mFullNetworkData (heading level 8)

```
bool otNeighborInfo::mFullNetworkData
```

**Description:** Full Network Data.

###### mIsChild (heading level 8)

```
bool otNeighborInfo::mIsChild
```

**Description:** Is the neighbor a child.

Represents the Thread Leader Data. 

###### Public Attributes (heading level 7)

###### mPartitionId (heading level 8)

```
uint32_t otLeaderData::mPartitionId
```

**Description:** Partition ID.

###### mWeighting (heading level 8)

```
uint8_t otLeaderData::mWeighting
```

**Description:** Leader Weight.

###### mDataVersion (heading level 8)

```
uint8_t otLeaderData::mDataVersion
```

**Description:** Full Network Data Version.

###### mStableDataVersion (heading level 8)

```
uint8_t otLeaderData::mStableDataVersion
```

**Description:** Stable Network Data Version.

###### mLeaderRouterId (heading level 8)

```
uint8_t otLeaderData::mLeaderRouterId
```

**Description:** Leader Router ID.

Holds diagnostic information for a Thread Router. 

###### Public Attributes (heading level 7)

###### mExtAddress (heading level 8)

```
otExtAddress otRouterInfo::mExtAddress
```

**Description:** IEEE 802.15.4 Extended Address.

###### mRloc16 (heading level 8)

```
uint16_t otRouterInfo::mRloc16
```

**Description:** RLOC16.

###### mRouterId (heading level 8)

```
uint8_t otRouterInfo::mRouterId
```

**Description:** Router ID.

###### mNextHop (heading level 8)

```
uint8_t otRouterInfo::mNextHop
```

**Description:** Next hop to router.

###### mPathCost (heading level 8)

```
uint8_t otRouterInfo::mPathCost
```

**Description:** Path cost to router.

###### mLinkQualityIn (heading level 8)

```
uint8_t otRouterInfo::mLinkQualityIn
```

**Description:** Link Quality In.

###### mLinkQualityOut (heading level 8)

```
uint8_t otRouterInfo::mLinkQualityOut
```

**Description:** Link Quality Out.

###### mAge (heading level 8)

```
uint8_t otRouterInfo::mAge
```

**Description:** Time last heard.

###### mAllocated (heading level 8)

```
bool otRouterInfo::mAllocated
```

**Description:** Router ID allocated or not.

###### mLinkEstablished (heading level 8)

```
bool otRouterInfo::mLinkEstablished
```

**Description:** Link established with Router ID or not.

###### mVersion (heading level 8)

```
uint8_t otRouterInfo::mVersion
```

**Description:** Thread version.

###### mCslClockAccuracy (heading level 8)

```
uint8_t otRouterInfo::mCslClockAccuracy
```

**Description:** Parent CSL parameters are only relevant when OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE is enabled.

**Details:** CSL clock accuracy, in ± ppm

###### mCslUncertainty (heading level 8)

```
uint8_t otRouterInfo::mCslUncertainty
```

**Description:** CSL uncertainty, in ±10 us.

Represents the IP level counters. 

###### Public Attributes (heading level 7)

###### mTxSuccess (heading level 8)

```
uint32_t otIpCounters::mTxSuccess
```

**Description:** The number of IPv6 packets successfully transmitted.

###### mRxSuccess (heading level 8)

```
uint32_t otIpCounters::mRxSuccess
```

**Description:** The number of IPv6 packets successfully received.

###### mTxFailure (heading level 8)

```
uint32_t otIpCounters::mTxFailure
```

**Description:** The number of IPv6 packets failed to transmit.

###### mRxFailure (heading level 8)

```
uint32_t otIpCounters::mRxFailure
```

**Description:** The number of IPv6 packets failed to receive.

Represents the Thread MLE counters. 

###### Public Attributes (heading level 7)

###### mDisabledRole (heading level 8)

```
uint16_t otMleCounters::mDisabledRole
```

**Description:** Number of times device entered OT_DEVICE_ROLE_DISABLED role.

###### mDetachedRole (heading level 8)

```
uint16_t otMleCounters::mDetachedRole
```

**Description:** Number of times device entered OT_DEVICE_ROLE_DETACHED role.

###### mChildRole (heading level 8)

```
uint16_t otMleCounters::mChildRole
```

**Description:** Number of times device entered OT_DEVICE_ROLE_CHILD role.

###### mRouterRole (heading level 8)

```
uint16_t otMleCounters::mRouterRole
```

**Description:** Number of times device entered OT_DEVICE_ROLE_ROUTER role.

###### mLeaderRole (heading level 8)

```
uint16_t otMleCounters::mLeaderRole
```

**Description:** Number of times device entered OT_DEVICE_ROLE_LEADER role.

###### mAttachAttempts (heading level 8)

```
uint16_t otMleCounters::mAttachAttempts
```

**Description:** Number of attach attempts while device was detached.

###### mPartitionIdChanges (heading level 8)

```
uint16_t otMleCounters::mPartitionIdChanges
```

**Description:** Number of changes to partition ID.

###### mBetterPartitionAttachAttempts (heading level 8)

```
uint16_t otMleCounters::mBetterPartitionAttachAttempts
```

**Description:** Number of attempts to attach to a better partition.

###### mBetterParentAttachAttempts (heading level 8)

```
uint16_t otMleCounters::mBetterParentAttachAttempts
```

**Description:** Number of attempts to attach to find a better parent (parent search).

###### mDisabledTime (heading level 8)

```
uint64_t otMleCounters::mDisabledTime
```

**Description:** Number of milliseconds device has been in OT_DEVICE_ROLE_DISABLED role.

###### mDetachedTime (heading level 8)

```
uint64_t otMleCounters::mDetachedTime
```

**Description:** Number of milliseconds device has been in OT_DEVICE_ROLE_DETACHED role.

###### mChildTime (heading level 8)

```
uint64_t otMleCounters::mChildTime
```

**Description:** Number of milliseconds device has been in OT_DEVICE_ROLE_CHILD role.

###### mRouterTime (heading level 8)

```
uint64_t otMleCounters::mRouterTime
```

**Description:** Number of milliseconds device has been in OT_DEVICE_ROLE_ROUTER role.

###### mLeaderTime (heading level 8)

```
uint64_t otMleCounters::mLeaderTime
```

**Description:** Number of milliseconds device has been in OT_DEVICE_ROLE_LEADER role.

###### mTrackedTime (heading level 8)

```
uint64_t otMleCounters::mTrackedTime
```

**Description:** Number of milliseconds tracked by previous counters.

###### mParentChanges (heading level 8)

```
uint16_t otMleCounters::mParentChanges
```

**Description:** Number of times device changed its parent.

**Details:** A parent change can happen if device detaches from its current parent and attaches to a different one, or even while device is attached when the periodic parent search feature is enabled (please see option OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE).

Represents the MLE Parent Response data. 

###### Public Attributes (heading level 7)

###### mExtAddr (heading level 8)

```
otExtAddress otThreadParentResponseInfo::mExtAddr
```

**Description:** IEEE 802.15.4 Extended Address of the Parent.

###### mRloc16 (heading level 8)

```
uint16_t otThreadParentResponseInfo::mRloc16
```

**Description:** Short address of the Parent.

###### mRssi (heading level 8)

```
int8_t otThreadParentResponseInfo::mRssi
```

**Description:** Rssi of the Parent.

###### mPriority (heading level 8)

```
int8_t otThreadParentResponseInfo::mPriority
```

**Description:** Parent priority.

###### mLinkQuality3 (heading level 8)

```
uint8_t otThreadParentResponseInfo::mLinkQuality3
```

**Description:** Parent Link Quality 3.

###### mLinkQuality2 (heading level 8)

```
uint8_t otThreadParentResponseInfo::mLinkQuality2
```

**Description:** Parent Link Quality 2.

###### mLinkQuality1 (heading level 8)

```
uint8_t otThreadParentResponseInfo::mLinkQuality1
```

**Description:** Parent Link Quality 1.

###### mIsAttached (heading level 8)

```
bool otThreadParentResponseInfo::mIsAttached
```

**Description:** Is the node receiving parent response attached.

Represents the Thread Discovery Request data. 

###### Public Attributes (heading level 7)

###### mExtAddress (heading level 8)

```
otExtAddress otThreadDiscoveryRequestInfo::mExtAddress
```

**Description:** IEEE 802.15.4 Extended Address of the requester.

###### mVersion (heading level 8)

```
uint8_t otThreadDiscoveryRequestInfo::mVersion
```

**Description:** Thread version.

###### mIsJoiner (heading level 8)

```
bool otThreadDiscoveryRequestInfo::mIsJoiner
```

**Description:** Whether is from joiner.

##### Joiner

This module includes functions for the Thread Joiner role. 

**Note**

- The functions in this module require `OPENTHREAD_CONFIG_JOINER_ENABLE=1`.

###### Modules

[otJoinerDiscerner](ot-joiner-discerner)

###### Enumerations

###### otJoinerState (heading level 7)

```
enum otJoinerState {
    OT_JOINER_STATE_IDLE = 0
    OT_JOINER_STATE_DISCOVER = 1
    OT_JOINER_STATE_CONNECT = 2
    OT_JOINER_STATE_CONNECTED = 3
    OT_JOINER_STATE_ENTRUST = 4
    OT_JOINER_STATE_JOINED = 5
}
```

**Description:**

Defines the Joiner State.

**Enumerator:**

|   |   |
|---|---|
|OT_JOINER_STATE_IDLE||
|OT_JOINER_STATE_DISCOVER||
|OT_JOINER_STATE_CONNECT||
|OT_JOINER_STATE_CONNECTED||
|OT_JOINER_STATE_ENTRUST||
|OT_JOINER_STATE_JOINED||

###### Typedefs

###### otJoinerState (heading level 7)

`typedef enum otJoinerState otJoinerState`

**Description:**

Defines the Joiner State.

###### otJoinerDiscerner (heading level 7)

`typedef struct otJoinerDiscerner otJoinerDiscerner`

**Description:**

Represents a Joiner Discerner.

###### otJoinerCallback (heading level 7)

`typedef void(* otJoinerCallback) (otError aError, void *aContext)`

**Description:**

Pointer is called to notify the completion of a join operation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aError|OT_ERROR_NONE if the join process succeeded. OT_ERROR_SECURITY if the join process failed due to security credentials. OT_ERROR_NOT_FOUND if no joinable network was discovered. OT_ERROR_RESPONSE_TIMEOUT if a response timed out.|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### Functions

###### otJoinerStart (heading level 7)

`otError otJoinerStart(otInstance *aInstance, const char *aPskd, const char *aProvisioningUrl, const char *aVendorName, const char *aVendorModel, const char *aVendorSwVersion, const char *aVendorData, otJoinerCallback aCallback, void *aContext)`

**Description:** Enables the Thread Joiner role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const char *|[in]|aPskd|A pointer to the PSKd.|
|const char *|[in]|aProvisioningUrl|A pointer to the Provisioning URL (may be NULL).|
|const char *|[in]|aVendorName|A pointer to the Vendor Name (may be NULL).|
|const char *|[in]|aVendorModel|A pointer to the Vendor Model (may be NULL).|
|const char *|[in]|aVendorSwVersion|A pointer to the Vendor SW Version (may be NULL).|
|const char *|[in]|aVendorData|A pointer to the Vendor Data (may be NULL).|
|[otJoinerCallback](api-joiner#ot-joiner-callback)|[in]|aCallback|A pointer to a function that is called when the join operation completes.|
|void *|[in]|aContext|A pointer to application-specific context.|

###### otJoinerStop (heading level 7)

`void otJoinerStop(otInstance *aInstance)`

**Description:** Disables the Thread Joiner role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otJoinerGetState (heading level 7)

`otJoinerState otJoinerGetState(otInstance *aInstance)`

**Description:** Gets the Joiner State.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The joiner state.

###### otJoinerGetId (heading level 7)

`const otExtAddress * otJoinerGetId(otInstance *aInstance)`

**Description:** Gets the Joiner ID.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

If a Joiner Discerner is not set, Joiner ID is the first 64 bits of the result of computing SHA-256 over factory-assigned IEEE EUI-64. Otherwise the Joiner ID is calculated from the Joiner Discerner value.

The Joiner ID is also used as the device's IEEE 802.15.4 Extended Address during the commissioning process.

**Returns**

- A pointer to the Joiner ID.

###### otJoinerSetDiscerner (heading level 7)

`otError otJoinerSetDiscerner(otInstance *aInstance, otJoinerDiscerner *aDiscerner)`

**Description:** Sets the Joiner Discerner.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otJoinerDiscerner](ot-joiner-discerner) *|[in]|aDiscerner|A pointer to a Joiner Discerner. If NULL clears any previously set discerner.|

The Joiner Discerner is used to calculate the Joiner ID during the Thread Commissioning process. For more information, refer to [otJoinerGetId](api-joiner#ot-joiner-get-id). **Note**

- The Joiner Discerner takes the place of the Joiner EUI-64 during the joiner session of Thread Commissioning.

###### otJoinerGetDiscerner (heading level 7)

`const otJoinerDiscerner * otJoinerGetDiscerner(otInstance *aInstance)`

**Description:** Gets the Joiner Discerner.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

For more information, refer to [otJoinerSetDiscerner](api-joiner#ot-joiner-set-discerner).

**Returns**

- A pointer to Joiner Discerner or NULL if none is set.

###### otJoinerStateToString (heading level 7)

`const char * otJoinerStateToString(otJoinerState aState)`

**Description:** Converts a given joiner state enumeration value to a human-readable string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otJoinerState](api-joiner#ot-joiner-state)|[in]|aState|The joiner state.|

**Returns**

- A human-readable string representation of `aState`.

###### Macros

`#define OT_JOINER_MAX_DISCERNER_LENGTH 64`

**Description**: Maximum length of a Joiner Discerner in bits.

Represents a Joiner Discerner. 

###### Public Attributes (heading level 7)

###### mValue (heading level 8)

```
uint64_t otJoinerDiscerner::mValue
```

**Description:** Discerner value (the lowest `mLength` bits specify the discerner).

###### mLength (heading level 8)

```
uint8_t otJoinerDiscerner::mLength
```

**Description:** Length (number of bits) - must be non-zero and at most `OT_JOINER_MAX_DISCERNER_LENGTH`.

##### Operational Dataset

Includes functions for the Operational Dataset API. 

For FTD builds only, Dataset Updater includes functions to manage dataset updates.

For FTD and MTD builds, the Operational Dataset API includes functions to manage Active and Pending datasets and dataset TLVs.

###### Modules

[otNetworkKey](ot-network-key)

[otNetworkName](ot-network-name)

[otExtendedPanId](ot-extended-pan-id)

[otPskc](ot-pskc)

[otSecurityPolicy](ot-security-policy)

[otOperationalDatasetComponents](ot-operational-dataset-components)

[otTimestamp](ot-timestamp)

[otOperationalDataset](ot-operational-dataset)

[otOperationalDatasetTlvs](ot-operational-dataset-tlvs)

###### Enumerations

###### otMeshcopTlvType (heading level 7)

```
enum otMeshcopTlvType {
    OT_MESHCOP_TLV_CHANNEL = 0
    OT_MESHCOP_TLV_PANID = 1
    OT_MESHCOP_TLV_EXTPANID = 2
    OT_MESHCOP_TLV_NETWORKNAME = 3
    OT_MESHCOP_TLV_PSKC = 4
    OT_MESHCOP_TLV_NETWORKKEY = 5
    OT_MESHCOP_TLV_NETWORK_KEY_SEQUENCE = 6
    OT_MESHCOP_TLV_MESHLOCALPREFIX = 7
    OT_MESHCOP_TLV_STEERING_DATA = 8
    OT_MESHCOP_TLV_BORDER_AGENT_RLOC = 9
    OT_MESHCOP_TLV_COMMISSIONER_ID = 10
    OT_MESHCOP_TLV_COMM_SESSION_ID = 11
    OT_MESHCOP_TLV_SECURITYPOLICY = 12
    OT_MESHCOP_TLV_GET = 13
    OT_MESHCOP_TLV_ACTIVETIMESTAMP = 14
    OT_MESHCOP_TLV_COMMISSIONER_UDP_PORT = 15
    OT_MESHCOP_TLV_STATE = 16
    OT_MESHCOP_TLV_JOINER_DTLS = 17
    OT_MESHCOP_TLV_JOINER_UDP_PORT = 18
    OT_MESHCOP_TLV_JOINER_IID = 19
    OT_MESHCOP_TLV_JOINER_RLOC = 20
    OT_MESHCOP_TLV_JOINER_ROUTER_KEK = 21
    OT_MESHCOP_TLV_DURATION = 23
    OT_MESHCOP_TLV_PROVISIONING_URL = 32
    OT_MESHCOP_TLV_VENDOR_NAME_TLV = 33
    OT_MESHCOP_TLV_VENDOR_MODEL_TLV = 34
    OT_MESHCOP_TLV_VENDOR_SW_VERSION_TLV = 35
    OT_MESHCOP_TLV_VENDOR_DATA_TLV = 36
    OT_MESHCOP_TLV_VENDOR_STACK_VERSION_TLV = 37
    OT_MESHCOP_TLV_UDP_ENCAPSULATION_TLV = 48
    OT_MESHCOP_TLV_IPV6_ADDRESS_TLV = 49
    OT_MESHCOP_TLV_PENDINGTIMESTAMP = 51
    OT_MESHCOP_TLV_DELAYTIMER = 52
    OT_MESHCOP_TLV_CHANNELMASK = 53
    OT_MESHCOP_TLV_COUNT = 54
    OT_MESHCOP_TLV_PERIOD = 55
    OT_MESHCOP_TLV_SCAN_DURATION = 56
    OT_MESHCOP_TLV_ENERGY_LIST = 57
    OT_MESHCOP_TLV_THREAD_DOMAIN_NAME = 59
    OT_MESHCOP_TLV_WAKEUP_CHANNEL = 74
    OT_MESHCOP_TLV_ADMITTER_STATE = 90
    OT_MESHCOP_TLV_ENROLLER_ID = 91
    OT_MESHCOP_TLV_ENROLLER_MODE = 92
    OT_MESHCOP_TLV_DISCOVERYREQUEST = 128
    OT_MESHCOP_TLV_DISCOVERYRESPONSE = 129
    OT_MESHCOP_TLV_JOINERADVERTISEMENT = 241
}
```

**Description:**

Represents meshcop TLV types.

**Enumerator:**

|   |   |
|---|---|
|OT_MESHCOP_TLV_CHANNEL|meshcop Channel TLV|
|OT_MESHCOP_TLV_PANID|meshcop Pan Id TLV|
|OT_MESHCOP_TLV_EXTPANID|meshcop Extended Pan Id TLV|
|OT_MESHCOP_TLV_NETWORKNAME|meshcop Network Name TLV|
|OT_MESHCOP_TLV_PSKC|meshcop PSKc TLV|
|OT_MESHCOP_TLV_NETWORKKEY|meshcop Network Key TLV|
|OT_MESHCOP_TLV_NETWORK_KEY_SEQUENCE|meshcop Network Key Sequence TLV|
|OT_MESHCOP_TLV_MESHLOCALPREFIX|meshcop Mesh Local Prefix TLV|
|OT_MESHCOP_TLV_STEERING_DATA|meshcop Steering Data TLV|
|OT_MESHCOP_TLV_BORDER_AGENT_RLOC|meshcop Border Agent Locator TLV|
|OT_MESHCOP_TLV_COMMISSIONER_ID|meshcop Commissioner ID TLV|
|OT_MESHCOP_TLV_COMM_SESSION_ID|meshcop Commissioner Session ID TLV|
|OT_MESHCOP_TLV_SECURITYPOLICY|meshcop Security Policy TLV|
|OT_MESHCOP_TLV_GET|meshcop Get TLV|
|OT_MESHCOP_TLV_ACTIVETIMESTAMP|meshcop Active Timestamp TLV|
|OT_MESHCOP_TLV_COMMISSIONER_UDP_PORT|meshcop Commissioner UDP Port TLV|
|OT_MESHCOP_TLV_STATE|meshcop State TLV|
|OT_MESHCOP_TLV_JOINER_DTLS|meshcop Joiner DTLS Encapsulation TLV|
|OT_MESHCOP_TLV_JOINER_UDP_PORT|meshcop Joiner UDP Port TLV|
|OT_MESHCOP_TLV_JOINER_IID|meshcop Joiner IID TLV|
|OT_MESHCOP_TLV_JOINER_RLOC|meshcop Joiner Router Locator TLV|
|OT_MESHCOP_TLV_JOINER_ROUTER_KEK|meshcop Joiner Router KEK TLV|
|OT_MESHCOP_TLV_DURATION|meshcop Duration TLV|
|OT_MESHCOP_TLV_PROVISIONING_URL|meshcop Provisioning URL TLV|
|OT_MESHCOP_TLV_VENDOR_NAME_TLV|meshcop Vendor Name TLV|
|OT_MESHCOP_TLV_VENDOR_MODEL_TLV|meshcop Vendor Model TLV|
|OT_MESHCOP_TLV_VENDOR_SW_VERSION_TLV|meshcop Vendor SW Version TLV|
|OT_MESHCOP_TLV_VENDOR_DATA_TLV|meshcop Vendor Data TLV|
|OT_MESHCOP_TLV_VENDOR_STACK_VERSION_TLV|meshcop Vendor Stack Version TLV|
|OT_MESHCOP_TLV_UDP_ENCAPSULATION_TLV|meshcop UDP encapsulation TLV|
|OT_MESHCOP_TLV_IPV6_ADDRESS_TLV|meshcop IPv6 address TLV|
|OT_MESHCOP_TLV_PENDINGTIMESTAMP|meshcop Pending Timestamp TLV|
|OT_MESHCOP_TLV_DELAYTIMER|meshcop Delay Timer TLV|
|OT_MESHCOP_TLV_CHANNELMASK|meshcop Channel Mask TLV|
|OT_MESHCOP_TLV_COUNT|meshcop Count TLV|
|OT_MESHCOP_TLV_PERIOD|meshcop Period TLV|
|OT_MESHCOP_TLV_SCAN_DURATION|meshcop Scan Duration TLV|
|OT_MESHCOP_TLV_ENERGY_LIST|meshcop Energy List TLV|
|OT_MESHCOP_TLV_THREAD_DOMAIN_NAME|meshcop Thread Domain Name TLV|
|OT_MESHCOP_TLV_WAKEUP_CHANNEL|meshcop Wake-up Channel TLV|
|OT_MESHCOP_TLV_ADMITTER_STATE|meshcop Admitter State TLV|
|OT_MESHCOP_TLV_ENROLLER_ID|meshcop Enroller ID TLV|
|OT_MESHCOP_TLV_ENROLLER_MODE|meshcop Enroller Mode TLV|
|OT_MESHCOP_TLV_DISCOVERYREQUEST|meshcop Discovery Request TLV|
|OT_MESHCOP_TLV_DISCOVERYRESPONSE|meshcop Discovery Response TLV|
|OT_MESHCOP_TLV_JOINERADVERTISEMENT|meshcop Joiner Advertisement TLV (experimental)|

###### Typedefs

###### otNetworkKey (heading level 7)

`typedef struct otNetworkKey otNetworkKey`

**Description:**

Represents a Thread Network Key.

###### otNetworkKeyRef (heading level 7)

`typedef otCryptoKeyRef otNetworkKeyRef`

**Description:**

This datatype represents KeyRef to NetworkKey.

**Details:**

Reference to Key

###### otNetworkName (heading level 7)

`typedef struct otNetworkName otNetworkName`

**Description:**

Represents a Network Name.

**Details:**

The `otNetworkName` is a null terminated C string (i.e., `m8` char array MUST end with null char `\0`).

###### otExtendedPanId (heading level 7)

`typedef struct otExtendedPanId otExtendedPanId`

**Description:**

Represents an Extended PAN ID.

###### otMeshLocalPrefix (heading level 7)

`typedef otIp6NetworkPrefix otMeshLocalPrefix`

**Description:**

Represents a Mesh Local Prefix.

###### otPskc (heading level 7)

`typedef struct otPskc otPskc`

**Description:**

Represents a PSKc.

###### otPskcRef (heading level 7)

`typedef otCryptoKeyRef otPskcRef`

**Description:**

This datatype represents KeyRef to PSKc.

**Details:**

Reference to Key

###### otSecurityPolicy (heading level 7)

`typedef struct otSecurityPolicy otSecurityPolicy`

**Description:**

Represent Security Policy.

###### otChannelMask (heading level 7)

`typedef uint32_t otChannelMask`

**Description:**

Represents a Channel Mask.

**Details:**

The least significant bit (LSB), also referred to as bit 0, corresponds to channel number 0, and so on.

###### otOperationalDatasetComponents (heading level 7)

`typedef struct otOperationalDatasetComponents otOperationalDatasetComponents`

**Description:**

Represents presence of different components in Active or Pending Operational Dataset.

###### otTimestamp (heading level 7)

`typedef struct otTimestamp otTimestamp`

**Description:**

Represents a Thread Dataset timestamp component.

###### otOperationalDataset (heading level 7)

`typedef struct otOperationalDataset otOperationalDataset`

**Description:**

Represents an Active or Pending Operational Dataset.

**Details:**

Components in Dataset are optional. `mComponents` structure specifies which components are present in the Dataset.

###### otOperationalDatasetTlvs (heading level 7)

`typedef struct otOperationalDatasetTlvs otOperationalDatasetTlvs`

**Description:**

Represents an Active or Pending Operational Dataset.

**Details:**

The Operational Dataset is TLV encoded as specified by Thread.

###### otMeshcopTlvType (heading level 7)

`typedef enum otMeshcopTlvType otMeshcopTlvType`

**Description:**

Represents meshcop TLV types.

###### otDatasetMgmtSetCallback (heading level 7)

`typedef void(* otDatasetMgmtSetCallback) (otError aResult, void *aContext)`

**Description:**

Pointer is called when a response to a MGMT_SET request is received or times out.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aResult|A result of the operation.|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### otDatasetUpdaterCallback (heading level 7)

`typedef void(* otDatasetUpdaterCallback) (otError aError, void *aContext)`

**Description:**

This callback function pointer is called when a Dataset update request finishes, reporting success or failure status of the Dataset update request.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aError|The error status. OT_ERROR_NONE indicates successful Dataset update. OT_ERROR_INVALID_STATE indicates failure due invalid state (MLE being disabled). OT_ERROR_ALREADY indicates failure due to another device within network requesting a conflicting Dataset update.|
||[in]|aContext|A pointer to the arbitrary context (provided by user in `otDatasetUpdaterRequestUpdate()`).|

**Details:**

Available when `OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE` is enabled.

###### Variables

###### OT_TOOL_PACKED_END (heading level 7)

```
OT_TOOL_PACKED_BEGIN struct otPskc OT_TOOL_PACKED_END
```

###### Functions

###### otDatasetIsCommissioned (heading level 7)

`bool otDatasetIsCommissioned(otInstance *aInstance)`

**Description:** Indicates whether a valid network is present in the Active Operational Dataset or not.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- TRUE if a valid network is present in the Active Operational Dataset, FALSE otherwise.

###### otDatasetGetActive (heading level 7)

`otError otDatasetGetActive(otInstance *aInstance, otOperationalDataset *aDataset)`

**Description:** Gets the Active Operational Dataset.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otOperationalDataset](ot-operational-dataset) *|[out]|aDataset|A pointer to where the Active Operational Dataset will be placed.|

###### otDatasetGetActiveTlvs (heading level 7)

`otError otDatasetGetActiveTlvs(otInstance *aInstance, otOperationalDatasetTlvs *aDataset)`

**Description:** Gets the Active Operational Dataset.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otOperationalDatasetTlvs](ot-operational-dataset-tlvs) *|[out]|aDataset|A pointer to where the Active Operational Dataset will be placed.|

###### otDatasetSetActive (heading level 7)

`otError otDatasetSetActive(otInstance *aInstance, const otOperationalDataset *aDataset)`

**Description:** Sets the Active Operational Dataset.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otOperationalDataset](ot-operational-dataset) *|[in]|aDataset|A pointer to the Active Operational Dataset.|

If the dataset does not include an Active Timestamp, the dataset is only partially complete.

If Thread is enabled on a device that has a partially complete Active Dataset, the device will attempt to attach to an existing Thread network using any existing information in the dataset. Only the Thread Network Key is needed to attach to a network.

If channel is not included in the dataset, the device will send MLE Announce messages across different channels to find neighbors on other channels.

If the device successfully attaches to a Thread network, the device will then retrieve the full Active Dataset from its Parent. Note that a router-capable device will not transition to the Router or Leader roles until it has a complete Active Dataset.

This function consistently returns `OT_ERROR_NONE` and can effectively be treated as having a `void` return type. Previously, other errors (e.g., `OT_ERROR_NOT_IMPLEMENTED`) were allowed for legacy reasons. However, as non-volatile storage is now mandatory for Thread operation, any failure to save the dataset will trigger an assertion. The `otError` return type is retained for backward compatibility.

###### otDatasetSetActiveTlvs (heading level 7)

`otError otDatasetSetActiveTlvs(otInstance *aInstance, const otOperationalDatasetTlvs *aDataset)`

**Description:** Sets the Active Operational Dataset.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otOperationalDatasetTlvs](ot-operational-dataset-tlvs) *|[in]|aDataset|A pointer to the Active Operational Dataset.|

If the dataset does not include an Active Timestamp, the dataset is only partially complete.

If Thread is enabled on a device that has a partially complete Active Dataset, the device will attempt to attach to an existing Thread network using any existing information in the dataset. Only the Thread Network Key is needed to attach to a network.

If channel is not included in the dataset, the device will send MLE Announce messages across different channels to find neighbors on other channels.

If the device successfully attaches to a Thread network, the device will then retrieve the full Active Dataset from its Parent. Note that a router-capable device will not transition to the Router or Leader roles until it has a complete Active Dataset.

###### otDatasetGetPending (heading level 7)

`otError otDatasetGetPending(otInstance *aInstance, otOperationalDataset *aDataset)`

**Description:** Gets the Pending Operational Dataset.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otOperationalDataset](ot-operational-dataset) *|[out]|aDataset|A pointer to where the Pending Operational Dataset will be placed.|

###### otDatasetGetPendingTlvs (heading level 7)

`otError otDatasetGetPendingTlvs(otInstance *aInstance, otOperationalDatasetTlvs *aDataset)`

**Description:** Gets the Pending Operational Dataset.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otOperationalDatasetTlvs](ot-operational-dataset-tlvs) *|[out]|aDataset|A pointer to where the Pending Operational Dataset will be placed.|

###### otDatasetSetPending (heading level 7)

`otError otDatasetSetPending(otInstance *aInstance, const otOperationalDataset *aDataset)`

**Description:** Sets the Pending Operational Dataset.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otOperationalDataset](ot-operational-dataset) *|[in]|aDataset|A pointer to the Pending Operational Dataset.|

This function consistently returns `OT_ERROR_NONE` and can effectively be treated as having a `void` return type. Previously, other errors (e.g., `OT_ERROR_NOT_IMPLEMENTED`) were allowed for legacy reasons. However, as non-volatile storage is now mandatory for Thread operation, any failure to save the dataset will trigger an assertion. The `otError` return type is retained for backward compatibility.

###### otDatasetSetPendingTlvs (heading level 7)

`otError otDatasetSetPendingTlvs(otInstance *aInstance, const otOperationalDatasetTlvs *aDataset)`

**Description:** Sets the Pending Operational Dataset.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otOperationalDatasetTlvs](ot-operational-dataset-tlvs) *|[in]|aDataset|A pointer to the Pending Operational Dataset.|

###### otDatasetSendMgmtActiveGet (heading level 7)

`otError otDatasetSendMgmtActiveGet(otInstance *aInstance, const otOperationalDatasetComponents *aDatasetComponents, const uint8_t *aTlvTypes, uint8_t aLength, const otIp6Address *aAddress)`

**Description:** Sends MGMT_ACTIVE_GET.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otOperationalDatasetComponents](ot-operational-dataset-components) *|[in]|aDatasetComponents|A pointer to a Dataset Components structure specifying which components to request.|
|const uint8_t *|[in]|aTlvTypes|A pointer to array containing additional raw TLV types to be requested.|
|uint8_t|[in]|aLength|The length of `aTlvTypes`.|
|const [otIp6Address](ot-ip6-address) *|[in]|aAddress|A pointer to the IPv6 destination, if it is NULL, will use Leader ALOC as default.|

###### otDatasetSendMgmtActiveSet (heading level 7)

`otError otDatasetSendMgmtActiveSet(otInstance *aInstance, const otOperationalDataset *aDataset, const uint8_t *aTlvs, uint8_t aLength, otDatasetMgmtSetCallback aCallback, void *aContext)`

**Description:** Sends MGMT_ACTIVE_SET.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otOperationalDataset](ot-operational-dataset) *|[in]|aDataset|A pointer to operational dataset.|
|const uint8_t *|[in]|aTlvs|A pointer to TLVs.|
|uint8_t|[in]|aLength|The length of TLVs.|
|[otDatasetMgmtSetCallback](api-operational-dataset#ot-dataset-mgmt-set-callback)|[in]|aCallback|A pointer to a function that is called on response reception or timeout.|
|void *|[in]|aContext|A pointer to application-specific context for `aCallback`.|

###### otDatasetSendMgmtPendingGet (heading level 7)

`otError otDatasetSendMgmtPendingGet(otInstance *aInstance, const otOperationalDatasetComponents *aDatasetComponents, const uint8_t *aTlvTypes, uint8_t aLength, const otIp6Address *aAddress)`

**Description:** Sends MGMT_PENDING_GET.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otOperationalDatasetComponents](ot-operational-dataset-components) *|[in]|aDatasetComponents|A pointer to a Dataset Components structure specifying which components to request.|
|const uint8_t *|[in]|aTlvTypes|A pointer to array containing additional raw TLV types to be requested.|
|uint8_t|[in]|aLength|The length of `aTlvTypes`.|
|const [otIp6Address](ot-ip6-address) *|[in]|aAddress|A pointer to the IPv6 destination, if it is NULL, will use Leader ALOC as default.|

###### otDatasetSendMgmtPendingSet (heading level 7)

`otError otDatasetSendMgmtPendingSet(otInstance *aInstance, const otOperationalDataset *aDataset, const uint8_t *aTlvs, uint8_t aLength, otDatasetMgmtSetCallback aCallback, void *aContext)`

**Description:** Sends MGMT_PENDING_SET.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otOperationalDataset](ot-operational-dataset) *|[in]|aDataset|A pointer to operational dataset.|
|const uint8_t *|[in]|aTlvs|A pointer to TLVs.|
|uint8_t|[in]|aLength|The length of TLVs.|
|[otDatasetMgmtSetCallback](api-operational-dataset#ot-dataset-mgmt-set-callback)|[in]|aCallback|A pointer to a function that is called on response reception or timeout.|
|void *|[in]|aContext|A pointer to application-specific context for `aCallback`.|

###### otDatasetGeneratePskc (heading level 7)

`otError otDatasetGeneratePskc(const char *aPassPhrase, const otNetworkName *aNetworkName, const otExtendedPanId *aExtPanId, otPskc *aPskc)`

**Description:** Generates PSKc from a given pass-phrase, network name, and extended PAN ID.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aPassPhrase|The commissioning pass-phrase.|
|const [otNetworkName](ot-network-name) *|[in]|aNetworkName|The network name for PSKc computation.|
|const [otExtendedPanId](ot-extended-pan-id) *|[in]|aExtPanId|The extended PAN ID for PSKc computation.|
|[otPskc](ot-pskc) *|[out]|aPskc|A pointer to variable to output the generated PSKc.|

PSKc is used to establish the Commissioner Session.

###### otNetworkNameFromString (heading level 7)

`otError otNetworkNameFromString(otNetworkName *aNetworkName, const char *aNameString)`

**Description:** Sets an `otNetworkName` instance from a given null terminated C string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otNetworkName](ot-network-name) *|[out]|aNetworkName|A pointer to the `otNetworkName` to set.|
|const char *|[in]|aNameString|A name C string.|

`aNameString` must follow UTF-8 encoding and the Network Name length must not be longer than `OT_NETWORK_NAME_MAX_SIZE`.

###### otDatasetIsValid (heading level 7)

`bool otDatasetIsValid(const otOperationalDatasetTlvs *aDatasetTlvs, bool aActive)`

**Description:** Indicates whether or not the given Operational Dataset TLVs is a valid Active or Pending Dataset.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otOperationalDatasetTlvs](ot-operational-dataset-tlvs) *|[in]|aDatasetTlvs|A pointer to dataset TLVs.|
|bool|[in]|aActive|TRUE for Active Dataset, FALSE for Pending Dataset.|

A valid Active Dataset MUST contain all the required TLVs (Active Timestamp, Channel, Channel Mask, Extended PAN ID, Mesh-Local Prefix, Network Key, Network Name, PAN ID, PSKc, and Security Policy).

A valid Pending Dataset MUST contain all the required TLVs for an Active Dataset and additionally MUST contain Pending Timestamp and Delay Timer TLVs.

This method also checks whether there are duplicated TLVs or the TLVs are not well-formed in the `aDatasetTlvs`.

**Returns**

- TRUE if `aDatasetTlvs` is a valid Dataset, FALSE otherwise.

###### otDatasetParseTlvs (heading level 7)

`otError otDatasetParseTlvs(const otOperationalDatasetTlvs *aDatasetTlvs, otOperationalDataset *aDataset)`

**Description:** Parses an Operational Dataset from a given `otOperationalDatasetTlvs`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otOperationalDatasetTlvs](ot-operational-dataset-tlvs) *|[in]|aDatasetTlvs|A pointer to dataset TLVs.|
|[otOperationalDataset](ot-operational-dataset) *|[out]|aDataset|A pointer to where the dataset will be placed.|

###### otDatasetTlvsCompare (heading level 7)

`bool otDatasetTlvsCompare(const otOperationalDatasetTlvs *aDatasetTlvsA, const otOperationalDatasetTlvs *aDatasetTlvsB)`

**Description:** Compares two Operational Dataset TLVs to determine if they contain the same set of TLVs.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otOperationalDatasetTlvs](ot-operational-dataset-tlvs) *|[in]|aDatasetTlvsA|A pointer to dataset TLVs A. Must not be NULL.|
|const [otOperationalDatasetTlvs](ot-operational-dataset-tlvs) *|[in]|aDatasetTlvsB|A pointer to dataset TLVs B. Must not be NULL.|

This function performs a deep comparison. It parses both `aDatasetTlvsA` and `aDatasetTlvsB` and checks if they contain the exact same set of TLVs (same type and same value). The order of TLVs within the `otOperationalDatasetTlvs` does not matter.

**Returns**

- TRUE if the two Operational Dataset TLVs match, FALSE otherwise (e.g., if any TLV differs, is missing, or if the TLVs are not well-formed).

###### otDatasetConvertToTlvs (heading level 7)

`void otDatasetConvertToTlvs(const otOperationalDataset *aDataset, otOperationalDatasetTlvs *aDatasetTlvs)`

**Description:** Converts a given Operational Dataset to `otOperationalDatasetTlvs`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otOperationalDataset](ot-operational-dataset) *|[in]|aDataset|An Operational dataset to convert to TLVs.|
|[otOperationalDatasetTlvs](ot-operational-dataset-tlvs) *|[out]|aDatasetTlvs|A pointer to dataset TLVs to return the result.|

###### otDatasetUpdateTlvs (heading level 7)

`otError otDatasetUpdateTlvs(const otOperationalDataset *aDataset, otOperationalDatasetTlvs *aDatasetTlvs)`

**Description:** Updates a given Operational Dataset.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otOperationalDataset](ot-operational-dataset) *|[in]|aDataset|Specifies the set of types and values to update.|
|[otOperationalDatasetTlvs](ot-operational-dataset-tlvs) *|[inout]|aDatasetTlvs|A pointer to dataset TLVs to update.|

`aDataset` contains the fields to be updated and their new value.

###### otDatasetCreateNewNetwork (heading level 7)

`otError otDatasetCreateNewNetwork(otInstance *aInstance, otOperationalDataset *aDataset)`

**Description:** For FTD only, creates a new Operational Dataset to use when forming a new network.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otOperationalDataset](ot-operational-dataset) *|[out]|aDataset|The Operational Dataset.|

###### otDatasetGetDelayTimerMinimal (heading level 7)

`uint32_t otDatasetGetDelayTimerMinimal(otInstance *aInstance)`

**Description:** For FTD only, gets a minimal delay timer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otDatasetSetDelayTimerMinimal (heading level 7)

`otError otDatasetSetDelayTimerMinimal(otInstance *aInstance, uint32_t aDelayTimerMinimal)`

**Description:** For FTD only, sets a minimal delay timer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aDelayTimerMinimal|The value of minimal delay timer (in ms).|

**Note**

- This API is reserved for testing and demo purposes only. Changing settings with this API will render a production application non-compliant with the Thread Specification.

###### otDatasetUpdaterRequestUpdate (heading level 7)

`otError otDatasetUpdaterRequestUpdate(otInstance *aInstance, const otOperationalDataset *aDataset, otDatasetUpdaterCallback aCallback, void *aContext)`

**Description:** Requests an update to Operational Dataset.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otOperationalDataset](ot-operational-dataset) *|[in]|aDataset|A pointer to the Dataset containing the fields to change.|
|[otDatasetUpdaterCallback](api-operational-dataset#ot-dataset-updater-callback)|[in]|aCallback|A callback to indicate when Dataset update request finishes.|
|void *|[in]|aContext|An arbitrary context passed to callback.|

Available when `OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE` is enabled.

`aDataset` should contain the fields to be updated and their new value. It must not contain Active or Pending Timestamp fields. The Delay field is optional, if not provided a default value (1000 ms) would be used.

###### otDatasetUpdaterCancelUpdate (heading level 7)

`void otDatasetUpdaterCancelUpdate(otInstance *aInstance)`

**Description:** Cancels an ongoing (if any) Operational Dataset update request.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Available when `OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE` is enabled.

###### otDatasetUpdaterIsUpdateOngoing (heading level 7)

`bool otDatasetUpdaterIsUpdateOngoing(otInstance *aInstance)`

**Description:** Indicates whether there is an ongoing Operation Dataset update request.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Available when `OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE` is enabled.

###### Macros

`#define OT_NETWORK_KEY_SIZE 16`

**Description**: Size of the Thread Network Key (bytes)

`#define OT_NETWORK_NAME_MAX_SIZE 16`

**Description**: Maximum size of the Thread Network Name field (bytes)

`#define OT_EXT_PAN_ID_SIZE 8`

**Description**: Size of a Thread PAN ID (bytes)

`#define OT_MESH_LOCAL_PREFIX_SIZE OT_IP6_PREFIX_SIZE`

**Description**: Size of the Mesh Local Prefix (bytes)

`#define OT_PSKC_MAX_SIZE 16`

**Description**: Maximum size of the PSKc (bytes)

`#define OT_CHANNEL_1_MASK (1 << 1)`

**Description**: Channel 1.

`#define OT_CHANNEL_2_MASK (1 << 2)`

**Description**: Channel 2.

`#define OT_CHANNEL_3_MASK (1 << 3)`

**Description**: Channel 3.

`#define OT_CHANNEL_4_MASK (1 << 4)`

**Description**: Channel 4.

`#define OT_CHANNEL_5_MASK (1 << 5)`

**Description**: Channel 5.

`#define OT_CHANNEL_6_MASK (1 << 6)`

**Description**: Channel 6.

`#define OT_CHANNEL_7_MASK (1 << 7)`

**Description**: Channel 7.

`#define OT_CHANNEL_8_MASK (1 << 8)`

**Description**: Channel 8.

`#define OT_CHANNEL_9_MASK (1 << 9)`

**Description**: Channel 9.

`#define OT_CHANNEL_10_MASK (1 << 10)`

**Description**: Channel 10.

`#define OT_CHANNEL_11_MASK (1 << 11)`

**Description**: Channel 11.

`#define OT_CHANNEL_12_MASK (1 << 12)`

**Description**: Channel 12.

`#define OT_CHANNEL_13_MASK (1 << 13)`

**Description**: Channel 13.

`#define OT_CHANNEL_14_MASK (1 << 14)`

**Description**: Channel 14.

`#define OT_CHANNEL_15_MASK (1 << 15)`

**Description**: Channel 15.

`#define OT_CHANNEL_16_MASK (1 << 16)`

**Description**: Channel 16.

`#define OT_CHANNEL_17_MASK (1 << 17)`

**Description**: Channel 17.

`#define OT_CHANNEL_18_MASK (1 << 18)`

**Description**: Channel 18.

`#define OT_CHANNEL_19_MASK (1 << 19)`

**Description**: Channel 19.

`#define OT_CHANNEL_20_MASK (1 << 20)`

**Description**: Channel 20.

`#define OT_CHANNEL_21_MASK (1 << 21)`

**Description**: Channel 21.

`#define OT_CHANNEL_22_MASK (1 << 22)`

**Description**: Channel 22.

`#define OT_CHANNEL_23_MASK (1 << 23)`

**Description**: Channel 23.

`#define OT_CHANNEL_24_MASK (1 << 24)`

**Description**: Channel 24.

`#define OT_CHANNEL_25_MASK (1 << 25)`

**Description**: Channel 25.

`#define OT_CHANNEL_26_MASK (1 << 26)`

**Description**: Channel 26.

`#define OT_OPERATIONAL_DATASET_MAX_LENGTH 254`

**Description**: Maximum length of Operational Dataset in bytes.

Represents a Thread Network Key. 

###### Public Attributes (heading level 7)

###### m8 (heading level 8)

```
uint8_t otNetworkKey::m8[OT_NETWORK_KEY_SIZE]
```

**Description:** Byte values.

Represents a Network Name. 

The `otNetworkName` is a null terminated C string (i.e., `m8` char array MUST end with null char `\0`). 

###### Public Attributes (heading level 7)

###### m8 (heading level 8)

```
char otNetworkName::m8[OT_NETWORK_NAME_MAX_SIZE+1]
```

**Description:** Byte values. The `+ 1` is for null char.

Represents an Extended PAN ID. 

###### Public Attributes (heading level 7)

###### m8 (heading level 8)

```
uint8_t otExtendedPanId::m8[OT_EXT_PAN_ID_SIZE]
```

**Description:** Byte values.

Represents PSKc. 

###### Public Attributes (heading level 7)

###### m8 (heading level 8)

```
uint8_t otPskc::m8[OT_PSKC_MAX_SIZE]
```

**Description:** Byte values.

Represent Security Policy. 

###### Public Attributes (heading level 7)

###### mRotationTime (heading level 8)

```
uint16_t otSecurityPolicy::mRotationTime
```

**Description:** The value for thrKeyRotation in units of hours.

###### mObtainNetworkKeyEnabled (heading level 8)

```
bool otSecurityPolicy::mObtainNetworkKeyEnabled
```

**Description:** Obtaining the Network Key for out-of-band commissioning is enabled.

###### mNativeCommissioningEnabled (heading level 8)

```
bool otSecurityPolicy::mNativeCommissioningEnabled
```

**Description:** Native Commissioning using PSKc is allowed.

###### mRoutersEnabled (heading level 8)

```
bool otSecurityPolicy::mRoutersEnabled
```

**Description:** Thread 1.0/1.1.x Routers are enabled.

###### mExternalCommissioningEnabled (heading level 8)

```
bool otSecurityPolicy::mExternalCommissioningEnabled
```

**Description:** External Commissioner authentication is allowed.

###### mCommercialCommissioningEnabled (heading level 8)

```
bool otSecurityPolicy::mCommercialCommissioningEnabled
```

**Description:** Commercial Commissioning is enabled.

###### mAutonomousEnrollmentEnabled (heading level 8)

```
bool otSecurityPolicy::mAutonomousEnrollmentEnabled
```

**Description:** Autonomous Enrollment is enabled.

###### mNetworkKeyProvisioningEnabled (heading level 8)

```
bool otSecurityPolicy::mNetworkKeyProvisioningEnabled
```

**Description:** Network Key Provisioning is enabled.

###### mTobleLinkEnabled (heading level 8)

```
bool otSecurityPolicy::mTobleLinkEnabled
```

**Description:** ToBLE link is enabled.

###### mNonCcmRoutersEnabled (heading level 8)

```
bool otSecurityPolicy::mNonCcmRoutersEnabled
```

**Description:** Non-CCM Routers enabled.

###### mVersionThresholdForRouting (heading level 8)

```
uint8_t otSecurityPolicy::mVersionThresholdForRouting
```

**Description:** Version-threshold for Routing.

Represents presence of different components in Active or Pending Operational Dataset. 

###### Public Attributes (heading level 7)

###### mIsActiveTimestampPresent (heading level 8)

```
bool otOperationalDatasetComponents::mIsActiveTimestampPresent
```

**Description:** TRUE if Active Timestamp is present, FALSE otherwise.

###### mIsPendingTimestampPresent (heading level 8)

```
bool otOperationalDatasetComponents::mIsPendingTimestampPresent
```

**Description:** TRUE if Pending Timestamp is present, FALSE otherwise.

###### mIsNetworkKeyPresent (heading level 8)

```
bool otOperationalDatasetComponents::mIsNetworkKeyPresent
```

**Description:** TRUE if Network Key is present, FALSE otherwise.

###### mIsNetworkNamePresent (heading level 8)

```
bool otOperationalDatasetComponents::mIsNetworkNamePresent
```

**Description:** TRUE if Network Name is present, FALSE otherwise.

###### mIsExtendedPanIdPresent (heading level 8)

```
bool otOperationalDatasetComponents::mIsExtendedPanIdPresent
```

**Description:** TRUE if Extended PAN ID is present, FALSE otherwise.

###### mIsMeshLocalPrefixPresent (heading level 8)

```
bool otOperationalDatasetComponents::mIsMeshLocalPrefixPresent
```

**Description:** TRUE if Mesh Local Prefix is present, FALSE otherwise.

###### mIsDelayPresent (heading level 8)

```
bool otOperationalDatasetComponents::mIsDelayPresent
```

**Description:** TRUE if Delay Timer is present, FALSE otherwise.

###### mIsPanIdPresent (heading level 8)

```
bool otOperationalDatasetComponents::mIsPanIdPresent
```

**Description:** TRUE if PAN ID is present, FALSE otherwise.

###### mIsChannelPresent (heading level 8)

```
bool otOperationalDatasetComponents::mIsChannelPresent
```

**Description:** TRUE if Channel is present, FALSE otherwise.

###### mIsPskcPresent (heading level 8)

```
bool otOperationalDatasetComponents::mIsPskcPresent
```

**Description:** TRUE if PSKc is present, FALSE otherwise.

###### mIsSecurityPolicyPresent (heading level 8)

```
bool otOperationalDatasetComponents::mIsSecurityPolicyPresent
```

**Description:** TRUE if Security Policy is present, FALSE otherwise.

###### mIsChannelMaskPresent (heading level 8)

```
bool otOperationalDatasetComponents::mIsChannelMaskPresent
```

**Description:** TRUE if Channel Mask is present, FALSE otherwise.

###### mIsWakeupChannelPresent (heading level 8)

```
bool otOperationalDatasetComponents::mIsWakeupChannelPresent
```

**Description:** TRUE if Wake-up Channel is present, FALSE otherwise.

Represents a Thread Dataset timestamp component. 

###### Public Attributes (heading level 7)

###### mSeconds (heading level 8)

```
uint64_t otTimestamp::mSeconds
```

###### mTicks (heading level 8)

```
uint16_t otTimestamp::mTicks
```

###### mAuthoritative (heading level 8)

```
bool otTimestamp::mAuthoritative
```

Represents an Active or Pending Operational Dataset. 

Components in Dataset are optional. `mComponents` structure specifies which components are present in the Dataset. 

###### Public Attributes (heading level 7)

###### mActiveTimestamp (heading level 8)

```
otTimestamp otOperationalDataset::mActiveTimestamp
```

**Description:** Active Timestamp.

###### mPendingTimestamp (heading level 8)

```
otTimestamp otOperationalDataset::mPendingTimestamp
```

**Description:** Pending Timestamp.

###### mNetworkKey (heading level 8)

```
otNetworkKey otOperationalDataset::mNetworkKey
```

**Description:** Network Key.

###### mNetworkName (heading level 8)

```
otNetworkName otOperationalDataset::mNetworkName
```

**Description:** Network Name.

###### mExtendedPanId (heading level 8)

```
otExtendedPanId otOperationalDataset::mExtendedPanId
```

**Description:** Extended PAN ID.

###### mMeshLocalPrefix (heading level 8)

```
otMeshLocalPrefix otOperationalDataset::mMeshLocalPrefix
```

**Description:** Mesh Local Prefix.

###### mDelay (heading level 8)

```
uint32_t otOperationalDataset::mDelay
```

**Description:** Delay Timer.

###### mPanId (heading level 8)

```
otPanId otOperationalDataset::mPanId
```

**Description:** PAN ID.

###### mChannel (heading level 8)

```
uint16_t otOperationalDataset::mChannel
```

**Description:** Channel.

###### mWakeupChannel (heading level 8)

```
uint16_t otOperationalDataset::mWakeupChannel
```

**Description:** Wake-up Channel.

###### mPskc (heading level 8)

```
otPskc otOperationalDataset::mPskc
```

**Description:** PSKc.

###### mSecurityPolicy (heading level 8)

```
otSecurityPolicy otOperationalDataset::mSecurityPolicy
```

**Description:** Security Policy.

###### mChannelMask (heading level 8)

```
otChannelMask otOperationalDataset::mChannelMask
```

**Description:** Channel Mask.

###### mComponents (heading level 8)

```
otOperationalDatasetComponents otOperationalDataset::mComponents
```

**Description:** Specifies which components are set in the Dataset.

Represents an Active or Pending Operational Dataset. 

The Operational Dataset is TLV encoded as specified by Thread. 

###### Public Attributes (heading level 7)

###### mTlvs (heading level 8)

```
uint8_t otOperationalDatasetTlvs::mTlvs[OT_OPERATIONAL_DATASET_MAX_LENGTH]
```

**Description:** Operational Dataset TLVs.

###### mLength (heading level 8)

```
uint8_t otOperationalDatasetTlvs::mLength
```

**Description:** Size of Operational Dataset in bytes.

##### Router/Leader

This module includes functions for Thread Routers and Leaders. 

###### Modules

[otChildInfo](ot-child-info)

[otCacheEntryInfo](ot-cache-entry-info)

[otCacheEntryIterator](ot-cache-entry-iterator)

[otDeviceProperties](ot-device-properties)

[otNeighborTableEntryInfo](ot-neighbor-table-entry-info)

###### Enumerations

###### otCacheEntryState (heading level 7)

```
enum otCacheEntryState {
    OT_CACHE_ENTRY_STATE_CACHED = 0
    OT_CACHE_ENTRY_STATE_SNOOPED = 1
    OT_CACHE_ENTRY_STATE_QUERY = 2
    OT_CACHE_ENTRY_STATE_RETRY_QUERY = 3
}
```

**Description:**

Defines the EID cache entry state.

**Enumerator:**

|   |   |
|---|---|
|OT_CACHE_ENTRY_STATE_CACHED||
|OT_CACHE_ENTRY_STATE_SNOOPED||
|OT_CACHE_ENTRY_STATE_QUERY||
|OT_CACHE_ENTRY_STATE_RETRY_QUERY||

###### otPowerSupply (heading level 7)

```
enum otPowerSupply {
    OT_POWER_SUPPLY_BATTERY = 0
    OT_POWER_SUPPLY_EXTERNAL = 1
    OT_POWER_SUPPLY_EXTERNAL_STABLE = 2
    OT_POWER_SUPPLY_EXTERNAL_UNSTABLE = 3
}
```

**Description:**

Represents the power supply property on a device.

**Details:**

This is used as a property in `otDeviceProperties` to calculate the leader weight.

**Enumerator:**

|   |   |
|---|---|
|OT_POWER_SUPPLY_BATTERY|Battery powered.|
|OT_POWER_SUPPLY_EXTERNAL|Externally powered (mains-powered).|
|OT_POWER_SUPPLY_EXTERNAL_STABLE|Stable external power with a battery backup or UPS.|
|OT_POWER_SUPPLY_EXTERNAL_UNSTABLE|Potentially unstable ext power (e.g. light bulb powered via a switch).|

###### otNeighborTableEvent (heading level 7)

```
enum otNeighborTableEvent {
    OT_NEIGHBOR_TABLE_EVENT_CHILD_ADDED
    OT_NEIGHBOR_TABLE_EVENT_CHILD_REMOVED
    OT_NEIGHBOR_TABLE_EVENT_CHILD_MODE_CHANGED
    OT_NEIGHBOR_TABLE_EVENT_ROUTER_ADDED
    OT_NEIGHBOR_TABLE_EVENT_ROUTER_REMOVED
}
```

**Description:**

Defines the constants used in `otNeighborTableCallback` to indicate changes in neighbor table.

**Enumerator:**

|   |   |
|---|---|
|OT_NEIGHBOR_TABLE_EVENT_CHILD_ADDED|A child is being added.|
|OT_NEIGHBOR_TABLE_EVENT_CHILD_REMOVED|A child is being removed.|
|OT_NEIGHBOR_TABLE_EVENT_CHILD_MODE_CHANGED|An existing child's mode is changed.|
|OT_NEIGHBOR_TABLE_EVENT_ROUTER_ADDED|A router is being added.|
|OT_NEIGHBOR_TABLE_EVENT_ROUTER_REMOVED|A router is being removed.|

###### Typedefs

###### otChildIp6AddressIterator (heading level 7)

`typedef uint16_t otChildIp6AddressIterator`

**Description:**

Used to iterate through IPv6 addresses of a Thread Child entry.

###### otCacheEntryState (heading level 7)

`typedef enum otCacheEntryState otCacheEntryState`

**Description:**

Defines the EID cache entry state.

###### otCacheEntryInfo (heading level 7)

`typedef struct otCacheEntryInfo otCacheEntryInfo`

**Description:**

Represents an EID cache entry.

###### otCacheEntryIterator (heading level 7)

`typedef struct otCacheEntryIterator otCacheEntryIterator`

**Description:**

Represents an iterator used for iterating through the EID cache table entries.

**Details:**

To initialize the iterator and start from the first entry in the cache table, set all its fields in the structure to zero (e.g., `memset` the iterator to zero).

###### otDeviceProperties (heading level 7)

`typedef struct otDeviceProperties otDeviceProperties`

**Description:**

Represents the device properties which are used for calculating the local leader weight on a device.

**Details:**

The parameters are set based on device's capability, whether acting as border router, its power supply config, etc.

`mIsUnstable` indicates operational stability of device and is determined via a vendor specific mechanism. It can include the following cases:

- Device internally detects that it loses external power supply more often than usual. What is usual is determined by the vendor.
- Device internally detects that it reboots more often than usual. What is usual is determined by the vendor.

###### otNeighborTableCallback (heading level 7)

`typedef void(* otNeighborTableCallback) (otNeighborTableEvent aEvent, const otNeighborTableEntryInfo *aEntryInfo)`

**Description:**

Pointer is called to notify that there is a change in the neighbor table.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aEvent|A event flag.|
||[in]|aEntryInfo|A pointer to table entry info.|

**Details:**

###### Functions

###### otThreadGetMaxAllowedChildren (heading level 7)

`uint16_t otThreadGetMaxAllowedChildren(otInstance *aInstance)`

**Description:** Gets the maximum number of children currently allowed.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The maximum number of children currently allowed.

**See Also**

- [otThreadSetMaxAllowedChildren](api-thread-router#ot-thread-set-max-allowed-children)

###### otThreadSetMaxAllowedChildren (heading level 7)

`otError otThreadSetMaxAllowedChildren(otInstance *aInstance, uint16_t aMaxChildren)`

**Description:** Sets the maximum number of children currently allowed.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aMaxChildren|The maximum allowed children.|

This parameter can only be set when Thread protocol operation has been stopped.

**See Also**

- [otThreadGetMaxAllowedChildren](api-thread-router#ot-thread-get-max-allowed-children)

###### otThreadIsRouterEligible (heading level 7)

`bool otThreadIsRouterEligible(otInstance *aInstance)`

**Description:** Indicates whether or not the device is allowed to take router or leader roles.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

A device is allowed to become a router if it is a Full Thread Device (FTD), is currently configured to be router-eligible (see `otThreadSetRouterEligible(true)`), and the active Security Policy permits routers.

###### otThreadSetRouterEligible (heading level 7)

`otError otThreadSetRouterEligible(otInstance *aInstance, bool aEligible)`

**Description:** Sets whether or not the device is router-eligible.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEligible|TRUE to configure the device as router-eligible, FALSE otherwise.|

If `aEligible` is false and the device is currently operating as a router, this call will cause the device to detach and attempt to reattach as a child.

###### otThreadSetPreferredRouterId (heading level 7)

`otError otThreadSetPreferredRouterId(otInstance *aInstance, uint8_t aRouterId)`

**Description:** Sets the preferred Router Id.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aRouterId|The preferred Router Id.|

Requires `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE`.

Upon becoming a router/leader the node attempts to use this Router Id. If the preferred Router Id is not set or if it can not be used, a randomly generated router id is picked. This property can be set only when the device role is either detached or disabled.

**Note**

- This API is reserved for testing and demo purposes only. Changing settings with this API will render a production application non-compliant with the Thread Specification.

###### otThreadGetDeviceProperties (heading level 7)

`const otDeviceProperties * otThreadGetDeviceProperties(otInstance *aInstance)`

**Description:** Get the current device properties.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

Requires `OPENTHREAD_CONFIG_MLE_DEVICE_PROPERTY_LEADER_WEIGHT_ENABLE`.

**Returns**

- The device properties `otDeviceProperties`.

###### otThreadSetDeviceProperties (heading level 7)

`void otThreadSetDeviceProperties(otInstance *aInstance, const otDeviceProperties *aDeviceProperties)`

**Description:** Set the device properties which are then used to determine and set the Leader Weight.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otDeviceProperties](ot-device-properties) *|[in]|aDeviceProperties|The device properties.|

Requires `OPENTHREAD_CONFIG_MLE_DEVICE_PROPERTY_LEADER_WEIGHT_ENABLE`.

###### otThreadGetLocalLeaderWeight (heading level 7)

`uint8_t otThreadGetLocalLeaderWeight(otInstance *aInstance)`

**Description:** Gets the Thread Leader Weight used when operating in the Leader role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The Thread Leader Weight value.

**See Also**

- otThreadSetLeaderWeight
- [otThreadSetDeviceProperties](api-thread-router#ot-thread-set-device-properties)

###### otThreadSetLocalLeaderWeight (heading level 7)

`void otThreadSetLocalLeaderWeight(otInstance *aInstance, uint8_t aWeight)`

**Description:** Sets the Thread Leader Weight used when operating in the Leader role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aWeight|The Thread Leader Weight value.|

Directly sets the Leader Weight to the new value, replacing its previous value (which may have been determined from the current `otDeviceProperties`).

**See Also**

- [otThreadGetLeaderWeight](api-thread-general#ot-thread-get-leader-weight)

###### otThreadGetPreferredLeaderPartitionId (heading level 7)

`uint32_t otThreadGetPreferredLeaderPartitionId(otInstance *aInstance)`

**Description:** Get the preferred Thread Leader Partition Id used when operating in the Leader role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The Thread Leader Partition Id value.

###### otThreadSetPreferredLeaderPartitionId (heading level 7)

`void otThreadSetPreferredLeaderPartitionId(otInstance *aInstance, uint32_t aPartitionId)`

**Description:** Set the preferred Thread Leader Partition Id used when operating in the Leader role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aPartitionId|The Thread Leader Partition Id value.|

###### otThreadGetJoinerUdpPort (heading level 7)

`uint16_t otThreadGetJoinerUdpPort(otInstance *aInstance)`

**Description:** Gets the Joiner UDP Port.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The Joiner UDP Port number.

**See Also**

- [otThreadSetJoinerUdpPort](api-thread-router#ot-thread-set-joiner-udp-port)

###### otThreadSetJoinerUdpPort (heading level 7)

`otError otThreadSetJoinerUdpPort(otInstance *aInstance, uint16_t aJoinerUdpPort)`

**Description:** Sets the Joiner UDP Port.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aJoinerUdpPort|The Joiner UDP Port number.|

**See Also**

- [otThreadGetJoinerUdpPort](api-thread-router#ot-thread-get-joiner-udp-port)

###### otThreadSetSteeringData (heading level 7)

`void otThreadSetSteeringData(otInstance *aInstance, const otExtAddress *aExtAddress)`

**Description:** Set Steering data out of band.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otExtAddress](ot-ext-address) *|[in]|aExtAddress|Address used to update the steering data. All zeros to clear the steering data (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.|

Configuration option `OPENTHREAD_CONFIG_MLE_STEERING_DATA_SET_OOB_ENABLE` should be set to enable setting of steering data out of band.

###### otThreadGetContextIdReuseDelay (heading level 7)

`uint32_t otThreadGetContextIdReuseDelay(otInstance *aInstance)`

**Description:** Get the CONTEXT_ID_REUSE_DELAY parameter used in the Leader role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The CONTEXT_ID_REUSE_DELAY value.

**See Also**

- [otThreadSetContextIdReuseDelay](api-thread-router#ot-thread-set-context-id-reuse-delay)

###### otThreadSetContextIdReuseDelay (heading level 7)

`void otThreadSetContextIdReuseDelay(otInstance *aInstance, uint32_t aDelay)`

**Description:** Set the CONTEXT_ID_REUSE_DELAY parameter used in the Leader role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aDelay|The CONTEXT_ID_REUSE_DELAY value.|

**Note**

- This API is reserved for testing and demo purposes only. Changing settings with this API will render a production application non-compliant with the Thread Specification.

**See Also**

- [otThreadGetContextIdReuseDelay](api-thread-router#ot-thread-get-context-id-reuse-delay)

###### otThreadGetNetworkIdTimeout (heading level 7)

`uint8_t otThreadGetNetworkIdTimeout(otInstance *aInstance)`

**Description:** Get the `NETWORK_ID_TIMEOUT` parameter.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Note**

- This API is reserved for testing and demo purposes only. Changing settings with this API will render a production application non-compliant with the Thread Specification.

**Returns**

- The `NETWORK_ID_TIMEOUT` value.

**See Also**

- [otThreadSetNetworkIdTimeout](api-thread-router#ot-thread-set-network-id-timeout)

###### otThreadSetNetworkIdTimeout (heading level 7)

`void otThreadSetNetworkIdTimeout(otInstance *aInstance, uint8_t aTimeout)`

**Description:** Set the `NETWORK_ID_TIMEOUT` parameter.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aTimeout|The `NETWORK_ID_TIMEOUT` value.|

**Note**

- This API is reserved for testing and demo purposes only. Changing settings with this API will render a production application non-compliant with the Thread Specification.

**See Also**

- [otThreadGetNetworkIdTimeout](api-thread-router#ot-thread-get-network-id-timeout)

###### otThreadGetRouterUpgradeThreshold (heading level 7)

`uint8_t otThreadGetRouterUpgradeThreshold(otInstance *aInstance)`

**Description:** Get the ROUTER_UPGRADE_THRESHOLD parameter used in the REED role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The ROUTER_UPGRADE_THRESHOLD value.

**See Also**

- [otThreadSetRouterUpgradeThreshold](api-thread-router#ot-thread-set-router-upgrade-threshold)

###### otThreadSetRouterUpgradeThreshold (heading level 7)

`void otThreadSetRouterUpgradeThreshold(otInstance *aInstance, uint8_t aThreshold)`

**Description:** Set the ROUTER_UPGRADE_THRESHOLD parameter used in the Leader role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aThreshold|The ROUTER_UPGRADE_THRESHOLD value.|

**Note**

- This API is reserved for testing and demo purposes only. Changing settings with this API will render a production application non-compliant with the Thread Specification.

**See Also**

- [otThreadGetRouterUpgradeThreshold](api-thread-router#ot-thread-get-router-upgrade-threshold)

###### otThreadGetChildRouterLinks (heading level 7)

`uint8_t otThreadGetChildRouterLinks(otInstance *aInstance)`

**Description:** Get the MLE_CHILD_ROUTER_LINKS parameter used in the REED role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

This parameter specifies the max number of neighboring routers with which the device (as an FED) will try to establish link.

**Returns**

- The MLE_CHILD_ROUTER_LINKS value.

**See Also**

- [otThreadSetChildRouterLinks](api-thread-router#ot-thread-set-child-router-links)

###### otThreadSetChildRouterLinks (heading level 7)

`otError otThreadSetChildRouterLinks(otInstance *aInstance, uint8_t aChildRouterLinks)`

**Description:** Set the MLE_CHILD_ROUTER_LINKS parameter used in the REED role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aChildRouterLinks|The MLE_CHILD_ROUTER_LINKS value.|

**See Also**

- [otThreadGetChildRouterLinks](api-thread-router#ot-thread-get-child-router-links)

###### otThreadReleaseRouterId (heading level 7)

`otError otThreadReleaseRouterId(otInstance *aInstance, uint8_t aRouterId)`

**Description:** Release a Router ID that has been allocated by the device in the Leader role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aRouterId|The Router ID to release. Valid range is [0, 62].|

**Note**

- This API is reserved for testing and demo purposes only. Changing settings with this API will render a production application non-compliant with the Thread Specification.

###### otThreadBecomeRouter (heading level 7)

`otError otThreadBecomeRouter(otInstance *aInstance)`

**Description:** Attempt to become a router.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Note**

- This API is reserved for testing and demo purposes only. Changing settings with this API will render a production application non-compliant with the Thread Specification.

###### otThreadBecomeLeader (heading level 7)

`otError otThreadBecomeLeader(otInstance *aInstance)`

**Description:** Become a leader and start a new partition.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

If the device is not attached, this API will force the device to start as the leader of the network. This use case is only intended for testing and demo purposes, and using the API while the device is detached can make a production application non-compliant with the Thread Specification.

If the device is already attached, this API can be used to try to take over as the leader, creating a new partition. For this to work, the local leader weight (`otThreadGetLocalLeaderWeight()`) must be larger than the weight of the current leader (`otThreadGetLeaderWeight()`). If it is not, `OT_ERROR_NOT_CAPABLE` is returned to indicate to the caller that they need to adjust the weight.

Taking over the leader role in this way is only allowed when triggered by an explicit user action. Using this API without such user action can make a production application non-compliant with the Thread Specification.

###### otThreadGetRouterDowngradeThreshold (heading level 7)

`uint8_t otThreadGetRouterDowngradeThreshold(otInstance *aInstance)`

**Description:** Get the ROUTER_DOWNGRADE_THRESHOLD parameter used in the Router role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The ROUTER_DOWNGRADE_THRESHOLD value.

**See Also**

- [otThreadSetRouterDowngradeThreshold](api-thread-router#ot-thread-set-router-downgrade-threshold)

###### otThreadSetRouterDowngradeThreshold (heading level 7)

`void otThreadSetRouterDowngradeThreshold(otInstance *aInstance, uint8_t aThreshold)`

**Description:** Set the ROUTER_DOWNGRADE_THRESHOLD parameter used in the Leader role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aThreshold|The ROUTER_DOWNGRADE_THRESHOLD value.|

**Note**

- This API is reserved for testing and demo purposes only. Changing settings with this API will render a production application non-compliant with the Thread Specification.

**See Also**

- [otThreadGetRouterDowngradeThreshold](api-thread-router#ot-thread-get-router-downgrade-threshold)

###### otThreadGetRouterSelectionJitter (heading level 7)

`uint8_t otThreadGetRouterSelectionJitter(otInstance *aInstance)`

**Description:** Get the ROUTER_SELECTION_JITTER parameter used in the REED/Router role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The ROUTER_SELECTION_JITTER value.

**See Also**

- [otThreadSetRouterSelectionJitter](api-thread-router#ot-thread-set-router-selection-jitter)

###### otThreadSetRouterSelectionJitter (heading level 7)

`void otThreadSetRouterSelectionJitter(otInstance *aInstance, uint8_t aRouterJitter)`

**Description:** Set the ROUTER_SELECTION_JITTER parameter used in the REED/Router role.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aRouterJitter|The ROUTER_SELECTION_JITTER value.|

**Note**

- This API is reserved for testing and demo purposes only. Changing settings with this API will render a production application non-compliant with the Thread Specification.

**See Also**

- [otThreadGetRouterSelectionJitter](api-thread-router#ot-thread-get-router-selection-jitter)

###### otThreadGetChildInfoById (heading level 7)

`otError otThreadGetChildInfoById(otInstance *aInstance, uint16_t aChildId, otChildInfo *aChildInfo)`

**Description:** Gets diagnostic information for an attached Child by its Child ID or RLOC16.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aChildId|The Child ID or RLOC16 for the attached child.|
|[otChildInfo](ot-child-info) *|[out]|aChildInfo|A pointer to where the child information is placed.|

###### otThreadGetChildInfoByIndex (heading level 7)

`otError otThreadGetChildInfoByIndex(otInstance *aInstance, uint16_t aChildIndex, otChildInfo *aChildInfo)`

**Description:** The function retains diagnostic information for an attached Child by the internal table index.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aChildIndex|The table index.|
|[otChildInfo](ot-child-info) *|[out]|aChildInfo|A pointer to where the child information is placed.|

**See Also**

- otGetMaxAllowedChildren

###### otThreadGetChildNextIp6Address (heading level 7)

`otError otThreadGetChildNextIp6Address(otInstance *aInstance, uint16_t aChildIndex, otChildIp6AddressIterator *aIterator, otIp6Address *aAddress)`

**Description:** Gets the next IPv6 address (using an iterator) for a given child.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aChildIndex|The child index.|
|[otChildIp6AddressIterator](api-thread-router#ot-child-ip6-address-iterator) *|[inout]|aIterator|A pointer to the iterator. On success the iterator will be updated to point to next entry in the list. To get the first IPv6 address the iterator should be set to OT_CHILD_IP6_ADDRESS_ITERATOR_INIT.|
|[otIp6Address](ot-ip6-address) *|[out]|aAddress|A pointer to an IPv6 address where the child's next address is placed (on success).|

**See Also**

- [otThreadGetChildInfoByIndex](api-thread-router#ot-thread-get-child-info-by-index)

###### otThreadGetRouterIdSequence (heading level 7)

`uint8_t otThreadGetRouterIdSequence(otInstance *aInstance)`

**Description:** Get the current Router ID Sequence.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The Router ID Sequence.

###### otThreadGetMaxRouterId (heading level 7)

`uint8_t otThreadGetMaxRouterId(otInstance *aInstance)`

**Description:** The function returns the maximum allowed router ID.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The maximum allowed router ID.

###### otThreadGetRouterInfo (heading level 7)

`otError otThreadGetRouterInfo(otInstance *aInstance, uint16_t aRouterId, otRouterInfo *aRouterInfo)`

**Description:** The function retains diagnostic information for a given Thread Router.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aRouterId|The router ID or RLOC16 for a given router.|
|[otRouterInfo](ot-router-info) *|[out]|aRouterInfo|A pointer to where the router information is placed.|

###### otThreadGetNextCacheEntry (heading level 7)

`otError otThreadGetNextCacheEntry(otInstance *aInstance, otCacheEntryInfo *aEntryInfo, otCacheEntryIterator *aIterator)`

**Description:** Gets the next EID cache entry (using an iterator).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otCacheEntryInfo](ot-cache-entry-info) *|[out]|aEntryInfo|A pointer to where the EID cache entry information is placed.|
|[otCacheEntryIterator](ot-cache-entry-iterator) *|[inout]|aIterator|A pointer to an iterator. It will be updated to point to next entry on success. To get the first entry, initialize the iterator by setting all its fields to zero (e.g., `memset` the iterator structure to zero).|

###### otThreadGetPskc (heading level 7)

`void otThreadGetPskc(otInstance *aInstance, otPskc *aPskc)`

**Description:** Get the Thread PSKc.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otPskc](ot-pskc) *|[out]|aPskc|A pointer to an `otPskc` to return the retrieved Thread PSKc.|

**See Also**

- [otThreadSetPskc](api-thread-router#ot-thread-set-pskc)

###### otThreadGetPskcRef (heading level 7)

`otPskcRef otThreadGetPskcRef(otInstance *aInstance)`

**Description:** Get Key Reference to Thread PSKc stored.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Requires the build-time feature `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` to be enabled.

**Returns**

- Key Reference to PSKc

**See Also**

- [otThreadSetPskcRef](api-thread-router#ot-thread-set-pskc-ref)

###### otThreadSetPskc (heading level 7)

`otError otThreadSetPskc(otInstance *aInstance, const otPskc *aPskc)`

**Description:** Set the Thread PSKc.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otPskc](ot-pskc) *|[in]|aPskc|A pointer to the new Thread PSKc.|

Will only succeed when Thread protocols are disabled. A successful call to this function will also invalidate the Active and Pending Operational Datasets in non-volatile memory.

**See Also**

- [otThreadGetPskc](api-thread-router#ot-thread-get-pskc)

###### otThreadSetPskcRef (heading level 7)

`otError otThreadSetPskcRef(otInstance *aInstance, otPskcRef aKeyRef)`

**Description:** Set the Key Reference to the Thread PSKc.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otPskcRef](api-operational-dataset#ot-pskc-ref)|[in]|aKeyRef|Key Reference to the new Thread PSKc.|

Requires the build-time feature `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` to be enabled.

Will only succeed when Thread protocols are disabled. Upon success, this will also invalidate the Active and Pending Operational Datasets in non-volatile memory.

**See Also**

- [otThreadGetPskcRef](api-thread-router#ot-thread-get-pskc-ref)

###### otThreadGetParentPriority (heading level 7)

`int8_t otThreadGetParentPriority(otInstance *aInstance)`

**Description:** Get the assigned parent priority.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The assigned parent priority value, -2 means not assigned.

**See Also**

- [otThreadSetParentPriority](api-thread-router#ot-thread-set-parent-priority)

###### otThreadSetParentPriority (heading level 7)

`otError otThreadSetParentPriority(otInstance *aInstance, int8_t aParentPriority)`

**Description:** Set the parent priority.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|int8_t|[in]|aParentPriority|The parent priority value.|

**Note**

- This API is reserved for testing and demo purposes only. Changing settings with this API will render a production application non-compliant with the Thread Specification.

**See Also**

- [otThreadGetParentPriority](api-thread-router#ot-thread-get-parent-priority)

###### otThreadGetMaxChildIpAddresses (heading level 7)

`uint8_t otThreadGetMaxChildIpAddresses(otInstance *aInstance)`

**Description:** Gets the maximum number of IP addresses that each MTD child may register with this device as parent.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The maximum number of IP addresses that each MTD child may register with this device as parent.

**See Also**

- [otThreadSetMaxChildIpAddresses](api-thread-router#ot-thread-set-max-child-ip-addresses)

###### otThreadSetMaxChildIpAddresses (heading level 7)

`otError otThreadSetMaxChildIpAddresses(otInstance *aInstance, uint8_t aMaxIpAddresses)`

**Description:** Sets or restores the maximum number of IP addresses that each MTD child may register with this device as parent.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aMaxIpAddresses|The maximum number of IP addresses that each MTD child may register with this device as parent. 0 to clear the setting and restore the default.|

Pass `0` to clear the setting and restore the default.

Available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.

**Note**

- Only used by Thread Test Harness to limit the address registrations of the reference parent in order to test the MTD DUT reaction.

**See Also**

- [otThreadGetMaxChildIpAddresses](api-thread-router#ot-thread-get-max-child-ip-addresses)

###### otThreadRegisterNeighborTableCallback (heading level 7)

`void otThreadRegisterNeighborTableCallback(otInstance *aInstance, otNeighborTableCallback aCallback)`

**Description:** Registers a neighbor table callback function.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otNeighborTableCallback](api-thread-router#ot-neighbor-table-callback)|[in]|aCallback|A pointer to callback handler function.|

The provided callback (if non-NULL) will be invoked when there is a change in the neighbor table (e.g., a child or a router neighbor entry is being added/removed or an existing child's mode is changed).

Subsequent calls to this method will overwrite the previous callback. Note that this callback in invoked while the neighbor/child table is being updated and always before the `otStateChangedCallback`.

###### otThreadSetCcmEnabled (heading level 7)

`void otThreadSetCcmEnabled(otInstance *aInstance, bool aEnabled)`

**Description:** Sets whether the device was commissioned using CCM.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnabled|TRUE if the device was commissioned using CCM, FALSE otherwise.|

**Note**

- This API requires `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE`, and is only used by Thread Test Harness to indicate whether this device was commissioned using CCM.

###### otThreadSetThreadVersionCheckEnabled (heading level 7)

`void otThreadSetThreadVersionCheckEnabled(otInstance *aInstance, bool aEnabled)`

**Description:** Sets whether the Security Policy TLV version-threshold for routing (VR field) is enabled.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnabled|TRUE to enable Security Policy TLV version-threshold for routing, FALSE otherwise.|

**Note**

- This API requires `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE`, and is only used by Thread Test Harness to indicate that thread protocol version check VR should be skipped.

###### otThreadSetTmfOriginFilterEnabled (heading level 7)

`void otThreadSetTmfOriginFilterEnabled(otInstance *aInstance, bool aEnabled)`

**Description:** Enables or disables the filter to drop TMF UDP messages from untrusted origin.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnabled|TRUE to enable filter, FALSE otherwise.|

TMF messages are only trusted when they originate from a trusted source, such as the Thread interface. In special cases, such as when a device uses platform UDP socket to send TMF messages, they will be dropped due to untrusted origin. This filter is enabled by default.

When this filter is disabled, UDP messages sent to the TMF port that originate from untrusted origin (such as host, CLI or an external IPv6 node) will be allowed.

**Note**

- This API requires `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` and is only used by Thread Test Harness to test network behavior by sending special TMF messages from the CLI on a POSIX host.

###### otThreadIsTmfOriginFilterEnabled (heading level 7)

`bool otThreadIsTmfOriginFilterEnabled(otInstance *aInstance)`

**Description:** Indicates whether the filter that drops TMF UDP messages from untrusted origin is enabled or not.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

This is intended for testing only and available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` config is enabled.

###### otThreadGetRouterIdRange (heading level 7)

`void otThreadGetRouterIdRange(otInstance *aInstance, uint8_t *aMinRouterId, uint8_t *aMaxRouterId)`

**Description:** Gets the range of router IDs that are allowed to assign to nodes within the thread network.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t *|[out]|aMinRouterId|The minimum router ID.|
|uint8_t *|[out]|aMaxRouterId|The maximum router ID.|

**Note**

- This API requires `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE`, and is only used for test purpose. All the router IDs in the range [aMinRouterId, aMaxRouterId] are allowed.

**See Also**

- [otThreadSetRouterIdRange](api-thread-router#ot-thread-set-router-id-range)

###### otThreadSetRouterIdRange (heading level 7)

`otError otThreadSetRouterIdRange(otInstance *aInstance, uint8_t aMinRouterId, uint8_t aMaxRouterId)`

**Description:** Sets the range of router IDs that are allowed to assign to nodes within the thread network.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aMinRouterId|The minimum router ID.|
|uint8_t|[in]|aMaxRouterId|The maximum router ID.|

**Note**

- This API requires `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE`, and is only used for test purpose. All the router IDs in the range [aMinRouterId, aMaxRouterId] are allowed.

**See Also**

- [otThreadGetRouterIdRange](api-thread-router#ot-thread-get-router-id-range)

###### otThreadGetAdvertisementTrickleIntervalMax (heading level 7)

`uint32_t otThreadGetAdvertisementTrickleIntervalMax(otInstance *aInstance)`

**Description:** Gets the current Interval Max value used by Advertisement trickle timer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

This API requires `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE`, and is intended for testing only.

**Returns**

- The Interval Max of Advertisement trickle timer in milliseconds.

###### otThreadIsRouterIdAllocated (heading level 7)

`bool otThreadIsRouterIdAllocated(otInstance *aInstance, uint8_t aRouterId)`

**Description:** Indicates whether or not a Router ID is currently allocated.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aRouterId|The router ID to check.|

###### otThreadGetNextHopAndPathCost (heading level 7)

`void otThreadGetNextHopAndPathCost(otInstance *aInstance, uint16_t aDestRloc16, uint16_t *aNextHopRloc16, uint8_t *aPathCost)`

**Description:** Gets the next hop and path cost towards a given RLOC16 destination.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aDestRloc16|The RLOC16 of destination.|
|uint16_t *|[out]|aNextHopRloc16|A pointer to return RLOC16 of next hop, 0xfffe if no next hop.|
|uint8_t *|[out]|aPathCost|A pointer to return path cost towards destination.|

Can be used with either `aNextHopRloc16` or `aPathCost` being NULL indicating caller does not want to get the value.

###### Macros

`#define OT_CHILD_IP6_ADDRESS_ITERATOR_INIT 0`

**Description**: Initializer for otChildIP6AddressIterator.

Holds diagnostic information for a Thread Child. 

###### Public Attributes (heading level 7)

###### mExtAddress (heading level 8)

```
otExtAddress otChildInfo::mExtAddress
```

**Description:** IEEE 802.15.4 Extended Address.

###### mTimeout (heading level 8)

```
uint32_t otChildInfo::mTimeout
```

**Description:** Timeout.

###### mAge (heading level 8)

```
uint32_t otChildInfo::mAge
```

**Description:** Seconds since last heard.

###### mConnectionTime (heading level 8)

```
uint64_t otChildInfo::mConnectionTime
```

**Description:** Seconds since attach.

###### mRloc16 (heading level 8)

```
uint16_t otChildInfo::mRloc16
```

**Description:** RLOC16.

###### mChildId (heading level 8)

```
uint16_t otChildInfo::mChildId
```

**Description:** Child ID.

###### mNetworkDataVersion (heading level 8)

```
uint8_t otChildInfo::mNetworkDataVersion
```

**Description:** Network Data Version.

###### mLinkQualityIn (heading level 8)

```
uint8_t otChildInfo::mLinkQualityIn
```

**Description:** Link Quality In.

###### mAverageRssi (heading level 8)

```
int8_t otChildInfo::mAverageRssi
```

**Description:** Average RSSI.

###### mLastRssi (heading level 8)

```
int8_t otChildInfo::mLastRssi
```

**Description:** Last observed RSSI.

###### mFrameErrorRate (heading level 8)

```
uint16_t otChildInfo::mFrameErrorRate
```

**Description:** Frame error rate (0xffff->100%). Requires error tracking feature.

###### mMessageErrorRate (heading level 8)

```
uint16_t otChildInfo::mMessageErrorRate
```

**Description:** (IPv6) msg error rate (0xffff->100%). Requires error tracking feature.

###### mQueuedMessageCnt (heading level 8)

```
uint16_t otChildInfo::mQueuedMessageCnt
```

**Description:** Number of queued messages for the child.

###### mSupervisionInterval (heading level 8)

```
uint16_t otChildInfo::mSupervisionInterval
```

**Description:** Supervision interval (in seconds).

###### mVersion (heading level 8)

```
uint8_t otChildInfo::mVersion
```

**Description:** MLE version.

###### mRxOnWhenIdle (heading level 8)

```
bool otChildInfo::mRxOnWhenIdle
```

**Description:** rx-on-when-idle

###### mFullThreadDevice (heading level 8)

```
bool otChildInfo::mFullThreadDevice
```

**Description:** Full Thread Device.

###### mFullNetworkData (heading level 8)

```
bool otChildInfo::mFullNetworkData
```

**Description:** Full Network Data.

###### mIsStateRestoring (heading level 8)

```
bool otChildInfo::mIsStateRestoring
```

**Description:** Is in restoring state.

###### mIsCslSynced (heading level 8)

```
bool otChildInfo::mIsCslSynced
```

**Description:** Is child CSL synchronized.

Represents an EID cache entry. 

###### Public Attributes (heading level 7)

###### mTarget (heading level 8)

```
otIp6Address otCacheEntryInfo::mTarget
```

**Description:** Target EID.

###### mRloc16 (heading level 8)

```
otShortAddress otCacheEntryInfo::mRloc16
```

**Description:** RLOC16.

###### mState (heading level 8)

```
otCacheEntryState otCacheEntryInfo::mState
```

**Description:** Entry state.

###### mCanEvict (heading level 8)

```
bool otCacheEntryInfo::mCanEvict
```

**Description:** Indicates whether the entry can be evicted.

###### mRampDown (heading level 8)

```
bool otCacheEntryInfo::mRampDown
```

**Description:** Whether in ramp-down mode while in `OT_CACHE_ENTRY_STATE_RETRY_QUERY`.

###### mValidLastTrans (heading level 8)

```
bool otCacheEntryInfo::mValidLastTrans
```

**Description:** Indicates whether last transaction time and ML-EID are valid.

###### mLastTransTime (heading level 8)

```
uint32_t otCacheEntryInfo::mLastTransTime
```

**Description:** Last transaction time (applicable in cached state).

###### mMeshLocalEid (heading level 8)

```
otIp6Address otCacheEntryInfo::mMeshLocalEid
```

**Description:** Mesh Local EID (applicable if entry in cached state).

###### mTimeout (heading level 8)

```
uint16_t otCacheEntryInfo::mTimeout
```

**Description:** Timeout in seconds (applicable if in snooped/query/retry-query states).

###### mRetryDelay (heading level 8)

```
uint16_t otCacheEntryInfo::mRetryDelay
```

**Description:** Retry delay in seconds (applicable if in query-retry state).

Represents an iterator used for iterating through the EID cache table entries. 

To initialize the iterator and start from the first entry in the cache table, set all its fields in the structure to zero (e.g., `memset` the iterator to zero). 

###### Public Attributes (heading level 7)

###### mData (heading level 8)

```
const void* otCacheEntryIterator::mData[2]
```

**Description:** Opaque data used by the core implementation. Should not be changed by user.

Represents the device properties which are used for calculating the local leader weight on a device. 

The parameters are set based on device's capability, whether acting as border router, its power supply config, etc.

`mIsUnstable` indicates operational stability of device and is determined via a vendor specific mechanism. It can include the following cases:

- Device internally detects that it loses external power supply more often than usual. What is usual is determined by the vendor.
- Device internally detects that it reboots more often than usual. What is usual is determined by the vendor.

###### Public Attributes (heading level 7)

###### mPowerSupply (heading level 8)

```
otPowerSupply otDeviceProperties::mPowerSupply
```

**Description:** Power supply config.

###### mIsBorderRouter (heading level 8)

```
bool otDeviceProperties::mIsBorderRouter
```

**Description:** Whether device is a border router.

###### mSupportsCcm (heading level 8)

```
bool otDeviceProperties::mSupportsCcm
```

**Description:** Whether device supports CCM (can act as a CCM border router).

###### mIsUnstable (heading level 8)

```
bool otDeviceProperties::mIsUnstable
```

**Description:** Operational stability of device (vendor specific).

###### mLeaderWeightAdjustment (heading level 8)

```
int8_t otDeviceProperties::mLeaderWeightAdjustment
```

**Description:** Weight adjustment. Should be -16 to +16 (clamped otherwise).

Represent a neighbor table entry info (child or router) and is used as a parameter in the neighbor table callback `otNeighborTableCallback`. 

###### Public Attributes (heading level 7)

###### mInstance (heading level 8)

```
otInstance* otNeighborTableEntryInfo::mInstance
```

**Description:** The OpenThread instance.

###### mChild (heading level 8)

```
otChildInfo otNeighborTableEntryInfo::mChild
```

**Description:** The child neighbor info.

###### mRouter (heading level 8)

```
otNeighborInfo otNeighborTableEntryInfo::mRouter
```

**Description:** The router neighbor info.

###### mInfo (heading level 8)

```
union otNeighborTableEntryInfo::@24 otNeighborTableEntryInfo::mInfo
```

##### Steering Data

This module includes helper functions for the MeshCoP Steering Data. 

All the functions in this header require `OPENTHREAD_CONFIG_MESHCOP_STEERING_DATA_API_ENABLE` to be enabled. 

###### Modules

[otSteeringData](ot-steering-data)

###### Typedefs

###### otSteeringData (heading level 7)

`typedef struct otSteeringData otSteeringData`

**Description:**

Represents the steering data.

###### Functions

###### otSteeringDataInit (heading level 7)

`otError otSteeringDataInit(otSteeringData *aSteeringData, uint8_t aLength)`

**Description:** Initializes the Steering Data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otSteeringData](ot-steering-data) *|[out]|aSteeringData|The Steering Data to initialize.|
|uint8_t|[in]|aLength|The length of the Steering Data in bytes.|

###### otSteeringDataIsValid (heading level 7)

`bool otSteeringDataIsValid(const otSteeringData *aSteeringData)`

**Description:** Checks whether the Steering Data has a valid length.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSteeringData](ot-steering-data) *|[in]|aSteeringData|The Steering Data to check.|

###### otSteeringDataSetToPermitAllJoiners (heading level 7)

`void otSteeringDataSetToPermitAllJoiners(otSteeringData *aSteeringData)`

**Description:** Sets the Steering Data to permit all joiners.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otSteeringData](ot-steering-data) *|[out]|aSteeringData|The Steering Data to update.|

###### otSteeringDataUpdateWithJoinerId (heading level 7)

`otError otSteeringDataUpdateWithJoinerId(otSteeringData *aSteeringData, const otExtAddress *aJoinerId)`

**Description:** Updates the Steering Data's bloom filter with a Joiner ID.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otSteeringData](ot-steering-data) *|[out]|aSteeringData|The Steering Data to update.|
|const [otExtAddress](ot-ext-address) *|[in]|aJoinerId|The Joiner ID to add.|

###### otSteeringDataUpdateWithDiscerner (heading level 7)

`otError otSteeringDataUpdateWithDiscerner(otSteeringData *aSteeringData, const otJoinerDiscerner *aDiscerner)`

**Description:** Updates the Steering Data's bloom filter with a Joiner Discerner.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otSteeringData](ot-steering-data) *|[out]|aSteeringData|The Steering Data to update|
|const [otJoinerDiscerner](ot-joiner-discerner) *|[in]|aDiscerner|The Joiner Discerner to add.|

###### otSteeringDataMerge (heading level 7)

`otError otSteeringDataMerge(otSteeringData *aSteeringData, const otSteeringData *aOtherSteeringData)`

**Description:** Merges two Steering Data bloom filters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otSteeringData](ot-steering-data) *|[out]|aSteeringData|The Steering Data to merge into.|
|const [otSteeringData](ot-steering-data) *|[in]|aOtherSteeringData|The other Steering Data to merge from.|

The `aOtherSteeringData` must have a length that is a divisor of the `aSteeringData` length.

###### otSteeringDataPermitsAllJoiners (heading level 7)

`bool otSteeringDataPermitsAllJoiners(const otSteeringData *aSteeringData)`

**Description:** Checks if the Steering Data permits all joiners.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSteeringData](ot-steering-data) *|[in]|aSteeringData|The Steering Data to check.|

###### otSteeringDataIsEmpty (heading level 7)

`bool otSteeringDataIsEmpty(const otSteeringData *aSteeringData)`

**Description:** Checks if the Steering Data is empty.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSteeringData](ot-steering-data) *|[in]|aSteeringData|The Steering Data to check.|

###### otSteeringDataContainsJoinerId (heading level 7)

`bool otSteeringDataContainsJoinerId(const otSteeringData *aSteeringData, const otExtAddress *aJoinerId)`

**Description:** Checks if the Steering Data contains a Joiner ID.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSteeringData](ot-steering-data) *|[in]|aSteeringData|The Steering Data to check.|
|const [otExtAddress](ot-ext-address) *|[in]|aJoinerId|The Joiner ID.|

###### otSteeringDataContainsDiscerner (heading level 7)

`bool otSteeringDataContainsDiscerner(const otSteeringData *aSteeringData, const otJoinerDiscerner *aDiscerner)`

**Description:** Checks if the Steering Data contains a Joiner Discerner.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otSteeringData](ot-steering-data) *|[in]|aSteeringData|The Steering Data to check.|
|const [otJoinerDiscerner](ot-joiner-discerner) *|[in]|aDiscerner|The Joiner Discerner.|

###### Macros

`#define OT_STEERING_DATA_MIN_LENGTH 1`

**Description**: Min Steering Data length (bytes)

`#define OT_STEERING_DATA_MAX_LENGTH 16`

**Description**: Max Steering Data length (bytes)

Represents the steering data. 

###### Public Attributes (heading level 7)

###### mLength (heading level 8)

```
uint8_t otSteeringData::mLength
```

**Description:** Length of Steering Data (bytes).

###### m8 (heading level 8)

```
uint8_t otSteeringData::m8[OT_STEERING_DATA_MAX_LENGTH]
```

**Description:** Byte values.

#### Add-Ons

##### Modules

[Channel Manager](api-channel-manager)

[Channel Monitoring](api-channel-monitor)

[Child Supervision](api-child-supervision)

[CoAP](api-coap-group)

[Command Line Interface](api-cli)

[Crypto - Thread Stack](api-crypto)

[Factory Diagnostics - Thread Stack](api-factory-diagnostics)

[Heap](api-heap)

[History Tracker](api-history-tracker)

[Jam Detection](api-jam-detection)

[Logging - Thread Stack](api-logging)

[Mesh Diagnostics](api-mesh-diag)

[Network Co-Processor](api-ncp)

[Network Time Synchronization](api-network-time)

[Radio Statistics](api-radio)

[Random Number Generator](api-random-group)

[SNTP](api-sntp)

[Verhoeff Checksum](api-verhoeff-checksum)

##### Channel Manager

This module includes functions for Channel Manager. 

The functions in this module are available when Channel Manager features `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE` are enabled. Channel Manager behavior depends on the device role. It manages the network-wide PAN channel on a Full Thread Device in rx-on-when-idle mode, or with `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE` set, selects CSL channel in synchronized rx-off-when-idle mode. On a Minimal Thread Device `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE` selects the CSL channel. 

###### Functions

###### otChannelManagerRequestChannelChange (heading level 7)

`void otChannelManagerRequestChannelChange(otInstance *aInstance, uint8_t aChannel)`

**Description:** Requests a Thread network channel change.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aChannel|The new channel for the Thread network.|

The network switches to the given channel after a specified delay (see [otChannelManagerSetDelay()](api-channel-manager#ot-channel-manager-set-delay)). The channel change is performed by updating the Pending Operational Dataset.

A subsequent call will cancel an ongoing previously requested channel change.

###### otChannelManagerGetRequestedChannel (heading level 7)

`uint8_t otChannelManagerGetRequestedChannel(otInstance *aInstance)`

**Description:** Gets the channel from the last successful call to `otChannelManagerRequestChannelChange()`

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

**Returns**

- The last requested channel or zero if there has been no channel change request yet.

###### otChannelManagerGetDelay (heading level 7)

`uint16_t otChannelManagerGetDelay(otInstance *aInstance)`

**Description:** Gets the delay (in seconds) used by Channel Manager for a network channel change.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Only available on FTDs.

**Returns**

- The delay (in seconds) for channel change.

###### otChannelManagerSetDelay (heading level 7)

`otError otChannelManagerSetDelay(otInstance *aInstance, uint16_t aDelay)`

**Description:** Sets the delay (in seconds) used for a network channel change.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aDelay|Delay in seconds.|

Only available on FTDs. The delay should preferably be longer than the maximum data poll interval used by all Sleepy End Devices within the Thread network.

###### otChannelManagerRequestChannelSelect (heading level 7)

`otError otChannelManagerRequestChannelSelect(otInstance *aInstance, bool aSkipQualityCheck)`

**Description:** Requests that `ChannelManager` checks and selects a new channel and starts a channel change.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aSkipQualityCheck|Indicates whether the quality check (step 1) should be skipped.|

Unlike the `otChannelManagerRequestChannelChange()` where the channel must be given as a parameter, this function asks the `ChannelManager` to select a channel by itself (based on collected channel quality info).

Once called, the Channel Manager will perform the following 3 steps:

1) `ChannelManager` decides if the channel change would be helpful. This check can be skipped if `aSkipQualityCheck` is set to true (forcing a channel selection to happen and skipping the quality check). This step uses the collected link quality metrics on the device (such as CCA failure rate, frame and message error rates per neighbor, etc.) to determine if the current channel quality is at the level that justifies a channel change.

2) If the first step passes, then `ChannelManager` selects a potentially better channel. It uses the collected channel quality data by `ChannelMonitor` module. The supported and favored channels are used at this step. (see `otChannelManagerSetSupportedChannels()` and `otChannelManagerSetFavoredChannels()`).

3) If the newly selected channel is different from the current channel, `ChannelManager` requests/starts the channel change process (internally invoking a `RequestChannelChange()`).

###### otChannelManagerRequestCslChannelSelect (heading level 7)

`otError otChannelManagerRequestCslChannelSelect(otInstance *aInstance, bool aSkipQualityCheck)`

**Description:** Requests that `ChannelManager` checks and selects a new CSL channel and starts a CSL channel change.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aSkipQualityCheck|Indicates whether the quality check (step 1) should be skipped.|

Only available with `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`. This function asks the `ChannelManager` to select a channel by itself (based on collected channel quality info).

Once called, the Channel Manager will perform the following 3 steps:

1) `ChannelManager` decides if the CSL channel change would be helpful. This check can be skipped if `aSkipQualityCheck` is set to true (forcing a CSL channel selection to happen and skipping the quality check). This step uses the collected link quality metrics on the device (such as CCA failure rate, frame and message error rates per neighbor, etc.) to determine if the current channel quality is at the level that justifies a CSL channel change.

2) If the first step passes, then `ChannelManager` selects a potentially better CSL channel. It uses the collected channel quality data by `ChannelMonitor` module. The supported and favored channels are used at this step. (see `otChannelManagerSetSupportedChannels()` and `otChannelManagerSetFavoredChannels()`).

3) If the newly selected CSL channel is different from the current CSL channel, `ChannelManager` starts the CSL channel change process.

###### otChannelManagerSetAutoChannelSelectionEnabled (heading level 7)

`void otChannelManagerSetAutoChannelSelectionEnabled(otInstance *aInstance, bool aEnabled)`

**Description:** Enables or disables the auto-channel-selection functionality for network channel.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnabled|Indicates whether to enable or disable this functionality.|

When enabled, `ChannelManager` will periodically invoke a `RequestChannelSelect(false)`. The period interval can be set by `otChannelManagerSetAutoChannelSelectionInterval()`.

###### otChannelManagerGetAutoChannelSelectionEnabled (heading level 7)

`bool otChannelManagerGetAutoChannelSelectionEnabled(otInstance *aInstance)`

**Description:** Indicates whether the auto-channel-selection functionality for a network channel is enabled or not.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- TRUE if enabled, FALSE if disabled.

###### otChannelManagerSetAutoCslChannelSelectionEnabled (heading level 7)

`void otChannelManagerSetAutoCslChannelSelectionEnabled(otInstance *aInstance, bool aEnabled)`

**Description:** Enables or disables the auto-channel-selection functionality for a CSL channel.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnabled|Indicates whether to enable or disable this functionality.|

Only available with `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`. When enabled, `ChannelManager` will periodically invoke a `otChannelManagerRequestCslChannelSelect()`. The period interval can be set by `otChannelManagerSetAutoChannelSelectionInterval()`.

###### otChannelManagerGetAutoCslChannelSelectionEnabled (heading level 7)

`bool otChannelManagerGetAutoCslChannelSelectionEnabled(otInstance *aInstance)`

**Description:** Indicates whether the auto-csl-channel-selection functionality is enabled or not.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Only available with `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`.

**Returns**

- TRUE if enabled, FALSE if disabled.

###### otChannelManagerSetAutoChannelSelectionInterval (heading level 7)

`otError otChannelManagerSetAutoChannelSelectionInterval(otInstance *aInstance, uint32_t aInterval)`

**Description:** Sets the period interval (in seconds) used by auto-channel-selection functionality.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aInterval|The interval in seconds.|

###### otChannelManagerGetAutoChannelSelectionInterval (heading level 7)

`uint32_t otChannelManagerGetAutoChannelSelectionInterval(otInstance *aInstance)`

**Description:** Gets the period interval (in seconds) used by auto-channel-selection functionality.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The interval in seconds.

###### otChannelManagerGetSupportedChannels (heading level 7)

`uint32_t otChannelManagerGetSupportedChannels(otInstance *aInstance)`

**Description:** Gets the supported channel mask.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The supported channels as a bit-mask.

###### otChannelManagerSetSupportedChannels (heading level 7)

`void otChannelManagerSetSupportedChannels(otInstance *aInstance, uint32_t aChannelMask)`

**Description:** Sets the supported channel mask.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aChannelMask|A channel mask.|

###### otChannelManagerGetFavoredChannels (heading level 7)

`uint32_t otChannelManagerGetFavoredChannels(otInstance *aInstance)`

**Description:** Gets the favored channel mask.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The favored channels as a bit-mask.

###### otChannelManagerSetFavoredChannels (heading level 7)

`void otChannelManagerSetFavoredChannels(otInstance *aInstance, uint32_t aChannelMask)`

**Description:** Sets the favored channel mask.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aChannelMask|A channel mask.|

###### otChannelManagerGetCcaFailureRateThreshold (heading level 7)

`uint16_t otChannelManagerGetCcaFailureRateThreshold(otInstance *aInstance)`

**Description:** Gets the CCA failure rate threshold.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The CCA failure rate threshold. Value 0 maps to 0% and 0xffff maps to 100%.

###### otChannelManagerSetCcaFailureRateThreshold (heading level 7)

`void otChannelManagerSetCcaFailureRateThreshold(otInstance *aInstance, uint16_t aThreshold)`

**Description:** Sets the CCA failure rate threshold.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aThreshold|A CCA failure rate threshold. Value 0 maps to 0% and 0xffff maps to 100%.|

##### Channel Monitoring

This module includes functions for channel monitoring feature. 

The functions in this module are available when channel monitor feature (`OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`) is enabled.

Channel monitoring will periodically monitor all channels to help determine the cleaner channels (channels with less interference).

When channel monitoring is active, a zero-duration Energy Scan is performed, collecting a single RSSI sample on every channel per sample interval. The RSSI samples are compared with a pre-specified RSSI threshold. As an indicator of channel quality, the channel monitoring module maintains and provides the average rate/percentage of RSSI samples that are above the threshold within (approximately) a specified sample window (referred to as channel occupancy). 

###### Functions

###### otChannelMonitorSetEnabled (heading level 7)

`otError otChannelMonitorSetEnabled(otInstance *aInstance, bool aEnabled)`

**Description:** Enables or disables the Channel Monitoring operation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aEnabled|TRUE to enable/start Channel Monitoring operation, FALSE to disable/stop it.|

Once operation starts, any previously collected data is cleared. However, after operation is disabled, the previous collected data is still valid and can be read.

**Note**

- OpenThread core internally enables or disables the Channel Monitoring operation when the IPv6 interface is brought up or down, for example in a call to `otIp6SetEnabled()`.

###### otChannelMonitorIsEnabled (heading level 7)

`bool otChannelMonitorIsEnabled(otInstance *aInstance)`

**Description:** Indicates whether the Channel Monitoring operation is enabled and running.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- TRUE if the Channel Monitoring operation is enabled, FALSE otherwise.

###### otChannelMonitorGetSampleInterval (heading level 7)

`uint32_t otChannelMonitorGetSampleInterval(otInstance *aInstance)`

**Description:** Get channel monitoring sample interval in milliseconds.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The channel monitor sample interval in milliseconds.

###### otChannelMonitorGetRssiThreshold (heading level 7)

`int8_t otChannelMonitorGetRssiThreshold(otInstance *aInstance)`

**Description:** Get channel monitoring RSSI threshold in dBm.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The RSSI threshold in dBm.

###### otChannelMonitorGetSampleWindow (heading level 7)

`uint32_t otChannelMonitorGetSampleWindow(otInstance *aInstance)`

**Description:** Get channel monitoring averaging sample window length (number of samples).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The averaging sample window.

###### otChannelMonitorGetSampleCount (heading level 7)

`uint32_t otChannelMonitorGetSampleCount(otInstance *aInstance)`

**Description:** Get channel monitoring total number of RSSI samples (per channel).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

The count indicates total number samples per channel by channel monitoring module since its start (since Thread network interface was enabled).

**Returns**

- Total number of RSSI samples (per channel) taken so far.

###### otChannelMonitorGetChannelOccupancy (heading level 7)

`uint16_t otChannelMonitorGetChannelOccupancy(otInstance *aInstance, uint8_t aChannel)`

**Description:** Gets the current channel occupancy for a given channel.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aChannel|The channel for which to get the link occupancy.|

The channel occupancy value represents the average rate/percentage of RSSI samples that were above RSSI threshold ("bad" RSSI samples).

For the first "sample window" samples, the average is maintained as the actual percentage (i.e., ratio of number of "bad" samples by total number of samples). After "window" samples, the averager uses an exponentially weighted moving average. Practically, this means the average is representative of up to `3 * window` last samples with highest weight given to the latest `kSampleWindow` samples.

Max value of `0xffff` indicates all RSSI samples were above RSSI threshold (i.e. 100% of samples were "bad").

**Returns**

- The current channel occupancy for the given channel.

##### Child Supervision

This module includes functions for Child Supervision feature. 

###### Functions

###### otChildSupervisionGetInterval (heading level 7)

`uint16_t otChildSupervisionGetInterval(otInstance *aInstance)`

**Description:** Gets the Child Supervision interval (in seconds) on a child.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

Child Supervision feature provides a mechanism for parent to ensure that a message is sent to each sleepy child within the supervision interval. If there is no transmission to the child within the supervision interval, OpenThread enqueues and sends a Child Supervision Message to the child.

**Returns**

- The child supervision interval. Zero indicates that supervision is disabled.

###### otChildSupervisionSetInterval (heading level 7)

`void otChildSupervisionSetInterval(otInstance *aInstance, uint16_t aInterval)`

**Description:** Sets the child supervision interval (in seconds) on the child.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aInterval|The supervision interval (in seconds). Zero to disable supervision.|

###### otChildSupervisionGetCheckTimeout (heading level 7)

`uint16_t otChildSupervisionGetCheckTimeout(otInstance *aInstance)`

**Description:** Gets the supervision check timeout interval (in seconds) on the child.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

If the device is a sleepy child and it does not hear from its parent within the specified check timeout, it initiates the re-attach process (MLE Child Update Request/Response exchange with its parent).

**Returns**

- The supervision check timeout. Zero indicates that supervision check on the child is disabled.

###### otChildSupervisionSetCheckTimeout (heading level 7)

`void otChildSupervisionSetCheckTimeout(otInstance *aInstance, uint16_t aTimeout)`

**Description:** Sets the supervision check timeout interval (in seconds) on the child.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aTimeout|The check timeout (in seconds). Zero to disable supervision check on the child.|

###### otChildSupervisionGetCheckFailureCounter (heading level 7)

`uint16_t otChildSupervisionGetCheckFailureCounter(otInstance *aInstance)`

**Description:** Get the value of supervision check timeout failure counter.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

The counter tracks the number of supervision check failures on the child. It is incremented when the child does not hear from its parent within the specified check timeout interval. 

###### otChildSupervisionResetCheckFailureCounter (heading level 7)

`void otChildSupervisionResetCheckFailureCounter(otInstance *aInstance)`

**Description:** Reset the supervision check timeout failure counter to zero.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

##### Crypto - Thread Stack

This module includes cryptographic functions. 

###### Typedefs

###### otCryptoSha256Hash (heading level 7)

`typedef otPlatCryptoSha256Hash otCryptoSha256Hash`

**Description:**

Represents a SHA-256 hash.

###### Functions

###### otCryptoHmacSha256 (heading level 7)

`void otCryptoHmacSha256(const otCryptoKey *aKey, const uint8_t *aBuf, uint16_t aBufLength, otCryptoSha256Hash *aHash)`

**Description:** Performs HMAC computation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otCryptoKey](ot-crypto-key) *|[in]|aKey|A pointer to the key.|
|const uint8_t *|[in]|aBuf|A pointer to the input buffer.|
|uint16_t|[in]|aBufLength|The length of `aBuf` in bytes.|
|[otCryptoSha256Hash](api-crypto#ot-crypto-sha256-hash) *|[out]|aHash|A pointer to a `otCryptoSha256Hash` structure to output the hash value.|

###### otCryptoAesCcm (heading level 7)

`void otCryptoAesCcm(const otCryptoKey *aKey, uint8_t aTagLength, const void *aNonce, uint8_t aNonceLength, const void *aHeader, uint32_t aHeaderLength, void *aPlainText, void *aCipherText, uint32_t aLength, bool aEncrypt, void *aTag)`

**Description:** Performs AES CCM computation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otCryptoKey](ot-crypto-key) *|[in]|aKey|A pointer to the key.|
|uint8_t|[in]|aTagLength|Length of tag in bytes.|
|const void *|[in]|aNonce|A pointer to the nonce.|
|uint8_t|[in]|aNonceLength|Length of nonce in bytes.|
|const void *|[in]|aHeader|A pointer to the header.|
|uint32_t|[in]|aHeaderLength|Length of header in bytes.|
|void *|[inout]|aPlainText|A pointer to the plaintext.|
|void *|[inout]|aCipherText|A pointer to the ciphertext.|
|uint32_t|[in]|aLength|Plaintext length in bytes.|
|bool|[in]|aEncrypt|`true` on encrypt and `false` on decrypt.|
|void *|[out]|aTag|A pointer to the tag.|

##### Factory Diagnostics - Thread Stack

This module includes functions that control the Thread stack's execution. 

###### Typedefs

###### otDiagOutputCallback (heading level 7)

`typedef otPlatDiagOutputCallback otDiagOutputCallback`

###### Functions

###### otDiagSetOutputCallback (heading level 7)

`void otDiagSetOutputCallback(otInstance *aInstance, otDiagOutputCallback aCallback, void *aContext)`

**Description:** Sets the diag output callback.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|otDiagOutputCallback|[in]|aCallback|A pointer to a function that is called on outputting diag messages.|
|void *|[in]|aContext|A pointer to the user context.|

###### otDiagProcessCmd (heading level 7)

`otError otDiagProcessCmd(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[])`

**Description:** Processes a factory diagnostics command line.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aArgsLength|The number of elements in `aArgs`.|
|char *|[in]|aArgs|An array of arguments.|

###### otDiagProcessCmdLine (heading level 7)

`otError otDiagProcessCmdLine(otInstance *aInstance, const char *aString)`

**Description:** Processes a factory diagnostics command line.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const char *|[in]|aString|A NULL-terminated input string.|

###### otDiagIsEnabled (heading level 7)

`bool otDiagIsEnabled(otInstance *aInstance)`

**Description:** Indicates whether or not the factory diagnostics mode is enabled.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

##### Heap

This module includes functions that set the external OpenThread heap. 

###### Functions

###### otHeapCAlloc (heading level 7)

`void * otHeapCAlloc(size_t aCount, size_t aSize)`

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|size_t|N/A|aCount||
|size_t|N/A|aSize||

**Note**

- This API is deprecated and use of it is discouraged.

###### otHeapFree (heading level 7)

`void otHeapFree(void *aPointer)`

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void *|N/A|aPointer||

**Note**

- This API is deprecated and use of it is discouraged.

##### Jam Detection

This module includes functions for signal jamming detection feature. 

The functions in this module are available when jam-detection feature (`OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE`) is enabled. 

###### Typedefs

###### otJamDetectionCallback (heading level 7)

`typedef void(* otJamDetectionCallback) (bool aJamState, void *aContext)`

**Description:**

Pointer is called if signal jam detection is enabled and a jam is detected.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aJamState|Current jam state (`true` if jam is detected, `false` otherwise).|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### Functions

###### otJamDetectionSetRssiThreshold (heading level 7)

`otError otJamDetectionSetRssiThreshold(otInstance *aInstance, int8_t aRssiThreshold)`

**Description:** Set the Jam Detection RSSI Threshold (in dBm).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|int8_t|[in]|aRssiThreshold|The RSSI threshold.|

###### otJamDetectionGetRssiThreshold (heading level 7)

`int8_t otJamDetectionGetRssiThreshold(otInstance *aInstance)`

**Description:** Get the Jam Detection RSSI Threshold (in dBm).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The Jam Detection RSSI Threshold.

###### otJamDetectionSetWindow (heading level 7)

`otError otJamDetectionSetWindow(otInstance *aInstance, uint8_t aWindow)`

**Description:** Set the Jam Detection Detection Window (in seconds).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aWindow|The Jam Detection window (valid range is 1 to 63)|

###### otJamDetectionGetWindow (heading level 7)

`uint8_t otJamDetectionGetWindow(otInstance *aInstance)`

**Description:** Get the Jam Detection Detection Window (in seconds).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The Jam Detection Window.

###### otJamDetectionSetBusyPeriod (heading level 7)

`otError otJamDetectionSetBusyPeriod(otInstance *aInstance, uint8_t aBusyPeriod)`

**Description:** Set the Jam Detection Busy Period (in seconds).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aBusyPeriod|The Jam Detection busy period (should be non-zero and less than or equal to Jam Detection Window)|

The number of aggregate seconds within the detection window where the RSSI must be above threshold to trigger detection.

###### otJamDetectionGetBusyPeriod (heading level 7)

`uint8_t otJamDetectionGetBusyPeriod(otInstance *aInstance)`

**Description:** Get the Jam Detection Busy Period (in seconds)

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The Jam Detection Busy Period.

###### otJamDetectionStart (heading level 7)

`otError otJamDetectionStart(otInstance *aInstance, otJamDetectionCallback aCallback, void *aContext)`

**Description:** Start the jamming detection.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otJamDetectionCallback](api-jam-detection#ot-jam-detection-callback)|[in]|aCallback|A pointer to a function called to notify of jamming state change.|
|void *|[in]|aContext|A pointer to application-specific context.|

###### otJamDetectionStop (heading level 7)

`otError otJamDetectionStop(otInstance *aInstance)`

**Description:** Stop the jamming detection.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otJamDetectionIsEnabled (heading level 7)

`bool otJamDetectionIsEnabled(otInstance *aInstance)`

**Description:** Get the Jam Detection Status (enabled/disabled)

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The Jam Detection status (true if enabled, false otherwise).

###### otJamDetectionGetState (heading level 7)

`bool otJamDetectionGetState(otInstance *aInstance)`

**Description:** Get the Jam Detection State.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The Jam Detection state (`true` jam is detected, ‘false’ otherwise).

###### otJamDetectionGetHistoryBitmap (heading level 7)

`uint64_t otJamDetectionGetHistoryBitmap(otInstance *aInstance)`

**Description:** Get the current history bitmap.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

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.

**Returns**

- The current history bitmap.

##### Network Co-Processor

This module includes functions that control the Thread stack's execution. 

###### Typedefs

###### otNcpHdlcSendCallback (heading level 7)

`typedef int(* otNcpHdlcSendCallback) (const uint8_t *aBuf, uint16_t aBufLength)`

**Description:**

Pointer is called to send HDLC encoded NCP data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aBuf|A pointer to a buffer with an output.|
||[in]|aBufLength|A length of the output data stored in the buffer.|

**Details:**

**Returns**

- Number of bytes processed by the callback.

###### otNcpDelegateAllowPeekPoke (heading level 7)

`typedef bool(* otNcpDelegateAllowPeekPoke) (uint32_t aAddress, uint16_t aCount)`

**Description:**

Defines delegate (function pointer) type to control behavior of peek/poke operation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aAddress|Start address of memory region.|
||[in]|aCount|Number of bytes to peek or poke.|

**Details:**

This delegate function is called to decide whether to allow peek or poke of a specific memory region. It is used if NCP support for peek/poke commands is enabled.

**Returns**

- TRUE to allow peek/poke of the given memory region, FALSE otherwise.

###### Functions

###### otNcpHdlcSendDone (heading level 7)

`void otNcpHdlcSendDone(void)`

**Description:** Is called after NCP send finished.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

###### otNcpHdlcReceive (heading level 7)

`void otNcpHdlcReceive(const uint8_t *aBuf, uint16_t aBufLength)`

**Description:** Is called after HDLC encoded NCP data received.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const uint8_t *|[in]|aBuf|A pointer to a buffer.|
|uint16_t|[in]|aBufLength|The length of the data stored in the buffer.|

###### otNcpHdlcInit (heading level 7)

`void otNcpHdlcInit(otInstance *aInstance, otNcpHdlcSendCallback aSendCallback)`

**Description:** Initialize the NCP based on HDLC framing.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otNcpHdlcSendCallback](api-ncp#ot-ncp-hdlc-send-callback)|[in]|aSendCallback|The function pointer used to send NCP data.|

###### otNcpHdlcInitMulti (heading level 7)

`void otNcpHdlcInitMulti(otInstance **aInstance, uint8_t aCount, otNcpHdlcSendCallback aSendCallback)`

**Description:** Initialize the NCP based on HDLC framing.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) **|[in]|aInstance|The OpenThread instance pointers array.|
|uint8_t|[in]|aCount|Number of elements in the array.|
|[otNcpHdlcSendCallback](api-ncp#ot-ncp-hdlc-send-callback)|[in]|aSendCallback|The function pointer used to send NCP data.|

###### otNcpSpiInit (heading level 7)

`void otNcpSpiInit(otInstance *aInstance)`

**Description:** Initialize the NCP based on SPI framing.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

###### otNcpStreamWrite (heading level 7)

`otError otNcpStreamWrite(int aStreamId, const uint8_t *aDataPtr, int aDataLen)`

**Description:** Send data to the host via a specific stream.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|int|[in]|aStreamId|A numeric identifier for the stream to write to. If set to '0', will default to the debug stream.|
|const uint8_t *|[in]|aDataPtr|A pointer to the data to send on the stream. If aDataLen is non-zero, this param MUST NOT be NULL.|
|int|[in]|aDataLen|The number of bytes of data from aDataPtr to send.|

Attempts to send the given data to the host using the given aStreamId. This is useful for reporting error messages, implementing debug/diagnostic consoles, and potentially other types of datastreams.

The write either is accepted in its entirety or rejected. Partial writes are not attempted.

###### otNcpPlatLogv (heading level 7)

`void otNcpPlatLogv(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, va_list aArgs)`

**Description:** Writes OpenThread Log using `otNcpStreamWrite`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otLogLevel](plat-logging#ot-log-level)|[in]|aLogLevel|The log level.|
|[otLogRegion](plat-logging#ot-log-region)|[in]|aLogRegion|The log region.|
|const char *|[in]|aFormat|A pointer to the format string.|
|va_list|[in]|aArgs|va_list matching aFormat.|

###### otNcpRegisterPeekPokeDelegates (heading level 7)

`void otNcpRegisterPeekPokeDelegates(otNcpDelegateAllowPeekPoke aAllowPeekDelegate, otNcpDelegateAllowPeekPoke aAllowPokeDelegate)`

**Description:** Registers peek/poke delegate functions with NCP module.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otNcpDelegateAllowPeekPoke](api-ncp#ot-ncp-delegate-allow-peek-poke)|[in]|aAllowPeekDelegate|Delegate function pointer for peek operation.|
|[otNcpDelegateAllowPeekPoke](api-ncp#ot-ncp-delegate-allow-peek-poke)|[in]|aAllowPokeDelegate|Delegate function pointer for poke operation.|

The delegate functions are called by NCP module to decide whether to allow peek or poke of a specific memory region. If the delegate pointer is set to NULL, it allows peek/poke operation for any address.

##### Network Time Synchronization

This module includes functions that control network time synchronization service. 

###### Enumerations

###### otNetworkTimeStatus (heading level 7)

```
enum otNetworkTimeStatus {
    OT_NETWORK_TIME_UNSYNCHRONIZED = -1
    OT_NETWORK_TIME_RESYNC_NEEDED = 0
    OT_NETWORK_TIME_SYNCHRONIZED = 1
}
```

**Description:**

Represents OpenThread time synchronization status.

**Enumerator:**

|   |   |
|---|---|
|OT_NETWORK_TIME_UNSYNCHRONIZED|The device hasn't attached to a network.|
|OT_NETWORK_TIME_RESYNC_NEEDED|The device hasn’t received time sync for more than two periods time.|
|OT_NETWORK_TIME_SYNCHRONIZED|The device network time is synchronized.|

###### Typedefs

###### otNetworkTimeStatus (heading level 7)

`typedef enum otNetworkTimeStatus otNetworkTimeStatus`

**Description:**

Represents OpenThread time synchronization status.

###### otNetworkTimeSyncCallbackFn (heading level 7)

`typedef void(* otNetworkTimeSyncCallbackFn) (void *aCallbackContext)`

**Description:**

Pointer is called when a network time sync or status change occurs.

###### Functions

###### otNetworkTimeGet (heading level 7)

`otNetworkTimeStatus otNetworkTimeGet(otInstance *aInstance, uint64_t *aNetworkTime)`

**Description:** Get the Thread network time.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint64_t *|[inout]|aNetworkTime|The Thread network time in microseconds.|

**Returns**

- The time synchronization status.

###### otNetworkTimeSetSyncPeriod (heading level 7)

`otError otNetworkTimeSetSyncPeriod(otInstance *aInstance, uint16_t aTimeSyncPeriod)`

**Description:** Set the time synchronization period.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint16_t|[in]|aTimeSyncPeriod|The time synchronization period, in seconds.|

Can only be called while Thread protocols are disabled.

###### otNetworkTimeGetSyncPeriod (heading level 7)

`uint16_t otNetworkTimeGetSyncPeriod(otInstance *aInstance)`

**Description:** Get the time synchronization period.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

**Returns**

- The time synchronization period.

###### otNetworkTimeSetXtalThreshold (heading level 7)

`otError otNetworkTimeSetXtalThreshold(otInstance *aInstance, uint16_t aXTALThreshold)`

**Description:** Set the time synchronization XTAL accuracy threshold for Router-Capable device.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint16_t|[in]|aXTALThreshold|The XTAL accuracy threshold for Router, in PPM.|

Can only be called while Thread protocols are disabled.

###### otNetworkTimeGetXtalThreshold (heading level 7)

`uint16_t otNetworkTimeGetXtalThreshold(otInstance *aInstance)`

**Description:** Get the time synchronization XTAL accuracy threshold for Router.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

**Returns**

- The XTAL accuracy threshold for Router, in PPM.

###### otNetworkTimeSyncSetCallback (heading level 7)

`void otNetworkTimeSyncSetCallback(otInstance *aInstance, otNetworkTimeSyncCallbackFn aCallbackFn, void *aCallbackContext)`

**Description:** Set a callback to be called when a network time sync or status change occurs.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otNetworkTimeSyncCallbackFn](api-network-time#ot-network-time-sync-callback-fn)|[in]|aCallbackFn|The callback function to be called|
|void *|[in]|aCallbackContext|The context to be passed to the callback function upon invocation|

This callback shall be called only when the network time offset jumps by OPENTHREAD_CONFIG_TIME_SYNC_JUMP_NOTIF_MIN_US or when the status changes.

###### Macros

`#define OT_TIME_SYNC_INVALID_SEQ 0`

**Description**: zero is considered as invalid time synchronization sequence.

##### Verhoeff Checksum

This module includes functions for Verhoeff checksum calculation and validation. 

The functions in this module are available when `OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE` is enabled. 

###### Functions

###### otVerhoeffChecksumCalculate (heading level 7)

`otError otVerhoeffChecksumCalculate(const char *aDecimalString, char *aChecksum)`

**Description:** Calculates the Verhoeff checksum for a given decimal string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aDecimalString|The string containing decimal digits.|
|char *|[out]|aChecksum|Pointer to a `char` to return the calculated checksum.|

Requires `OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE`.

###### otVerhoeffChecksumValidate (heading level 7)

`otError otVerhoeffChecksumValidate(const char *aDecimalString)`

**Description:** Validates the Verhoeff checksum for a given decimal string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aDecimalString|The string containing decimal digits (last char is treated as checksum).|

Requires `OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE`.

###### Macros

`#define OT_VERHOEFF_CHECKSUM_MAX_STRING_LENGTH 128`

**Description**: Specifies the maximum length of decimal string input in `otVerhoeffChecksum` functions.

##### CoAP

###### Modules

[CoAP](api-coap)

[CoAP Secure](api-coap-secure)

###### CoAP Secure

This module includes functions that control CoAP Secure (CoAP over DTLS) communication. 

The functions in this module are available when CoAP Secure API feature (`OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE`) is enabled. 

###### Enumerations (heading level 7)

###### otCoapSecureConnectEvent (heading level 8)

```
enum otCoapSecureConnectEvent {
    OT_COAP_SECURE_CONNECTED = 0
    OT_COAP_SECURE_DISCONNECTED_PEER_CLOSED
    OT_COAP_SECURE_DISCONNECTED_LOCAL_CLOSED
    OT_COAP_SECURE_DISCONNECTED_MAX_ATTEMPTS
    OT_COAP_SECURE_DISCONNECTED_ERROR
    OT_COAP_SECURE_DISCONNECTED_TIMEOUT
}
```

**Description:**

CoAP secure connection event types.

**Enumerator:**

|   |   |
|---|---|
|OT_COAP_SECURE_CONNECTED|Connection established.|
|OT_COAP_SECURE_DISCONNECTED_PEER_CLOSED|Disconnected by peer.|
|OT_COAP_SECURE_DISCONNECTED_LOCAL_CLOSED|Disconnected locally.|
|OT_COAP_SECURE_DISCONNECTED_MAX_ATTEMPTS|Disconnected due to reaching the max connection attempts.|
|OT_COAP_SECURE_DISCONNECTED_ERROR|Disconnected due to an error.|
|OT_COAP_SECURE_DISCONNECTED_TIMEOUT|Disconnected locally due to session timeout.|

###### Typedefs (heading level 7)

###### otCoapSecureConnectEvent (heading level 8)

`typedef enum otCoapSecureConnectEvent otCoapSecureConnectEvent`

**Description:**

CoAP secure connection event types.

###### otHandleCoapSecureClientConnect (heading level 8)

`typedef void(* otHandleCoapSecureClientConnect) (otCoapSecureConnectEvent aEvent, void *aContext)`

**Description:**

Pointer is called when the DTLS connection state changes.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aEvent|The connection event.|
||[in]|aContext|A pointer to arbitrary context information.|

**Details:**

###### otCoapSecureAutoStopCallback (heading level 8)

`typedef void(* otCoapSecureAutoStopCallback) (void *aContext)`

**Description:**

Callback function pointer to notify when the CoAP secure agent is automatically stopped due to reaching the maximum number of connection attempts.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to arbitrary context information.|

**Details:**

###### Functions (heading level 7)

###### otCoapSecureStart (heading level 8)

`otError otCoapSecureStart(otInstance *aInstance, uint16_t aPort)`

**Description:** Starts the CoAP Secure service.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aPort|The local UDP port to bind to.|

###### otCoapSecureStartWithMaxConnAttempts (heading level 8)

`otError otCoapSecureStartWithMaxConnAttempts(otInstance *aInstance, uint16_t aPort, uint16_t aMaxAttempts, otCoapSecureAutoStopCallback aCallback, void *aContext)`

**Description:** Starts the CoAP secure service and sets the maximum number of allowed connection attempts before stopping the agent automatically.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aPort|The local UDP port to bind to.|
|uint16_t|[in]|aMaxAttempts|Maximum number of allowed connection request attempts. Zero indicates no limit.|
|[otCoapSecureAutoStopCallback](api-coap-secure#ot-coap-secure-auto-stop-callback)|[in]|aCallback|Callback to notify if max number of attempts has reached and agent is stopped.|
|void *|[in]|aContext|A pointer to arbitrary context to use with `aCallback`.|

###### otCoapSecureStop (heading level 8)

`void otCoapSecureStop(otInstance *aInstance)`

**Description:** Stops the CoAP Secure server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otCoapSecureSetPsk (heading level 8)

`void otCoapSecureSetPsk(otInstance *aInstance, const uint8_t *aPsk, uint16_t aPskLength, const uint8_t *aPskIdentity, uint16_t aPskIdLength)`

**Description:** Sets the Pre-Shared Key (PSK) and cipher suite DTLS_PSK_WITH_AES_128_CCM_8.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const uint8_t *|[in]|aPsk|A pointer to the PSK.|
|uint16_t|[in]|aPskLength|The PSK length.|
|const uint8_t *|[in]|aPskIdentity|The Identity Name for the PSK.|
|uint16_t|[in]|aPskIdLength|The PSK Identity Length.|

**Note**

- This function requires the build-time feature `MBEDTLS_KEY_EXCHANGE_PSK_ENABLED` to be enabled.

###### otCoapSecureGetPeerCertificateBase64 (heading level 8)

`otError otCoapSecureGetPeerCertificateBase64(otInstance *aInstance, unsigned char *aPeerCert, size_t *aCertLength, size_t aCertBufferSize)`

**Description:** Returns the peer x509 certificate base64 encoded.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|unsigned char *|[out]|aPeerCert|A pointer to the base64 encoded certificate buffer.|
|size_t *|[out]|aCertLength|The length of the base64 encoded peer certificate.|
|size_t|[in]|aCertBufferSize|The buffer size of aPeerCert.|

**Note**

- This function requires the build-time features `MBEDTLS_BASE64_C` and `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` to be enabled.

###### otCoapSecureSetSslAuthMode (heading level 8)

`void otCoapSecureSetSslAuthMode(otInstance *aInstance, bool aVerifyPeerCertificate)`

**Description:** Sets the authentication mode for the coap secure connection.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|bool|[in]|aVerifyPeerCertificate|true, to verify the peer certificate.|

Disable or enable the verification of peer certificate. Must be called before start.

###### otCoapSecureSetCertificate (heading level 8)

`void otCoapSecureSetCertificate(otInstance *aInstance, const uint8_t *aX509Cert, uint32_t aX509Length, const uint8_t *aPrivateKey, uint32_t aPrivateKeyLength)`

**Description:** Sets the local device's X509 certificate with corresponding private key for DTLS session with DTLS_ECDHE_ECDSA_WITH_AES_128_CCM_8.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const uint8_t *|[in]|aX509Cert|A pointer to the PEM formatted X509 certificate.|
|uint32_t|[in]|aX509Length|The length of certificate.|
|const uint8_t *|[in]|aPrivateKey|A pointer to the PEM formatted private key.|
|uint32_t|[in]|aPrivateKeyLength|The length of the private key.|

**Note**

- This function requires `MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=1`.

###### otCoapSecureSetCaCertificateChain (heading level 8)

`void otCoapSecureSetCaCertificateChain(otInstance *aInstance, const uint8_t *aX509CaCertificateChain, uint32_t aX509CaCertChainLength)`

**Description:** Sets the trusted top level CAs.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const uint8_t *|[in]|aX509CaCertificateChain|A pointer to the PEM formatted X509 CA chain.|
|uint32_t|[in]|aX509CaCertChainLength|The length of chain.|

It is needed for validating the certificate of the peer.

DTLS mode "ECDHE ECDSA with AES 128 CCM 8" for Application CoAPS.

**Note**

- This function requires `MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=1`.

###### otCoapSecureConnect (heading level 8)

`otError otCoapSecureConnect(otInstance *aInstance, const otSockAddr *aSockAddr, otHandleCoapSecureClientConnect aHandler, void *aContext)`

**Description:** Initializes DTLS session with a peer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otSockAddr](ot-sock-addr) *|[in]|aSockAddr|A pointer to the remote socket address.|
|[otHandleCoapSecureClientConnect](api-coap-secure#ot-handle-coap-secure-client-connect)|[in]|aHandler|A pointer to a function that will be called when the DTLS connection state changes.|
|void *|[in]|aContext|A pointer to arbitrary context information.|

###### otCoapSecureDisconnect (heading level 8)

`void otCoapSecureDisconnect(otInstance *aInstance)`

**Description:** Stops the DTLS connection.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otCoapSecureIsConnected (heading level 8)

`bool otCoapSecureIsConnected(otInstance *aInstance)`

**Description:** Indicates whether or not the DTLS session is connected.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otCoapSecureIsConnectionActive (heading level 8)

`bool otCoapSecureIsConnectionActive(otInstance *aInstance)`

**Description:** Indicates whether or not the DTLS session is active.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otCoapSecureIsClosed (heading level 8)

`bool otCoapSecureIsClosed(otInstance *aInstance)`

**Description:** Indicates whether or not the DTLS session is closed.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otCoapSecureSendRequestBlockWise (heading level 8)

`otError otCoapSecureSendRequestBlockWise(otInstance *aInstance, otMessage *aMessage, otCoapResponseHandler aHandler, void *aContext, otCoapBlockwiseTransmitHook aTransmitHook, otCoapBlockwiseReceiveHook aReceiveHook)`

**Description:** Sends a CoAP request block-wise over secure DTLS connection.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A reference to the message to send.|
|[otCoapResponseHandler](api-coap#ot-coap-response-handler)|[in]|aHandler|A function pointer that shall be called on response reception or time-out.|
|void *|[in]|aContext|A pointer to arbitrary context information.|
|[otCoapBlockwiseTransmitHook](api-coap#ot-coap-blockwise-transmit-hook)|[in]|aTransmitHook|A function pointer that is called on Block1 response reception.|
|[otCoapBlockwiseReceiveHook](api-coap#ot-coap-blockwise-receive-hook)|[in]|aReceiveHook|A function pointer that is called on Block2 response reception.|

Is available when OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE configuration is enabled.

If a response for a request is expected, respective function and context information should be provided. If no response is expected, these arguments should be NULL pointers. If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message.

###### otCoapSecureSendRequest (heading level 8)

`otError otCoapSecureSendRequest(otInstance *aInstance, otMessage *aMessage, otCoapResponseHandler aHandler, void *aContext)`

**Description:** Sends a CoAP request over secure DTLS connection.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A reference to the message to send.|
|[otCoapResponseHandler](api-coap#ot-coap-response-handler)|[in]|aHandler|A function pointer that shall be called on response reception or time-out.|
|void *|[in]|aContext|A pointer to arbitrary context information.|

If a response for a request is expected, respective function and context information should be provided. If no response is expected, these arguments should be NULL pointers. If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message.

###### otCoapSecureAddResource (heading level 8)

`void otCoapSecureAddResource(otInstance *aInstance, otCoapResource *aResource)`

**Description:** Adds a resource to the CoAP Secure server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otCoapResource](ot-coap-resource) *|[in]|aResource|A pointer to the resource.|

###### otCoapSecureRemoveResource (heading level 8)

`void otCoapSecureRemoveResource(otInstance *aInstance, otCoapResource *aResource)`

**Description:** Removes a resource from the CoAP Secure server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otCoapResource](ot-coap-resource) *|[in]|aResource|A pointer to the resource.|

###### otCoapSecureAddBlockWiseResource (heading level 8)

`void otCoapSecureAddBlockWiseResource(otInstance *aInstance, otCoapBlockwiseResource *aResource)`

**Description:** Adds a block-wise resource to the CoAP Secure server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otCoapBlockwiseResource](ot-coap-blockwise-resource) *|[in]|aResource|A pointer to the resource.|

###### otCoapSecureRemoveBlockWiseResource (heading level 8)

`void otCoapSecureRemoveBlockWiseResource(otInstance *aInstance, otCoapBlockwiseResource *aResource)`

**Description:** Removes a block-wise resource from the CoAP Secure server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otCoapBlockwiseResource](ot-coap-blockwise-resource) *|[in]|aResource|A pointer to the resource.|

###### otCoapSecureSetDefaultHandler (heading level 8)

`void otCoapSecureSetDefaultHandler(otInstance *aInstance, otCoapRequestHandler aHandler, void *aContext)`

**Description:** Sets the default handler for unhandled CoAP Secure requests.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otCoapRequestHandler](api-coap#ot-coap-request-handler)|[in]|aHandler|A function pointer that shall be called when an unhandled request arrives.|
|void *|[in]|aContext|A pointer to arbitrary context information. May be NULL if not used.|

###### otCoapSecureSetClientConnectEventCallback (heading level 8)

`void otCoapSecureSetClientConnectEventCallback(otInstance *aInstance, otHandleCoapSecureClientConnect aHandler, void *aContext)`

**Description:** Sets the connect event callback to indicate when a Client connection to the CoAP Secure server has changed.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otHandleCoapSecureClientConnect](api-coap-secure#ot-handle-coap-secure-client-connect)|[in]|aHandler|A pointer to a function that will be called once DTLS connection has changed.|
|void *|[in]|aContext|A pointer to arbitrary context information. May be NULL if not used.|

###### otCoapSecureSendResponseBlockWise (heading level 8)

`otError otCoapSecureSendResponseBlockWise(otInstance *aInstance, otMessage *aMessage, const otMessageInfo *aMessageInfo, void *aContext, otCoapBlockwiseTransmitHook aTransmitHook)`

**Description:** Sends a CoAP response block-wise from the CoAP Secure server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the CoAP response to send.|
|const [otMessageInfo](ot-message-info) *|[in]|aMessageInfo|A pointer to the message info associated with `aMessage`.|
|void *|[in]|aContext|A pointer to arbitrary context information. May be NULL if not used.|
|[otCoapBlockwiseTransmitHook](api-coap#ot-coap-blockwise-transmit-hook)|[in]|aTransmitHook|A function pointer that is called on Block1 request reception.|

Is available when OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE configuration is enabled.

###### otCoapSecureSendResponse (heading level 8)

`otError otCoapSecureSendResponse(otInstance *aInstance, otMessage *aMessage, const otMessageInfo *aMessageInfo)`

**Description:** Sends a CoAP response from the CoAP Secure server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the CoAP response to send.|
|const [otMessageInfo](ot-message-info) *|[in]|aMessageInfo|A pointer to the message info associated with `aMessage`.|

###### Macros (heading level 7)

`#define OT_DEFAULT_COAP_SECURE_PORT 5684`

**Description**: Default CoAP Secure port, as specified in RFC 7252.

###### CoAP

This module includes functions that control CoAP communication. 

The functions in this module are available when CoAP API feature (`OPENTHREAD_CONFIG_COAP_API_ENABLE`) is enabled. 

###### Modules (heading level 7)

[otCoapOption](ot-coap-option)

[otCoapOptionIterator](ot-coap-option-iterator)

[otCoapToken](ot-coap-token)

[otCoapResource](ot-coap-resource)

[otCoapTxParameters](ot-coap-tx-parameters)

[otCoapBlockwiseResource](ot-coap-blockwise-resource)

###### Enumerations (heading level 7)

###### otCoapType (heading level 8)

```
enum otCoapType {
    OT_COAP_TYPE_CONFIRMABLE = 0
    OT_COAP_TYPE_NON_CONFIRMABLE = 1
    OT_COAP_TYPE_ACKNOWLEDGMENT = 2
    OT_COAP_TYPE_RESET = 3
}
```

**Description:**

CoAP Type values (2 bit unsigned integer).

**Enumerator:**

|   |   |
|---|---|
|OT_COAP_TYPE_CONFIRMABLE|Confirmable.|
|OT_COAP_TYPE_NON_CONFIRMABLE|Non-confirmable.|
|OT_COAP_TYPE_ACKNOWLEDGMENT|Acknowledgment.|
|OT_COAP_TYPE_RESET|Reset.|

###### otCoapCode (heading level 8)

```
enum otCoapCode {
    OT_COAP_CODE_EMPTY = OT_COAP_CODE(0, 0)
    OT_COAP_CODE_GET = OT_COAP_CODE(0, 1)
    OT_COAP_CODE_POST = OT_COAP_CODE(0, 2)
    OT_COAP_CODE_PUT = OT_COAP_CODE(0, 3)
    OT_COAP_CODE_DELETE = OT_COAP_CODE(0, 4)
    OT_COAP_CODE_RESPONSE_MIN = OT_COAP_CODE(2, 0)
    OT_COAP_CODE_CREATED = OT_COAP_CODE(2, 1)
    OT_COAP_CODE_DELETED = OT_COAP_CODE(2, 2)
    OT_COAP_CODE_VALID = OT_COAP_CODE(2, 3)
    OT_COAP_CODE_CHANGED = OT_COAP_CODE(2, 4)
    OT_COAP_CODE_CONTENT = OT_COAP_CODE(2, 5)
    OT_COAP_CODE_CONTINUE = OT_COAP_CODE(2, 31)
    OT_COAP_CODE_BAD_REQUEST = OT_COAP_CODE(4, 0)
    OT_COAP_CODE_UNAUTHORIZED = OT_COAP_CODE(4, 1)
    OT_COAP_CODE_BAD_OPTION = OT_COAP_CODE(4, 2)
    OT_COAP_CODE_FORBIDDEN = OT_COAP_CODE(4, 3)
    OT_COAP_CODE_NOT_FOUND = OT_COAP_CODE(4, 4)
    OT_COAP_CODE_METHOD_NOT_ALLOWED = OT_COAP_CODE(4, 5)
    OT_COAP_CODE_NOT_ACCEPTABLE = OT_COAP_CODE(4, 6)
    OT_COAP_CODE_REQUEST_INCOMPLETE = OT_COAP_CODE(4, 8)
    OT_COAP_CODE_PRECONDITION_FAILED = OT_COAP_CODE(4, 12)
    OT_COAP_CODE_REQUEST_TOO_LARGE = OT_COAP_CODE(4, 13)
    OT_COAP_CODE_UNSUPPORTED_FORMAT = OT_COAP_CODE(4, 15)
    OT_COAP_CODE_INTERNAL_ERROR = OT_COAP_CODE(5, 0)
    OT_COAP_CODE_NOT_IMPLEMENTED = OT_COAP_CODE(5, 1)
    OT_COAP_CODE_BAD_GATEWAY = OT_COAP_CODE(5, 2)
    OT_COAP_CODE_SERVICE_UNAVAILABLE = OT_COAP_CODE(5, 3)
    OT_COAP_CODE_GATEWAY_TIMEOUT = OT_COAP_CODE(5, 4)
    OT_COAP_CODE_PROXY_NOT_SUPPORTED = OT_COAP_CODE(5, 5)
}
```

**Description:**

CoAP Code values.

**Enumerator:**

|   |   |
|---|---|
|OT_COAP_CODE_EMPTY|Empty message code.|
|OT_COAP_CODE_GET|Get.|
|OT_COAP_CODE_POST|Post.|
|OT_COAP_CODE_PUT|Put.|
|OT_COAP_CODE_DELETE|Delete.|
|OT_COAP_CODE_RESPONSE_MIN|2.00|
|OT_COAP_CODE_CREATED|Created.|
|OT_COAP_CODE_DELETED|Deleted.|
|OT_COAP_CODE_VALID|Valid.|
|OT_COAP_CODE_CHANGED|Changed.|
|OT_COAP_CODE_CONTENT|Content.|
|OT_COAP_CODE_CONTINUE|RFC7959 Continue.|
|OT_COAP_CODE_BAD_REQUEST|Bad Request.|
|OT_COAP_CODE_UNAUTHORIZED|Unauthorized.|
|OT_COAP_CODE_BAD_OPTION|Bad Option.|
|OT_COAP_CODE_FORBIDDEN|Forbidden.|
|OT_COAP_CODE_NOT_FOUND|Not Found.|
|OT_COAP_CODE_METHOD_NOT_ALLOWED|Method Not Allowed.|
|OT_COAP_CODE_NOT_ACCEPTABLE|Not Acceptable.|
|OT_COAP_CODE_REQUEST_INCOMPLETE|RFC7959 Request Entity Incomplete.|
|OT_COAP_CODE_PRECONDITION_FAILED|Precondition Failed.|
|OT_COAP_CODE_REQUEST_TOO_LARGE|Request Entity Too Large.|
|OT_COAP_CODE_UNSUPPORTED_FORMAT|Unsupported Content-Format.|
|OT_COAP_CODE_INTERNAL_ERROR|Internal Server Error.|
|OT_COAP_CODE_NOT_IMPLEMENTED|Not Implemented.|
|OT_COAP_CODE_BAD_GATEWAY|Bad Gateway.|
|OT_COAP_CODE_SERVICE_UNAVAILABLE|Service Unavailable.|
|OT_COAP_CODE_GATEWAY_TIMEOUT|Gateway Timeout.|
|OT_COAP_CODE_PROXY_NOT_SUPPORTED|Proxying Not Supported.|

###### otCoapOptionType (heading level 8)

```
enum otCoapOptionType {
    OT_COAP_OPTION_IF_MATCH = 1
    OT_COAP_OPTION_URI_HOST = 3
    OT_COAP_OPTION_E_TAG = 4
    OT_COAP_OPTION_IF_NONE_MATCH = 5
    OT_COAP_OPTION_OBSERVE = 6
    OT_COAP_OPTION_URI_PORT = 7
    OT_COAP_OPTION_LOCATION_PATH = 8
    OT_COAP_OPTION_URI_PATH = 11
    OT_COAP_OPTION_CONTENT_FORMAT = 12
    OT_COAP_OPTION_MAX_AGE = 14
    OT_COAP_OPTION_URI_QUERY = 15
    OT_COAP_OPTION_ACCEPT = 17
    OT_COAP_OPTION_LOCATION_QUERY = 20
    OT_COAP_OPTION_BLOCK2 = 23
    OT_COAP_OPTION_BLOCK1 = 27
    OT_COAP_OPTION_SIZE2 = 28
    OT_COAP_OPTION_PROXY_URI = 35
    OT_COAP_OPTION_PROXY_SCHEME = 39
    OT_COAP_OPTION_SIZE1 = 60
}
```

**Description:**

CoAP Option Numbers.

**Enumerator:**

|   |   |
|---|---|
|OT_COAP_OPTION_IF_MATCH|If-Match.|
|OT_COAP_OPTION_URI_HOST|Uri-Host.|
|OT_COAP_OPTION_E_TAG|ETag.|
|OT_COAP_OPTION_IF_NONE_MATCH|If-None-Match.|
|OT_COAP_OPTION_OBSERVE|Observe [RFC7641].|
|OT_COAP_OPTION_URI_PORT|Uri-Port.|
|OT_COAP_OPTION_LOCATION_PATH|Location-Path.|
|OT_COAP_OPTION_URI_PATH|Uri-Path.|
|OT_COAP_OPTION_CONTENT_FORMAT|Content-Format.|
|OT_COAP_OPTION_MAX_AGE|Max-Age.|
|OT_COAP_OPTION_URI_QUERY|Uri-Query.|
|OT_COAP_OPTION_ACCEPT|Accept.|
|OT_COAP_OPTION_LOCATION_QUERY|Location-Query.|
|OT_COAP_OPTION_BLOCK2|Block2 (RFC7959)|
|OT_COAP_OPTION_BLOCK1|Block1 (RFC7959)|
|OT_COAP_OPTION_SIZE2|Size2 (RFC7959)|
|OT_COAP_OPTION_PROXY_URI|Proxy-Uri.|
|OT_COAP_OPTION_PROXY_SCHEME|Proxy-Scheme.|
|OT_COAP_OPTION_SIZE1|Size1.|

###### otCoapOptionContentFormat (heading level 8)

```
enum otCoapOptionContentFormat {
    OT_COAP_OPTION_CONTENT_FORMAT_TEXT_PLAIN = 0
    OT_COAP_OPTION_CONTENT_FORMAT_COSE_ENCRYPT0 = 16
    OT_COAP_OPTION_CONTENT_FORMAT_COSE_MAC0 = 17
    OT_COAP_OPTION_CONTENT_FORMAT_COSE_SIGN1 = 18
    OT_COAP_OPTION_CONTENT_FORMAT_LINK_FORMAT = 40
    OT_COAP_OPTION_CONTENT_FORMAT_XML = 41
    OT_COAP_OPTION_CONTENT_FORMAT_OCTET_STREAM = 42
    OT_COAP_OPTION_CONTENT_FORMAT_EXI = 47
    OT_COAP_OPTION_CONTENT_FORMAT_JSON = 50
    OT_COAP_OPTION_CONTENT_FORMAT_JSON_PATCH_JSON = 51
    OT_COAP_OPTION_CONTENT_FORMAT_MERGE_PATCH_JSON = 52
    OT_COAP_OPTION_CONTENT_FORMAT_CBOR = 60
    OT_COAP_OPTION_CONTENT_FORMAT_CWT = 61
    OT_COAP_OPTION_CONTENT_FORMAT_COSE_ENCRYPT = 96
    OT_COAP_OPTION_CONTENT_FORMAT_COSE_MAC = 97
    OT_COAP_OPTION_CONTENT_FORMAT_COSE_SIGN = 98
    OT_COAP_OPTION_CONTENT_FORMAT_COSE_KEY = 101
    OT_COAP_OPTION_CONTENT_FORMAT_COSE_KEY_SET = 102
    OT_COAP_OPTION_CONTENT_FORMAT_SENML_JSON = 110
    OT_COAP_OPTION_CONTENT_FORMAT_SENSML_JSON = 111
    OT_COAP_OPTION_CONTENT_FORMAT_SENML_CBOR = 112
    OT_COAP_OPTION_CONTENT_FORMAT_SENSML_CBOR = 113
    OT_COAP_OPTION_CONTENT_FORMAT_SENML_EXI = 114
    OT_COAP_OPTION_CONTENT_FORMAT_SENSML_EXI = 115
    OT_COAP_OPTION_CONTENT_FORMAT_COAP_GROUP_JSON = 256
    OT_COAP_OPTION_CONTENT_FORMAT_SENML_XML = 310
    OT_COAP_OPTION_CONTENT_FORMAT_SENSML_XML = 311
}
```

**Description:**

CoAP Content Format codes.

**Details:**

The full list is documented at [https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#content-formats](https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#content-formats)

**Enumerator:**

|   |   |
|---|---|
|OT_COAP_OPTION_CONTENT_FORMAT_TEXT_PLAIN|txt/plain; charset=utf-8: [RFC2046][RFC3676][RFC5147]|
|OT_COAP_OPTION_CONTENT_FORMAT_COSE_ENCRYPT0|application/cose; cose-type="cose-encrypt0": [RFC8152]|
|OT_COAP_OPTION_CONTENT_FORMAT_COSE_MAC0|application/cose; cose-type="cose-mac0": [RFC8152]|
|OT_COAP_OPTION_CONTENT_FORMAT_COSE_SIGN1|application/cose; cose-type="cose-sign1": [RFC8152]|
|OT_COAP_OPTION_CONTENT_FORMAT_LINK_FORMAT|application/link-format: [RFC6690]|
|OT_COAP_OPTION_CONTENT_FORMAT_XML|application/xml: [RFC3023]|
|OT_COAP_OPTION_CONTENT_FORMAT_OCTET_STREAM|application/octet-stream: [RFC2045][RFC2046]|
|OT_COAP_OPTION_CONTENT_FORMAT_EXI|application/exi: ["Efficient XML Interchange (EXI)"]|
|OT_COAP_OPTION_CONTENT_FORMAT_JSON|application/json: [RFC7159]|
|OT_COAP_OPTION_CONTENT_FORMAT_JSON_PATCH_JSON|application/json-patch+json: [RFC6902]|
|OT_COAP_OPTION_CONTENT_FORMAT_MERGE_PATCH_JSON|application/merge-patch+json: [RFC7396]|
|OT_COAP_OPTION_CONTENT_FORMAT_CBOR|application/cbor: [RFC7049]|
|OT_COAP_OPTION_CONTENT_FORMAT_CWT|application/cwt: [RFC8392]|
|OT_COAP_OPTION_CONTENT_FORMAT_COSE_ENCRYPT|application/cose; cose-type="cose-encrypt": [RFC8152]|
|OT_COAP_OPTION_CONTENT_FORMAT_COSE_MAC|application/cose; cose-type="cose-mac": [RFC8152]|
|OT_COAP_OPTION_CONTENT_FORMAT_COSE_SIGN|application/cose; cose-type="cose-sign": [RFC8152]|
|OT_COAP_OPTION_CONTENT_FORMAT_COSE_KEY|application/cose-key: [RFC8152]|
|OT_COAP_OPTION_CONTENT_FORMAT_COSE_KEY_SET|application/cose-key-set: [RFC8152]|
|OT_COAP_OPTION_CONTENT_FORMAT_SENML_JSON|application/senml+json: [RFC8428]|
|OT_COAP_OPTION_CONTENT_FORMAT_SENSML_JSON|application/sensml+json: [RFC8428]|
|OT_COAP_OPTION_CONTENT_FORMAT_SENML_CBOR|application/senml+cbor: [RFC8428]|
|OT_COAP_OPTION_CONTENT_FORMAT_SENSML_CBOR|application/sensml+cbor: [RFC8428]|
|OT_COAP_OPTION_CONTENT_FORMAT_SENML_EXI|application/senml-exi: [RFC8428]|
|OT_COAP_OPTION_CONTENT_FORMAT_SENSML_EXI|application/sensml-exi: [RFC8428]|
|OT_COAP_OPTION_CONTENT_FORMAT_COAP_GROUP_JSON|application/coap-group+json: [RFC7390]|
|OT_COAP_OPTION_CONTENT_FORMAT_SENML_XML|application/senml+xml: [RFC8428]|
|OT_COAP_OPTION_CONTENT_FORMAT_SENSML_XML|application/sensml+xml: [RFC8428]|

###### otCoapBlockSzx (heading level 8)

```
enum otCoapBlockSzx {
    OT_COAP_OPTION_BLOCK_SZX_16 = 0
    OT_COAP_OPTION_BLOCK_SZX_32 = 1
    OT_COAP_OPTION_BLOCK_SZX_64 = 2
    OT_COAP_OPTION_BLOCK_SZX_128 = 3
    OT_COAP_OPTION_BLOCK_SZX_256 = 4
    OT_COAP_OPTION_BLOCK_SZX_512 = 5
    OT_COAP_OPTION_BLOCK_SZX_1024 = 6
}
```

**Description:**

CoAP Block Size Exponents.

**Enumerator:**

|   |   |
|---|---|
|OT_COAP_OPTION_BLOCK_SZX_16||
|OT_COAP_OPTION_BLOCK_SZX_32||
|OT_COAP_OPTION_BLOCK_SZX_64||
|OT_COAP_OPTION_BLOCK_SZX_128||
|OT_COAP_OPTION_BLOCK_SZX_256||
|OT_COAP_OPTION_BLOCK_SZX_512||
|OT_COAP_OPTION_BLOCK_SZX_1024||

###### Typedefs (heading level 7)

###### otCoapType (heading level 8)

`typedef enum otCoapType otCoapType`

**Description:**

CoAP Type values (2 bit unsigned integer).

###### otCoapCode (heading level 8)

`typedef enum otCoapCode otCoapCode`

**Description:**

CoAP Code values.

###### otCoapOptionType (heading level 8)

`typedef enum otCoapOptionType otCoapOptionType`

**Description:**

CoAP Option Numbers.

###### otCoapOption (heading level 8)

`typedef struct otCoapOption otCoapOption`

**Description:**

Represents a CoAP option.

###### otCoapOptionIterator (heading level 8)

`typedef struct otCoapOptionIterator otCoapOptionIterator`

**Description:**

Acts as an iterator for CoAP options.

###### otCoapOptionContentFormat (heading level 8)

`typedef enum otCoapOptionContentFormat otCoapOptionContentFormat`

**Description:**

CoAP Content Format codes.

**Details:**

The full list is documented at [https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#content-formats](https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#content-formats)

###### otCoapBlockSzx (heading level 8)

`typedef enum otCoapBlockSzx otCoapBlockSzx`

**Description:**

CoAP Block Size Exponents.

###### otCoapResponseHandler (heading level 8)

`typedef void(* otCoapResponseHandler) (void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aResult)`

**Description:**

Pointer is called when a CoAP response is received or on the request timeout.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to application-specific context.|
||[in]|aMessage|A pointer to the message buffer containing the response. NULL if no response was received.|
||[in]|aMessageInfo|A pointer to the message info for `aMessage`. NULL if no response was received.|
||[in]|aResult|A result of the CoAP transaction.|

**Details:**

###### otCoapRequestHandler (heading level 8)

`typedef void(* otCoapRequestHandler) (void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)`

**Description:**

Pointer is called when a CoAP request with a given Uri-Path is received.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to arbitrary context information.|
||[in]|aMessage|A pointer to the message.|
||[in]|aMessageInfo|A pointer to the message info for `aMessage`.|

**Details:**

###### otCoapResponseFallback (heading level 8)

`typedef bool(* otCoapResponseFallback) (void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)`

**Description:**

Pointer is called as a fallback if a response did not match a stored CoAP request.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to arbitrary context information.|
||[in]|aMessage|A pointer to the message.|
||[in]|aMessageInfo|A pointer to the message info for `aMessage`.|

**Details:**

###### otCoapToken (heading level 8)

`typedef struct otCoapToken otCoapToken`

**Description:**

Represents a CoAP message token.

###### otCoapResource (heading level 8)

`typedef struct otCoapResource otCoapResource`

**Description:**

Represents a CoAP resource.

###### otCoapTxParameters (heading level 8)

`typedef struct otCoapTxParameters otCoapTxParameters`

**Description:**

Represents the CoAP transmission parameters.

**Details:**

**Note**

- mAckTimeout * ((2 ** (mMaxRetransmit + 1)) - 1) * (mAckRandomFactorNumerator / mAckRandomFactorDenominator) must not exceed what can be represented by a uint32_t (0xffffffff). This limitation allows OpenThread to avoid 64-bit arithmetic.

###### otCoapBlockwiseReceiveHook (heading level 8)

`typedef otError(* otCoapBlockwiseReceiveHook) (void *aContext, const uint8_t *aBlock, uint32_t aPosition, uint16_t aBlockLength, bool aMore, uint32_t aTotalLength)`

**Description:**

Pointer is called when a CoAP message with a block-wise transfer option is received.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to application-specific context.|
||[in]|aBlock|A pointer to the block segment.|
||[in]|aPosition|The position of `aBlock` in a sequence in bytes.|
||[in]|aBlockLength|The length of the block segment in bytes.|
||[in]|aMore|Flag if more block segments are following.|
||[in]|aTotalLength|The total length in bytes of the transferred information (indicated by a Size1 or Size2 option).|

**Details:**

Is available when OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE configuration is enabled.

###### otCoapBlockwiseTransmitHook (heading level 8)

`typedef otError(* otCoapBlockwiseTransmitHook) (void *aContext, uint8_t *aBlock, uint32_t aPosition, uint16_t *aBlockLength, bool *aMore)`

**Description:**

Pointer is called before the next block in a block-wise transfer is sent.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to application-specific context.|
||[inout]|aBlock|A pointer to where the block segment can be written to.|
||[in]|aPosition|The position in a sequence from which to obtain the block segment.|
||[inout]|aBlockLength|On entry, the maximum block segment length in bytes.|
||[out]|aMore|A pointer to the flag if more block segments will follow.|

**Details:**

Is available when OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE configuration is enabled.

**Warnings**

- By changing the value of aBlockLength, the block size of the whole exchange is renegotiated. It is recommended to do this after the first block has been received as later changes could cause problems with other CoAP implementations.

###### otCoapBlockwiseResource (heading level 8)

`typedef struct otCoapBlockwiseResource otCoapBlockwiseResource`

**Description:**

Represents a CoAP resource with block-wise transfer.

###### Functions (heading level 7)

###### otCoapMessageInit (heading level 8)

`otError otCoapMessageInit(otMessage *aMessage, otCoapType aType, otCoapCode aCode)`

**Description:** Initializes a CoAP message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[inout]|aMessage|A pointer to the CoAP message to initialize.|
|[otCoapType](api-coap#ot-coap-type)|[in]|aType|The CoAP Type.|
|[otCoapCode](api-coap#ot-coap-code)|[in]|aCode|The CoAP Code.|

This function initializes the CoAP header, erasing any previously written content in the message. The Message ID is set to zero, and the token is empty (zero-length).

###### otCoapMessageInitResponse (heading level 8)

`otError otCoapMessageInitResponse(otMessage *aResponse, const otMessage *aRequest, otCoapType aType, otCoapCode aCode)`

**Description:** Initializes a CoAP message as a response to a request.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[inout]|aResponse|A pointer to the CoAP response message to initialize.|
|const [otMessage](api-message#ot-message) *|[in]|aRequest|A pointer to the CoAP request message.|
|[otCoapType](api-coap#ot-coap-type)|[in]|aType|The CoAP Type for the response.|
|[otCoapCode](api-coap#ot-coap-code)|[in]|aCode|The CoAP Code for the response.|

This function initializes the CoAP header, erasing any previously written content in the message. The Message ID and Token are copied from the request message.

###### otCoapMessageWriteToken (heading level 8)

`otError otCoapMessageWriteToken(otMessage *aMessage, const otCoapToken *aToken)`

**Description:** Writes the Token in the CoAP message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[inout]|aMessage|The CoAP message.|
|const [otCoapToken](ot-coap-token) *|[in]|aToken|The Token to write.|

###### otCoapMessageGenerateToken (heading level 8)

`void otCoapMessageGenerateToken(otMessage *aMessage, uint8_t aTokenLength)`

**Description:** Writes a randomly generated Token of a given length in the CoAP message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[inout]|aMessage|The CoAP message.|
|uint8_t|[in]|aTokenLength|The Length of a Token (in bytes).|

###### otCoapMessageAppendContentFormatOption (heading level 8)

`otError otCoapMessageAppendContentFormatOption(otMessage *aMessage, otCoapOptionContentFormat aContentFormat)`

**Description:** Appends the Content Format CoAP option as specified in [https://tools.ietf.org/html/rfc7252#page-92](https://tools.ietf.org/html/rfc7252#page-92).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[inout]|aMessage|A pointer to the CoAP message.|
|[otCoapOptionContentFormat](api-coap#ot-coap-option-content-format-1)|[in]|aContentFormat|One of the content formats listed in otCoapOptionContentFormat above.|

This **must** be called before setting otCoapMessageSetPayloadMarker if a payload is to be included in the message.

The function is a convenience wrapper around otCoapMessageAppendUintOption, and if the desired format type code isn't listed in otCoapOptionContentFormat, this base function should be used instead.

###### otCoapMessageAppendOption (heading level 8)

`otError otCoapMessageAppendOption(otMessage *aMessage, uint16_t aNumber, uint16_t aLength, const void *aValue)`

**Description:** Appends a CoAP option in a header.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[inout]|aMessage|A pointer to the CoAP message.|
|uint16_t|[in]|aNumber|The CoAP Option number.|
|uint16_t|[in]|aLength|The CoAP Option length.|
|const void *|[in]|aValue|A pointer to the CoAP value.|

###### otCoapMessageAppendUintOption (heading level 8)

`otError otCoapMessageAppendUintOption(otMessage *aMessage, uint16_t aNumber, uint32_t aValue)`

**Description:** Appends an unsigned integer CoAP option as specified in [https://tools.ietf.org/html/rfc7252#section-3.2](https://tools.ietf.org/html/rfc7252#section-3.2).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[inout]|aMessage|A pointer to the CoAP message.|
|uint16_t|[in]|aNumber|The CoAP Option number.|
|uint32_t|[in]|aValue|The CoAP Option unsigned integer value.|

**See Also**

- otCoapMessageGetOptionUintValue

###### otCoapMessageAppendObserveOption (heading level 8)

`otError otCoapMessageAppendObserveOption(otMessage *aMessage, uint32_t aObserve)`

**Description:** Appends an Observe option.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[inout]|aMessage|A pointer to the CoAP message.|
|uint32_t|[in]|aObserve|Observe field value.|

###### otCoapMessageAppendUriPathOptions (heading level 8)

`otError otCoapMessageAppendUriPathOptions(otMessage *aMessage, const char *aUriPath)`

**Description:** Appends a Uri-Path option.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[inout]|aMessage|A pointer to the CoAP message.|
|const char *|[in]|aUriPath|A pointer to a NULL-terminated string.|

###### otCoapMessageAppendUriQueryOptions (heading level 8)

`otError otCoapMessageAppendUriQueryOptions(otMessage *aMessage, const char *aUriQuery)`

**Description:** Appends a Uri-Query option.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[inout]|aMessage|A pointer to the CoAP message.|
|const char *|[in]|aUriQuery|A pointer to a NULL-terminated string.|

###### otCoapBlockSizeFromExponent (heading level 8)

`uint16_t otCoapBlockSizeFromExponent(otCoapBlockSzx aSize)`

**Description:** Converts a CoAP Block option SZX field to the actual block size.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCoapBlockSzx](api-coap#ot-coap-block-szx)|[in]|aSize|Block size exponent.|

**Returns**

- The actual size exponent value.

###### otCoapMessageAppendBlock2Option (heading level 8)

`otError otCoapMessageAppendBlock2Option(otMessage *aMessage, uint32_t aNum, bool aMore, otCoapBlockSzx aSize)`

**Description:** Appends a Block2 option.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[inout]|aMessage|A pointer to the CoAP message.|
|uint32_t|[in]|aNum|Current block number.|
|bool|[in]|aMore|Boolean to indicate more blocks are to be sent.|
|[otCoapBlockSzx](api-coap#ot-coap-block-szx)|[in]|aSize|Block Size Exponent.|

###### otCoapMessageAppendBlock1Option (heading level 8)

`otError otCoapMessageAppendBlock1Option(otMessage *aMessage, uint32_t aNum, bool aMore, otCoapBlockSzx aSize)`

**Description:** Appends a Block1 option.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[inout]|aMessage|A pointer to the CoAP message.|
|uint32_t|[in]|aNum|Current block number.|
|bool|[in]|aMore|Boolean to indicate more blocks are to be sent.|
|[otCoapBlockSzx](api-coap#ot-coap-block-szx)|[in]|aSize|Block Size Exponent.|

###### otCoapMessageAppendProxyUriOption (heading level 8)

`otError otCoapMessageAppendProxyUriOption(otMessage *aMessage, const char *aUriPath)`

**Description:** Appends a Proxy-Uri option.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[inout]|aMessage|A pointer to the CoAP message.|
|const char *|[in]|aUriPath|A pointer to a NULL-terminated string.|

###### otCoapMessageAppendMaxAgeOption (heading level 8)

`otError otCoapMessageAppendMaxAgeOption(otMessage *aMessage, uint32_t aMaxAge)`

**Description:** Appends a Max-Age option.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[inout]|aMessage|A pointer to the CoAP message.|
|uint32_t|[in]|aMaxAge|The Max-Age value.|

###### otCoapMessageAppendUriQueryOption (heading level 8)

`otError otCoapMessageAppendUriQueryOption(otMessage *aMessage, const char *aUriQuery)`

**Description:** Appends a single Uri-Query option.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[inout]|aMessage|A pointer to the CoAP message.|
|const char *|[in]|aUriQuery|A pointer to NULL-terminated string, which should contain a single key=value pair.|

###### otCoapMessageSetPayloadMarker (heading level 8)

`otError otCoapMessageSetPayloadMarker(otMessage *aMessage)`

**Description:** Adds Payload Marker indicating beginning of the payload to the CoAP header.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[inout]|aMessage|A pointer to the CoAP message.|

###### otCoapMessageGetType (heading level 8)

`otCoapType otCoapMessageGetType(const otMessage *aMessage)`

**Description:** Returns the Type value.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the CoAP message.|

**Returns**

- The Type value.

###### otCoapMessageGetCode (heading level 8)

`otCoapCode otCoapMessageGetCode(const otMessage *aMessage)`

**Description:** Returns the Code value.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the CoAP message.|

**Returns**

- The Code value.

###### otCoapMessageSetCode (heading level 8)

`void otCoapMessageSetCode(otMessage *aMessage, otCoapCode aCode)`

**Description:** Sets the Code value.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[inout]|aMessage|A pointer to the CoAP message to initialize.|
|[otCoapCode](api-coap#ot-coap-code)|[in]|aCode|CoAP message code.|

###### otCoapMessageCodeToString (heading level 8)

`const char * otCoapMessageCodeToString(const otMessage *aMessage)`

**Description:** Returns the CoAP Code as human readable string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the CoAP message.|

@ returns The CoAP Code as string. 

###### otCoapMessageGetMessageId (heading level 8)

`uint16_t otCoapMessageGetMessageId(const otMessage *aMessage)`

**Description:** Returns the Message ID value.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the CoAP message.|

**Returns**

- The Message ID value.

###### otCoapMessageReadToken (heading level 8)

`otError otCoapMessageReadToken(const otMessage *aMessage, otCoapToken *aToken)`

**Description:** Reads the Token from the CoAP message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otMessage](api-message#ot-message) *|[in]|aMessage|The CoAP message.|
|[otCoapToken](ot-coap-token) *|[out]|aToken|A pointer to a `otCoapToken` to output the read Token.|

###### otCoapMessageAreTokensEqual (heading level 8)

`bool otCoapMessageAreTokensEqual(const otCoapToken *aFirstToken, const otCoapToken *aSecondToken)`

**Description:** Indicates whether two given CoAP Tokens are equal.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otCoapToken](ot-coap-token) *|[in]|aFirstToken|The first Token to compare.|
|const [otCoapToken](ot-coap-token) *|[in]|aSecondToken|The second Token to compare.|

###### otCoapOptionIteratorInit (heading level 8)

`otError otCoapOptionIteratorInit(otCoapOptionIterator *aIterator, const otMessage *aMessage)`

**Description:** Initializes an iterator for the options in the given message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCoapOptionIterator](ot-coap-option-iterator) *|[inout]|aIterator|A pointer to the CoAP message option iterator.|
|const [otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the CoAP message.|

###### otCoapOptionIteratorGetFirstOptionMatching (heading level 8)

`const otCoapOption * otCoapOptionIteratorGetFirstOptionMatching(otCoapOptionIterator *aIterator, uint16_t aOption)`

**Description:** Returns a pointer to the first option matching the specified option number.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCoapOptionIterator](ot-coap-option-iterator) *|[in]|aIterator|A pointer to the CoAP message option iterator.|
|uint16_t|[in]|aOption|The option number sought.|

**Returns**

- A pointer to the first matching option. If no matching option is present NULL pointer is returned.

###### otCoapOptionIteratorGetFirstOption (heading level 8)

`const otCoapOption * otCoapOptionIteratorGetFirstOption(otCoapOptionIterator *aIterator)`

**Description:** Returns a pointer to the first option.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCoapOptionIterator](ot-coap-option-iterator) *|[inout]|aIterator|A pointer to the CoAP message option iterator.|

**Returns**

- A pointer to the first option. If no option is present NULL pointer is returned.

###### otCoapOptionIteratorGetNextOptionMatching (heading level 8)

`const otCoapOption * otCoapOptionIteratorGetNextOptionMatching(otCoapOptionIterator *aIterator, uint16_t aOption)`

**Description:** Returns a pointer to the next option matching the specified option number.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCoapOptionIterator](ot-coap-option-iterator) *|[in]|aIterator|A pointer to the CoAP message option iterator.|
|uint16_t|[in]|aOption|The option number sought.|

**Returns**

- A pointer to the next matching option. If no further matching option is present NULL pointer is returned.

###### otCoapOptionIteratorGetNextOption (heading level 8)

`const otCoapOption * otCoapOptionIteratorGetNextOption(otCoapOptionIterator *aIterator)`

**Description:** Returns a pointer to the next option.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCoapOptionIterator](ot-coap-option-iterator) *|[inout]|aIterator|A pointer to the CoAP message option iterator.|

**Returns**

- A pointer to the next option. If no more options are present NULL pointer is returned.

###### otCoapOptionIteratorGetOptionUintValue (heading level 8)

`otError otCoapOptionIteratorGetOptionUintValue(otCoapOptionIterator *aIterator, uint64_t *aValue)`

**Description:** Fills current option value into `aValue` assuming the current value is an unsigned integer encoded according to [https://tools.ietf.org/html/rfc7252#section-3.2](https://tools.ietf.org/html/rfc7252#section-3.2).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCoapOptionIterator](ot-coap-option-iterator) *|[inout]|aIterator|A pointer to the CoAP message option iterator.|
|uint64_t *|[out]|aValue|A pointer to an unsigned integer to receive the option value.|

**See Also**

- [otCoapMessageAppendUintOption](api-coap#ot-coap-message-append-uint-option)

###### otCoapOptionIteratorGetOptionValue (heading level 8)

`otError otCoapOptionIteratorGetOptionValue(otCoapOptionIterator *aIterator, void *aValue)`

**Description:** Fills current option value into `aValue`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCoapOptionIterator](ot-coap-option-iterator) *|[inout]|aIterator|A pointer to the CoAP message option iterator.|
|void *|[out]|aValue|A pointer to a buffer to receive the option value.|

###### otCoapNewMessage (heading level 8)

`otMessage * otCoapNewMessage(otInstance *aInstance, const otMessageSettings *aSettings)`

**Description:** Creates a new CoAP message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otMessageSettings](ot-message-settings) *|[in]|aSettings|A pointer to the message settings or NULL to set default settings.|

**Note**

- If `aSettings` is 'NULL', the link layer security is enabled and the message priority is set to OT_MESSAGE_PRIORITY_NORMAL by default.

**Returns**

- A pointer to the message buffer or NULL if no message buffers are available or parameters are invalid.

###### otCoapSendRequestWithParameters (heading level 8)

`otError otCoapSendRequestWithParameters(otInstance *aInstance, otMessage *aMessage, const otMessageInfo *aMessageInfo, otCoapResponseHandler aHandler, void *aContext, const otCoapTxParameters *aTxParameters)`

**Description:** Sends a CoAP request with custom transmission parameters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the message to send.|
|const [otMessageInfo](ot-message-info) *|[in]|aMessageInfo|A pointer to the message info associated with `aMessage`.|
|[otCoapResponseHandler](api-coap#ot-coap-response-handler)|[in]|aHandler|A function pointer that shall be called on response reception or timeout.|
|void *|[in]|aContext|A pointer to arbitrary context information. May be NULL if not used.|
|const [otCoapTxParameters](ot-coap-tx-parameters) *|[in]|aTxParameters|A pointer to transmission parameters for this request. Use NULL for defaults. Otherwise, parameters given must meet the following conditions:<br/><br/>1. mMaxRetransmit is no more than OT_COAP_MAX_RETRANSMIT.<br/>2. mAckRandomFactorNumerator / mAckRandomFactorDenominator must not be below 1.0.<br/>3. The calculated exchange life time must not overflow uint32_t.|

If a response for a request is expected, respective function and context information should be provided. If no response is expected, these arguments should be NULL pointers.

###### otCoapSendRequest (heading level 8)

`otError otCoapSendRequest(otInstance *aInstance, otMessage *aMessage, const otMessageInfo *aMessageInfo, otCoapResponseHandler aHandler, void *aContext)`

**Description:** Sends a CoAP request.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the message to send.|
|const [otMessageInfo](ot-message-info) *|[in]|aMessageInfo|A pointer to the message info associated with `aMessage`.|
|[otCoapResponseHandler](api-coap#ot-coap-response-handler)|[in]|aHandler|A function pointer that shall be called on response reception or timeout.|
|void *|[in]|aContext|A pointer to arbitrary context information. May be NULL if not used.|

If a response for a request is expected, respective function and context information should be provided. If no response is expected, these arguments should be NULL pointers.

###### otCoapStart (heading level 8)

`otError otCoapStart(otInstance *aInstance, uint16_t aPort)`

**Description:** Starts the CoAP server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint16_t|[in]|aPort|The local UDP port to bind to.|

###### otCoapStop (heading level 8)

`otError otCoapStop(otInstance *aInstance)`

**Description:** Stops the CoAP server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

###### otCoapAddResource (heading level 8)

`void otCoapAddResource(otInstance *aInstance, otCoapResource *aResource)`

**Description:** Adds a resource to the CoAP server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otCoapResource](ot-coap-resource) *|[in]|aResource|A pointer to the resource.|

###### otCoapRemoveResource (heading level 8)

`void otCoapRemoveResource(otInstance *aInstance, otCoapResource *aResource)`

**Description:** Removes a resource from the CoAP server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otCoapResource](ot-coap-resource) *|[in]|aResource|A pointer to the resource.|

###### otCoapSetDefaultHandler (heading level 8)

`void otCoapSetDefaultHandler(otInstance *aInstance, otCoapRequestHandler aHandler, void *aContext)`

**Description:** Sets the default handler for unhandled CoAP requests.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otCoapRequestHandler](api-coap#ot-coap-request-handler)|[in]|aHandler|A function pointer that shall be called when an unhandled request arrives.|
|void *|[in]|aContext|A pointer to arbitrary context information. May be NULL if not used.|

###### otCoapSetResponseFallback (heading level 8)

`void otCoapSetResponseFallback(otInstance *aInstance, otCoapResponseFallback aHandler, void *aContext)`

**Description:** Sets a fallback handler for CoAP responses not matching any active/pending request.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otCoapResponseFallback](api-coap#ot-coap-response-fallback)|[in]|aHandler|A function pointer that shall be called as a fallback for responses without matching active/pending CoAP requests.|
|void *|[in]|aContext|A pointer to arbitrary context information. May be NULL if not used.|

###### otCoapSendResponseWithParameters (heading level 8)

`otError otCoapSendResponseWithParameters(otInstance *aInstance, otMessage *aMessage, const otMessageInfo *aMessageInfo, const otCoapTxParameters *aTxParameters)`

**Description:** Sends a CoAP response from the server with custom transmission parameters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the CoAP response to send.|
|const [otMessageInfo](ot-message-info) *|[in]|aMessageInfo|A pointer to the message info associated with `aMessage`.|
|const [otCoapTxParameters](ot-coap-tx-parameters) *|[in]|aTxParameters|A pointer to transmission parameters for this response. Use NULL for defaults.|

###### otCoapSendResponse (heading level 8)

`otError otCoapSendResponse(otInstance *aInstance, otMessage *aMessage, const otMessageInfo *aMessageInfo)`

**Description:** Sends a CoAP response from the server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the CoAP response to send.|
|const [otMessageInfo](ot-message-info) *|[in]|aMessageInfo|A pointer to the message info associated with `aMessage`.|

###### otCoapAddBlockWiseResource (heading level 8)

`void otCoapAddBlockWiseResource(otInstance *aInstance, otCoapBlockwiseResource *aResource)`

**Description:** Adds a block-wise resource to the CoAP server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otCoapBlockwiseResource](ot-coap-blockwise-resource) *|[in]|aResource|A pointer to the resource.|

Requires `OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE`.

###### otCoapRemoveBlockWiseResource (heading level 8)

`void otCoapRemoveBlockWiseResource(otInstance *aInstance, otCoapBlockwiseResource *aResource)`

**Description:** Removes a block-wise resource from the CoAP server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otCoapBlockwiseResource](ot-coap-blockwise-resource) *|[in]|aResource|A pointer to the resource.|

Requires `OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE`.

###### otCoapSendRequestBlockWiseWithParameters (heading level 8)

`otError otCoapSendRequestBlockWiseWithParameters(otInstance *aInstance, otMessage *aMessage, const otMessageInfo *aMessageInfo, otCoapResponseHandler aHandler, void *aContext, const otCoapTxParameters *aTxParameters, otCoapBlockwiseTransmitHook aTransmitHook, otCoapBlockwiseReceiveHook aReceiveHook)`

**Description:** Sends a CoAP request block-wise with custom transmission parameters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the message to send.|
|const [otMessageInfo](ot-message-info) *|[in]|aMessageInfo|A pointer to the message info associated with `aMessage`.|
|[otCoapResponseHandler](api-coap#ot-coap-response-handler)|[in]|aHandler|A function pointer that shall be called on response reception or timeout.|
|void *|[in]|aContext|A pointer to arbitrary context information. May be NULL if not used.|
|const [otCoapTxParameters](ot-coap-tx-parameters) *|[in]|aTxParameters|A pointer to transmission parameters for this request. Use NULL for defaults.|
|[otCoapBlockwiseTransmitHook](api-coap#ot-coap-blockwise-transmit-hook)|[in]|aTransmitHook|A pointer to a hook function for outgoing block-wise transfer.|
|[otCoapBlockwiseReceiveHook](api-coap#ot-coap-blockwise-receive-hook)|[in]|aReceiveHook|A pointer to a hook function for incoming block-wise transfer.|

Is available when OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE configuration is enabled.

If a response for a request is expected, respective function and context information should be provided. If the response is expected to be block-wise, a respective hook function should be provided. If no response is expected, these arguments should be NULL pointers.

###### otCoapSendRequestBlockWise (heading level 8)

`otError otCoapSendRequestBlockWise(otInstance *aInstance, otMessage *aMessage, const otMessageInfo *aMessageInfo, otCoapResponseHandler aHandler, void *aContext, otCoapBlockwiseTransmitHook aTransmitHook, otCoapBlockwiseReceiveHook aReceiveHook)`

**Description:** Sends a CoAP request block-wise.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the message to send.|
|const [otMessageInfo](ot-message-info) *|[in]|aMessageInfo|A pointer to the message info associated with `aMessage`.|
|[otCoapResponseHandler](api-coap#ot-coap-response-handler)|[in]|aHandler|A function pointer that shall be called on response reception or timeout.|
|void *|[in]|aContext|A pointer to arbitrary context information. May be NULL if not used.|
|[otCoapBlockwiseTransmitHook](api-coap#ot-coap-blockwise-transmit-hook)|[in]|aTransmitHook|A pointer to a hook function for outgoing block-wise transfer.|
|[otCoapBlockwiseReceiveHook](api-coap#ot-coap-blockwise-receive-hook)|[in]|aReceiveHook|A pointer to a hook function for incoming block-wise transfer.|

Is available when OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE configuration is enabled.

If a response for a request is expected, respective function and context information should be provided. If the response is expected to be block-wise, a respective hook function should be provided. If no response is expected, these arguments should be NULL pointers.

###### otCoapSendResponseBlockWiseWithParameters (heading level 8)

`otError otCoapSendResponseBlockWiseWithParameters(otInstance *aInstance, otMessage *aMessage, const otMessageInfo *aMessageInfo, const otCoapTxParameters *aTxParameters, void *aContext, otCoapBlockwiseTransmitHook aTransmitHook)`

**Description:** Sends a CoAP response block-wise from the server with custom transmission parameters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the CoAP response to send.|
|const [otMessageInfo](ot-message-info) *|[in]|aMessageInfo|A pointer to the message info associated with `aMessage`.|
|const [otCoapTxParameters](ot-coap-tx-parameters) *|[in]|aTxParameters|A pointer to transmission parameters for this response. Use NULL for defaults.|
|void *|[in]|aContext|A pointer to arbitrary context information. May be NULL if not used.|
|[otCoapBlockwiseTransmitHook](api-coap#ot-coap-blockwise-transmit-hook)|[in]|aTransmitHook|A pointer to a hook function for outgoing block-wise transfer.|

Is available when OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE configuration is enabled.

###### otCoapSendResponseBlockWise (heading level 8)

`otError otCoapSendResponseBlockWise(otInstance *aInstance, otMessage *aMessage, const otMessageInfo *aMessageInfo, void *aContext, otCoapBlockwiseTransmitHook aTransmitHook)`

**Description:** Sends a CoAP response block-wise from the server.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the CoAP response to send.|
|const [otMessageInfo](ot-message-info) *|[in]|aMessageInfo|A pointer to the message info associated with `aMessage`.|
|void *|[in]|aContext|A pointer to arbitrary context information. May be NULL if not used.|
|[otCoapBlockwiseTransmitHook](api-coap#ot-coap-blockwise-transmit-hook)|[in]|aTransmitHook|A pointer to a hook function for outgoing block-wise transfer.|

Is available when OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE configuration is enabled.

###### otCoapMessageSetToken (heading level 8)

`otError otCoapMessageSetToken(otMessage *aMessage, const uint8_t *aToken, uint8_t aTokenLength)`

**Description:** Sets the Token value and length in a CoAP message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMessage](api-message#ot-message) *|[inout]|aMessage|A pointer to the CoAP message.|
|const uint8_t *|[in]|aToken|A pointer to the Token value.|
|uint8_t|[in]|aTokenLength|The Length of `aToken`.|

**Deprecated**This function is deprecated. Use `otCoapMessageWriteToken()` instead.

###### otCoapMessageGetTokenLength (heading level 8)

`uint8_t otCoapMessageGetTokenLength(const otMessage *aMessage)`

**Description:** Returns the Token length.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the CoAP message.|

**Deprecated**This function is deprecated. Use `otCoapMessageReadToken()` instead.

**Returns**

- The Token length.

###### otCoapMessageGetToken (heading level 8)

`const uint8_t * otCoapMessageGetToken(const otMessage *aMessage)`

**Description:** Returns a pointer to the Token value.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otMessage](api-message#ot-message) *|[in]|aMessage|A pointer to the CoAP message.|

**Deprecated**This function is deprecated. Use `otCoapMessageReadToken()` instead.

**Note**

- A previously returned pointer (`const uint8_t *`) will be invalidated upon the next call to this function.

**Returns**

- A pointer to the Token value.

###### Macros (heading level 7)

`#define OT_DEFAULT_COAP_PORT 5683`

**Description**: Default CoAP port, as specified in RFC 7252.

`#define OT_COAP_DEFAULT_TOKEN_LENGTH 2`

**Description**: Default token length.

`#define OT_COAP_MAX_TOKEN_LENGTH 8`

**Description**: Max token length as specified (RFC 7252).

`#define OT_COAP_MAX_RETRANSMIT 20`

**Description**: Max retransmit supported by OpenThread.

`#define OT_COAP_MIN_ACK_TIMEOUT 1000`

**Description**: Minimal ACK timeout in milliseconds supported by OpenThread.

`#define OT_COAP_CODE (c, d)`

**Description**: Helper macro to define CoAP Code values.

Represents a CoAP option. 

###### Public Attributes (heading level 8)

###### mNumber (heading level 9)

```
uint16_t otCoapOption::mNumber
```

**Description:** Option Number.

###### mLength (heading level 9)

```
uint16_t otCoapOption::mLength
```

**Description:** Option Length.

Acts as an iterator for CoAP options. 

###### Public Attributes (heading level 8)

###### mMessage (heading level 9)

```
const otMessage* otCoapOptionIterator::mMessage
```

**Description:** CoAP message.

###### mOption (heading level 9)

```
otCoapOption otCoapOptionIterator::mOption
```

**Description:** CoAP message option.

###### mNextOptionOffset (heading level 9)

```
uint16_t otCoapOptionIterator::mNextOptionOffset
```

**Description:** Byte offset of next option.

Represents a CoAP message token. 

###### Public Attributes (heading level 8)

###### m8 (heading level 9)

```
uint8_t otCoapToken::m8[OT_COAP_MAX_TOKEN_LENGTH]
```

**Description:** The token bytes.

###### mLength (heading level 9)

```
uint8_t otCoapToken::mLength
```

**Description:** The token length in bytes.

Represents a CoAP resource. 

###### Public Attributes (heading level 8)

###### mUriPath (heading level 9)

```
const char* otCoapResource::mUriPath
```

**Description:** The URI Path string.

###### mHandler (heading level 9)

```
otCoapRequestHandler otCoapResource::mHandler
```

**Description:** The callback for handling a received request.

###### mContext (heading level 9)

```
void* otCoapResource::mContext
```

**Description:** Application-specific context.

###### mNext (heading level 9)

```
struct otCoapResource* otCoapResource::mNext
```

**Description:** The next CoAP resource in the list.

Represents the CoAP transmission parameters. 

**Note**

- mAckTimeout * ((2 ** (mMaxRetransmit + 1)) - 1) * (mAckRandomFactorNumerator / mAckRandomFactorDenominator) must not exceed what can be represented by a uint32_t (0xffffffff). This limitation allows OpenThread to avoid 64-bit arithmetic.

###### Public Attributes (heading level 8)

###### mAckTimeout (heading level 9)

```
uint32_t otCoapTxParameters::mAckTimeout
```

**Description:** Minimum spacing before first retransmission when ACK is not received, in milliseconds (RFC7252 default value is 2000ms).

###### mAckRandomFactorNumerator (heading level 9)

```
uint8_t otCoapTxParameters::mAckRandomFactorNumerator
```

**Description:** Numerator of ACK_RANDOM_FACTOR used to calculate maximum spacing before first retransmission when ACK is not received (RFC7252 default value of ACK_RANDOM_FACTOR is 1.5; must not be decreased below 1).

###### mAckRandomFactorDenominator (heading level 9)

```
uint8_t otCoapTxParameters::mAckRandomFactorDenominator
```

**Description:** Denominator of ACK_RANDOM_FACTOR used to calculate maximum spacing before first retransmission when ACK is not received (RFC7252 default value of ACK_RANDOM_FACTOR is 1.5; must not be decreased below 1).

###### mMaxRetransmit (heading level 9)

```
uint8_t otCoapTxParameters::mMaxRetransmit
```

**Description:** Maximum number of retransmissions for CoAP Confirmable messages (RFC7252 default value is 4).

Represents a CoAP resource with block-wise transfer. 

###### Public Attributes (heading level 8)

###### mUriPath (heading level 9)

```
const char* otCoapBlockwiseResource::mUriPath
```

**Description:** The URI Path string.

###### mHandler (heading level 9)

```
otCoapRequestHandler otCoapBlockwiseResource::mHandler
```

**Description:** The callback for handling a received request.

###### mReceiveHook (heading level 9)

```
otCoapBlockwiseReceiveHook otCoapBlockwiseResource::mReceiveHook
```

**Description:** The callback for handling incoming block-wise transfer.

**Details:** This callback is available when OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE configuration is enabled.

###### mTransmitHook (heading level 9)

```
otCoapBlockwiseTransmitHook otCoapBlockwiseResource::mTransmitHook
```

**Description:** The callback for handling outgoing block-wise transfer.

**Details:** This callback is available when OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE configuration is enabled.

###### mContext (heading level 9)

```
void* otCoapBlockwiseResource::mContext
```

**Description:** Application-specific context.

###### mNext (heading level 9)

```
struct otCoapBlockwiseResource* otCoapBlockwiseResource::mNext
```

**Description:** The next CoAP resource in the list.

##### Command Line Interface

This module includes functions that control the Thread stack's execution. 

###### Modules

[otCliCommand](ot-cli-command)

###### Typedefs

###### otCliOutputCallback (heading level 7)

`typedef int(* otCliOutputCallback) (void *aContext, const char *aFormat, va_list aArguments)`

**Description:**

Pointer is called to notify about Console output.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[out]|aContext|A user context pointer.|
||[in]|aFormat|The format string.|
||[in]|aArguments|The format string arguments.|

**Details:**

**Returns**

- Number of bytes written by the callback.

###### otCliCommand (heading level 7)

`typedef struct otCliCommand otCliCommand`

###### Functions

###### otCliInit (heading level 7)

`void otCliInit(otInstance *aInstance, otCliOutputCallback aCallback, void *aContext)`

**Description:** Initialize the CLI module.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otCliOutputCallback](api-cli#ot-cli-output-callback)|[in]|aCallback|A callback method called to process CLI output.|
|void *|[in]|aContext|A user context pointer.|

###### otCliInputLine (heading level 7)

`void otCliInputLine(char *aBuf)`

**Description:** Is called to feed in a console input line.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|char *|[in]|aBuf|A pointer to a null-terminated string.|

###### otCliSetUserCommands (heading level 7)

`otError otCliSetUserCommands(const otCliCommand *aUserCommands, uint8_t aLength, void *aContext)`

**Description:** Set a user command table.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otCliCommand](ot-cli-command) *|[in]|aUserCommands|A pointer to an array with user commands.|
|uint8_t|[in]|aLength|`aUserCommands` length.|
|void *|[in]|aContext|`The` context passed to the handler.|

###### otCliOutputBytes (heading level 7)

`void otCliOutputBytes(const uint8_t *aBytes, uint8_t aLength)`

**Description:** Write a number of bytes to the CLI console as a hex string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const uint8_t *|[in]|aBytes|A pointer to data which should be printed.|
|uint8_t|[in]|aLength|`aBytes` length.|

###### otCliOutputFormat (heading level 7)

`void otCliOutputFormat(const char *aFmt,...)`

**Description:** Write formatted string to the CLI console.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aFmt|A pointer to the format string.|
|...|[in]|undefined|A matching list of arguments.|

###### otCliAppendResult (heading level 7)

`void otCliAppendResult(otError aError)`

**Description:** Write error code to the CLI console.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otError](api-error#ot-error)|[in]|aError|Error code value.|

If the `aError` is `OT_ERROR_PENDING` nothing will be outputted.

###### otCliPlatLogv (heading level 7)

`void otCliPlatLogv(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, va_list aArgs)`

**Description:** Callback to write the OpenThread Log to the CLI console.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otLogLevel](plat-logging#ot-log-level)|[in]|aLogLevel|The log level.|
|[otLogRegion](plat-logging#ot-log-region)|[in]|aLogRegion|The log region.|
|const char *|[in]|aFormat|A pointer to the format string.|
|va_list|[in]|aArgs|va_list matching aFormat.|

###### otCliVendorSetUserCommands (heading level 7)

`void otCliVendorSetUserCommands(void)`

**Description:** Callback to allow vendor specific commands to be added to the user command table.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

Available when `OPENTHREAD_CONFIG_CLI_VENDOR_COMMANDS_ENABLE` is enabled and `OPENTHREAD_CONFIG_CLI_MAX_USER_CMD_ENTRIES` is greater than 1.

Represents a CLI command. 

###### Public Attributes (heading level 7)

###### mName (heading level 8)

```
const char * otCliCommand::mName
```

**Description:** A pointer to the command string.

###### mCommand (heading level 8)

```
otError(* otCliCommand::mCommand) (void *aContext, uint8_t aArgsLength, char *aArgs[])
```

**Description:** A function pointer to process the command.

###### mCommand (heading level 8)

```
void(* otCliCommand::mCommand) (void *aContext, uint8_t aArgsLength, char *aArgs[])
```

**Description:** A function pointer to process the command.

##### History Tracker

Records the history of different events, for example RX and TX messages or network info changes. 

All tracked entries are timestamped.

The functions in this module are available when `OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE` is enabled. 

###### Modules

[otHistoryTrackerIterator](ot-history-tracker-iterator)

[otHistoryTrackerNetworkInfo](ot-history-tracker-network-info)

[otHistoryTrackerUnicastAddressInfo](ot-history-tracker-unicast-address-info)

[otHistoryTrackerMulticastAddressInfo](ot-history-tracker-multicast-address-info)

[otHistoryTrackerMessageInfo](ot-history-tracker-message-info)

[otHistoryTrackerNeighborInfo](ot-history-tracker-neighbor-info)

[otHistoryTrackerRouterInfo](ot-history-tracker-router-info)

[otHistoryTrackerOnMeshPrefixInfo](ot-history-tracker-on-mesh-prefix-info)

[otHistoryTrackerExternalRouteInfo](ot-history-tracker-external-route-info)

[otHistoryTrackerDnsSrpAddrInfo](ot-history-tracker-dns-srp-addr-info)

[otHistoryTrackerFavoredOmrPrefix](ot-history-tracker-favored-omr-prefix)

[otHistoryTrackerFavoredOnLinkPrefix](ot-history-tracker-favored-on-link-prefix)

[otHistoryTrackerDhcp6PdInfo](ot-history-tracker-dhcp6-pd-info)

[otHistoryTrackerAilRouter](ot-history-tracker-ail-router)

###### Enumerations

###### otHistoryTrackerAddressEvent (heading level 7)

```
enum otHistoryTrackerAddressEvent {
    OT_HISTORY_TRACKER_ADDRESS_EVENT_ADDED = 0
    OT_HISTORY_TRACKER_ADDRESS_EVENT_REMOVED = 1
}
```

**Description:**

Defines the events for an IPv6 (unicast or multicast) address info (i.e., whether address is added or removed).

**Enumerator:**

|   |   |
|---|---|
|OT_HISTORY_TRACKER_ADDRESS_EVENT_ADDED|Address is added.|
|OT_HISTORY_TRACKER_ADDRESS_EVENT_REMOVED|Address is removed.|

###### @1 (heading level 7)

```
enum @1 {
    OT_HISTORY_TRACKER_MSG_PRIORITY_LOW = OT_MESSAGE_PRIORITY_LOW
    OT_HISTORY_TRACKER_MSG_PRIORITY_NORMAL = OT_MESSAGE_PRIORITY_NORMAL
    OT_HISTORY_TRACKER_MSG_PRIORITY_HIGH = OT_MESSAGE_PRIORITY_HIGH
    OT_HISTORY_TRACKER_MSG_PRIORITY_NET = OT_MESSAGE_PRIORITY_HIGH + 1
}
```

**Description:**

Constants representing message priority used in `otHistoryTrackerMessageInfo` struct.

**Enumerator:**

|   |   |
|---|---|
|OT_HISTORY_TRACKER_MSG_PRIORITY_LOW|Low priority level.|
|OT_HISTORY_TRACKER_MSG_PRIORITY_NORMAL|Normal priority level.|
|OT_HISTORY_TRACKER_MSG_PRIORITY_HIGH|High priority level.|
|OT_HISTORY_TRACKER_MSG_PRIORITY_NET|Network Control priority level.|

###### otHistoryTrackerNeighborEvent (heading level 7)

```
enum otHistoryTrackerNeighborEvent {
    OT_HISTORY_TRACKER_NEIGHBOR_EVENT_ADDED = 0
    OT_HISTORY_TRACKER_NEIGHBOR_EVENT_REMOVED = 1
    OT_HISTORY_TRACKER_NEIGHBOR_EVENT_CHANGED = 2
    OT_HISTORY_TRACKER_NEIGHBOR_EVENT_RESTORING = 3
}
```

**Description:**

Defines the events in a neighbor info (i.e.

**Details:**

whether neighbor is added, removed, or changed).

Event `OT_HISTORY_TRACKER_NEIGHBOR_EVENT_RESTORING` is applicable to child neighbors only. It is triggered after the device (re)starts and when the previous children list is retrieved from non-volatile settings and the device tries to restore connection to them.

**Enumerator:**

|   |   |
|---|---|
|OT_HISTORY_TRACKER_NEIGHBOR_EVENT_ADDED|Neighbor is added.|
|OT_HISTORY_TRACKER_NEIGHBOR_EVENT_REMOVED|Neighbor is removed.|
|OT_HISTORY_TRACKER_NEIGHBOR_EVENT_CHANGED|Neighbor changed (e.g., device mode flags changed).|
|OT_HISTORY_TRACKER_NEIGHBOR_EVENT_RESTORING|Neighbor is being restored (applicable to child only).|

###### otHistoryTrackerRouterEvent (heading level 7)

```
enum otHistoryTrackerRouterEvent {
    OT_HISTORY_TRACKER_ROUTER_EVENT_ADDED = 0
    OT_HISTORY_TRACKER_ROUTER_EVENT_REMOVED = 1
    OT_HISTORY_TRACKER_ROUTER_EVENT_NEXT_HOP_CHANGED = 2
    OT_HISTORY_TRACKER_ROUTER_EVENT_COST_CHANGED = 3
}
```

**Description:**

Defines the events in a router info (i.e.

**Details:**

whether router is added, removed, or changed).

**Enumerator:**

|   |   |
|---|---|
|OT_HISTORY_TRACKER_ROUTER_EVENT_ADDED|Router is added (router ID allocated).|
|OT_HISTORY_TRACKER_ROUTER_EVENT_REMOVED|Router entry is removed (router ID released).|
|OT_HISTORY_TRACKER_ROUTER_EVENT_NEXT_HOP_CHANGED|Router entry next hop and cost changed.|
|OT_HISTORY_TRACKER_ROUTER_EVENT_COST_CHANGED|Router entry path cost changed (next hop as before).|

###### otHistoryTrackerNetDataEvent (heading level 7)

```
enum otHistoryTrackerNetDataEvent {
    OT_HISTORY_TRACKER_NET_DATA_ENTRY_ADDED = 0
    OT_HISTORY_TRACKER_NET_DATA_ENTRY_REMOVED = 1
}
```

**Description:**

Defines the events for a Network Data entry (i.e., whether an entry is added or removed).

**Enumerator:**

|   |   |
|---|---|
|OT_HISTORY_TRACKER_NET_DATA_ENTRY_ADDED|Network data entry is added.|
|OT_HISTORY_TRACKER_NET_DATA_ENTRY_REMOVED|Network data entry is removed.|

###### otHistoryTrackerDnsSrpAddrType (heading level 7)

```
enum otHistoryTrackerDnsSrpAddrType {
    OT_HISTORY_TRACKER_DNS_SRP_ADDR_TYPE_UNICAST_LOCAL
    OT_HISTORY_TRACKER_DNS_SRP_ADDR_TYPE_UNICAST_INFRA
    OT_HISTORY_TRACKER_DNS_SRP_ADDR_TYPE_ANYCAST
}
```

**Description:**

Represents the DNS/SRP server address type parsed from Network Data service entries.

**Enumerator:**

|   |   |
|---|---|
|OT_HISTORY_TRACKER_DNS_SRP_ADDR_TYPE_UNICAST_LOCAL|Unicast address type local (in server data).|
|OT_HISTORY_TRACKER_DNS_SRP_ADDR_TYPE_UNICAST_INFRA|Unicast address type infrastructure (in service data).|
|OT_HISTORY_TRACKER_DNS_SRP_ADDR_TYPE_ANYCAST|Anycast address type.|

###### otHistoryTrackerBorderAgentEpskcEvent (heading level 7)

```
enum otHistoryTrackerBorderAgentEpskcEvent {
    OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_ACTIVATED
    OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_CONNECTED
    OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_PETITIONED
    OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_RETRIEVED_ACTIVE_DATASET
    OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_RETRIEVED_PENDING_DATASET
    OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_KEEP_ALIVE
    OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_LOCAL_CLOSE
    OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_REMOTE_CLOSE
    OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_SESSION_ERROR
    OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_SESSION_TIMEOUT
    OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_MAX_ATTEMPTS
    OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_EPSKC_TIMEOUT
    OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_UNKNOWN
}
```

**Description:**

Represents events during the Border Agent's ePSKc journey.

**Enumerator:**

|   |   |
|---|---|
|OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_ACTIVATED|ePSKc mode is activated.|
|OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_CONNECTED|Secure session is connected.|
|OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_PETITIONED|Commissioner petition is received.|
|OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_RETRIEVED_ACTIVE_DATASET|Active dataset is retrieved.|
|OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_RETRIEVED_PENDING_DATASET|Pending dataset is retrieved.|
|OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_KEEP_ALIVE|Keep alive message is received.|
|OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_LOCAL_CLOSE|Deactivated by a call to the API.|
|OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_REMOTE_CLOSE|Disconnected by the peer.|
|OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_SESSION_ERROR|Disconnected due to some error.|
|OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_SESSION_TIMEOUT|Disconnected due to timeout.|
|OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_MAX_ATTEMPTS|Max allowed attempts reached.|
|OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_EPSKC_TIMEOUT|ePSKc mode timed out.|
|OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_UNKNOWN|Deactivated for an unknown reason.|

###### otHistoryTrackerAilRouterEvent (heading level 7)

```
enum otHistoryTrackerAilRouterEvent {
    OT_HISTORY_TRACKER_AIL_ROUTER_EVENT_ADDED = 0
    OT_HISTORY_TRACKER_AIL_ROUTER_EVENT_CHANGED = 1
    OT_HISTORY_TRACKER_AIL_ROUTER_EVENT_REMOVED = 2
}
```

**Description:**

Defines events for discovered routers on an Adjacent Infrastructure Link (AIL).

**Details:**

This applies when a device is acting as a Border Router, processing received Router Advertisements and tracking AIL routers.

`OT_HISTORY_TRACKER_AIL_ROUTER_EVENT_CHANGED` is used if any of the properties in the `otHistoryTrackerAilRouter` structure associated with a specific router changes.

**Enumerator:**

|   |   |
|---|---|
|OT_HISTORY_TRACKER_AIL_ROUTER_EVENT_ADDED|A new AIL router is discovered.|
|OT_HISTORY_TRACKER_AIL_ROUTER_EVENT_CHANGED|A property in the router's information has changed.|
|OT_HISTORY_TRACKER_AIL_ROUTER_EVENT_REMOVED|The AIL router is removed and no longer tracked.|

###### Typedefs

###### otHistoryTrackerIterator (heading level 7)

`typedef struct otHistoryTrackerIterator otHistoryTrackerIterator`

**Description:**

Represents an iterator to iterate through a history list.

**Details:**

The fields in this type are opaque (intended for use by OpenThread core) and therefore should not be accessed/used by caller.

Before using an iterator, it MUST be initialized using `otHistoryTrackerInitIterator()`,

###### otHistoryTrackerNetworkInfo (heading level 7)

`typedef struct otHistoryTrackerNetworkInfo otHistoryTrackerNetworkInfo`

**Description:**

Represents Thread network info.

###### otHistoryTrackerUnicastAddressInfo (heading level 7)

`typedef struct otHistoryTrackerUnicastAddressInfo otHistoryTrackerUnicastAddressInfo`

**Description:**

Represent a unicast IPv6 address info.

###### otHistoryTrackerMulticastAddressInfo (heading level 7)

`typedef struct otHistoryTrackerMulticastAddressInfo otHistoryTrackerMulticastAddressInfo`

**Description:**

Represent an IPv6 multicast address info.

###### otHistoryTrackerMessageInfo (heading level 7)

`typedef struct otHistoryTrackerMessageInfo otHistoryTrackerMessageInfo`

**Description:**

Represents a RX/TX IPv6 message info.

**Details:**

Some of the fields in this struct are applicable to a RX message or a TX message only, e.g., `mAveRxRss` is the average RSS of all fragment frames that form a received message and is only applicable for a RX message.

###### otHistoryTrackerNeighborInfo (heading level 7)

`typedef struct otHistoryTrackerNeighborInfo otHistoryTrackerNeighborInfo`

**Description:**

Represents a neighbor info.

###### otHistoryTrackerRouterInfo (heading level 7)

`typedef struct otHistoryTrackerRouterInfo otHistoryTrackerRouterInfo`

**Description:**

Represents a router table entry event.

###### otHistoryTrackerOnMeshPrefixInfo (heading level 7)

`typedef struct otHistoryTrackerOnMeshPrefixInfo otHistoryTrackerOnMeshPrefixInfo`

**Description:**

Represent a Network Data on mesh prefix info.

###### otHistoryTrackerExternalRouteInfo (heading level 7)

`typedef struct otHistoryTrackerExternalRouteInfo otHistoryTrackerExternalRouteInfo`

**Description:**

Represent a Network Data extern route info.

###### otHistoryTrackerDnsSrpAddrInfo (heading level 7)

`typedef struct otHistoryTrackerDnsSrpAddrInfo otHistoryTrackerDnsSrpAddrInfo`

**Description:**

Represents DNS/SRP server address information parsed from a Network Data service entry.

**Details:**

The `mType` field specifies the entry type. Some fields are only applicable to specific types.

- The `mPort` field is only applicable for `OT_HISTORY_TRACKER_DNS_SRP_ADDR_TYPE_UNICAST_*` types.
- The `mSequenceNumber` field is only applicable for the `OT_HISTORY_TRACKER_DNS_SRP_ADDR_TYPE_ANYCAST` type.
- Other fields are common and used for all address types.

###### otHistoryTrackerFavoredOmrPrefix (heading level 7)

`typedef struct otHistoryTrackerFavoredOmrPrefix otHistoryTrackerFavoredOmrPrefix`

**Description:**

Represents a favored OMR prefix tracked by a device acting as a Border Router (BR).

**Details:**

The `mIsLocal` field indicates whether the favored OMR prefix is the same as the local one maintained by this BR. The local OMR prefix can be either based on (random) ULA or a prefix delegated via DHCPv6-PD.

###### otHistoryTrackerFavoredOnLinkPrefix (heading level 7)

`typedef struct otHistoryTrackerFavoredOnLinkPrefix otHistoryTrackerFavoredOnLinkPrefix`

**Description:**

Represents a favored on-link prefix on AIL tracked by a device acting as a Border Router (BR).

**Details:**

The `mIsLocal` field indicates whether the favored on-link prefix is the same as the local one maintained by this BR.

###### otHistoryTrackerDhcp6PdInfo (heading level 7)

`typedef struct otHistoryTrackerDhcp6PdInfo otHistoryTrackerDhcp6PdInfo`

**Description:**

Represents the DHCPv6-PD state and delegated prefix (if any) by a device acting as Border Router (BR).

###### otHistoryTrackerAilRouter (heading level 7)

`typedef struct otHistoryTrackerAilRouter otHistoryTrackerAilRouter`

**Description:**

Represents information about a discovered router on an Adjacent Infrastructure Link (AIL).

**Details:**

This applies when a device is acting as a Border Router, processing received Router Advertisements and tracking information about discovered AIL routers.

`mProvidesDefaultRoute` indicates whether the router provides a default route. If it does, `mDefRoutePreference` specifies the route preference.

`mFavoredOnLinkPrefix` indicates the favored on-link prefix advertised by the router. If there is no on-link prefix, this will be an empty prefix (i.e., its length will be zero).

###### otHistoryTrackerNetInfoCallback (heading level 7)

`typedef void(* otHistoryTrackerNetInfoCallback) (otError aError, const otHistoryTrackerNetworkInfo *aNetworkInfo, uint32_t aEntryAge, void *aContext)`

**Description:**

Callback function pointer type to report the retrieved Network Info entries from a query to another device.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aError|Indicates the status of the query and entries being reported:<br/><br/>- `OT_ERROR_PENDING`: There are more entries to be reported.<br/>- `OT_ERROR_NONE`: This is the last entry, and the query is complete.<br/>- `OT_ERROR_RESPONSE_TIMEOUT`: Timed out waiting for a response.<br/>- `OT_ERROR_PARSE`: The received query answer does not follow the expected format.|
||[in]|aNetworkInfo|The network information entry. This may be `NULL` if `aError` is `OT_ERROR_NONE` (indicating the end of the list) or on certain error conditions.|
||[in]|aEntryAge|The entry age in milliseconds. Applicable only when `aNetworkInfo` is not `NULL`.|
||[in]|aContext|An arbitrary callback context provided by the caller during the query.|

**Details:**

Used when `OPENTHREAD_CONFIG_HISTORY_TRACKER_CLIENT_ENABLE` is enabled.

###### Functions

###### otHistoryTrackerInitIterator (heading level 7)

`void otHistoryTrackerInitIterator(otHistoryTrackerIterator *aIterator)`

**Description:** Initializes an `otHistoryTrackerIterator`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otHistoryTrackerIterator](ot-history-tracker-iterator) *|[in]|aIterator|A pointer to the iterator to initialize (MUST NOT be NULL).|

An iterator MUST be initialized before it is used.

An iterator can be initialized again to start from the beginning of the list.

When iterating over entries in a list, to ensure the entry ages are consistent, the age is given relative to the time the iterator was initialized, i.e., the entry age is provided as the duration (in milliseconds) from the event (when entry was recorded) to the iterator initialization time.

###### otHistoryTrackerIterateNetInfoHistory (heading level 7)

`const otHistoryTrackerNetworkInfo * otHistoryTrackerIterateNetInfoHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, uint32_t *aEntryAge)`

**Description:** Iterates over the entries in the network info history list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otHistoryTrackerIterator](ot-history-tracker-iterator) *|[inout]|aIterator|A pointer to an iterator. MUST be initialized or the behavior is undefined.|
|uint32_t *|[out]|aEntryAge|A pointer to a variable to output the entry's age. MUST NOT be NULL. Age is provided as the duration (in milliseconds) from when entry was recorded to `aIterator` initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries older than max age.|

**Returns**

- A pointer to `otHistoryTrackerNetworkInfo` entry or `NULL` if no more entries in the list.

###### otHistoryTrackerIterateUnicastAddressHistory (heading level 7)

`const otHistoryTrackerUnicastAddressInfo * otHistoryTrackerIterateUnicastAddressHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, uint32_t *aEntryAge)`

**Description:** Iterates over the entries in the unicast address history list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otHistoryTrackerIterator](ot-history-tracker-iterator) *|[inout]|aIterator|A pointer to an iterator. MUST be initialized or the behavior is undefined.|
|uint32_t *|[out]|aEntryAge|A pointer to a variable to output the entry's age. MUST NOT be NULL. Age is provided as the duration (in milliseconds) from when entry was recorded to `aIterator` initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries older than max age.|

**Returns**

- A pointer to `otHistoryTrackerUnicastAddressInfo` entry or `NULL` if no more entries in the list.

###### otHistoryTrackerIterateMulticastAddressHistory (heading level 7)

`const otHistoryTrackerMulticastAddressInfo * otHistoryTrackerIterateMulticastAddressHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, uint32_t *aEntryAge)`

**Description:** Iterates over the entries in the multicast address history list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otHistoryTrackerIterator](ot-history-tracker-iterator) *|[inout]|aIterator|A pointer to an iterator. MUST be initialized or the behavior is undefined.|
|uint32_t *|[out]|aEntryAge|A pointer to a variable to output the entry's age. MUST NOT be NULL. Age is provided as the duration (in milliseconds) from when entry was recorded to `aIterator` initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries older than max age.|

**Returns**

- A pointer to `otHistoryTrackerMulticastAddressInfo` entry or `NULL` if no more entries in the list.

###### otHistoryTrackerIterateRxHistory (heading level 7)

`const otHistoryTrackerMessageInfo * otHistoryTrackerIterateRxHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, uint32_t *aEntryAge)`

**Description:** Iterates over the entries in the RX message history list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otHistoryTrackerIterator](ot-history-tracker-iterator) *|[inout]|aIterator|A pointer to an iterator. MUST be initialized or the behavior is undefined.|
|uint32_t *|[out]|aEntryAge|A pointer to a variable to output the entry's age. MUST NOT be NULL. Age is provided as the duration (in milliseconds) from when entry was recorded to `aIterator` initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries older than max age.|

**Returns**

- The `otHistoryTrackerMessageInfo` entry or `NULL` if no more entries in the list.

###### otHistoryTrackerIterateTxHistory (heading level 7)

`const otHistoryTrackerMessageInfo * otHistoryTrackerIterateTxHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, uint32_t *aEntryAge)`

**Description:** Iterates over the entries in the TX message history list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otHistoryTrackerIterator](ot-history-tracker-iterator) *|[inout]|aIterator|A pointer to an iterator. MUST be initialized or the behavior is undefined.|
|uint32_t *|[out]|aEntryAge|A pointer to a variable to output the entry's age. MUST NOT be NULL. Age is provided as the duration (in milliseconds) from when entry was recorded to `aIterator` initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries older than max age.|

**Returns**

- The `otHistoryTrackerMessageInfo` entry or `NULL` if no more entries in the list.

###### otHistoryTrackerIterateNeighborHistory (heading level 7)

`const otHistoryTrackerNeighborInfo * otHistoryTrackerIterateNeighborHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, uint32_t *aEntryAge)`

**Description:** Iterates over the entries in the neighbor history list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otHistoryTrackerIterator](ot-history-tracker-iterator) *|[inout]|aIterator|A pointer to an iterator. MUST be initialized or the behavior is undefined.|
|uint32_t *|[out]|aEntryAge|A pointer to a variable to output the entry's age. MUST NOT be NULL. Age is provided as the duration (in milliseconds) from when entry was recorded to `aIterator` initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries older than max age.|

**Returns**

- The `otHistoryTrackerNeighborInfo` entry or `NULL` if no more entries in the list.

###### otHistoryTrackerIterateRouterHistory (heading level 7)

`const otHistoryTrackerRouterInfo * otHistoryTrackerIterateRouterHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, uint32_t *aEntryAge)`

**Description:** Iterates over the entries in the router history list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otHistoryTrackerIterator](ot-history-tracker-iterator) *|[inout]|aIterator|A pointer to an iterator. MUST be initialized or the behavior is undefined.|
|uint32_t *|[out]|aEntryAge|A pointer to a variable to output the entry's age. MUST NOT be NULL. Age is provided as the duration (in milliseconds) from when entry was recorded to `aIterator` initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries older than max age.|

**Returns**

- The `otHistoryTrackerRouterInfo` entry or `NULL` if no more entries in the list.

###### otHistoryTrackerIterateOnMeshPrefixHistory (heading level 7)

`const otHistoryTrackerOnMeshPrefixInfo * otHistoryTrackerIterateOnMeshPrefixHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, uint32_t *aEntryAge)`

**Description:** Iterates over the entries in the Network Data on mesh prefix entry history list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otHistoryTrackerIterator](ot-history-tracker-iterator) *|[inout]|aIterator|A pointer to an iterator. MUST be initialized or the behavior is undefined.|
|uint32_t *|[out]|aEntryAge|A pointer to a variable to output the entry's age. MUST NOT be NULL. Age is provided as the duration (in milliseconds) from when entry was recorded to `aIterator` initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries older than max age.|

**Returns**

- The `otHistoryTrackerOnMeshPrefixInfo` entry or `NULL` if no more entries in the list.

###### otHistoryTrackerIterateExternalRouteHistory (heading level 7)

`const otHistoryTrackerExternalRouteInfo * otHistoryTrackerIterateExternalRouteHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, uint32_t *aEntryAge)`

**Description:** Iterates over the entries in the Network Data external route entry history list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otHistoryTrackerIterator](ot-history-tracker-iterator) *|[inout]|aIterator|A pointer to an iterator. MUST be initialized or the behavior is undefined.|
|uint32_t *|[out]|aEntryAge|A pointer to a variable to output the entry's age. MUST NOT be NULL. Age is provided as the duration (in milliseconds) from when entry was recorded to `aIterator` initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries older than max age.|

**Returns**

- The `otHistoryTrackerExternalRouteInfo` entry or `NULL` if no more entries in the list.

###### otHistoryTrackerIterateDnsSrpAddrHistory (heading level 7)

`const otHistoryTrackerDnsSrpAddrInfo * otHistoryTrackerIterateDnsSrpAddrHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, uint32_t *aEntryAge)`

**Description:** Iterates over the entries in the Network Data SRP/DNS address history list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otHistoryTrackerIterator](ot-history-tracker-iterator) *|[inout]|aIterator|A pointer to an iterator. MUST be initialized or the behavior is undefined.|
|uint32_t *|[out]|aEntryAge|A pointer to a variable to output the entry's age. MUST NOT be NULL. Age is provided as the duration (in milliseconds) from when entry was recorded to `aIterator` initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries older than max age.|

**Returns**

- The `otHistoryTrackerDnsSrpAddrInfo` entry or `NULL` if no more entries in the list.

###### otHistoryTrackerIterateBorderAgentEpskcEventHistory (heading level 7)

`const otHistoryTrackerBorderAgentEpskcEvent * otHistoryTrackerIterateBorderAgentEpskcEventHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, uint32_t *aEntryAge)`

**Description:** Iterates over the entries in the Border Agent ePSKc history list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otHistoryTrackerIterator](ot-history-tracker-iterator) *|[inout]|aIterator|A pointer to an iterator. MUST be initialized or the behavior is undefined.|
|uint32_t *|[out]|aEntryAge|A pointer to a variable to output the entry's age. MUST NOT be NULL. Age is provided as the duration (in milliseconds) from when entry was recorded to `aIterator` initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries older than max age.|

**Returns**

- The `otHistoryTrackerBorderAgentEpskcEvent` entry or `NULL` if no more entries in the list.

###### otHistoryTrackerIterateFavoredOmrPrefixHistory (heading level 7)

`const otHistoryTrackerFavoredOmrPrefix * otHistoryTrackerIterateFavoredOmrPrefixHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, uint32_t *aEntryAge)`

**Description:** Iterates over the entries in the favored OMR prefix history list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otHistoryTrackerIterator](ot-history-tracker-iterator) *|[inout]|aIterator|A pointer to an iterator. MUST be initialized or the behavior is undefined.|
|uint32_t *|[out]|aEntryAge|A pointer to a variable to output the entry's age. MUST NOT be NULL. Age is provided as the duration (in milliseconds) from when entry was recorded to `aIterator` initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries older than max age.|

Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` (device acting as Border Router).

**Returns**

- The `otHistoryTrackerFavoredOmrPrefix` entry or `NULL` if no more entries in the list.

###### otHistoryTrackerIterateFavoredOnLinkPrefixHistory (heading level 7)

`const otHistoryTrackerFavoredOnLinkPrefix * otHistoryTrackerIterateFavoredOnLinkPrefixHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, uint32_t *aEntryAge)`

**Description:** Iterates over the entries in the favored on-link prefix history list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otHistoryTrackerIterator](ot-history-tracker-iterator) *|[inout]|aIterator|A pointer to an iterator. MUST be initialized or the behavior is undefined.|
|uint32_t *|[out]|aEntryAge|A pointer to a variable to output the entry's age. MUST NOT be NULL. Age is provided as the duration (in milliseconds) from when the entry was recorded to `aIterator` initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries older than the max age.|

Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` (device acting as Border Router).

**Returns**

- The `otHistoryTrackerFavoredOnLinkPrefix` entry or `NULL` if no more entries in the list.

###### otHistoryTrackerIterateDhcp6PdHistory (heading level 7)

`const otHistoryTrackerDhcp6PdInfo * otHistoryTrackerIterateDhcp6PdHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, uint32_t *aEntryAge)`

**Description:** Iterates over the entries in the DHCPv6-PD history list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otHistoryTrackerIterator](ot-history-tracker-iterator) *|[inout]|aIterator|A pointer to an iterator. MUST be initialized or the behavior is undefined.|
|uint32_t *|[out]|aEntryAge|A pointer to a variable to output the entry's age. MUST NOT be NULL. Age is provided as the duration (in milliseconds) from when entry was recorded to `aIterator` initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries older than max age.|

Requires both `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` and `OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE`.

**Returns**

- The `otHistoryTrackerDhcp6PdInfo` entry or `NULL` if no more entries in the list.

###### otHistoryTrackerIterateAilRoutersHistory (heading level 7)

`const otHistoryTrackerAilRouter * otHistoryTrackerIterateAilRoutersHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, uint32_t *aEntryAge)`

**Description:** Iterates over the entries in the BR AIL routers history list.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otHistoryTrackerIterator](ot-history-tracker-iterator) *|[inout]|aIterator|A pointer to an iterator. MUST be initialized or the behavior is undefined.|
|uint32_t *|[out]|aEntryAge|A pointer to a variable to output the entry's age. MUST NOT be NULL. Age is provided as the duration (in milliseconds) from when the entry was recorded to `aIterator` initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries older than the max age.|

Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` (device acting as Border Router).

**Returns**

- The `otHistoryTrackerAilRouter` entry or `NULL` if no more entries in the list.

###### otHistoryTrackerEntryAgeToString (heading level 7)

`void otHistoryTrackerEntryAgeToString(uint32_t aEntryAge, char *aBuffer, uint16_t aSize)`

**Description:** Converts a given entry age to a human-readable string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint32_t|[in]|aEntryAge|The entry age (duration in msec).|
|char *|[out]|aBuffer|A pointer to a char array to output the string (MUST NOT be NULL).|
|uint16_t|[in]|aSize|The size of `aBuffer`. Recommended to use `OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE`.|

The entry age string follows the format `hours:minutes:seconds:milliseconds` (if shorter than one day) or `days:hours:minutes:seconds`(if longer than one day).

If the resulting string does not fit in `aBuffer` (within its `aSize` characters), the string will be truncated but the outputted string is always null-terminated.

###### otHistoryTrackerQueryNetInfo (heading level 7)

`otError otHistoryTrackerQueryNetInfo(otInstance *aInstance, uint16_t aRloc16, uint16_t aMaxEntries, uint32_t aMaxEntryAge, otHistoryTrackerNetInfoCallback aCallback, void *aContext)`

**Description:** Queries for Network Info history entries from a specified RLOC16.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|uint16_t|[in]|aRloc16|The RLOC16 of the device to query.|
|uint16_t|[in]|aMaxEntries|The maximum number of entries to request (0 indicates all available entries).|
|uint32_t|[in]|aMaxEntryAge|The maximum age (in milliseconds) of entries to request (0 indicates no age limit).|
|[otHistoryTrackerNetInfoCallback](api-history-tracker#ot-history-tracker-net-info-callback)|[in]|aCallback|A pointer to a callback function to be called when the query response is received.|
|void *|[in]|aContext|A user-defined context pointer to be passed to the callback function.|

Requires `OPENTHREAD_CONFIG_HISTORY_TRACKER_CLIENT_ENABLE`.

Upon successful initiation of the query, the provided `aCallback` will be invoked to report the requested entries.

The callback parameter `aError` indicates if any error occurs. If there are more entries to be provided, `aError` will be set to `OT_ERROR_PENDING`. The end of the list is indicated by `aError` being set to `OT_ERROR_NONE` with a null entry pointer. Any other errors, such as `OT_ERROR_RESPONSE_TIMEOUT` or `OT_ERROR_PARSE` (if the received response has an invalid format), will also be indicated by `aError`.

###### otHistoryTrackerCancelQuery (heading level 7)

`void otHistoryTrackerCancelQuery(otInstance *aInstance)`

**Description:** Cancels any ongoing query.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|

Requires `OPENTHREAD_CONFIG_HISTORY_TRACKER_CLIENT_ENABLE`.

###### Macros

`#define OT_HISTORY_TRACKER_MAX_AGE (49 * 24 * 60 * 60 * 1000u)`

**Description**: This constant specifies the maximum age of entries which is 49 days (in msec).

`#define OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE 21`

**Description**: Recommended size for string representation of an entry age.

`#define OT_HISTORY_TRACKER_NO_NEXT_HOP 63`

**Description**: No next hop - For `mNextHop` in `otHistoryTrackerRouterInfo`.

`#define OT_HISTORY_TRACKER_INFINITE_PATH_COST 0`

**Description**: Infinite path cost - used in `otHistoryTrackerRouterInfo`.

Represents an iterator to iterate through a history list. 

The fields in this type are opaque (intended for use by OpenThread core) and therefore should not be accessed/used by caller.

Before using an iterator, it MUST be initialized using `otHistoryTrackerInitIterator()`, 

###### Public Attributes (heading level 7)

###### mData32 (heading level 8)

```
uint32_t otHistoryTrackerIterator::mData32
```

###### mData16 (heading level 8)

```
uint16_t otHistoryTrackerIterator::mData16
```

Represents Thread network info. 

###### Public Attributes (heading level 7)

###### mRole (heading level 8)

```
otDeviceRole otHistoryTrackerNetworkInfo::mRole
```

**Description:** Device Role.

###### mMode (heading level 8)

```
otLinkModeConfig otHistoryTrackerNetworkInfo::mMode
```

**Description:** Device Mode.

###### mRloc16 (heading level 8)

```
uint16_t otHistoryTrackerNetworkInfo::mRloc16
```

**Description:** Device RLOC16.

###### mPartitionId (heading level 8)

```
uint32_t otHistoryTrackerNetworkInfo::mPartitionId
```

**Description:** Partition ID (valid when attached).

Represent a unicast IPv6 address info. 

###### Public Attributes (heading level 7)

###### mAddress (heading level 8)

```
otIp6Address otHistoryTrackerUnicastAddressInfo::mAddress
```

**Description:** The unicast IPv6 address.

###### mPrefixLength (heading level 8)

```
uint8_t otHistoryTrackerUnicastAddressInfo::mPrefixLength
```

**Description:** The Prefix length (in bits).

###### mAddressOrigin (heading level 8)

```
uint8_t otHistoryTrackerUnicastAddressInfo::mAddressOrigin
```

**Description:** The address origin (`OT_ADDRESS_ORIGIN_*` constants).

###### mEvent (heading level 8)

```
otHistoryTrackerAddressEvent otHistoryTrackerUnicastAddressInfo::mEvent
```

**Description:** Indicates the event (address is added/removed).

###### mScope (heading level 8)

```
uint8_t otHistoryTrackerUnicastAddressInfo::mScope
```

**Description:** The IPv6 scope.

###### mPreferred (heading level 8)

```
bool otHistoryTrackerUnicastAddressInfo::mPreferred
```

**Description:** If the address is preferred.

###### mValid (heading level 8)

```
bool otHistoryTrackerUnicastAddressInfo::mValid
```

**Description:** If the address is valid.

###### mRloc (heading level 8)

```
bool otHistoryTrackerUnicastAddressInfo::mRloc
```

**Description:** If the address is an RLOC.

Represent an IPv6 multicast address info. 

###### Public Attributes (heading level 7)

###### mAddress (heading level 8)

```
otIp6Address otHistoryTrackerMulticastAddressInfo::mAddress
```

**Description:** The IPv6 multicast address.

###### mAddressOrigin (heading level 8)

```
uint8_t otHistoryTrackerMulticastAddressInfo::mAddressOrigin
```

**Description:** The address origin (`OT_ADDRESS_ORIGIN_*` constants).

###### mEvent (heading level 8)

```
otHistoryTrackerAddressEvent otHistoryTrackerMulticastAddressInfo::mEvent
```

**Description:** Indicates the event (address is added/removed).

Represents a RX/TX IPv6 message info. 

Some of the fields in this struct are applicable to a RX message or a TX message only, e.g., `mAveRxRss` is the average RSS of all fragment frames that form a received message and is only applicable for a RX message. 

###### Public Attributes (heading level 7)

###### mPayloadLength (heading level 8)

```
uint16_t otHistoryTrackerMessageInfo::mPayloadLength
```

**Description:** IPv6 payload length (exclude IP6 header itself).

###### mNeighborRloc16 (heading level 8)

```
uint16_t otHistoryTrackerMessageInfo::mNeighborRloc16
```

**Description:** RLOC16 of neighbor which sent/received the msg (`0xfffe` if no RLOC16).

###### mSource (heading level 8)

```
otSockAddr otHistoryTrackerMessageInfo::mSource
```

**Description:** Source IPv6 address and port (if UDP/TCP)

###### mDestination (heading level 8)

```
otSockAddr otHistoryTrackerMessageInfo::mDestination
```

**Description:** Destination IPv6 address and port (if UDP/TCP).

###### mChecksum (heading level 8)

```
uint16_t otHistoryTrackerMessageInfo::mChecksum
```

**Description:** Message checksum (valid only for UDP/TCP/ICMP6).

###### mIpProto (heading level 8)

```
uint8_t otHistoryTrackerMessageInfo::mIpProto
```

**Description:** IP Protocol number (`OT_IP6_PROTO_*` enumeration).

###### mIcmp6Type (heading level 8)

```
uint8_t otHistoryTrackerMessageInfo::mIcmp6Type
```

**Description:** ICMP6 type if msg is ICMP6, zero otherwise (`OT_ICMP6_TYPE_*` enumeration).

###### mAveRxRss (heading level 8)

```
int8_t otHistoryTrackerMessageInfo::mAveRxRss
```

**Description:** RSS of received message or OT_RADIO_INVALID_RSSI if not known.

###### mLinkSecurity (heading level 8)

```
bool otHistoryTrackerMessageInfo::mLinkSecurity
```

**Description:** Indicates whether msg used link security.

###### mTxSuccess (heading level 8)

```
bool otHistoryTrackerMessageInfo::mTxSuccess
```

**Description:** Indicates TX success (e.g., ack received). Applicable for TX msg only.

###### mPriority (heading level 8)

```
uint8_t otHistoryTrackerMessageInfo::mPriority
```

**Description:** Message priority (`OT_HISTORY_TRACKER_MSG_PRIORITY_*` enumeration).

###### mRadioIeee802154 (heading level 8)

```
bool otHistoryTrackerMessageInfo::mRadioIeee802154
```

**Description:** Indicates whether msg was sent/received over a 15.4 radio link.

###### mRadioTrelUdp6 (heading level 8)

```
bool otHistoryTrackerMessageInfo::mRadioTrelUdp6
```

**Description:** Indicates whether msg was sent/received over a TREL radio link.

Represents a neighbor info. 

###### Public Attributes (heading level 7)

###### mExtAddress (heading level 8)

```
otExtAddress otHistoryTrackerNeighborInfo::mExtAddress
```

**Description:** Neighbor's Extended Address.

###### mRloc16 (heading level 8)

```
uint16_t otHistoryTrackerNeighborInfo::mRloc16
```

**Description:** Neighbor's RLOC16.

###### mAverageRssi (heading level 8)

```
int8_t otHistoryTrackerNeighborInfo::mAverageRssi
```

**Description:** Average RSSI of rx frames from neighbor at the time of recording entry.

###### mEvent (heading level 8)

```
uint8_t otHistoryTrackerNeighborInfo::mEvent
```

**Description:** Indicates the event (`OT_HISTORY_TRACKER_NEIGHBOR_EVENT_*` enumeration).

###### mRxOnWhenIdle (heading level 8)

```
bool otHistoryTrackerNeighborInfo::mRxOnWhenIdle
```

**Description:** Rx-on-when-idle.

###### mFullThreadDevice (heading level 8)

```
bool otHistoryTrackerNeighborInfo::mFullThreadDevice
```

**Description:** Full Thread Device.

###### mFullNetworkData (heading level 8)

```
bool otHistoryTrackerNeighborInfo::mFullNetworkData
```

**Description:** Full Network Data.

###### mIsChild (heading level 8)

```
bool otHistoryTrackerNeighborInfo::mIsChild
```

**Description:** Indicates whether or not the neighbor is a child.

Represents a router table entry event. 

###### Public Attributes (heading level 7)

###### mEvent (heading level 8)

```
uint8_t otHistoryTrackerRouterInfo::mEvent
```

**Description:** Router entry event (`OT_HISTORY_TRACKER_ROUTER_EVENT_*` enumeration).

###### mRouterId (heading level 8)

```
uint8_t otHistoryTrackerRouterInfo::mRouterId
```

**Description:** Router ID.

###### mNextHop (heading level 8)

```
uint8_t otHistoryTrackerRouterInfo::mNextHop
```

**Description:** Next Hop Router ID - `OT_HISTORY_TRACKER_NO_NEXT_HOP` if no next hop.

###### mOldPathCost (heading level 8)

```
uint8_t otHistoryTrackerRouterInfo::mOldPathCost
```

**Description:** Old path cost - `OT_HISTORY_TRACKER_INFINITE_PATH_COST` if infinite or unknown.

###### mPathCost (heading level 8)

```
uint8_t otHistoryTrackerRouterInfo::mPathCost
```

**Description:** New path cost - `OT_HISTORY_TRACKER_INFINITE_PATH_COST` if infinite or unknown.

Represent a Network Data on mesh prefix info. 

###### Public Attributes (heading level 7)

###### mPrefix (heading level 8)

```
otBorderRouterConfig otHistoryTrackerOnMeshPrefixInfo::mPrefix
```

**Description:** The on mesh prefix entry.

###### mEvent (heading level 8)

```
otHistoryTrackerNetDataEvent otHistoryTrackerOnMeshPrefixInfo::mEvent
```

**Description:** Indicates the event (added/removed).

Represent a Network Data extern route info. 

###### Public Attributes (heading level 7)

###### mRoute (heading level 8)

```
otExternalRouteConfig otHistoryTrackerExternalRouteInfo::mRoute
```

**Description:** The external route entry.

###### mEvent (heading level 8)

```
otHistoryTrackerNetDataEvent otHistoryTrackerExternalRouteInfo::mEvent
```

**Description:** Indicates the event (added/removed).

Represents DNS/SRP server address information parsed from a Network Data service entry. 

The `mType` field specifies the entry type. Some fields are only applicable to specific types.

- The `mPort` field is only applicable for `OT_HISTORY_TRACKER_DNS_SRP_ADDR_TYPE_UNICAST_*` types.
- The `mSequenceNumber` field is only applicable for the `OT_HISTORY_TRACKER_DNS_SRP_ADDR_TYPE_ANYCAST` type.
- Other fields are common and used for all address types.

###### Public Attributes (heading level 7)

###### mAddress (heading level 8)

```
otIp6Address otHistoryTrackerDnsSrpAddrInfo::mAddress
```

**Description:** The server address.

###### mRloc16 (heading level 8)

```
uint16_t otHistoryTrackerDnsSrpAddrInfo::mRloc16
```

**Description:** The RLOC16 of the Border Router adding/removing the entry.

###### mPort (heading level 8)

```
uint16_t otHistoryTrackerDnsSrpAddrInfo::mPort
```

**Description:** Port number.

###### mSequenceNumber (heading level 8)

```
uint8_t otHistoryTrackerDnsSrpAddrInfo::mSequenceNumber
```

**Description:** Anycast sequence number.

###### mVersion (heading level 8)

```
uint8_t otHistoryTrackerDnsSrpAddrInfo::mVersion
```

**Description:** Version number.

###### mType (heading level 8)

```
otHistoryTrackerDnsSrpAddrType otHistoryTrackerDnsSrpAddrInfo::mType
```

**Description:** Address type.

###### mEvent (heading level 8)

```
otHistoryTrackerNetDataEvent otHistoryTrackerDnsSrpAddrInfo::mEvent
```

**Description:** Indicates the event (added/removed).

Represents a favored OMR prefix tracked by a device acting as a Border Router (BR). 

The `mIsLocal` field indicates whether the favored OMR prefix is the same as the local one maintained by this BR. The local OMR prefix can be either based on (random) ULA or a prefix delegated via DHCPv6-PD. 

###### Public Attributes (heading level 7)

###### mOmrPrefix (heading level 8)

```
otIp6Prefix otHistoryTrackerFavoredOmrPrefix::mOmrPrefix
```

**Description:** The OMR prefix.

###### mPreference (heading level 8)

```
signed int otHistoryTrackerFavoredOmrPrefix::mPreference
```

**Description:** The 2-bit signed preference (`OT_ROUTE_PREFERENCE_*` values).

###### mIsLocal (heading level 8)

```
bool otHistoryTrackerFavoredOmrPrefix::mIsLocal
```

**Description:** `true` if the prefix is the local OMR prefix; `false` otherwise.

Represents a favored on-link prefix on AIL tracked by a device acting as a Border Router (BR). 

The `mIsLocal` field indicates whether the favored on-link prefix is the same as the local one maintained by this BR. 

###### Public Attributes (heading level 7)

###### mOnLinkPrefix (heading level 8)

```
otIp6Prefix otHistoryTrackerFavoredOnLinkPrefix::mOnLinkPrefix
```

**Description:** The on-link prefix.

###### mIsLocal (heading level 8)

```
bool otHistoryTrackerFavoredOnLinkPrefix::mIsLocal
```

**Description:** `true` if the prefix is the local on-link prefix; `false` otherwise.

Represents the DHCPv6-PD state and delegated prefix (if any) by a device acting as Border Router (BR). 

###### Public Attributes (heading level 7)

###### mPrefix (heading level 8)

```
otIp6Prefix otHistoryTrackerDhcp6PdInfo::mPrefix
```

**Description:** The delegated prefix if any. If none, it is set to `::/0`.

###### mState (heading level 8)

```
otBorderRoutingDhcp6PdState otHistoryTrackerDhcp6PdInfo::mState
```

**Description:** The DHCPv6 state.

Represents information about a discovered router on an Adjacent Infrastructure Link (AIL). 

This applies when a device is acting as a Border Router, processing received Router Advertisements and tracking information about discovered AIL routers.

`mProvidesDefaultRoute` indicates whether the router provides a default route. If it does, `mDefRoutePreference` specifies the route preference.

`mFavoredOnLinkPrefix` indicates the favored on-link prefix advertised by the router. If there is no on-link prefix, this will be an empty prefix (i.e., its length will be zero). 

###### Public Attributes (heading level 7)

###### mEvent (heading level 8)

```
otHistoryTrackerAilRouterEvent otHistoryTrackerAilRouter::mEvent
```

**Description:** The event type (e.g., added, changed, removed).

###### mDefRoutePreference (heading level 8)

```
int8_t otHistoryTrackerAilRouter::mDefRoutePreference
```

**Description:** Def route preference.

###### mAddress (heading level 8)

```
otIp6Address otHistoryTrackerAilRouter::mAddress
```

**Description:** The IPv6 address of the AIL router.

###### mFavoredOnLinkPrefix (heading level 8)

```
otIp6Prefix otHistoryTrackerAilRouter::mFavoredOnLinkPrefix
```

**Description:** The favored on-link prefix, if any.

###### mProvidesDefaultRoute (heading level 8)

```
bool otHistoryTrackerAilRouter::mProvidesDefaultRoute
```

**Description:** Whether the router provides a default route.

###### mManagedAddressConfigFlag (heading level 8)

```
bool otHistoryTrackerAilRouter::mManagedAddressConfigFlag
```

**Description:** The Managed Address Config flag (`M` flag).

###### mOtherConfigFlag (heading level 8)

```
bool otHistoryTrackerAilRouter::mOtherConfigFlag
```

**Description:** The Other Config flag (`O` flag).

###### mSnacRouterFlag (heading level 8)

```
bool otHistoryTrackerAilRouter::mSnacRouterFlag
```

**Description:** The SNAC Router flag (`S` flag).

###### mIsLocalDevice (heading level 8)

```
bool otHistoryTrackerAilRouter::mIsLocalDevice
```

**Description:** This router is the local device (this BR).

###### mIsReachable (heading level 8)

```
bool otHistoryTrackerAilRouter::mIsReachable
```

**Description:** This router is reachable.

###### mIsPeerBr (heading level 8)

```
bool otHistoryTrackerAilRouter::mIsPeerBr
```

**Description:** This router is (likely) a peer BR.

##### Logging - Thread Stack

This module includes OpenThread logging related definitions. 

###### Modules

[otLogHexDumpInfo](ot-log-hex-dump-info)

###### Functions

###### otGetLogLevel (heading level 7)

`otLogLevel otGetLogLevel(otInstance *aInstance)`

**Description:** Returns the current log level for a given OpenThread instance.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|

If dynamic log level feature `OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE` is enabled, this function returns the currently set dynamic log level:

- In a single-instance configuration, it returns the instance's log level.
- In a multi-instance configuration, it returns the instance-specific log level if it has been explicitly set (see `otSetLogLevel()`). Otherwise, it returns the global log level (see `otLoggingGetLevel()`).

If the dynamic log level feature is not enabled, this function returns the build-time configured log level.

**Returns**

- The log level.

###### otSetLogLevel (heading level 7)

`otError otSetLogLevel(otInstance *aInstance, otLogLevel aLogLevel)`

**Description:** Sets the log level for a given OpenThread instance.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otLogLevel](plat-logging#ot-log-level)|[in]|aLogLevel|The log level.|

**Note**

- This function requires `OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE=1`.

In a single-instance configuration, this function sets the log level for the instance.

In a multi-instance configuration (`OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE`), if `OPENTHREAD_CONFIG_LOG_INSTANCE_AWARE_API_ENABLE` is not enabled, this function returns `OT_ERROR_NOT_CAPABLE`. When the log level is explicitly set on an instance, it overrides the global log level set using `otLoggingSetLevel()`.

###### otLoggingGetLevel (heading level 7)

`otLogLevel otLoggingGetLevel(void)`

**Description:** Returns the current global log level.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

In a single-instance configuration, this function behaves the same as `otGetLogLevel()`. In a multi-instance configuration, it returns the global log level which is used for all instances.

**Returns**

- The global log level.

###### otLoggingSetLevel (heading level 7)

`otError otLoggingSetLevel(otLogLevel aLogLevel)`

**Description:** Sets the global log level.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otLogLevel](plat-logging#ot-log-level)|[in]|aLogLevel|The log level.|

**Note**

- This function requires `OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE=1`.

In a single-instance configuration, this function behaves the same as `otSetLogLevel()` (which is the recommended function to use).

In a multi-instance configuration, it sets the global log level which is used by all instances. The log level can be explicitly set on a specific instance using `otSetLogLevel()`, which will then be used instead of the global value.

###### otLogCritPlat (heading level 7)

`void otLogCritPlat(const char *aFormat,...)`

**Description:** Emits a log message at critical log level.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aFormat|The format string.|
|...|[in]|undefined|Arguments for the format specification.|

Is intended for use by platform. If `OPENTHREAD_CONFIG_LOG_PLATFORM` is not set or the current log level is below critical, this function does not emit any log message.

###### otLogWarnPlat (heading level 7)

`void otLogWarnPlat(const char *aFormat,...)`

**Description:** Emits a log message at warning log level.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aFormat|The format string.|
|...|[in]|undefined|Arguments for the format specification.|

Is intended for use by platform. If `OPENTHREAD_CONFIG_LOG_PLATFORM` is not set or the current log level is below warning, this function does not emit any log message.

###### otLogNotePlat (heading level 7)

`void otLogNotePlat(const char *aFormat,...)`

**Description:** Emits a log message at note log level.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aFormat|The format string.|
|...|[in]|undefined|Arguments for the format specification.|

Is intended for use by platform. If `OPENTHREAD_CONFIG_LOG_PLATFORM` is not set or the current log level is below note, this function does not emit any log message.

###### otLogInfoPlat (heading level 7)

`void otLogInfoPlat(const char *aFormat,...)`

**Description:** Emits a log message at info log level.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aFormat|The format string.|
|...|[in]|undefined|Arguments for the format specification.|

Is intended for use by platform. If `OPENTHREAD_CONFIG_LOG_PLATFORM` is not set or the current log level is below info, this function does not emit any log message.

###### otLogDebgPlat (heading level 7)

`void otLogDebgPlat(const char *aFormat,...)`

**Description:** Emits a log message at debug log level.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aFormat|The format string.|
|...|[in]|undefined|Arguments for the format specification.|

Is intended for use by platform. If `OPENTHREAD_CONFIG_LOG_PLATFORM` is not set or the current log level is below debug, this function does not emit any log message.

###### otDumpCritPlat (heading level 7)

`void otDumpCritPlat(const char *aText, const void *aData, uint16_t aDataLength)`

**Description:** Generates a memory dump at critical log level.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aText|A string that is printed before the bytes.|
|const void *|[in]|aData|A pointer to the data buffer.|
|uint16_t|[in]|aDataLength|Number of bytes in `aData`.|

If `OPENTHREAD_CONFIG_LOG_PLATFORM` or `OPENTHREAD_CONFIG_LOG_PKT_DUMP` is not set or the current log level is below critical this function does not emit any log message.

###### otDumpWarnPlat (heading level 7)

`void otDumpWarnPlat(const char *aText, const void *aData, uint16_t aDataLength)`

**Description:** Generates a memory dump at warning log level.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aText|A string that is printed before the bytes.|
|const void *|[in]|aData|A pointer to the data buffer.|
|uint16_t|[in]|aDataLength|Number of bytes in `aData`.|

If `OPENTHREAD_CONFIG_LOG_PLATFORM` or `OPENTHREAD_CONFIG_LOG_PKT_DUMP` is not set or the current log level is below warning this function does not emit any log message.

###### otDumpNotePlat (heading level 7)

`void otDumpNotePlat(const char *aText, const void *aData, uint16_t aDataLength)`

**Description:** Generates a memory dump at note log level.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aText|A string that is printed before the bytes.|
|const void *|[in]|aData|A pointer to the data buffer.|
|uint16_t|[in]|aDataLength|Number of bytes in `aData`.|

If `OPENTHREAD_CONFIG_LOG_PLATFORM` or `OPENTHREAD_CONFIG_LOG_PKT_DUMP` is not set or the current log level is below note this function does not emit any log message.

###### otDumpInfoPlat (heading level 7)

`void otDumpInfoPlat(const char *aText, const void *aData, uint16_t aDataLength)`

**Description:** Generates a memory dump at info log level.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aText|A string that is printed before the bytes.|
|const void *|[in]|aData|A pointer to the data buffer.|
|uint16_t|[in]|aDataLength|Number of bytes in `aData`.|

If `OPENTHREAD_CONFIG_LOG_PLATFORM` or `OPENTHREAD_CONFIG_LOG_PKT_DUMP` is not set or the current log level is below info this function does not emit any log message.

###### otDumpDebgPlat (heading level 7)

`void otDumpDebgPlat(const char *aText, const void *aData, uint16_t aDataLength)`

**Description:** Generates a memory dump at debug log level.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aText|A string that is printed before the bytes.|
|const void *|[in]|aData|A pointer to the data buffer.|
|uint16_t|[in]|aDataLength|Number of bytes in `aData`.|

If `OPENTHREAD_CONFIG_LOG_PLATFORM` or `OPENTHREAD_CONFIG_LOG_PKT_DUMP` is not set or the current log level is below debug this function does not emit any log message.

###### otLogPlat (heading level 7)

`void otLogPlat(otLogLevel aLogLevel, const char *aPlatModuleName, const char *aFormat,...)`

**Description:** Emits a log message at given log level using a platform module name.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otLogLevel](plat-logging#ot-log-level)|[in]|aLogLevel|The log level.|
|const char *|[in]|aPlatModuleName|The platform sub-module name.|
|const char *|[in]|aFormat|The format string.|
|...|[in]|undefined|Arguments for the format specification.|

This is is intended for use by platform. If `OPENTHREAD_CONFIG_LOG_PLATFORM` is not set or the current log level is below `aLogLevel` , this function does not emit any log message.

The `aPlatModuleName` name is used to determine the log module name in the emitted log message, following the `P-{PlatModuleName}---` format. This means that the prefix string "P-" is added to indicate that this is a platform sub-module, followed by the next 12 characters of the `PlatModuleName` string, with padded hyphens `-` at the end to ensure that the region name is 14 characters long.

###### otLogPlatArgs (heading level 7)

`void otLogPlatArgs(otLogLevel aLogLevel, const char *aPlatModuleName, const char *aFormat, va_list aArgs)`

**Description:** Emits a log message at given log level using a platform module name.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otLogLevel](plat-logging#ot-log-level)|[in]|aLogLevel|The log level.|
|const char *|[in]|aPlatModuleName|The platform sub-module name.|
|const char *|[in]|aFormat|The format string.|
|va_list|[in]|aArgs|Arguments for the format specification.|

This is is intended for use by platform. If `OPENTHREAD_CONFIG_LOG_PLATFORM` is not set or the current log level is below `aLogLevel` , this function does not emit any log message.

The `aPlatModuleName` name is used to determine the log module name in the emitted log message, following the `P-{PlatModuleName}---` format. This means that the prefix string "P-" is added to indicate that this is a platform sub-module, followed by the next 12 characters of the `PlatModuleName` string, with padded hyphens `-` at the end to ensure that the region name is 14 characters long.

###### otLogCli (heading level 7)

`void otLogCli(otLogLevel aLogLevel, const char *aFormat,...)`

**Description:** Emits a log message at a given log level.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otLogLevel](plat-logging#ot-log-level)|[in]|aLogLevel|The log level.|
|const char *|[in]|aFormat|The format string.|
|...|[in]|undefined|Arguments for the format specification.|

Is intended for use by CLI only. If `OPENTHREAD_CONFIG_LOG_CLI` is not set or the current log level is below the given log level, this function does not emit any log message.

###### otLogGenerateNextHexDumpLine (heading level 7)

`otError otLogGenerateNextHexDumpLine(otLogHexDumpInfo *aInfo)`

**Description:** Generates the next hex dump line.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otLogHexDumpInfo](ot-log-hex-dump-info) *|[inout]|aInfo|A pointer to `otLogHexDumpInfo` to use to generate hex dump.|

Can call this method back-to-back to generate the hex dump output line by line. On the first call the `mIterator` field in `aInfo` MUST be set to zero.

Here is an example of the generated hex dump output:

"==========================[{mTitle} len=070]============================" "| 41 D8 87 34 12 FF FF 25 | 4C 57 DA F2 FB 2F 62 7F | A..4...%LW.../b. |" "| 3B 01 F0 4D 4C 4D 4C 54 | 4F 00 15 15 00 00 00 00 | ;..MLMLTO....... |" "| 00 00 00 01 80 DB 60 82 | 7E 33 72 3B CC B3 A1 84 | ......`.~3r;.... |" "| 3B E6 AD B2 0B 45 E7 45 | C5 B9 00 1A CB 2D 6D 1C | ;....E.E.....-m. |" "| 10 3E 3C F5 D3 70       |                         | .><..p           |" "------------------------------------------------------------------------"

###### Macros

`#define OT_LOG_HEX_DUMP_LINE_SIZE 73`

**Description**: Hex dump line string size.

Represents information used for generating hex dump output. 

###### Public Attributes (heading level 7)

###### mDataBytes (heading level 8)

```
const uint8_t* otLogHexDumpInfo::mDataBytes
```

**Description:** The data byes.

###### mDataLength (heading level 8)

```
uint16_t otLogHexDumpInfo::mDataLength
```

**Description:** The data length (number of bytes in `mDataBytes`)

###### mTitle (heading level 8)

```
const char* otLogHexDumpInfo::mTitle
```

**Description:** Title string to add table header (MUST NOT be `NULL`).

###### mLine (heading level 8)

```
char otLogHexDumpInfo::mLine[OT_LOG_HEX_DUMP_LINE_SIZE]
```

**Description:** Buffer to output one line of generated hex dump.

###### mIterator (heading level 8)

```
uint16_t otLogHexDumpInfo::mIterator
```

**Description:** Iterator used by OT stack. MUST be initialized to zero.

##### Mesh Diagnostics

This module includes definitions and functions for Mesh Diagnostics. 

The Mesh Diagnostics APIs require `OPENTHREAD_CONFIG_MESH_DIAG_ENABLE` and `OPENTHREAD_FTD`. 

###### Modules

[otMeshDiagDiscoverConfig](ot-mesh-diag-discover-config)

[otMeshDiagRouterInfo](ot-mesh-diag-router-info)

[otMeshDiagChildInfo](ot-mesh-diag-child-info)

[otMeshDiagChildEntry](ot-mesh-diag-child-entry)

[otMeshDiagRouterNeighborEntry](ot-mesh-diag-router-neighbor-entry)

###### Typedefs

###### otMeshDiagDiscoverConfig (heading level 7)

`typedef struct otMeshDiagDiscoverConfig otMeshDiagDiscoverConfig`

**Description:**

Represents the set of configurations used when discovering mesh topology indicating which items to discover.

###### otMeshDiagIp6AddrIterator (heading level 7)

`typedef struct otMeshDiagIp6AddrIterator otMeshDiagIp6AddrIterator`

**Description:**

An opaque iterator to iterate over list of IPv6 addresses of a router.

**Details:**

Pointers to instance of this type are provided in `otMeshDiagRouterInfo`.

###### otMeshDiagChildIterator (heading level 7)

`typedef struct otMeshDiagChildIterator otMeshDiagChildIterator`

**Description:**

An opaque iterator to iterate over list of children of a router.

**Details:**

Pointers to instance of this type are provided in `otMeshDiagRouterInfo`.

###### otMeshDiagRouterInfo (heading level 7)

`typedef struct otMeshDiagRouterInfo otMeshDiagRouterInfo`

**Description:**

Represents information about a router in Thread mesh discovered using `otMeshDiagDiscoverTopology()`.

###### otMeshDiagChildInfo (heading level 7)

`typedef struct otMeshDiagChildInfo otMeshDiagChildInfo`

**Description:**

Represents information about a discovered child in Thread mesh using `otMeshDiagDiscoverTopology()`.

###### otMeshDiagDiscoverCallback (heading level 7)

`typedef void(* otMeshDiagDiscoverCallback) (otError aError, otMeshDiagRouterInfo *aRouterInfo, void *aContext)`

**Description:**

Pointer type represents the callback used by `otMeshDiagDiscoverTopology()` to provide information about a discovered router.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aError|OT_ERROR_PENDING Indicates there are more routers to be discovered. OT_ERROR_NONE Indicates this is the last router and mesh discovery is done. OT_ERROR_RESPONSE_TIMEOUT Timed out waiting for response from one or more routers.|
||[in]|aRouterInfo|The discovered router info (can be null if `aError` is OT_ERROR_RESPONSE_TIMEOUT).|
||[in]|aContext|Application-specific context.|

**Details:**

When `aError` is `OT_ERROR_PENDING`, it indicates that the discovery is not yet finished and there will be more routers to discover and the callback will be invoked again.

###### otMeshDiagChildEntry (heading level 7)

`typedef struct otMeshDiagChildEntry otMeshDiagChildEntry`

**Description:**

Represents information about a child entry from `otMeshDiagQueryChildTable()`.

**Details:**

`mSupportsErrRate` indicates whether or not the error tracking feature is supported and `mFrameErrorRate` and `mMessageErrorRate` values are valid. The frame error rate tracks frame tx errors (towards the child) at MAC layer, while `mMessageErrorRate` tracks the IPv6 message error rate (above MAC layer and after MAC retries) when an IPv6 message is dropped. For example, if the message is large and requires 6LoWPAN fragmentation, message tx is considered as failed if one of its fragment frame tx fails (for example, never acked).

###### otMeshDiagQueryChildTableCallback (heading level 7)

`typedef void(* otMeshDiagQueryChildTableCallback) (otError aError, const otMeshDiagChildEntry *aChildEntry, void *aContext)`

**Description:**

Represents the callback used by `otMeshDiagQueryChildTable()` to provide information about child table entries.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aError|OT_ERROR_PENDING Indicates there are more entries in the table. OT_ERROR_NONE Indicates the table is finished. OT_ERROR_RESPONSE_TIMEOUT Timed out waiting for response.|
||[in]|aChildEntry|The child entry (can be null if `aError` is OT_ERROR_RESPONSE_TIMEOUT or OT_ERROR_NONE).|
||[in]|aContext|Application-specific context.|

**Details:**

When `aError` is `OT_ERROR_PENDING`, it indicates that the table still has more entries and the callback will be invoked again.

###### otMeshDiagChildIp6AddrsCallback (heading level 7)

`typedef void(* otMeshDiagChildIp6AddrsCallback) (otError aError, uint16_t aChildRloc16, otMeshDiagIp6AddrIterator *aIp6AddrIterator, void *aContext)`

**Description:**

Represents the callback used by `otMeshDiagQueryChildrenIp6Addrs()` to provide information about an MTD child and its list of IPv6 addresses.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aError|OT_ERROR_PENDING Indicates there are more children in the table. OT_ERROR_NONE Indicates the table is finished. OT_ERROR_RESPONSE_TIMEOUT Timed out waiting for response.|
||[in]|aChildRloc16|The RLOC16 of the child. `0xfffe` is used on `OT_ERROR_RESPONSE_TIMEOUT`.|
||[in]|aIp6AddrIterator|An iterator to go through the IPv6 addresses of the child with `aRloc` using `otMeshDiagGetNextIp6Address()`. Set to NULL on `OT_ERROR_RESPONSE_TIMEOUT`.|
||[in]|aContext|Application-specific context.|

**Details:**

When `aError` is `OT_ERROR_PENDING`, it indicates that there are more children and the callback will be invoked again.

###### otMeshDiagRouterNeighborEntry (heading level 7)

`typedef struct otMeshDiagRouterNeighborEntry otMeshDiagRouterNeighborEntry`

**Description:**

Represents information about a router neighbor entry from `otMeshDiagQueryRouterNeighborTable()`.

**Details:**

`mSupportsErrRate` indicates whether or not the error tracking feature is supported and `mFrameErrorRate` and `mMessageErrorRate` values are valid. The frame error rate tracks frame tx errors (towards the child) at MAC layer, while `mMessageErrorRate` tracks the IPv6 message error rate (above MAC layer and after MAC retries) when an IPv6 message is dropped. For example, if the message is large and requires 6LoWPAN fragmentation, message tx is considered as failed if one of its fragment frame tx fails (for example, never acked).

###### otMeshDiagQueryRouterNeighborTableCallback (heading level 7)

`typedef void(* otMeshDiagQueryRouterNeighborTableCallback) (otError aError, const otMeshDiagRouterNeighborEntry *aNeighborEntry, void *aContext)`

**Description:**

Represents the callback used by `otMeshDiagQueryRouterNeighborTable()` to provide information about neighbor router table entries.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aError|OT_ERROR_PENDING Indicates there are more entries in the table. OT_ERROR_NONE Indicates the table is finished. OT_ERROR_RESPONSE_TIMEOUT Timed out waiting for response.|
||[in]|aNeighborEntry|The neighbor entry (can be null if `aError` is RESPONSE_TIMEOUT or NONE).|
||[in]|aContext|Application-specific context.|

**Details:**

When `aError` is `OT_ERROR_PENDING`, it indicates that the table still has more entries and the callback will be invoked again.

###### Functions

###### otMeshDiagDiscoverTopology (heading level 7)

`otError otMeshDiagDiscoverTopology(otInstance *aInstance, const otMeshDiagDiscoverConfig *aConfig, otMeshDiagDiscoverCallback aCallback, void *aContext)`

**Description:** Starts network topology discovery.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otMeshDiagDiscoverConfig](ot-mesh-diag-discover-config) *|[in]|aConfig|The configuration to use for discovery (e.g., which items to discover).|
|[otMeshDiagDiscoverCallback](api-mesh-diag#ot-mesh-diag-discover-callback)|[in]|aCallback|The callback to report the discovered routers.|
|void *|[in]|aContext|A context to pass in `aCallback`.|

###### otMeshDiagCancel (heading level 7)

`void otMeshDiagCancel(otInstance *aInstance)`

**Description:** Cancels an ongoing topology discovery if there is one, otherwise no action.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

When ongoing discovery is cancelled, the callback from `otMeshDiagDiscoverTopology()` will not be called anymore. 

###### otMeshDiagGetNextIp6Address (heading level 7)

`otError otMeshDiagGetNextIp6Address(otMeshDiagIp6AddrIterator *aIterator, otIp6Address *aIp6Address)`

**Description:** Iterates through the discovered IPv6 addresses of a router or an MTD child.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMeshDiagIp6AddrIterator](api-mesh-diag#ot-mesh-diag-ip6-addr-iterator) *|[inout]|aIterator|The address iterator to use.|
|[otIp6Address](ot-ip6-address) *|[out]|aIp6Address|A pointer to return the next IPv6 address (if any).|

MUST be used

- from the callback `otMeshDiagDiscoverCallback()` and use the `mIp6AddrIterator` from the `aRouterInfo` struct that is provided as input to the callback, or
- from the callback `otMeshDiagChildIp6AddrsCallback()` along with provided `aIp6AddrIterator`.

###### otMeshDiagGetNextChildInfo (heading level 7)

`otError otMeshDiagGetNextChildInfo(otMeshDiagChildIterator *aIterator, otMeshDiagChildInfo *aChildInfo)`

**Description:** Iterates through the discovered children of a router.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otMeshDiagChildIterator](api-mesh-diag#ot-mesh-diag-child-iterator) *|[inout]|aIterator|The address iterator to use.|
|[otMeshDiagChildInfo](ot-mesh-diag-child-info) *|[out]|aChildInfo|A pointer to return the child info (if any).|

This function MUST be used from the callback `otMeshDiagDiscoverCallback()` and use the `mChildIterator` from the `aRouterInfo` struct that is provided as input to the callback.

###### otMeshDiagQueryChildTable (heading level 7)

`otError otMeshDiagQueryChildTable(otInstance *aInstance, uint16_t aRloc16, otMeshDiagQueryChildTableCallback aCallback, void *aContext)`

**Description:** Starts query for child table for a given router.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|uint16_t|[in]|aRloc16|The RLOC16 of router to query.|
|[otMeshDiagQueryChildTableCallback](api-mesh-diag#ot-mesh-diag-query-child-table-callback)|[in]|aCallback|The callback to report the queried child table.|
|void *|[in]|aContext|A context to pass in `aCallback`.|

###### otMeshDiagQueryChildrenIp6Addrs (heading level 7)

`otError otMeshDiagQueryChildrenIp6Addrs(otInstance *aInstance, uint16_t aRloc16, otMeshDiagChildIp6AddrsCallback aCallback, void *aContext)`

**Description:** Sends a query to a parent to retrieve the IPv6 addresses of all its MTD children.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|uint16_t|[in]|aRloc16|The RLOC16 of parent to query.|
|[otMeshDiagChildIp6AddrsCallback](api-mesh-diag#ot-mesh-diag-child-ip6-addrs-callback)|[in]|aCallback|The callback to report the queried child IPv6 address list.|
|void *|[in]|aContext|A context to pass in `aCallback`.|

###### otMeshDiagQueryRouterNeighborTable (heading level 7)

`otError otMeshDiagQueryRouterNeighborTable(otInstance *aInstance, uint16_t aRloc16, otMeshDiagQueryRouterNeighborTableCallback aCallback, void *aContext)`

**Description:** Starts query for router neighbor table for a given router.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|uint16_t|[in]|aRloc16|The RLOC16 of router to query.|
|[otMeshDiagQueryRouterNeighborTableCallback](api-mesh-diag#ot-mesh-diag-query-router-neighbor-table-callback)|[in]|aCallback|The callback to report the queried table.|
|void *|[in]|aContext|A context to pass in `aCallback`.|

###### otMeshDiagSetResponseTimeout (heading level 7)

`void otMeshDiagSetResponseTimeout(otInstance *aInstance, uint32_t aTimeout)`

**Description:** Sets the response timeout value to use for any future mesh diagnostic queries.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aTimeout|The timeout interval in milliseconds.|

The default response timeout value is specified by `OPENTHREAD_CONFIG_MESH_DIAG_RESPONSE_TIMEOUT` configuration.

Changing the response timeout does not impact any ongoing query.

The provided `aTimeout` value will be clamped to stay between 50 milliseconds and 10 minutes.

###### otMeshDiagGetResponseTimeout (heading level 7)

`uint32_t otMeshDiagGetResponseTimeout(otInstance *aInstance)`

**Description:** Gets the response timeout value.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The response timeout interval in milliseconds.

###### Macros

`#define OT_MESH_DIAG_VERSION_UNKNOWN 0xffff`

**Description**: Specifies that Thread Version is unknown.

Represents the set of configurations used when discovering mesh topology indicating which items to discover. 

###### Public Attributes (heading level 7)

###### mDiscoverIp6Addresses (heading level 8)

```
bool otMeshDiagDiscoverConfig::mDiscoverIp6Addresses
```

**Description:** Whether or not to discover IPv6 addresses of every router.

###### mDiscoverChildTable (heading level 8)

```
bool otMeshDiagDiscoverConfig::mDiscoverChildTable
```

**Description:** Whether or not to discover children of every router.

Represents information about a router in Thread mesh discovered using `otMeshDiagDiscoverTopology()`. 

###### Public Attributes (heading level 7)

###### mExtAddress (heading level 8)

```
otExtAddress otMeshDiagRouterInfo::mExtAddress
```

**Description:** Extended MAC address.

###### mRloc16 (heading level 8)

```
uint16_t otMeshDiagRouterInfo::mRloc16
```

**Description:** RLOC16.

###### mRouterId (heading level 8)

```
uint8_t otMeshDiagRouterInfo::mRouterId
```

**Description:** Router ID.

###### mVersion (heading level 8)

```
uint16_t otMeshDiagRouterInfo::mVersion
```

**Description:** Thread Version. `OT_MESH_DIAG_VERSION_UNKNOWN` if unknown.

###### mIsThisDevice (heading level 8)

```
bool otMeshDiagRouterInfo::mIsThisDevice
```

**Description:** Whether router is this device itself.

###### mIsThisDeviceParent (heading level 8)

```
bool otMeshDiagRouterInfo::mIsThisDeviceParent
```

**Description:** Whether router is parent of this device (when device is a child).

###### mIsLeader (heading level 8)

```
bool otMeshDiagRouterInfo::mIsLeader
```

**Description:** Whether router is leader.

###### mIsBorderRouter (heading level 8)

```
bool otMeshDiagRouterInfo::mIsBorderRouter
```

**Description:** Whether router acts as a border router providing ext connectivity.

###### mLinkQualities (heading level 8)

```
uint8_t otMeshDiagRouterInfo::mLinkQualities[OT_NETWORK_MAX_ROUTER_ID+1]
```

**Description:** Provides the link quality from this router to other routers, also indicating whether a link is established between the routers.

**Details:** The array is indexed based on Router ID. `mLinkQualities[routerId]` indicates the incoming link quality, the router sees to the router with `routerId`. Link quality is a value in [0, 3]. Value zero indicates no link. Larger value indicate better link quality (as defined by Thread specification).

###### mIp6AddrIterator (heading level 8)

```
otMeshDiagIp6AddrIterator* otMeshDiagRouterInfo::mIp6AddrIterator
```

**Description:** A pointer to an iterator to go through the list of IPv6 addresses of the router.

**Details:** The pointer is valid only while `otMeshDiagRouterInfo` is valid. It can be used in `otMeshDiagGetNextIp6Address` to iterate through the IPv6 addresses.

The pointer can be NULL when there was no request to discover IPv6 addresses (in `otMeshDiagDiscoverConfig`) or if the router did not provide the list.

###### mChildIterator (heading level 8)

```
otMeshDiagChildIterator* otMeshDiagRouterInfo::mChildIterator
```

**Description:** A pointer to an iterator to go through the list of children of the router.

**Details:** The pointer is valid only while `otMeshDiagRouterInfo` is valid. It can be used in `otMeshDiagGetNextChildInfo` to iterate through the children of the router.

The pointer can be NULL when there was no request to discover children (in `otMeshDiagDiscoverConfig`) or if the router did not provide the list.

Represents information about a discovered child in Thread mesh using `otMeshDiagDiscoverTopology()`. 

###### Public Attributes (heading level 7)

###### mRloc16 (heading level 8)

```
uint16_t otMeshDiagChildInfo::mRloc16
```

**Description:** RLOC16.

###### mMode (heading level 8)

```
otLinkModeConfig otMeshDiagChildInfo::mMode
```

**Description:** Device mode.

###### mLinkQuality (heading level 8)

```
uint8_t otMeshDiagChildInfo::mLinkQuality
```

**Description:** Incoming link quality to child from parent.

###### mIsThisDevice (heading level 8)

```
bool otMeshDiagChildInfo::mIsThisDevice
```

**Description:** Whether child is this device itself.

###### mIsBorderRouter (heading level 8)

```
bool otMeshDiagChildInfo::mIsBorderRouter
```

**Description:** Whether child acts as a border router providing ext connectivity.

Represents information about a child entry from `otMeshDiagQueryChildTable()`. 

`mSupportsErrRate` indicates whether or not the error tracking feature is supported and `mFrameErrorRate` and `mMessageErrorRate` values are valid. The frame error rate tracks frame tx errors (towards the child) at MAC layer, while `mMessageErrorRate` tracks the IPv6 message error rate (above MAC layer and after MAC retries) when an IPv6 message is dropped. For example, if the message is large and requires 6LoWPAN fragmentation, message tx is considered as failed if one of its fragment frame tx fails (for example, never acked). 

###### Public Attributes (heading level 7)

###### mRxOnWhenIdle (heading level 8)

```
bool otMeshDiagChildEntry::mRxOnWhenIdle
```

**Description:** Is rx-on when idle (vs sleepy).

###### mDeviceTypeFtd (heading level 8)

```
bool otMeshDiagChildEntry::mDeviceTypeFtd
```

**Description:** Is device FTD (vs MTD).

###### mFullNetData (heading level 8)

```
bool otMeshDiagChildEntry::mFullNetData
```

**Description:** Whether device gets full Network Data (vs stable sub-set).

###### mCslSynchronized (heading level 8)

```
bool otMeshDiagChildEntry::mCslSynchronized
```

**Description:** Is CSL capable and CSL synchronized.

###### mSupportsErrRate (heading level 8)

```
bool otMeshDiagChildEntry::mSupportsErrRate
```

**Description:** `mFrameErrorRate` and `mMessageErrorRate` values are valid.

###### mRloc16 (heading level 8)

```
uint16_t otMeshDiagChildEntry::mRloc16
```

**Description:** RLOC16.

###### mExtAddress (heading level 8)

```
otExtAddress otMeshDiagChildEntry::mExtAddress
```

**Description:** Extended Address.

###### mVersion (heading level 8)

```
uint16_t otMeshDiagChildEntry::mVersion
```

**Description:** Version.

###### mTimeout (heading level 8)

```
uint32_t otMeshDiagChildEntry::mTimeout
```

**Description:** Timeout in seconds.

###### mAge (heading level 8)

```
uint32_t otMeshDiagChildEntry::mAge
```

**Description:** Seconds since last heard from the child.

###### mConnectionTime (heading level 8)

```
uint32_t otMeshDiagChildEntry::mConnectionTime
```

**Description:** Seconds since child attach.

###### mSupervisionInterval (heading level 8)

```
uint16_t otMeshDiagChildEntry::mSupervisionInterval
```

**Description:** Supervision interval in seconds. Zero to indicate not used.

###### mLinkMargin (heading level 8)

```
uint8_t otMeshDiagChildEntry::mLinkMargin
```

**Description:** Link Margin in dB.

###### mAverageRssi (heading level 8)

```
int8_t otMeshDiagChildEntry::mAverageRssi
```

**Description:** Average RSSI.

###### mLastRssi (heading level 8)

```
int8_t otMeshDiagChildEntry::mLastRssi
```

**Description:** RSSI of last received frame.

###### mFrameErrorRate (heading level 8)

```
uint16_t otMeshDiagChildEntry::mFrameErrorRate
```

**Description:** Frame error rate (0x0000->0%, 0xffff->100%).

###### mMessageErrorRate (heading level 8)

```
uint16_t otMeshDiagChildEntry::mMessageErrorRate
```

**Description:** (IPv6) msg error rate (0x0000->0%, 0xffff->100%).

###### mQueuedMessageCount (heading level 8)

```
uint16_t otMeshDiagChildEntry::mQueuedMessageCount
```

**Description:** Number of queued messages for indirect tx to child.

###### mCslPeriod (heading level 8)

```
uint16_t otMeshDiagChildEntry::mCslPeriod
```

**Description:** CSL Period in unit of 10-symbols-time. Zero indicates CSL is disabled.

###### mCslTimeout (heading level 8)

```
uint32_t otMeshDiagChildEntry::mCslTimeout
```

**Description:** CSL Timeout in seconds.

###### mCslChannel (heading level 8)

```
uint8_t otMeshDiagChildEntry::mCslChannel
```

**Description:** CSL channel.

Represents information about a router neighbor entry from `otMeshDiagQueryRouterNeighborTable()`. 

`mSupportsErrRate` indicates whether or not the error tracking feature is supported and `mFrameErrorRate` and `mMessageErrorRate` values are valid. The frame error rate tracks frame tx errors (towards the child) at MAC layer, while `mMessageErrorRate` tracks the IPv6 message error rate (above MAC layer and after MAC retries) when an IPv6 message is dropped. For example, if the message is large and requires 6LoWPAN fragmentation, message tx is considered as failed if one of its fragment frame tx fails (for example, never acked). 

###### Public Attributes (heading level 7)

###### mSupportsErrRate (heading level 8)

```
bool otMeshDiagRouterNeighborEntry::mSupportsErrRate
```

**Description:** `mFrameErrorRate` and `mMessageErrorRate` values are valid.

###### mRloc16 (heading level 8)

```
uint16_t otMeshDiagRouterNeighborEntry::mRloc16
```

**Description:** RLOC16.

###### mExtAddress (heading level 8)

```
otExtAddress otMeshDiagRouterNeighborEntry::mExtAddress
```

**Description:** Extended Address.

###### mVersion (heading level 8)

```
uint16_t otMeshDiagRouterNeighborEntry::mVersion
```

**Description:** Version.

###### mConnectionTime (heading level 8)

```
uint32_t otMeshDiagRouterNeighborEntry::mConnectionTime
```

**Description:** Seconds since link establishment.

###### mLinkMargin (heading level 8)

```
uint8_t otMeshDiagRouterNeighborEntry::mLinkMargin
```

**Description:** Link Margin in dB.

###### mAverageRssi (heading level 8)

```
int8_t otMeshDiagRouterNeighborEntry::mAverageRssi
```

**Description:** Average RSSI.

###### mLastRssi (heading level 8)

```
int8_t otMeshDiagRouterNeighborEntry::mLastRssi
```

**Description:** RSSI of last received frame.

###### mFrameErrorRate (heading level 8)

```
uint16_t otMeshDiagRouterNeighborEntry::mFrameErrorRate
```

**Description:** Frame error rate (0x0000->0%, 0xffff->100%).

###### mMessageErrorRate (heading level 8)

```
uint16_t otMeshDiagRouterNeighborEntry::mMessageErrorRate
```

**Description:** (IPv6) msg error rate (0x0000->0%, 0xffff->100%).

##### Radio Statistics

This module includes functions for radio statistics. 

###### Modules

[otRadioTimeStats](ot-radio-time-stats)

###### Typedefs

###### otRadioTimeStats (heading level 7)

`typedef struct otRadioTimeStats otRadioTimeStats`

**Description:**

Contains the statistics of radio.

###### Functions

###### otRadioTimeStatsGet (heading level 7)

`const otRadioTimeStats * otRadioTimeStatsGet(otInstance *aInstance)`

**Description:** Gets the radio statistics.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

The radio statistics include the time when the radio is in TX/RX/Sleep state. These times are in units of microseconds. All times are calculated from the last reset of radio statistics.

**Returns**

- A const pointer to the [otRadioTimeStats](ot-radio-time-stats) struct that contains the data.

###### otRadioTimeStatsReset (heading level 7)

`void otRadioTimeStatsReset(otInstance *aInstance)`

**Description:** Resets the radio statistics.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

All times are reset to 0.

Contains the statistics of radio. 

###### Public Attributes (heading level 7)

###### mDisabledTime (heading level 8)

```
uint64_t otRadioTimeStats::mDisabledTime
```

**Description:** The total time that radio is in disabled state, in unit of microseconds.

###### mSleepTime (heading level 8)

```
uint64_t otRadioTimeStats::mSleepTime
```

**Description:** The total time that radio is in sleep state, in unit of microseconds.

###### mTxTime (heading level 8)

```
uint64_t otRadioTimeStats::mTxTime
```

###### mRxTime (heading level 8)

```
uint64_t otRadioTimeStats::mRxTime
```

**Description:** > The total time that radio is doing transmission, in unit of microseconds.

##### Random Number Generator

###### Modules

[RNG Cryptographic](api-random-crypto)

[RNG Non-cryptographic](api-random-non-crypto)

###### RNG Cryptographic

This module includes functions that generates cryptographic random numbers. 

###### Functions (heading level 7)

###### otRandomCryptoFillBuffer (heading level 8)

`otError otRandomCryptoFillBuffer(uint8_t *aBuffer, uint16_t aSize)`

**Description:** Fills a given buffer with cryptographically secure random bytes.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t *|[out]|aBuffer|A pointer to a buffer to fill with the random bytes.|
|uint16_t|[in]|aSize|Size of buffer (number of bytes to fill).|

###### RNG Non-cryptographic

This module includes functions that generates non cryptographic random numbers. 

###### Functions (heading level 7)

###### otRandomNonCryptoGetUint32 (heading level 8)

`uint32_t otRandomNonCryptoGetUint32(void)`

**Description:** Generates and returns a random `uint32_t` value.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

**Returns**

- A random `uint32_t` value.

###### otRandomNonCryptoGetUint8 (heading level 8)

`uint8_t otRandomNonCryptoGetUint8(void)`

**Description:** Generates and returns a random byte.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

**Returns**

- A random `uint8_t` value.

###### otRandomNonCryptoGetUint16 (heading level 8)

`uint16_t otRandomNonCryptoGetUint16(void)`

**Description:** Generates and returns a random `uint16_t` value.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

**Returns**

- A random `uint16_t` value.

###### otRandomNonCryptoGetUint8InRange (heading level 8)

`uint8_t otRandomNonCryptoGetUint8InRange(uint8_t aMin, uint8_t aMax)`

**Description:** Generates and returns a random `uint8_t` value within a given range `[aMin, aMax)`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t|[in]|aMin|A minimum value (this value can be included in returned random result).|
|uint8_t|[in]|aMax|A maximum value (this value is excluded from returned random result).|

**Returns**

- A random `uint8_t` value in the given range (i.e., aMin <= random value < aMax).

###### otRandomNonCryptoGetUint16InRange (heading level 8)

`uint16_t otRandomNonCryptoGetUint16InRange(uint16_t aMin, uint16_t aMax)`

**Description:** Generates and returns a random `uint16_t` value within a given range `[aMin, aMax)`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint16_t|[in]|aMin|A minimum value (this value can be included in returned random result).|
|uint16_t|[in]|aMax|A maximum value (this value is excluded from returned random result).|

**Note**

- The returned random value can include the `aMin` value but excludes the `aMax`.

**Returns**

- A random `uint16_t` value in the given range (i.e., aMin <= random value < aMax).

###### otRandomNonCryptoGetUint32InRange (heading level 8)

`uint32_t otRandomNonCryptoGetUint32InRange(uint32_t aMin, uint32_t aMax)`

**Description:** Generates and returns a random `uint32_t` value within a given range `[aMin, aMax)`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint32_t|[in]|aMin|A minimum value (this value can be included in returned random result).|
|uint32_t|[in]|aMax|A maximum value (this value is excluded from returned random result).|

**Note**

- The returned random value can include the `aMin` value but excludes the `aMax`.

**Returns**

- A random `uint32_t` value in the given range (i.e., aMin <= random value < aMax).

###### otRandomNonCryptoFillBuffer (heading level 8)

`void otRandomNonCryptoFillBuffer(uint8_t *aBuffer, uint16_t aSize)`

**Description:** Fills a given buffer with random bytes.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t *|[out]|aBuffer|A pointer to a buffer to fill with the random bytes.|
|uint16_t|[in]|aSize|Size of buffer (number of bytes to fill).|

###### otRandomNonCryptoAddJitter (heading level 8)

`uint32_t otRandomNonCryptoAddJitter(uint32_t aValue, uint16_t aJitter)`

**Description:** Adds a random jitter within a given range to a given value.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint32_t|[in]|aValue|A value to which the random jitter is added.|
|uint16_t|[in]|aJitter|Maximum jitter. Random jitter is selected from the range `[-aJitter, aJitter]`.|

**Returns**

- The given value with an added random jitter.

##### SNTP

This module includes functions that control SNTP communication. 

###### Modules

[otSntpQuery](ot-sntp-query)

###### Typedefs

###### otSntpQuery (heading level 7)

`typedef struct otSntpQuery otSntpQuery`

**Description:**

Implements SNTP Query parameters.

###### otSntpResponseHandler (heading level 7)

`typedef void(* otSntpResponseHandler) (void *aContext, uint64_t aTime, otError aResult)`

**Description:**

Pointer is called when a SNTP response is received.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to application-specific context.|
||[in]|aTime|Specifies the time at the server when the response left for the client, in UNIX time.|
||[in]|aResult|A result of the SNTP transaction.|

**Details:**

###### Functions

###### otSntpClientQuery (heading level 7)

`otError otSntpClientQuery(otInstance *aInstance, const otSntpQuery *aQuery, otSntpResponseHandler aHandler, void *aContext)`

**Description:** Sends a SNTP query.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|const [otSntpQuery](ot-sntp-query) *|[in]|aQuery|A pointer to specify SNTP query parameters.|
|[otSntpResponseHandler](api-sntp#ot-sntp-response-handler)|[in]|aHandler|A function pointer that shall be called on response reception or time-out.|
|void *|[in]|aContext|A pointer to arbitrary context information.|

Is available only if feature `OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE` is enabled.

###### otSntpClientSetUnixEra (heading level 7)

`void otSntpClientSetUnixEra(otInstance *aInstance, uint32_t aUnixEra)`

**Description:** Sets the unix era number.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aUnixEra|Unix era number.|

The default value of unix era is set to 0. The subsequent eras start after year 2106.

###### Macros

`#define OT_SNTP_DEFAULT_SERVER_IP "2001:4860:4806:8::"`

**Description**: Defines default SNTP Server address - Google NTP Server.

`#define OT_SNTP_DEFAULT_SERVER_PORT 123`

**Description**: Defines default SNTP Server port.

Implements SNTP Query parameters. 

###### Public Attributes (heading level 7)

###### mMessageInfo (heading level 8)

```
const otMessageInfo* otSntpQuery::mMessageInfo
```

**Description:** A reference to the message info related with SNTP Server.

#### Provisional

This module includes the OpenThread provisional APIs. 

These APIs are not stable and users should use them with caution. 

##### Modules

[Link](api-provisional-link)

[Peer-to-Peer](api-provisional-p2p)

##### Link

This module includes provisional functions that control link-layer configuration. 

###### Modules

[otWakeupRequest](ot-wakeup-request)

###### Enumerations

###### otWakeupType (heading level 7)

```
enum otWakeupType {
    OT_WAKEUP_TYPE_EXT_ADDRESS = 0
    OT_WAKEUP_TYPE_IDENTIFIER = 1
    OT_WAKEUP_TYPE_GROUP_IDENTIFIER = 2
}
```

**Description:**

Represents the wake-up request type.

**Enumerator:**

|   |   |
|---|---|
|OT_WAKEUP_TYPE_EXT_ADDRESS|Wake up the peer by the extended address.|
|OT_WAKEUP_TYPE_IDENTIFIER|Wake up the peer by the wake-up identifier.|
|OT_WAKEUP_TYPE_GROUP_IDENTIFIER|Wake up peers by the group wake-up identifier.|

###### Typedefs

###### otWakeupId (heading level 7)

`typedef uint64_t otWakeupId`

**Description:**

Represents the wake-up identifier.

###### otWakeupType (heading level 7)

`typedef enum otWakeupType otWakeupType`

**Description:**

Represents the wake-up request type.

###### otWakeupRequest (heading level 7)

`typedef struct otWakeupRequest otWakeupRequest`

**Description:**

Represents the request to wake up the peer.

Represents the request to wake up the peer. 

###### Public Attributes (heading level 7)

###### mWakeupId (heading level 8)

```
otWakeupId otWakeupRequest::mWakeupId
```

**Description:** Wake-up identifier of the Wake-up Listener.

###### mExtAddress (heading level 8)

```
otExtAddress otWakeupRequest::mExtAddress
```

**Description:** IEEE 802.15.4 Extended Address of the Wake-up Listener.

###### mShared (heading level 8)

```
union otWakeupRequest::@18 otWakeupRequest::mShared
```

###### mType (heading level 8)

```
otWakeupType otWakeupRequest::mType
```

**Description:** Indicates the wake-up request type (`OT_WAKEUP_TYPE_*` enumeration).

##### Peer-to-Peer

This module includes provisional functions for the Thread P2P link. 

**Note**

- The functions in this module require `OPENTHREAD_CONFIG_P2P_ENABLE=1`.

###### Modules

[otP2pRequest](ot-p2p-request)

###### Enumerations

###### otP2pEvent (heading level 7)

```
enum otP2pEvent {
    OT_P2P_EVENT_LINKED = 0
    OT_P2P_EVENT_UNLINKED = 1
}
```

**Description:**

Defines events of the P2P link.

**Enumerator:**

|   |   |
|---|---|
|OT_P2P_EVENT_LINKED|The P2P link has been established.|
|OT_P2P_EVENT_UNLINKED|The P2P link has been torn down.|

###### Typedefs

###### otP2pRequest (heading level 7)

`typedef struct otP2pRequest otP2pRequest`

**Description:**

Represents a request for establishing P2P links.

###### otP2pLinkDoneCallback (heading level 7)

`typedef void(* otP2pLinkDoneCallback) (void *aContext)`

**Description:**

Notifies the caller that the P2P link establishment process has ended.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to the application-specific context.|

**Details:**

###### otP2pUnlinkDoneCallback (heading level 7)

`typedef void(* otP2pUnlinkDoneCallback) (void *aContext)`

**Description:**

Notifies the caller that the P2P link tear down process has ended.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|A pointer to application-specific context.|

**Details:**

###### otP2pEvent (heading level 7)

`typedef enum otP2pEvent otP2pEvent`

**Description:**

Defines events of the P2P link.

###### otP2pEventCallback (heading level 7)

`typedef void(* otP2pEventCallback) (otP2pEvent aEvent, const otExtAddress *aExtAddress, void *aContext)`

**Description:**

Callback function pointer to signal events of the P2P link.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aEvent|The P2P link event.|
||[in]|aExtAddress|A pointer to the peer's Extended Address of the P2P link.|
||[in]|aContext|A pointer to the application-specific context.|

**Details:**

###### Functions

###### otP2pWakeupAndLink (heading level 7)

`otError otP2pWakeupAndLink(otInstance *aInstance, const otP2pRequest *aP2pRequest, otP2pLinkDoneCallback aCallback, void *aContext)`

**Description:** Attempts to wake up peers and establish P2P links with peers.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otP2pRequest](ot-p2p-request) *|[in]|aP2pRequest|A pointer to P2P request.|
|[otP2pLinkDoneCallback](api-provisional-p2p#ot-p2p-link-done-callback)|[in]|aCallback|A pointer to the function that is called when the P2P link establishment process ends .|
|void *|[in]|aContext|A pointer to the callback application-specific context.|

If the `aP2pRequest` indicates a group wake-up, this method establishes multiple P2P links with peers. Otherwise, it establishes at most one P2P link.

###### otP2pUnlink (heading level 7)

`otError otP2pUnlink(otInstance *aInstance, const otExtAddress *aExtAddress, otP2pUnlinkDoneCallback aCallback, void *aContext)`

**Description:** Tears down the P2P link specified by the Extended Address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otExtAddress](ot-ext-address) *|[in]|aExtAddress|A pointer to the P2P peer's Extended Address.|
|[otP2pUnlinkDoneCallback](api-provisional-p2p#ot-p2p-unlink-done-callback)|[in]|aCallback|A pointer to function that is called when the P2P link tear down process has ended.|
|void *|[in]|aContext|A pointer to callback application-specific context.|

###### otP2pSetEventCallback (heading level 7)

`void otP2pSetEventCallback(otInstance *aInstance, otP2pEventCallback aCallback, void *aContext)`

**Description:** Sets the callback function to notify event changes of P2P links.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otP2pEventCallback](api-provisional-p2p#ot-p2p-event-callback)|[in]|aCallback|The callback function pointer.|
|void *|[in]|aContext|A pointer to the callback application-specific context.|

A subsequent call to this function will replace any previously set callback.

Represents a request for establishing P2P links. 

###### Public Attributes (heading level 7)

###### mWakeupRequest (heading level 8)

```
otWakeupRequest otP2pRequest::mWakeupRequest
```

**Description:** Wake-up request.

### Platform Abstraction

This module includes the platform abstraction used by the OpenThread stack. 

#### Modules

[Alarm](plat-alarm)

[BLE](plat-ble)

[Crypto - Platform](plat-crypto)

[DNS - Platform](plat-dns)

[DNS-SD (mDNS)](plat-dns-sd)

[Entropy](plat-entropy)

[Factory Diagnostics - Platform](plat-factory-diagnostics)

[Infrastructure Interface](plat-infra-if)

[Logging - Platform](plat-logging)

[Memory](plat-memory)

[Message Pool](plat-messagepool)

[Miscellaneous](plat-misc)

[Multicast DNS](plat-mdns)

[Multipan](plat-multipan)

[Network Simulator](plat-otns)

[Radio](plat-radio)

[Settings](plat-settings)

[SPI Slave](plat-spi-slave)

[Time Service](plat-time)

[Toolchain](plat-toolchain)

[TREL - Platform](plat-trel)

#### Alarm

This module includes the platform abstraction for the alarm service. 

##### Functions

###### otPlatAlarmMicroStartAt

`void otPlatAlarmMicroStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt)`

**Description:** Set the alarm to fire at `aDt` microseconds after `aT0`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint32_t|[in]|aT0|The reference time.|
|uint32_t|[in]|aDt|The time delay in microseconds from `aT0`.|

For `aT0`, the platform MUST support all values in [0, 2^32-1]. For `aDt`, the platform MUST support all values in [0, 2^31-1].

###### otPlatAlarmMicroStop

`void otPlatAlarmMicroStop(otInstance *aInstance)`

**Description:** Stop the alarm.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

###### otPlatAlarmMicroGetNow

`uint32_t otPlatAlarmMicroGetNow(void)`

**Description:** Get the current time.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

The current time MUST represent a free-running timer. When maintaining current time, the time value MUST utilize the entire range [0, 2^32-1] and MUST NOT wrap before 2^32.

**Returns**

- The current time in microseconds.

###### otPlatAlarmMicroFired

`void otPlatAlarmMicroFired(otInstance *aInstance)`

**Description:** Signal that the alarm has fired.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

###### otPlatAlarmMilliStartAt

`void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt)`

**Description:** Set the alarm to fire at `aDt` milliseconds after `aT0`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint32_t|[in]|aT0|The reference time.|
|uint32_t|[in]|aDt|The time delay in milliseconds from `aT0`.|

For `aT0` the platform MUST support all values in [0, 2^32-1]. For `aDt`, the platform MUST support all values in [0, 2^31-1].

###### otPlatAlarmMilliStop

`void otPlatAlarmMilliStop(otInstance *aInstance)`

**Description:** Stop the alarm.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

###### otPlatAlarmMilliGetNow

`uint32_t otPlatAlarmMilliGetNow(void)`

**Description:** Get the current time.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

The current time MUST represent a free-running timer. When maintaining current time, the time value MUST utilize the entire range [0, 2^32-1] and MUST NOT wrap before 2^32.

**Returns**

- The current time in milliseconds.

###### otPlatAlarmMilliFired

`void otPlatAlarmMilliFired(otInstance *aInstance)`

**Description:** Signal that the alarm has fired.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

###### otPlatDiagAlarmFired

`void otPlatDiagAlarmFired(otInstance *aInstance)`

**Description:** Signal diagnostics module that the alarm has fired.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

#### DNS - Platform

This module includes the platform abstraction for sending recursive DNS query to upstream DNS servers. 

##### Typedefs

###### otPlatDnsUpstreamQuery

`typedef struct otPlatDnsUpstreamQuery otPlatDnsUpstreamQuery`

**Description:**

This opaque type represents an upstream DNS query transaction.

##### Functions

###### otPlatDnsIsUpstreamQueryAvailable

`bool otPlatDnsIsUpstreamQueryAvailable(otInstance *aInstance)`

**Description:** Indicates whether upstream DNS query functionality is available on the platform.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|

This function allows the platform to inform the OpenThread stack if no upstream DNS server is available.

This function is used to optimize query handling. If this function returns `false` (e.g., no upstream DNS server is currently available), one can avoid attempting an upstream resolution (which would likely time out) and instead immediately send an appropriate negative response (e.g., `SERVFAIL`) to the DNS client.

###### otPlatDnsStartUpstreamQuery

`void otPlatDnsStartUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn, const otMessage *aQuery)`

**Description:** Starts an upstream query transaction.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otPlatDnsUpstreamQuery](plat-dns#ot-plat-dns-upstream-query) *|[in]|aTxn|A pointer to the opaque DNS query transaction object.|
|const [otMessage](api-message#ot-message) *|[in]|aQuery|A message buffer of the DNS payload that should be sent to upstream DNS server.|

- In success case (and errors represented by DNS protocol messages), the platform is expected to call `otPlatDnsUpstreamQueryDone`.
- The OpenThread core may cancel a query transaction (possibly due to a timeout) by invoking `otPlatDnsCancelUpstreamQuery()`. The platform MUST still call `otPlatDnsUpstreamQueryDone()` on a cancelled transaction to release the transaction.

###### otPlatDnsCancelUpstreamQuery

`void otPlatDnsCancelUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn)`

**Description:** Cancels a transaction of upstream query.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otPlatDnsUpstreamQuery](plat-dns#ot-plat-dns-upstream-query) *|[in]|aTxn|A pointer to the opaque DNS query transaction object.|

The platform MUST call `otPlatDnsUpstreamQueryDone()` to release the transaction.

###### otPlatDnsUpstreamQueryDone

`void otPlatDnsUpstreamQueryDone(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn, otMessage *aResponse)`

**Description:** The platform calls this function to finish DNS query.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otPlatDnsUpstreamQuery](plat-dns#ot-plat-dns-upstream-query) *|[in]|aTxn|A pointer to the opaque DNS query transaction object.|
|[otMessage](api-message#ot-message) *|[in]|aResponse|A message buffer of the DNS response payload or NULL to close a transaction without a response.|

The transaction will be released, so the platform MUST NOT call on the same transaction twice. This function passes the ownership of `aResponse` to OpenThread stack.

Platform can pass NULL to close a transaction without a response.

#### Entropy

This module includes the platform abstraction for entropy generation. 

##### Functions

###### otPlatEntropyGet

`otError otPlatEntropyGet(uint8_t *aOutput, uint16_t aOutputLength)`

**Description:** Fill buffer with entropy.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t *|[out]|aOutput|A pointer to where the true random values are placed. Must not be NULL.|
|uint16_t|[in]|aOutputLength|Size of `aBuffer`.|

MUST be implemented using a true random number generator (TRNG).

#### Factory Diagnostics - Platform

This module includes the platform abstraction for diagnostics features. 

##### Enumerations

###### otGpioMode

```
enum otGpioMode {
    OT_GPIO_MODE_INPUT = 0
    OT_GPIO_MODE_OUTPUT = 1
}
```

**Description:**

Defines the gpio modes.

**Enumerator:**

|   |   |
|---|---|
|OT_GPIO_MODE_INPUT|Input mode without pull resistor.|
|OT_GPIO_MODE_OUTPUT|Output mode.|

##### Typedefs

###### otPlatDiagOutputCallback

`typedef void(* otPlatDiagOutputCallback) (const char *aFormat, va_list aArguments, void *aContext)`

**Description:**

Pointer to callback to output platform diag messages.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aFormat|The format string.|
||[in]|aArguments|The format string arguments.|
||[out]|aContext|A pointer to the user context.|

**Details:**

##### Functions

###### otPlatDiagSetOutputCallback

`void otPlatDiagSetOutputCallback(otInstance *aInstance, otPlatDiagOutputCallback aCallback, void *aContext)`

**Description:** Sets the platform diag output callback.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otPlatDiagOutputCallback](plat-factory-diagnostics#ot-plat-diag-output-callback)|[in]|aCallback|A pointer to a function that is called on outputting diag messages.|
|void *|[in]|aContext|A pointer to the user context.|

###### otPlatDiagProcess

`otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[])`

**Description:** Processes a factory diagnostics command line.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance for current request.|
|uint8_t|[in]|aArgsLength|The number of arguments in `aArgs`.|
|char *|[in]|aArgs|The arguments of diagnostics command line.|

###### otPlatDiagModeSet

`void otPlatDiagModeSet(bool aMode)`

**Description:** Enables/disables the factory diagnostics mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|bool|[in]|aMode|TRUE to enable diagnostics mode, FALSE otherwise.|

###### otPlatDiagModeGet

`bool otPlatDiagModeGet(void)`

**Description:** Indicates whether or not factory diagnostics mode is enabled.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

**Returns**

- TRUE if factory diagnostics mode is enabled, FALSE otherwise.

###### otPlatDiagChannelSet

`void otPlatDiagChannelSet(uint8_t aChannel)`

**Description:** Sets the channel to use for factory diagnostics.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t|[in]|aChannel|The channel value.|

###### otPlatDiagTxPowerSet

`void otPlatDiagTxPowerSet(int8_t aTxPower)`

**Description:** Sets the transmit power to use for factory diagnostics.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|int8_t|[in]|aTxPower|The transmit power value.|

###### otPlatDiagRadioReceived

`void otPlatDiagRadioReceived(otInstance *aInstance, otRadioFrame *aFrame, otError aError)`

**Description:** Processes the received radio frame.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance for current request.|
|[otRadioFrame](ot-radio-frame) *|[in]|aFrame|The received radio frame.|
|[otError](api-error#ot-error)|[in]|aError|The received radio frame status.|

###### otPlatDiagAlarmCallback

`void otPlatDiagAlarmCallback(otInstance *aInstance)`

**Description:** Processes the alarm event.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance for current request.|

###### otPlatDiagGpioSet

`otError otPlatDiagGpioSet(uint32_t aGpio, bool aValue)`

**Description:** Sets the gpio value.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint32_t|[in]|aGpio|The gpio number.|
|bool|[in]|aValue|true to set the gpio to high level, or false otherwise.|

###### otPlatDiagGpioGet

`otError otPlatDiagGpioGet(uint32_t aGpio, bool *aValue)`

**Description:** Gets the gpio value.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint32_t|[in]|aGpio|The gpio number.|
|bool *|[out]|aValue|A pointer where to put gpio value.|

###### otPlatDiagGpioSetMode

`otError otPlatDiagGpioSetMode(uint32_t aGpio, otGpioMode aMode)`

**Description:** Sets the gpio mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint32_t|[in]|aGpio|The gpio number.|
|[otGpioMode](plat-factory-diagnostics#ot-gpio-mode)|[out]|aMode|The gpio mode.|

###### otPlatDiagGpioGetMode

`otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode)`

**Description:** Gets the gpio mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint32_t|[in]|aGpio|The gpio number.|
|[otGpioMode](plat-factory-diagnostics#ot-gpio-mode) *|[out]|aMode|A pointer where to put gpio mode.|

###### otPlatDiagRadioSetRawPowerSetting

`otError otPlatDiagRadioSetRawPowerSetting(otInstance *aInstance, const uint8_t *aRawPowerSetting, uint16_t aRawPowerSettingLength)`

**Description:** Set the radio raw power setting for diagnostics module.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|const uint8_t *|[in]|aRawPowerSetting|A pointer to the raw power setting byte array.|
|uint16_t|[in]|aRawPowerSettingLength|The length of the `aRawPowerSetting`.|

###### otPlatDiagRadioGetRawPowerSetting

`otError otPlatDiagRadioGetRawPowerSetting(otInstance *aInstance, uint8_t *aRawPowerSetting, uint16_t *aRawPowerSettingLength)`

**Description:** Get the radio raw power setting for diagnostics module.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint8_t *|[out]|aRawPowerSetting|A pointer to the raw power setting byte array.|
|uint16_t *|[inout]|aRawPowerSettingLength|On input, a pointer to the size of `aRawPowerSetting`. On output, a pointer to the length of the raw power setting data.|

###### otPlatDiagRadioRawPowerSettingEnable

`otError otPlatDiagRadioRawPowerSettingEnable(otInstance *aInstance, bool aEnable)`

**Description:** Enable/disable the platform layer to use the raw power setting set by `otPlatDiagRadioSetRawPowerSetting()`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|bool|[in]|aEnable|TRUE to enable or FALSE to disable the raw power setting.|

###### otPlatDiagRadioTransmitCarrier

`otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable)`

**Description:** Start/stop the platform layer to transmit continuous carrier wave.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|bool|[in]|aEnable|TRUE to enable or FALSE to disable the platform layer to transmit continuous carrier wave.|

###### otPlatDiagRadioTransmitStream

`otError otPlatDiagRadioTransmitStream(otInstance *aInstance, bool aEnable)`

**Description:** Start/stop the platform layer to transmit stream of characters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|bool|[in]|aEnable|TRUE to enable or FALSE to disable the platform layer to transmit stream.|

###### otPlatDiagRadioGetPowerSettings

`otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance, uint8_t aChannel, int16_t *aTargetPower, int16_t *aActualPower, uint8_t *aRawPowerSetting, uint16_t *aRawPowerSettingLength)`

**Description:** Get the power settings for the given channel.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint8_t|[in]|aChannel|The radio channel.|
|int16_t *|[out]|aTargetPower|The target power in 0.01 dBm.|
|int16_t *|[out]|aActualPower|The actual power in 0.01 dBm.|
|uint8_t *|[out]|aRawPowerSetting|A pointer to the raw power setting byte array.|
|uint16_t *|[inout]|aRawPowerSettingLength|On input, a pointer to the size of `aRawPowerSetting`. On output, a pointer to the length of the raw power setting data.|

#### Logging - Platform

This module includes the platform abstraction for the debug log service. 

##### Enumerations

###### otLogRegion

```
enum otLogRegion {
    OT_LOG_REGION_API = 1
    OT_LOG_REGION_MLE = 2
    OT_LOG_REGION_ARP = 3
    OT_LOG_REGION_NET_DATA = 4
    OT_LOG_REGION_ICMP = 5
    OT_LOG_REGION_IP6 = 6
    OT_LOG_REGION_TCP = 7
    OT_LOG_REGION_MAC = 8
    OT_LOG_REGION_MEM = 9
    OT_LOG_REGION_NCP = 10
    OT_LOG_REGION_MESH_COP = 11
    OT_LOG_REGION_NET_DIAG = 12
    OT_LOG_REGION_PLATFORM = 13
    OT_LOG_REGION_COAP = 14
    OT_LOG_REGION_CLI = 15
    OT_LOG_REGION_CORE = 16
    OT_LOG_REGION_UTIL = 17
    OT_LOG_REGION_BBR = 18
    OT_LOG_REGION_MLR = 19
    OT_LOG_REGION_DUA = 20
    OT_LOG_REGION_BR = 21
    OT_LOG_REGION_SRP = 22
    OT_LOG_REGION_DNS = 23
}
```

**Description:**

Represents log regions.

**Details:**

The support for log region is removed and instead each core module can define its own name to appended to the logs. However, the `otLogRegion` enumeration is still defined as before to help with platforms which we may be using it in their `otPlatLog()` implementation. The OT core will always emit all logs with `OT_LOG_REGION_CORE`.

**Enumerator:**

|   |   |
|---|---|
|OT_LOG_REGION_API|OpenThread API.|
|OT_LOG_REGION_MLE|MLE.|
|OT_LOG_REGION_ARP|EID-to-RLOC mapping.|
|OT_LOG_REGION_NET_DATA|Network Data.|
|OT_LOG_REGION_ICMP|ICMPv6.|
|OT_LOG_REGION_IP6|IPv6.|
|OT_LOG_REGION_TCP|TCP.|
|OT_LOG_REGION_MAC|IEEE 802.15.4 MAC.|
|OT_LOG_REGION_MEM|Memory.|
|OT_LOG_REGION_NCP|NCP.|
|OT_LOG_REGION_MESH_COP|Mesh Commissioning Protocol.|
|OT_LOG_REGION_NET_DIAG|Network Diagnostic.|
|OT_LOG_REGION_PLATFORM|Platform.|
|OT_LOG_REGION_COAP|CoAP.|
|OT_LOG_REGION_CLI|CLI.|
|OT_LOG_REGION_CORE|OpenThread Core.|
|OT_LOG_REGION_UTIL|Utility module.|
|OT_LOG_REGION_BBR|Backbone Router (available since Thread 1.2)|
|OT_LOG_REGION_MLR|Multicast Listener Registration (available since Thread 1.2)|
|OT_LOG_REGION_DUA|Domain Unicast Address (available since Thread 1.2)|
|OT_LOG_REGION_BR|Border Router.|
|OT_LOG_REGION_SRP|Service Registration Protocol (SRP)|
|OT_LOG_REGION_DNS|DNS.|

##### Typedefs

###### otLogLevel

`typedef int otLogLevel`

**Description:**

Represents the log level.

###### otLogRegion

`typedef enum otLogRegion otLogRegion`

**Description:**

Represents log regions.

**Details:**

The support for log region is removed and instead each core module can define its own name to appended to the logs. However, the `otLogRegion` enumeration is still defined as before to help with platforms which we may be using it in their `otPlatLog()` implementation. The OT core will always emit all logs with `OT_LOG_REGION_CORE`.

##### Functions

###### otPlatLog

`void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat,...)`

**Description:** Outputs logs.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otLogLevel](plat-logging#ot-log-level)|[in]|aLogLevel|The log level.|
|[otLogRegion](plat-logging#ot-log-region)|[in]|aLogRegion|The log region.|
|const char *|[in]|aFormat|A pointer to the format string.|
|...|[in]|undefined|Arguments for the format specification.|

This platform API is used to output logs when the configuration `OPENTHREAD_CONFIG_LOG_INSTANCE_AWARE_API_ENABLE` is disabled. When the configuration is enabled, `otPlatLogOutput()` is used instead.

Note that the support for log region is removed. The OT core will always emit all logs with `OT_LOG_REGION_CORE` as `aLogRegion`.

###### otPlatLogOutput

`void otPlatLogOutput(otInstance *aInstance, otLogLevel aLogLevel, const char *aLogLine)`

**Description:** Outputs a log line.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otLogLevel](plat-logging#ot-log-level)|[in]|aLogLevel|The log level.|
|const char *|[in]|aLogLine|A pointer to the null-terminated string containing the log line.|

This platform API is an alternative to `otPlatLog()` and is used when the configuration `OPENTHREAD_CONFIG_LOG_INSTANCE_AWARE_API_ENABLE` is enabled.

Unlike `otPlatLog()`, this API also provides a pointer to the OpenThread instance (`otInstance*`) from which the log is generated. This is particularly helpful in a multi-instance setup to distinguish logs from different instances. Additionally, it provides the log line as a fully formatted null-terminated string instead of a format string and variable arguments.

###### otPlatLogHandleLevelChanged

`void otPlatLogHandleLevelChanged(otLogLevel aLogLevel)`

**Description:** Handles OpenThread log level changes.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otLogLevel](plat-logging#ot-log-level)|[in]|aLogLevel|The new OpenThread log level.|

This platform function is optional. An empty weak implementation is provided by OpenThread core.

This platform function is called whenever the OpenThread log level changes:

- In a single-instance configuration, this is called when the log level changes.
- In a multi-instance configuration, this is called when the global log level changes.

**Note**

- Only applicable when `OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE=1`.

###### otPlatLogHandleLogLevelChanged

`void otPlatLogHandleLogLevelChanged(otInstance *aInstance, otLogLevel aLogLevel)`

**Description:** Handles OpenThread instance-specific log level changes.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otLogLevel](plat-logging#ot-log-level)|[in]|aLogLevel|The new OpenThread log level for the instance.|

This platform function is optional. An empty weak implementation is provided by OpenThread core.

This platform function is called whenever the instance-specific log level changes:

- In a single-instance configuration, this is called along with `otPlatLogHandleLevelChanged()`.
- In a multi-instance configuration, if `OPENTHREAD_CONFIG_LOG_INSTANCE_AWARE_API_ENABLE` is used, this is called when the instance-specific log level changes.

##### Macros

`#define OT_LOG_LEVEL_NONE 0`

**Description**: Log level None.

`#define OT_LOG_LEVEL_CRIT 1`

**Description**: Log level Critical.

`#define OT_LOG_LEVEL_WARN 2`

**Description**: Log level Warning.

`#define OT_LOG_LEVEL_NOTE 3`

**Description**: Log level Notice.

`#define OT_LOG_LEVEL_INFO 4`

**Description**: Log level Informational.

`#define OT_LOG_LEVEL_DEBG 5`

**Description**: Log level Debug.

#### Memory

This module includes the platform abstraction for dynamic memory allocation. 

##### Functions

###### otPlatCAlloc

`void * otPlatCAlloc(size_t aNum, size_t aSize)`

**Description:** Dynamically allocates new memory.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|size_t|[in]|aNum|The number of blocks to allocate|
|size_t|[in]|aSize|The size of each block to allocate|

On platforms that support it, they should redirect to `calloc`. For those that don't support `calloc`, they should implement the standard `calloc` behavior.

See: [https://man7.org/linux/man-pages/man3/calloc.3.html](https://man7.org/linux/man-pages/man3/calloc.3.html)

Is required for `OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE`.

###### otPlatFree

`void otPlatFree(void *aPtr)`

**Description:** Frees memory that was dynamically allocated by `otPlatCAlloc()`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void *|[in]|aPtr|A pointer the memory blocks to free. The pointer may be NULL.|

Is required for `OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE`.

#### Miscellaneous

This module includes platform abstractions for miscellaneous behaviors. 

##### Enumerations

###### otPlatResetReason

```
enum otPlatResetReason {
    OT_PLAT_RESET_REASON_POWER_ON = 0
    OT_PLAT_RESET_REASON_EXTERNAL = 1
    OT_PLAT_RESET_REASON_SOFTWARE = 2
    OT_PLAT_RESET_REASON_FAULT = 3
    OT_PLAT_RESET_REASON_CRASH = 4
    OT_PLAT_RESET_REASON_ASSERT = 5
    OT_PLAT_RESET_REASON_OTHER = 6
    OT_PLAT_RESET_REASON_UNKNOWN = 7
    OT_PLAT_RESET_REASON_WATCHDOG = 8
    OT_PLAT_RESET_REASON_COUNT
}
```

**Description:**

Enumeration of possible reset reason codes.

**Details:**

These are in the same order as the Spinel reset reason codes.

**Enumerator:**

|   |   |
|---|---|
|OT_PLAT_RESET_REASON_POWER_ON||
|OT_PLAT_RESET_REASON_EXTERNAL||
|OT_PLAT_RESET_REASON_SOFTWARE||
|OT_PLAT_RESET_REASON_FAULT||
|OT_PLAT_RESET_REASON_CRASH||
|OT_PLAT_RESET_REASON_ASSERT||
|OT_PLAT_RESET_REASON_OTHER||
|OT_PLAT_RESET_REASON_UNKNOWN||
|OT_PLAT_RESET_REASON_WATCHDOG||
|OT_PLAT_RESET_REASON_COUNT||

###### otPlatMcuPowerState

```
enum otPlatMcuPowerState {
    OT_PLAT_MCU_POWER_STATE_ON = 0
    OT_PLAT_MCU_POWER_STATE_LOW_POWER = 1
    OT_PLAT_MCU_POWER_STATE_OFF = 2
}
```

**Description:**

Enumeration of micro-controller's power states.

**Details:**

These values are used for NCP configuration when `OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL` is enabled.

The power state specifies the desired power state of NCP's micro-controller (MCU) when the underlying platform's operating system enters idle mode (i.e., all active tasks/events are processed and the MCU can potentially enter a energy-saving power state).

The power state primarily determines how the host should interact with the NCP and whether the host needs an external trigger (a "poke") to NCP before it can communicate with the NCP or not.

After a reset, the MCU power state MUST be `OT_PLAT_POWER_STATE_ON`.

**Enumerator:**

|   |   |
|---|---|
|OT_PLAT_MCU_POWER_STATE_ON|NCP's MCU stays on and active all the time.|
|OT_PLAT_MCU_POWER_STATE_LOW_POWER|NCP's MCU can enter low-power (energy-saving) state.|
|OT_PLAT_MCU_POWER_STATE_OFF|NCP is fully off.|

##### Functions

###### otPlatReset

`void otPlatReset(otInstance *aInstance)`

**Description:** Performs a software reset on the platform, if supported.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

###### otPlatResetToBootloader

`otError otPlatResetToBootloader(otInstance *aInstance)`

**Description:** Performs a hardware reset on the platform to launch bootloader mode, if supported.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

Used when `OPENTHREAD_CONFIG_PLATFORM_BOOTLOADER_MODE_ENABLE` is enabled.

###### otPlatGetResetReason

`otPlatResetReason otPlatGetResetReason(otInstance *aInstance)`

**Description:** Returns the reason for the last platform reset.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

###### otPlatAssertFail

`void otPlatAssertFail(const char *aFilename, int aLineNumber)`

**Description:** Provides a platform specific implementation for assert.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aFilename|The name of the file where the assert occurred.|
|int|[in]|aLineNumber|The line number in the file where the assert occurred.|

###### otPlatWakeHost

`void otPlatWakeHost(void)`

**Description:** Performs a platform specific operation to wake the host MCU.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

This is used only for NCP configurations. 

###### otPlatSetMcuPowerState

`otError otPlatSetMcuPowerState(otInstance *aInstance, otPlatMcuPowerState aState)`

**Description:** Sets the desired MCU power state.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to OpenThread instance.|
|[otPlatMcuPowerState](plat-misc#ot-plat-mcu-power-state)|[in]|aState|The new MCU power state.|

This is only applicable and used for NCP configuration when `OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL` is enabled.

###### otPlatGetMcuPowerState

`otPlatMcuPowerState otPlatGetMcuPowerState(otInstance *aInstance)`

**Description:** Gets the current desired MCU power state.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to OpenThread instance.|

This is only applicable and used for NCP configuration when `OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL` is enabled.

After a reset, the power state MUST return `OT_PLAT_POWER_STATE_ON`. During operation, power state SHOULD only change through an explicit successful call to `otPlatSetMcuPowerState()`.

**Returns**

- The current power state.

###### otPlatLogCrashDump

`otError otPlatLogCrashDump(void)`

**Description:** Logs a crash dump using OpenThread logging APIs.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

**Note**

- This API is an optional logging platform API. It's up to the platform layer to implement it.

#### Multipan

This module includes the platform abstraction for multipan support. 

##### Functions

###### otPlatMultipanGetActiveInstance

`otError otPlatMultipanGetActiveInstance(otInstance **aInstance)`

**Description:** Get instance currently in control of the radio.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) **|[out]|aInstance|Pointer to the variable for storing the active instance pointer.|

If radio does not operate in parallel on all interfaces, this function returns an instance object with granted radio access.

###### otPlatMultipanSetActiveInstance

`otError otPlatMultipanSetActiveInstance(otInstance *aInstance, bool aCompletePending)`

**Description:** Set `aInstance` as the current active instance controlling radio.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|bool|[in]|aCompletePending|True if ongoing radio operation should complete before interface switch (Soft switch), false for force switch.|

This function allows selecting the currently active instance on platforms that do not support parallel communication on multiple interfaces. In other words, if more than one instance is in a receive state, calling [otPlatMultipanSetActiveInstance](plat-multipan#ot-plat-multipan-set-active-instance) guarantees that specified instance will be the one receiving. This function returns if the request was received properly. After interface switching is complete, the platform should call [otPlatMultipanSwitchoverDone](plat-multipan#ot-plat-multipan-switchover-done). Switching interfaces may take longer if `aCompletePending` is set true.

###### otPlatMultipanSwitchoverDone

`void otPlatMultipanSwitchoverDone(otInstance *aInstance, bool aSuccess)`

**Description:** The platform completed the interface switching procedure.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|bool|[in]|aSuccess|True if successfully switched the interfaces, false if switching failed.|

Should be invoked immediately after processing [otPlatMultipanSetActiveInstance](plat-multipan#ot-plat-multipan-set-active-instance) if no delay is needed, or if some longer radio operations need to complete first, after the switch in interfaces is fully complete.

###### otPlatMultipanIidToInstance

`otInstance * otPlatMultipanIidToInstance(uint8_t aIid)`

**Description:** Get the instance pointer corresponding to the given IID.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t|[in]|aIid|The IID of the interface.|

###### otPlatMultipanInstanceToIid

`uint8_t otPlatMultipanInstanceToIid(otInstance *aInstance)`

**Description:** Get the IID corresponding to the given OpenThread instance pointer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

#### Network Simulator

This module includes the platform abstraction for OTNS. 

##### Functions

###### otPlatOtnsStatus

`void otPlatOtnsStatus(const char *aStatus)`

**Description:** Exports status information to OTNS.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const char *|[in]|aStatus|The status string.|

The status information is represented by a null-terminated string with format recognizable by OTNS. Each call to `otPlatOtnsStatus` can send multiple statuses, separated by ';', e.x. "parid=577fbc37;lrid=5". Each status contains key and value separated by '='. Status value can be further separated into multiple fields using ',', e.x. "ping_request=fdde:ad00:beef:0:459e:d7b4:b65e:5480,4,112000".

New statuses should follow these conventions.

Currently, OTNS only supports virtual time simulation.

#### Settings

This module includes the platform abstraction for non-volatile storage of settings. 

##### Enumerations

###### @17

```
enum @17 {
    OT_SETTINGS_KEY_ACTIVE_DATASET = 0x0001
    OT_SETTINGS_KEY_PENDING_DATASET = 0x0002
    OT_SETTINGS_KEY_NETWORK_INFO = 0x0003
    OT_SETTINGS_KEY_PARENT_INFO = 0x0004
    OT_SETTINGS_KEY_CHILD_INFO = 0x0005
    OT_SETTINGS_KEY_SLAAC_IID_SECRET_KEY = 0x0007
    OT_SETTINGS_KEY_DAD_INFO = 0x0008
    OT_SETTINGS_KEY_SRP_ECDSA_KEY = 0x000b
    OT_SETTINGS_KEY_SRP_CLIENT_INFO = 0x000c
    OT_SETTINGS_KEY_SRP_SERVER_INFO = 0x000d
    OT_SETTINGS_KEY_BR_ULA_PREFIX = 0x000f
    OT_SETTINGS_KEY_BR_ON_LINK_PREFIXES = 0x0010
    OT_SETTINGS_KEY_BORDER_AGENT_ID = 0x0011
    OT_SETTINGS_KEY_TCAT_COMMR_CERT = 0x0012
    OT_SETTINGS_KEY_VENDOR_RESERVED_MIN = 0x8000
    OT_SETTINGS_KEY_VENDOR_RESERVED_MAX = 0xffff
}
```

**Description:**

Defines the keys of settings.

**Details:**

Note: When adding a new settings key, if the settings corresponding to the key contains security sensitive information, the developer MUST add the key to the array `aSensitiveKeys` which is passed in `otPlatSettingsInit()`.

**Enumerator:**

|   |   |
|---|---|
|OT_SETTINGS_KEY_ACTIVE_DATASET|Active Operational Dataset.|
|OT_SETTINGS_KEY_PENDING_DATASET|Pending Operational Dataset.|
|OT_SETTINGS_KEY_NETWORK_INFO|Thread network information.|
|OT_SETTINGS_KEY_PARENT_INFO|Parent information.|
|OT_SETTINGS_KEY_CHILD_INFO|Child information.|
|OT_SETTINGS_KEY_SLAAC_IID_SECRET_KEY|SLAAC key to generate semantically opaque IID.|
|OT_SETTINGS_KEY_DAD_INFO|Duplicate Address Detection (DAD) information.|
|OT_SETTINGS_KEY_SRP_ECDSA_KEY|SRP client ECDSA public/private key pair.|
|OT_SETTINGS_KEY_SRP_CLIENT_INFO|The SRP client info (selected SRP server address).|
|OT_SETTINGS_KEY_SRP_SERVER_INFO|The SRP server info (UDP port).|
|OT_SETTINGS_KEY_BR_ULA_PREFIX|BR ULA prefix.|
|OT_SETTINGS_KEY_BR_ON_LINK_PREFIXES|BR local on-link prefixes.|
|OT_SETTINGS_KEY_BORDER_AGENT_ID|Unique Border Agent/Router ID.|
|OT_SETTINGS_KEY_TCAT_COMMR_CERT|TCAT Commissioner certificate.|
|OT_SETTINGS_KEY_VENDOR_RESERVED_MIN||
|OT_SETTINGS_KEY_VENDOR_RESERVED_MAX||

##### Functions

###### otPlatSettingsInit

`void otPlatSettingsInit(otInstance *aInstance, const uint16_t *aSensitiveKeys, uint16_t aSensitiveKeysLength)`

**Description:** Performs any initialization for the settings subsystem, if necessary.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|const uint16_t *|[in]|aSensitiveKeys|A pointer to an array containing the list of sensitive keys. May be NULL only if `aSensitiveKeysLength` is 0, which means that there is no sensitive keys.|
|uint16_t|[in]|aSensitiveKeysLength|The number of entries in the `aSensitiveKeys` array.|

Also sets the sensitive keys that should be stored in the secure area.

Note that the memory pointed by `aSensitiveKeys` MUST not be released before `aInstance` is destroyed.

###### otPlatSettingsDeinit

`void otPlatSettingsDeinit(otInstance *aInstance)`

**Description:** Performs any de-initialization for the settings subsystem, if necessary.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

###### otPlatSettingsGet

`otError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength)`

**Description:** Fetches the value of a setting.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint16_t|[in]|aKey|The key associated with the requested setting.|
|int|[in]|aIndex|The index of the specific item to get.|
|uint8_t *|[out]|aValue|A pointer to where the value of the setting should be written. May be set to NULL if just testing for the presence or length of a setting.|
|uint16_t *|[inout]|aValueLength|A pointer to the length of the value. When called, this pointer should point to an integer containing the maximum value size that can be written to `aValue`. At return, the actual length of the setting is written. This may be set to NULL if performing a presence check.|

Fetches the value of the setting identified by `aKey` and write it to the memory pointed to by aValue. It then writes the length to the integer pointed to by `aValueLength`. The initial value of `aValueLength` is the maximum number of bytes to be written to `aValue`.

Can be used to check for the existence of a key without fetching the value by setting `aValue` and `aValueLength` to NULL. You can also check the length of the setting without fetching it by setting only aValue to NULL.

Note that the underlying storage implementation is not required to maintain the order of settings with multiple values. The order of such values MAY change after ANY write operation to the store.

###### otPlatSettingsSet

`otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)`

**Description:** Sets or replaces the value of a setting.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint16_t|[in]|aKey|The key associated with the setting to change.|
|const uint8_t *|[in]|aValue|A pointer to where the new value of the setting should be read from. MUST NOT be NULL if `aValueLength` is non-zero.|
|uint16_t|[in]|aValueLength|The length of the data pointed to by aValue. May be zero.|

Sets or replaces the value of a setting identified by `aKey`.

Calling this function successfully may cause unrelated settings with multiple values to be reordered.

OpenThread stack guarantees to use `otPlatSettingsSet()` method for a `aKey` that was either previously set using `otPlatSettingsSet()` (i.e., contains a single value) or is empty and/or fully deleted (contains no value).

Platform layer can rely and use this fact for optimizing its implementation.

###### otPlatSettingsAdd

`otError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)`

**Description:** Adds a value to a setting.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint16_t|[in]|aKey|The key associated with the setting to change.|
|const uint8_t *|[in]|aValue|A pointer to where the new value of the setting should be read from. MUST NOT be NULL if `aValueLength` is non-zero.|
|uint16_t|[in]|aValueLength|The length of the data pointed to by `aValue`. May be zero.|

Adds the value to a setting identified by `aKey`, without replacing any existing values.

Note that the underlying implementation is not required to maintain the order of the items associated with a specific key. The added value may be added to the end, the beginning, or even somewhere in the middle. The order of any pre-existing values may also change.

Calling this function successfully may cause unrelated settings with multiple values to be reordered.

OpenThread stack guarantees to use `otPlatSettingsAdd()` method for a `aKey` that was either previously managed by `otPlatSettingsAdd()` (i.e., contains one or more items) or is empty and/or fully deleted (contains no value).

Platform layer can rely and use this fact for optimizing its implementation.

###### otPlatSettingsDelete

`otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex)`

**Description:** Removes a setting from the setting store.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint16_t|[in]|aKey|The key associated with the requested setting.|
|int|[in]|aIndex|The index of the value to be removed. If set to -1, all values for this `aKey` will be removed.|

Deletes a specific value from the setting identified by aKey from the settings store.

Note that the underlying implementation is not required to maintain the order of the items associated with a specific key.

###### otPlatSettingsWipe

`void otPlatSettingsWipe(otInstance *aInstance)`

**Description:** Removes all settings from the setting store.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

Deletes all settings from the settings store, resetting it to its initial factory state.

#### SPI Slave

This module includes the platform abstraction for SPI slave communication. 

##### Typedefs

###### otPlatSpiSlaveTransactionCompleteCallback

`typedef bool(* otPlatSpiSlaveTransactionCompleteCallback) (void *aContext, uint8_t *aOutputBuf, uint16_t aOutputBufLen, uint8_t *aInputBuf, uint16_t aInputBufLen, uint16_t aTransactionLength)`

**Description:**

Indicates that a SPI transaction has completed with the given length.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|Context pointer passed into `otPlatSpiSlaveEnable()`.|
||[in]|aOutputBuf|Value of `aOutputBuf` from last call to `otPlatSpiSlavePrepareTransaction()`.|
||[in]|aOutputBufLen|Value of `aOutputBufLen` from last call to `otPlatSpiSlavePrepareTransaction()`.|
||[in]|aInputBuf|Value of aInputBuf from last call to `otPlatSpiSlavePrepareTransaction()`.|
||[in]|aInputBufLen|Value of aInputBufLen from last call to `otPlatSpiSlavePrepareTransaction()`|
||[in]|aTransactionLength|Length of the completed transaction, in bytes.|

**Details:**

The data written to the slave has been written to the pointer indicated by the `aInputBuf` argument to the previous call to `otPlatSpiSlavePrepareTransaction()`.

Once this function is called, `otPlatSpiSlavePrepareTransaction()` is invalid and must be called again for the next transaction to be valid.

Note that this function is always called at the end of a transaction, even if `otPlatSpiSlavePrepareTransaction()` has not yet been called. In such cases, `aOutputBufLen` and `aInputBufLen` will be zero.

This callback can be called from ISR context. The return value from this function indicates if any further processing is required. If `TRUE` is returned the platform spi-slave driver implementation must invoke the transaction process callback (`aProcessCallback` set in `otPlatSpiSlaveEnable()`) which unlike this callback must be called from the same OS context that any other OpenThread API/callback is called.

**Returns**

- TRUE if after this call returns the platform should invoke the process callback `aProcessCallback`, FALSE if there is nothing to process and no need to invoke the process callback.

###### otPlatSpiSlaveTransactionProcessCallback

`typedef void(* otPlatSpiSlaveTransactionProcessCallback) (void *aContext)`

**Description:**

Invoked after a transaction complete callback is called and returns `TRUE` to do any further processing required.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aContext|Context pointer passed into `otPlatSpiSlaveEnable()`.|

**Details:**

Unlike `otPlatSpiSlaveTransactionCompleteCallback` which can be called from any OS context (e.g., ISR), this callback MUST be called from the same OS context as any other OpenThread API/callback.

##### Functions

###### otPlatSpiSlaveEnable

`otError otPlatSpiSlaveEnable(otPlatSpiSlaveTransactionCompleteCallback aCompleteCallback, otPlatSpiSlaveTransactionProcessCallback aProcessCallback, void *aContext)`

**Description:** Initialize the SPI slave interface.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otPlatSpiSlaveTransactionCompleteCallback](plat-spi-slave#ot-plat-spi-slave-transaction-complete-callback)|[in]|aCompleteCallback|Pointer to transaction complete callback.|
|[otPlatSpiSlaveTransactionProcessCallback](plat-spi-slave#ot-plat-spi-slave-transaction-process-callback)|[in]|aProcessCallback|Pointer to process callback.|
|void *|[in]|aContext|Context pointer to be passed to callbacks.|

Note that SPI slave is not fully ready until a transaction is prepared using `otPlatSPISlavePrepareTransaction()`.

If `otPlatSPISlavePrepareTransaction() is not called before the master begins a transaction, the resulting SPI transaction will send all`0xFF` bytes and discard all received bytes.

###### otPlatSpiSlaveDisable

`void otPlatSpiSlaveDisable(void)`

**Description:** Shutdown and disable the SPI slave interface.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

###### otPlatSpiSlavePrepareTransaction

`otError otPlatSpiSlavePrepareTransaction(uint8_t *aOutputBuf, uint16_t aOutputBufLen, uint8_t *aInputBuf, uint16_t aInputBufLen, bool aRequestTransactionFlag)`

**Description:** Prepare data for the next SPI transaction.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t *|[in]|aOutputBuf|Data to be written to MISO pin|
|uint16_t|[in]|aOutputBufLen|Size of the output buffer, in bytes|
|uint8_t *|[in]|aInputBuf|Data to be read from MOSI pin|
|uint16_t|[in]|aInputBufLen|Size of the input buffer, in bytes|
|bool|[in]|aRequestTransactionFlag|Set to true if host interrupt should be set|

Data pointers MUST remain valid until the transaction complete callback is called by the SPI slave driver, or until after the next call to `otPlatSpiSlavePrepareTransaction()`.

May be called more than once before the SPI master initiates the transaction. Each **successful** call to this function will cause the previous values from earlier calls to be discarded.

Not calling this function after a completed transaction is the same as if this function was previously called with both buffer lengths set to zero and `aRequestTransactionFlag` set to `false`.

Once `aOutputBufLen` bytes of `aOutputBuf` has been clocked out, the MISO pin shall be set high until the master finishes the SPI transaction. This is the functional equivalent of padding the end of `aOutputBuf` with `0xFF` bytes out to the length of the transaction.

Once `aInputBufLen` bytes of aInputBuf have been clocked in from MOSI, all subsequent values from the MOSI pin are ignored until the SPI master finishes the transaction.

Note that even if `aInputBufLen` or `aOutputBufLen` (or both) are exhausted before the SPI master finishes a transaction, the ongoing size of the transaction must still be kept track of to be passed to the transaction complete callback. For example, if `aInputBufLen` is equal to 10 and `aOutputBufLen` equal to 20 and the SPI master clocks out 30 bytes, the value 30 is passed to the transaction complete callback.

If a `NULL` pointer is passed in as `aOutputBuf` or `aInputBuf` it means that that buffer pointer should not change from its previous/current value. In this case, the corresponding length argument should be ignored. For example, `otPlatSpiSlavePrepareTransaction(NULL, 0, aInputBuf, aInputLen, false)` changes the input buffer pointer and its length but keeps the output buffer pointer same as before.

Any call to this function while a transaction is in progress will cause all of the arguments to be ignored and the return value to be `OT_ERROR_BUSY`.

#### Time Service

This module includes the platform abstraction for the time service. 

##### Functions

###### otPlatTimeGet

`uint64_t otPlatTimeGet(void)`

**Description:** Get the current platform time in microseconds referenced to a continuous monotonic local clock (64 bits width).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

The clock SHALL NOT wrap during the device's uptime. Implementations SHALL therefore identify and compensate for internal counter overflows. The clock does not have a defined epoch and it SHALL NOT introduce any continuous or discontinuous adjustments (e.g. leap seconds). Implementations SHALL compensate for any sleep times of the device.

Implementations MAY choose to discipline the platform clock and compensate for sleep times by any means (e.g. by combining a high precision/low power RTC with a high resolution counter) as long as the exposed combined clock provides continuous monotonic microsecond resolution ticks within the accuracy limits announced by [otPlatTimeGetXtalAccuracy](plat-time#ot-plat-time-get-xtal-accuracy).

**Returns**

- The current time in microseconds.

###### otPlatTimeGetXtalAccuracy

`uint16_t otPlatTimeGetXtalAccuracy(void)`

**Description:** Get the current estimated worst case accuracy (maximum ± deviation from the nominal frequency) of the local platform clock in units of PPM.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

**Note**

- Implementations MAY estimate this value based on current operating conditions (e.g. temperature).

In case the implementation does not estimate the current value but returns a fixed value, this value MUST be the worst-case accuracy over all possible foreseen operating conditions (temperature, pressure, etc) of the implementation.

**Returns**

- The current platform clock accuracy, in PPM.

#### Toolchain

This module defines a toolchain abstraction layer through macros. 

Usage:

```c
typedef
OT_TOOL_PACKED_BEGIN
struct
{
    char mField1;
    union
    {
        char mField2;
        long mField3;
    } OT_TOOL_PACKED_FIELD;
} OT_TOOL_PACKED_END packed_struct_t;

```

##### Macros

`#define OT_MUST_USE_RESULT `

**Description**: Compiler-specific indication that a class or enum must be used when it is the return value of a function.

`#define OT_TOOL_PACKED_BEGIN `

**Description**: Compiler-specific indication that a class or struct must be byte packed.

`#define OT_TOOL_PACKED_FIELD `

**Description**: Indicate to the compiler a nested struct or union to be packed within byte packed class or struct.

`#define OT_TOOL_WEAK `

**Description**: Compiler-specific weak symbol modifier.

`#define OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK (aFmtIndex, aStartIndex)`

**Description**: Specifies that a function or method takes `printf` style arguments and should be type-checked against a format string.

`#define OT_UNUSED_VARIABLE (VARIABLE)`

**Description**: Suppress unused variable warning in specific toolchains.

`#define OT_UNREACHABLE_CODE (CODE)`

**Description**: Suppress Unreachable code warning in specific toolchains.

`#define OT_APPLE_IGNORE_GNU_FOLDING_CONSTANT (...)`

`#define OT_FALL_THROUGH `

**Description**: Suppress fall through warning in specific compiler.

`#define OT_SUPPRESS_GCC_STRING_OP_BEGIN `

`#define OT_SUPPRESS_GCC_STRING_OP_END `

#### BLE

This module includes the platform abstraction for BLE Host communication. 

The platform needs to implement Bluetooth LE 4.2 or higher. 

##### Modules

[otBleLinkCapabilities](ot-ble-link-capabilities)

[otBleRadioPacket](ot-ble-radio-packet)

##### Typedefs

###### otBleLinkCapabilities

`typedef struct otBleLinkCapabilities otBleLinkCapabilities`

**Description:**

Represent BLE link capabilities.

###### otBleRadioPacket

`typedef struct otBleRadioPacket otBleRadioPacket`

**Description:**

Represents a BLE packet.

##### Functions

###### otPlatBleEnable

`otError otPlatBleEnable(otInstance *aInstance)`

**Description:** Enable the Bluetooth Low Energy radio.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

**Note**

- BLE Device should use the highest ATT_MTU supported that does not exceed OT_BLE_ATT_MTU_MAX octets.

###### otPlatBleDisable

`otError otPlatBleDisable(otInstance *aInstance)`

**Description:** Disable the Bluetooth Low Energy radio.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

When disabled, the BLE stack will flush event queues and not generate new events. The BLE peripheral is turned off or put into a low power sleep state. Any dynamic memory used by the stack should be released, but static memory may remain reserved.

###### otPlatBleGetAdvertisementBuffer

`otError otPlatBleGetAdvertisementBuffer(otInstance *aInstance, uint8_t **aAdvertisementBuffer)`

**Description:** Gets BLE Advertising buffer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint8_t **|[in]|aAdvertisementBuffer|The formatted TCAT advertisement frame.|
||[in]|aAdvertisementLen|The TCAT advertisement frame length.|

**Note**

- This function shall be used only for BLE Peripheral role. Returned buffer should have enough space to fit max advertisement defined by specification.

###### otPlatBleGapAdvSetData

`otError otPlatBleGapAdvSetData(otInstance *aInstance, uint8_t *aAdvertisementData, uint16_t aAdvertisementLen)`

**Description:** Sets BLE Advertising data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint8_t *|[in]|aAdvertisementData|The formatted TCAT advertisement frame.|
|uint16_t|[in]|aAdvertisementLen|The TCAT advertisement frame length.|

**Note**

- This function shall be used only for BLE Peripheral role.

###### otPlatBleGapAdvUpdateData

`otError otPlatBleGapAdvUpdateData(otInstance *aInstance, uint8_t *aAdvertisementData, uint16_t aAdvertisementLen)`

**Description:** Updates BLE Advertising data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint8_t *|[in]|aAdvertisementData|The formatted TCAT advertisement frame.|
|uint16_t|[in]|aAdvertisementLen|The TCAT advertisement frame length.|

**Note**

- This function shall be used only for BLE Peripheral role.

###### otPlatBleGapAdvStart

`otError otPlatBleGapAdvStart(otInstance *aInstance, uint16_t aInterval)`

**Description:** Starts BLE Advertising procedure.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint16_t|[in]|aInterval|The interval between subsequent advertising packets in OT_BLE_ADV_INTERVAL_UNIT units. Shall be within OT_BLE_ADV_INTERVAL_MIN and OT_BLE_ADV_INTERVAL_MAX range or OT_BLE_ADV_INTERVAL_DEFAULT for a default value set at compile time.|

The BLE device shall use undirected advertising with no filter applied. A single BLE Advertising packet must be sent on all advertising channels (37, 38 and 39).

**Note**

- This function shall be used only for BLE Peripheral role.

###### otPlatBleGapAdvStop

`otError otPlatBleGapAdvStop(otInstance *aInstance)`

**Description:** Stops BLE Advertising procedure.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

**Note**

- This function shall be used only for BLE Peripheral role.

###### otPlatBleGapOnConnected

`void otPlatBleGapOnConnected(otInstance *aInstance, uint16_t aConnectionId)`

**Description:** The BLE driver calls this method to notify OpenThread that a BLE Central Device has been connected.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint16_t|[in]|aConnectionId|The identifier of the open connection.|

###### otPlatBleGapOnDisconnected

`void otPlatBleGapOnDisconnected(otInstance *aInstance, uint16_t aConnectionId)`

**Description:** The BLE driver calls this method to notify OpenThread that the BLE Central Device has been disconnected.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint16_t|[in]|aConnectionId|The identifier of the closed connection.|

###### otPlatBleGapDisconnect

`otError otPlatBleGapDisconnect(otInstance *aInstance)`

**Description:** Disconnects BLE connection.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

The BLE device shall use the Remote User Terminated Connection (0x13) reason code when disconnecting from the peer BLE device..

###### otPlatBleGattMtuGet

`otError otPlatBleGattMtuGet(otInstance *aInstance, uint16_t *aMtu)`

**Description:** Reads currently used value of ATT_MTU from the platform BLE driver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint16_t *|[out]|aMtu|A pointer to output the current ATT_MTU value.|

###### otPlatBleGattOnMtuUpdate

`void otPlatBleGattOnMtuUpdate(otInstance *aInstance, uint16_t aMtu)`

**Description:** The BLE driver calls this method to notify OpenThread that ATT_MTU has been updated.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint16_t|[in]|aMtu|The updated ATT_MTU value. It MUST be >=OT_BLE_ATT_MTU_MIN.|

###### otPlatBleGattServerIndicate

`otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, const otBleRadioPacket *aPacket)`

**Description:** Sends ATT Handle Value Indication.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint16_t|[in]|aHandle|The handle of the attribute to be indicated.|
|const [otBleRadioPacket](ot-ble-radio-packet) *|[in]|aPacket|A pointer to the packet contains value to be indicated.|

**Note**

- This function shall be used only for GATT Server.

###### otPlatBleGattServerOnWriteRequest

`void otPlatBleGattServerOnWriteRequest(otInstance *aInstance, uint16_t aHandle, const otBleRadioPacket *aPacket)`

**Description:** The BLE driver calls this method to notify OpenThread that an ATT Write Request packet has been received.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint16_t|[in]|aHandle|The handle of the attribute to be written.|
|const [otBleRadioPacket](ot-ble-radio-packet) *|[in]|aPacket|A pointer to the packet contains value to be written to the attribute.|

**Note**

- This function shall be used only for GATT Server.

###### otPlatBleGetLinkCapabilities

`void otPlatBleGetLinkCapabilities(otInstance *aInstance, otBleLinkCapabilities *aBleLinkCapabilities)`

**Description:** Function to retrieve from platform BLE link capabilities.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otBleLinkCapabilities](ot-ble-link-capabilities) *|[out]|aBleLinkCapabilities|The pointer to retrieve the BLE ling capabilities.|

###### otPlatBleSupportsMultiRadio

`bool otPlatBleSupportsMultiRadio(otInstance *aInstance)`

**Description:** Function to retrieve from platform multiradio support of BLE and IEEE.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

##### Macros

`#define OT_BLE_TIMESLOT_UNIT 625`

**Description**: Time slot duration on PHY layer in microseconds (0.625ms).

`#define OT_BLE_ADV_INTERVAL_MIN 0x0020`

**Description**: Minimum allowed interval for advertising packet in OT_BLE_ADV_INTERVAL_UNIT units (20ms).

`#define OT_BLE_ADV_INTERVAL_MAX 0x4000`

**Description**: Maximum allowed interval for advertising packet in OT_BLE_ADV_INTERVAL_UNIT units (10.24s).

`#define OT_BLE_ADV_INTERVAL_DEFAULT 100`

**Description**: Default interval for advertising packet (ms).

`#define OT_BLE_ADV_INTERVAL_UNIT OT_BLE_TIMESLOT_UNIT`

**Description**: Unit used to calculate interval duration (0.625ms).

`#define OT_BLE_ATT_MTU_MIN 23`

**Description**: Minimum allowed ATT MTU size (MUST be 23).

`#define OT_BLE_ATT_MTU_MAX 67`

**Description**: Maximum allowed ATT MTU size (MUST be >= OT_BLE_ATT_MTU_MIN).

`#define OT_BLE_ATT_MTU_DEFAULT 23`

**Description**: Default ATT MTU size (used if no MTU indication is given yet, or none can be given).

`#define OT_BLE_DEFAULT_POWER 0`

**Description**: Default power value for BLE.

`#define OT_TOBLE_SERVICE_UUID 0xfffb`

**Description**: TOBLE service UUID.

Represent BLE link capabilities. 

###### Public Attributes

###### mRsv (heading level 7)

```
uint8_t otBleLinkCapabilities::mRsv
```

###### mL2CapDirect (heading level 7)

```
bool otBleLinkCapabilities::mL2CapDirect
```

###### mGattNotifications (heading level 7)

```
bool otBleLinkCapabilities::mGattNotifications
```

Represents a BLE packet. 

###### Public Attributes

###### mValue (heading level 7)

```
uint8_t* otBleRadioPacket::mValue
```

**Description:** The value of an attribute.

###### mLength (heading level 7)

```
uint16_t otBleRadioPacket::mLength
```

**Description:** Length of the `mValue`.

###### mPower (heading level 7)

```
int8_t otBleRadioPacket::mPower
```

**Description:** Transmit/receive power in dBm.

#### Crypto - Platform

This module includes the platform abstraction for Crypto. 

##### Modules

[otCryptoKey](ot-crypto-key)

[otCryptoContext](ot-crypto-context)

[otPlatCryptoSha256Hash](ot-plat-crypto-sha256-hash)

[otPlatCryptoEcdsaKeyPair](ot-plat-crypto-ecdsa-key-pair)

[otPlatCryptoEcdsaPublicKey](ot-plat-crypto-ecdsa-public-key)

[otPlatCryptoEcdsaSignature](ot-plat-crypto-ecdsa-signature)

##### Enumerations

###### otCryptoKeyType

```
enum otCryptoKeyType {
    OT_CRYPTO_KEY_TYPE_RAW
    OT_CRYPTO_KEY_TYPE_AES
    OT_CRYPTO_KEY_TYPE_HMAC
    OT_CRYPTO_KEY_TYPE_ECDSA
    OT_CRYPTO_KEY_TYPE_DERIVE
}
```

**Description:**

Defines the key types.

**Enumerator:**

|   |   |
|---|---|
|OT_CRYPTO_KEY_TYPE_RAW|Key Type: Raw Data.|
|OT_CRYPTO_KEY_TYPE_AES|Key Type: AES.|
|OT_CRYPTO_KEY_TYPE_HMAC|Key Type: HMAC.|
|OT_CRYPTO_KEY_TYPE_ECDSA|Key Type: ECDSA.|
|OT_CRYPTO_KEY_TYPE_DERIVE|Key Type: Derive.|

###### otCryptoKeyAlgorithm

```
enum otCryptoKeyAlgorithm {
    OT_CRYPTO_KEY_ALG_VENDOR
    OT_CRYPTO_KEY_ALG_AES_ECB
    OT_CRYPTO_KEY_ALG_HMAC_SHA_256
    OT_CRYPTO_KEY_ALG_ECDSA
    OT_CRYPTO_KEY_ALG_HKDF_SHA256
}
```

**Description:**

Defines the key algorithms.

**Enumerator:**

|   |   |
|---|---|
|OT_CRYPTO_KEY_ALG_VENDOR|Key Algorithm: Vendor Defined.|
|OT_CRYPTO_KEY_ALG_AES_ECB|Key Algorithm: AES ECB.|
|OT_CRYPTO_KEY_ALG_HMAC_SHA_256|Key Algorithm: HMAC SHA-256.|
|OT_CRYPTO_KEY_ALG_ECDSA|Key Algorithm: ECDSA.|
|OT_CRYPTO_KEY_ALG_HKDF_SHA256|Key Algorithm: HKDF SHA-256.|

###### @7

```
enum @7 {
    OT_CRYPTO_KEY_USAGE_NONE = 0
    OT_CRYPTO_KEY_USAGE_EXPORT = 1 << 0
    OT_CRYPTO_KEY_USAGE_ENCRYPT = 1 << 1
    OT_CRYPTO_KEY_USAGE_DECRYPT = 1 << 2
    OT_CRYPTO_KEY_USAGE_SIGN_HASH = 1 << 3
    OT_CRYPTO_KEY_USAGE_VERIFY_HASH = 1 << 4
    OT_CRYPTO_KEY_USAGE_DERIVE = 1 << 5
}
```

**Description:**

Defines the key usage flags.

**Enumerator:**

|   |   |
|---|---|
|OT_CRYPTO_KEY_USAGE_NONE|Key Usage: Key Usage is empty.|
|OT_CRYPTO_KEY_USAGE_EXPORT|Key Usage: Key can be exported.|
|OT_CRYPTO_KEY_USAGE_ENCRYPT|Key Usage: Encryption (vendor defined).|
|OT_CRYPTO_KEY_USAGE_DECRYPT|Key Usage: AES ECB.|
|OT_CRYPTO_KEY_USAGE_SIGN_HASH|Key Usage: Sign Hash.|
|OT_CRYPTO_KEY_USAGE_VERIFY_HASH|Key Usage: Verify Hash.|
|OT_CRYPTO_KEY_USAGE_DERIVE|Key Usage: Derive.|

###### otCryptoKeyStorage

```
enum otCryptoKeyStorage {
    OT_CRYPTO_KEY_STORAGE_VOLATILE
    OT_CRYPTO_KEY_STORAGE_PERSISTENT
}
```

**Description:**

Defines the key storage types.

**Enumerator:**

|   |   |
|---|---|
|OT_CRYPTO_KEY_STORAGE_VOLATILE|Key Persistence: Key is volatile.|
|OT_CRYPTO_KEY_STORAGE_PERSISTENT|Key Persistence: Key is persistent.|

##### Typedefs

###### otCryptoKeyRef

`typedef uint32_t otCryptoKeyRef`

**Description:**

This datatype represents the key reference.

###### otCryptoKey

`typedef struct otCryptoKey otCryptoKey`

###### otCryptoContext

`typedef struct otCryptoContext otCryptoContext`

###### otPlatCryptoSha256Hash

`typedef struct otPlatCryptoSha256Hash otPlatCryptoSha256Hash`

**Description:**

Represents a SHA-256 hash.

###### otPlatCryptoEcdsaKeyPair

`typedef struct otPlatCryptoEcdsaKeyPair otPlatCryptoEcdsaKeyPair`

###### otPlatCryptoEcdsaPublicKey

`typedef struct otPlatCryptoEcdsaPublicKey otPlatCryptoEcdsaPublicKey`

###### otPlatCryptoEcdsaSignature

`typedef struct otPlatCryptoEcdsaSignature otPlatCryptoEcdsaSignature`

##### Variables

###### OT_TOOL_PACKED_END

```
OT_TOOL_PACKED_BEGIN struct otPlatCryptoEcdsaSignature OT_TOOL_PACKED_END
```

##### Functions

###### otPlatCryptoInit

`void otPlatCryptoInit(void)`

**Description:** Initialize the Crypto module.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

###### otPlatCryptoImportKey

`otError otPlatCryptoImportKey(otCryptoKeyRef *aKeyRef, otCryptoKeyType aKeyType, otCryptoKeyAlgorithm aKeyAlgorithm, int aKeyUsage, otCryptoKeyStorage aKeyPersistence, const uint8_t *aKey, size_t aKeyLen)`

**Description:** Import a key into PSA ITS.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoKeyRef](plat-crypto#ot-crypto-key-ref) *|[inout]|aKeyRef|Pointer to the key ref to be used for crypto operations.|
|[otCryptoKeyType](plat-crypto#ot-crypto-key-type)|[in]|aKeyType|Key Type encoding for the key.|
|[otCryptoKeyAlgorithm](plat-crypto#ot-crypto-key-algorithm)|[in]|aKeyAlgorithm|Key algorithm encoding for the key.|
|int|[in]|aKeyUsage|Key Usage encoding for the key (combinations of `OT_CRYPTO_KEY_USAGE_*`).|
|[otCryptoKeyStorage](plat-crypto#ot-crypto-key-storage)|[in]|aKeyPersistence|Key Persistence for this key|
|const uint8_t *|[in]|aKey|Actual key to be imported.|
|size_t|[in]|aKeyLen|Length of the key to be imported.|

**Note**

- If OT_CRYPTO_KEY_STORAGE_PERSISTENT is passed for aKeyPersistence then `aKeyRef` is input and platform should use the given aKeyRef and MUST not change it.

If OT_CRYPTO_KEY_STORAGE_VOLATILE is passed for aKeyPersistence then `aKeyRef` is output, the initial value does not matter and platform API MUST update it to return the new key ref.

This API is only used by OT core when `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` is enabled. 

###### otPlatCryptoExportKey

`otError otPlatCryptoExportKey(otCryptoKeyRef aKeyRef, uint8_t *aBuffer, size_t aBufferLen, size_t *aKeyLen)`

**Description:** Export a key stored in PSA ITS.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoKeyRef](plat-crypto#ot-crypto-key-ref)|[in]|aKeyRef|The key ref to be used for crypto operations.|
|uint8_t *|[out]|aBuffer|Pointer to the buffer where key needs to be exported.|
|size_t|[in]|aBufferLen|Length of the buffer passed to store the exported key.|
|size_t *|[out]|aKeyLen|Pointer to return the length of the exported key.|

**Note**

- This API is only used by OT core when `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` is enabled.

###### otPlatCryptoDestroyKey

`otError otPlatCryptoDestroyKey(otCryptoKeyRef aKeyRef)`

**Description:** Destroy a key stored in PSA ITS.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoKeyRef](plat-crypto#ot-crypto-key-ref)|[in]|aKeyRef|The key ref to be destroyed|

**Note**

- This API is only used by OT core when `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` is enabled.

###### otPlatCryptoHasKey

`bool otPlatCryptoHasKey(otCryptoKeyRef aKeyRef)`

**Description:** Check if the key ref passed has an associated key in PSA ITS.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoKeyRef](plat-crypto#ot-crypto-key-ref)|[in]|aKeyRef|The Key Ref to check.|

**Note**

- This API is only used by OT core when `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` is enabled.

###### otPlatCryptoCAlloc

`void * otPlatCryptoCAlloc(size_t aNum, size_t aSize)`

**Description:** Dynamically allocates new memory for the Crypto subsystem.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|size_t|[in]|aNum|The number of blocks to allocate|
|size_t|[in]|aSize|The size of each block to allocate|

On platforms that support it, they should redirect to `calloc`. For those that don't support `calloc`, they should implement the standard `calloc` behavior.

See: [https://man7.org/linux/man-pages/man3/calloc.3.html](https://man7.org/linux/man-pages/man3/calloc.3.html)

Is required for `OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE`.

###### otPlatCryptoFree

`void otPlatCryptoFree(void *aPtr)`

**Description:** Frees memory that was dynamically allocated by `otPlatCryptoCAlloc()`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void *|[in]|aPtr|A pointer the memory blocks to free. The pointer may be NULL.|

Is required for `OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE`.

###### otPlatCryptoHmacSha256Init

`otError otPlatCryptoHmacSha256Init(otCryptoContext *aContext)`

**Description:** Initialize the HMAC operation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoContext](ot-crypto-context) *|[in]|aContext|Context for HMAC operation.|

**Note**

- If `OPENTHREAD_CONFIG_CRYPTO_PLATFORM_ALLOCS_CONTEXT` is enabled, `aContext` is populated by the platform. Otherwise OpenThread core allocates and populates it.
- The platform driver shall point the context to the correct object such as psa_mac_operation_t or mbedtls_md_context_t.

###### otPlatCryptoHmacSha256Deinit

`otError otPlatCryptoHmacSha256Deinit(otCryptoContext *aContext)`

**Description:** Uninitialize the HMAC operation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoContext](ot-crypto-context) *|[in]|aContext|Context for HMAC operation.|

###### otPlatCryptoHmacSha256Start

`otError otPlatCryptoHmacSha256Start(otCryptoContext *aContext, const otCryptoKey *aKey)`

**Description:** Start HMAC operation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoContext](ot-crypto-context) *|[in]|aContext|Context for HMAC operation.|
|const [otCryptoKey](ot-crypto-key) *|[in]|aKey|Key material to be used for HMAC operation.|

###### otPlatCryptoHmacSha256Update

`otError otPlatCryptoHmacSha256Update(otCryptoContext *aContext, const void *aBuf, uint16_t aBufLength)`

**Description:** Update the HMAC operation with new input.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoContext](ot-crypto-context) *|[in]|aContext|Context for HMAC operation.|
|const void *|[in]|aBuf|A pointer to the input buffer.|
|uint16_t|[in]|aBufLength|The length of `aBuf` in bytes.|

###### otPlatCryptoHmacSha256Finish

`otError otPlatCryptoHmacSha256Finish(otCryptoContext *aContext, uint8_t *aBuf, size_t aBufLength)`

**Description:** Complete the HMAC operation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoContext](ot-crypto-context) *|[in]|aContext|Context for HMAC operation.|
|uint8_t *|[out]|aBuf|A pointer to the output buffer.|
|size_t|[in]|aBufLength|The length of `aBuf` in bytes.|

###### otPlatCryptoAesInit

`otError otPlatCryptoAesInit(otCryptoContext *aContext)`

**Description:** Initialise the AES operation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoContext](ot-crypto-context) *|[in]|aContext|Context for AES operation.|

**Note**

- If `OPENTHREAD_CONFIG_CRYPTO_PLATFORM_ALLOCS_CONTEXT` is enabled, `aContext` is populated by the platform. Otherwise OpenThread core allocates and populates it.
- The platform driver shall point the context to the correct object such as psa_key_id or mbedtls_aes_context_t.

###### otPlatCryptoAesSetKey

`otError otPlatCryptoAesSetKey(otCryptoContext *aContext, const otCryptoKey *aKey)`

**Description:** Set the key for AES operation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoContext](ot-crypto-context) *|[in]|aContext|Context for AES operation.|
|const [otCryptoKey](ot-crypto-key) *|[out]|aKey|Key to use for AES operation.|

###### otPlatCryptoAesEncrypt

`otError otPlatCryptoAesEncrypt(otCryptoContext *aContext, const uint8_t *aInput, uint8_t *aOutput)`

**Description:** Encrypt the given data.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoContext](ot-crypto-context) *|[in]|aContext|Context for AES operation.|
|const uint8_t *|[in]|aInput|Pointer to the input buffer.|
|uint8_t *|[in]|aOutput|Pointer to the output buffer.|

###### otPlatCryptoAesFree

`otError otPlatCryptoAesFree(otCryptoContext *aContext)`

**Description:** Free the AES context.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoContext](ot-crypto-context) *|[in]|aContext|Context for AES operation.|

###### otPlatCryptoHkdfInit

`otError otPlatCryptoHkdfInit(otCryptoContext *aContext)`

**Description:** Initialise the HKDF context.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoContext](ot-crypto-context) *|[in]|aContext|Context for HKDF operation.|

**Note**

- If `OPENTHREAD_CONFIG_CRYPTO_PLATFORM_ALLOCS_CONTEXT` is enabled, `aContext` is populated by the platform. Otherwise OpenThread core allocates and populates it.
- The platform driver shall point the context to the correct object such as psa_key_derivation_operation_t or HmacSha256::Hash

###### otPlatCryptoHkdfExpand

`otError otPlatCryptoHkdfExpand(otCryptoContext *aContext, const uint8_t *aInfo, uint16_t aInfoLength, uint8_t *aOutputKey, uint16_t aOutputKeyLength)`

**Description:** Perform HKDF Expand step.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoContext](ot-crypto-context) *|[in]|aContext|Operation context for HKDF operation.|
|const uint8_t *|[in]|aInfo|Pointer to the Info sequence.|
|uint16_t|[in]|aInfoLength|Length of the Info sequence.|
|uint8_t *|[out]|aOutputKey|Pointer to the output Key.|
|uint16_t|[in]|aOutputKeyLength|Size of the output key buffer.|

###### otPlatCryptoHkdfExtract

`otError otPlatCryptoHkdfExtract(otCryptoContext *aContext, const uint8_t *aSalt, uint16_t aSaltLength, const otCryptoKey *aInputKey)`

**Description:** Perform HKDF Extract step.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoContext](ot-crypto-context) *|[in]|aContext|Operation context for HKDF operation.|
|const uint8_t *|[in]|aSalt|Pointer to the Salt for HKDF.|
|uint16_t|[in]|aSaltLength|Length of Salt.|
|const [otCryptoKey](ot-crypto-key) *|[in]|aInputKey|Pointer to the input key.|

###### otPlatCryptoHkdfDeinit

`otError otPlatCryptoHkdfDeinit(otCryptoContext *aContext)`

**Description:** Uninitialize the HKDF context.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoContext](ot-crypto-context) *|[in]|aContext|Context for HKDF operation.|

###### otPlatCryptoSha256Init

`otError otPlatCryptoSha256Init(otCryptoContext *aContext)`

**Description:** Initialise the SHA-256 operation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoContext](ot-crypto-context) *|[in]|aContext|Context for SHA-256 operation.|

**Note**

- If `OPENTHREAD_CONFIG_CRYPTO_PLATFORM_ALLOCS_CONTEXT` is enabled, `aContext` is populated by the platform. Otherwise OpenThread core allocates and populates it.
- The platform driver shall point the context to the correct object such as psa_hash_operation_t or mbedtls_sha256_context.

###### otPlatCryptoSha256Deinit

`otError otPlatCryptoSha256Deinit(otCryptoContext *aContext)`

**Description:** Uninitialize the SHA-256 operation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoContext](ot-crypto-context) *|[in]|aContext|Context for SHA-256 operation.|

###### otPlatCryptoSha256Start

`otError otPlatCryptoSha256Start(otCryptoContext *aContext)`

**Description:** Start SHA-256 operation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoContext](ot-crypto-context) *|[in]|aContext|Context for SHA-256 operation.|

###### otPlatCryptoSha256Update

`otError otPlatCryptoSha256Update(otCryptoContext *aContext, const void *aBuf, uint16_t aBufLength)`

**Description:** Update SHA-256 operation with new input.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoContext](ot-crypto-context) *|[in]|aContext|Context for SHA-256 operation.|
|const void *|[in]|aBuf|A pointer to the input buffer.|
|uint16_t|[in]|aBufLength|The length of `aBuf` in bytes.|

###### otPlatCryptoSha256Finish

`otError otPlatCryptoSha256Finish(otCryptoContext *aContext, uint8_t *aHash, uint16_t aHashSize)`

**Description:** Finish SHA-256 operation.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoContext](ot-crypto-context) *|[in]|aContext|Context for SHA-256 operation.|
|uint8_t *|[in]|aHash|A pointer to the output buffer, where hash needs to be stored.|
|uint16_t|[in]|aHashSize|The length of `aHash` in bytes.|

###### otPlatCryptoRandomInit

`void otPlatCryptoRandomInit(void)`

**Description:** Initialize cryptographically-secure pseudorandom number generator (CSPRNG).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

###### otPlatCryptoRandomDeinit

`void otPlatCryptoRandomDeinit(void)`

**Description:** Deinitialize cryptographically-secure pseudorandom number generator (CSPRNG).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

###### otPlatCryptoRandomGet

`otError otPlatCryptoRandomGet(uint8_t *aBuffer, uint16_t aSize)`

**Description:** Fills a given buffer with cryptographically secure random bytes.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t *|[out]|aBuffer|A pointer to a buffer to fill with the random bytes.|
|uint16_t|[in]|aSize|Size of buffer (number of bytes to fill).|

###### otPlatCryptoEcdsaGenerateKey

`otError otPlatCryptoEcdsaGenerateKey(otPlatCryptoEcdsaKeyPair *aKeyPair)`

**Description:** Generate and populate the output buffer with a new ECDSA key-pair.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otPlatCryptoEcdsaKeyPair](ot-plat-crypto-ecdsa-key-pair) *|[out]|aKeyPair|A pointer to an ECDSA key-pair structure to store the generated key-pair.|

###### otPlatCryptoEcdsaGetPublicKey

`otError otPlatCryptoEcdsaGetPublicKey(const otPlatCryptoEcdsaKeyPair *aKeyPair, otPlatCryptoEcdsaPublicKey *aPublicKey)`

**Description:** Get the associated public key from the input context.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otPlatCryptoEcdsaKeyPair](ot-plat-crypto-ecdsa-key-pair) *|[in]|aKeyPair|A pointer to an ECDSA key-pair structure where the key-pair is stored.|
|[otPlatCryptoEcdsaPublicKey](ot-plat-crypto-ecdsa-public-key) *|[out]|aPublicKey|A pointer to an ECDSA public key structure to store the public key.|

###### otPlatCryptoEcdsaSign

`otError otPlatCryptoEcdsaSign(const otPlatCryptoEcdsaKeyPair *aKeyPair, const otPlatCryptoSha256Hash *aHash, otPlatCryptoEcdsaSignature *aSignature)`

**Description:** Calculate the ECDSA signature for a hashed message using the private key from the input context.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otPlatCryptoEcdsaKeyPair](ot-plat-crypto-ecdsa-key-pair) *|[in]|aKeyPair|A pointer to an ECDSA key-pair structure where the key-pair is stored.|
|const [otPlatCryptoSha256Hash](ot-plat-crypto-sha256-hash) *|[in]|aHash|A pointer to a SHA-256 hash structure where the hash value for signature calculation is stored.|
|[otPlatCryptoEcdsaSignature](ot-plat-crypto-ecdsa-signature) *|[out]|aSignature|A pointer to an ECDSA signature structure to output the calculated signature.|

Uses the deterministic digital signature generation procedure from RFC 6979.

###### otPlatCryptoEcdsaVerify

`otError otPlatCryptoEcdsaVerify(const otPlatCryptoEcdsaPublicKey *aPublicKey, const otPlatCryptoSha256Hash *aHash, const otPlatCryptoEcdsaSignature *aSignature)`

**Description:** Use the key from the input context to verify the ECDSA signature of a hashed message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const [otPlatCryptoEcdsaPublicKey](ot-plat-crypto-ecdsa-public-key) *|[in]|aPublicKey|A pointer to an ECDSA public key structure where the public key for signature verification is stored.|
|const [otPlatCryptoSha256Hash](ot-plat-crypto-sha256-hash) *|[in]|aHash|A pointer to a SHA-256 hash structure where the hash value for signature verification is stored.|
|const [otPlatCryptoEcdsaSignature](ot-plat-crypto-ecdsa-signature) *|[in]|aSignature|A pointer to an ECDSA signature structure where the signature value to be verified is stored.|

###### otPlatCryptoEcdsaSignUsingKeyRef

`otError otPlatCryptoEcdsaSignUsingKeyRef(otCryptoKeyRef aKeyRef, const otPlatCryptoSha256Hash *aHash, otPlatCryptoEcdsaSignature *aSignature)`

**Description:** Calculate the ECDSA signature for a hashed message using the Key reference passed.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoKeyRef](plat-crypto#ot-crypto-key-ref)|[in]|aKeyRef|Key Reference to the slot where the key-pair is stored.|
|const [otPlatCryptoSha256Hash](ot-plat-crypto-sha256-hash) *|[in]|aHash|A pointer to a SHA-256 hash structure where the hash value for signature calculation is stored.|
|[otPlatCryptoEcdsaSignature](ot-plat-crypto-ecdsa-signature) *|[out]|aSignature|A pointer to an ECDSA signature structure to output the calculated signature.|

Uses the deterministic digital signature generation procedure from RFC 6979.

**Note**

- This API is only used by OT core when `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` is enabled.

###### otPlatCryptoEcdsaExportPublicKey

`otError otPlatCryptoEcdsaExportPublicKey(otCryptoKeyRef aKeyRef, otPlatCryptoEcdsaPublicKey *aPublicKey)`

**Description:** Get the associated public key from the key reference passed.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoKeyRef](plat-crypto#ot-crypto-key-ref)|[in]|aKeyRef|Key Reference to the slot where the key-pair is stored.|
|[otPlatCryptoEcdsaPublicKey](ot-plat-crypto-ecdsa-public-key) *|[out]|aPublicKey|A pointer to an ECDSA public key structure to store the public key.|

The public key is stored differently depending on the crypto backend library being used (OPENTHREAD_CONFIG_CRYPTO_LIB).

This API must make sure to return the public key as a byte sequence representation of an uncompressed curve point (RFC 6605 - sec 4)

**Note**

- This API is only used by OT core when `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` is enabled.

###### otPlatCryptoEcdsaGenerateAndImportKey

`otError otPlatCryptoEcdsaGenerateAndImportKey(otCryptoKeyRef aKeyRef)`

**Description:** Generate and import a new ECDSA key-pair at reference passed.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoKeyRef](plat-crypto#ot-crypto-key-ref)|[in]|aKeyRef|Key Reference to the slot where the key-pair is stored.|

**Note**

- This API is only used by OT core when `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` is enabled.

###### otPlatCryptoEcdsaVerifyUsingKeyRef

`otError otPlatCryptoEcdsaVerifyUsingKeyRef(otCryptoKeyRef aKeyRef, const otPlatCryptoSha256Hash *aHash, const otPlatCryptoEcdsaSignature *aSignature)`

**Description:** Use the keyref to verify the ECDSA signature of a hashed message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otCryptoKeyRef](plat-crypto#ot-crypto-key-ref)|[in]|aKeyRef|Key Reference to the slot where the key-pair is stored.|
|const [otPlatCryptoSha256Hash](ot-plat-crypto-sha256-hash) *|[in]|aHash|A pointer to a SHA-256 hash structure where the hash value for signature verification is stored.|
|const [otPlatCryptoEcdsaSignature](ot-plat-crypto-ecdsa-signature) *|[in]|aSignature|A pointer to an ECDSA signature structure where the signature value to be verified is stored.|

**Note**

- This API is only used by OT core when `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` is enabled.

###### otPlatCryptoPbkdf2GenerateKey

`otError otPlatCryptoPbkdf2GenerateKey(const uint8_t *aPassword, uint16_t aPasswordLen, const uint8_t *aSalt, uint16_t aSaltLen, uint32_t aIterationCounter, uint16_t aKeyLen, uint8_t *aKey)`

**Description:** Perform PKCS#5 PBKDF2 using CMAC (AES-CMAC-PRF-128).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|const uint8_t *|[in]|aPassword|Password to use when generating key.|
|uint16_t|[in]|aPasswordLen|Length of password.|
|const uint8_t *|[in]|aSalt|Salt to use when generating key.|
|uint16_t|[in]|aSaltLen|Length of salt.|
|uint32_t|[in]|aIterationCounter|Iteration count.|
|uint16_t|[in]|aKeyLen|Length of generated key in bytes.|
|uint8_t *|[out]|aKey|A pointer to the generated key.|

##### Macros

`#define OT_CRYPTO_SHA256_HASH_SIZE 32`

**Description**: Length of SHA256 hash (in bytes).

`#define OT_CRYPTO_ECDSA_MAX_DER_SIZE 125`

**Description**: Max buffer size (in bytes) for representing the EDCSA key-pair in DER format.

`#define OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE 64`

**Description**: Buffer size (in bytes) for representing the EDCSA public key.

`#define OT_CRYPTO_ECDSA_SIGNATURE_SIZE 64`

**Description**: Buffer size (in bytes) for representing the EDCSA signature.

`#define OT_CRYPTO_PBDKF2_MAX_SALT_SIZE 30`

**Description**: Max PBKDF2 SALT length: salt prefix (6) + extended panid (8) + network name (16)

Represents the Key Material required for Crypto operations. 

###### Public Attributes

###### mKey (heading level 7)

```
const uint8_t* otCryptoKey::mKey
```

**Description:** Pointer to the buffer containing key. NULL indicates to use `mKeyRef`.

###### mKeyLength (heading level 7)

```
uint16_t otCryptoKey::mKeyLength
```

**Description:** The key length in bytes (applicable when `mKey` is not NULL).

###### mKeyRef (heading level 7)

```
uint32_t otCryptoKey::mKeyRef
```

**Description:** The PSA key ref (requires `mKey` to be NULL).

Stores the context object for platform APIs. 

If `OPENTHREAD_CONFIG_CRYPTO_PLATFORM_ALLOCS_CONTEXT` is enabled, the platform allocates and populates this. Otherwise OpenThread core allocates and populates this. 

###### Public Attributes

###### mContext (heading level 7)

```
void* otCryptoContext::mContext
```

**Description:** Pointer to the context.

###### mContextSize (heading level 7)

```
uint16_t otCryptoContext::mContextSize
```

**Description:** The length of the context in bytes.

Represents a SHA-256 hash. 

###### Public Attributes

###### m8 (heading level 7)

```
uint8_t otPlatCryptoSha256Hash::m8[OT_CRYPTO_SHA256_HASH_SIZE]
```

**Description:** Hash bytes.

Represents an ECDSA key pair (public and private keys). 

The key pair is stored using Distinguished Encoding Rules (DER) format (per RFC 5915). 

###### Public Attributes

###### mDerBytes (heading level 7)

```
uint8_t otPlatCryptoEcdsaKeyPair::mDerBytes[OT_CRYPTO_ECDSA_MAX_DER_SIZE]
```

###### mDerLength (heading level 7)

```
uint8_t otPlatCryptoEcdsaKeyPair::mDerLength
```

Represents a ECDSA public key. 

The public key is stored as a byte sequence representation of an uncompressed curve point (RFC 6605 - sec 4). 

###### Public Attributes

###### m8 (heading level 7)

```
uint8_t otPlatCryptoEcdsaPublicKey::m8[OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE]
```

Represents an ECDSA signature. 

The signature is encoded as the concatenated binary representation of two MPIs `r` and `s` which are calculated during signing (RFC 6605 - section 4). 

###### Public Attributes

###### m8 (heading level 7)

```
uint8_t otPlatCryptoEcdsaSignature::m8[OT_CRYPTO_ECDSA_SIGNATURE_SIZE]
```

#### DNS-SD (mDNS)

This module includes the platform abstraction for DNS-SD (e.g., mDNS) on the infrastructure network. 

The DNS-SD platform APIs are used only when `OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE` is enabled. 

##### Modules

[otPlatDnssdService](ot-plat-dnssd-service)

[otPlatDnssdHost](ot-plat-dnssd-host)

[otPlatDnssdKey](ot-plat-dnssd-key)

[otPlatDnssdBrowseResult](ot-plat-dnssd-browse-result)

[otPlatDnssdBrowser](ot-plat-dnssd-browser)

[otPlatDnssdSrvResult](ot-plat-dnssd-srv-result)

[otPlatDnssdSrvResolver](ot-plat-dnssd-srv-resolver)

[otPlatDnssdTxtResult](ot-plat-dnssd-txt-result)

[otPlatDnssdTxtResolver](ot-plat-dnssd-txt-resolver)

[otPlatDnssdAddressAndTtl](ot-plat-dnssd-address-and-ttl)

[otPlatDnssdAddressResult](ot-plat-dnssd-address-result)

[otPlatDnssdAddressResolver](ot-plat-dnssd-address-resolver)

[otPlatDnssdRecordResult](ot-plat-dnssd-record-result)

[otPlatDnssdRecordQuerier](ot-plat-dnssd-record-querier)

##### Enumerations

###### otPlatDnssdState

```
enum otPlatDnssdState {
    OT_PLAT_DNSSD_STOPPED
    OT_PLAT_DNSSD_READY
}
```

**Description:**

Represents the state of the DNS-SD platform.

**Enumerator:**

|   |   |
|---|---|
|OT_PLAT_DNSSD_STOPPED|Stopped and unable to register any service or host, or start any browser/resolver.|
|OT_PLAT_DNSSD_READY|Running and ready to register service or host.|

##### Typedefs

###### otPlatDnssdState

`typedef enum otPlatDnssdState otPlatDnssdState`

**Description:**

Represents the state of the DNS-SD platform.

###### otPlatDnssdRequestId

`typedef uint32_t otPlatDnssdRequestId`

**Description:**

Represents a request ID for registering/unregistering a service or host.

###### otPlatDnssdRegisterCallback

`typedef void(* otPlatDnssdRegisterCallback) (otInstance *aInstance, otPlatDnssdRequestId aRequestId, otError aError)`

**Description:**

Represents the callback function used when registering/unregistering a host or service.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aInstance|The OpenThread instance.|
||[in]|aRequestId|The request ID.|
||[in]|aError|Error indicating the outcome of request.|

**Details:**

See `otPlatDnssdRegisterService()`, `otPlatDnssdUnregisterService()`, `otPlatDnssdRegisterHost()`, and `otPlatDnssdUnregisterHost()` for more details about when to invoke the callback and the `aError` values that can be returned in each case.

###### otPlatDnssdService

`typedef struct otPlatDnssdService otPlatDnssdService`

**Description:**

Represents a DNS-SD service.

**Details:**

See `otPlatDnssdRegisterService()`, `otPlatDnssdUnregisterService()` for more details about fields in each case.

###### otPlatDnssdHost

`typedef struct otPlatDnssdHost otPlatDnssdHost`

**Description:**

Represents a DNS-SD host.

**Details:**

See `otPlatDnssdRegisterHost()`, `otPlatDnssdUnregisterHost()` for more details about fields in each case.

###### otPlatDnssdKey

`typedef struct otPlatDnssdKey otPlatDnssdKey`

**Description:**

Represents a DNS-SD key record.

**Details:**

See `otPlatDnssdRegisterKey()`, `otPlatDnssdUnregisterKey()` for more details about fields in each case.

###### otPlatDnssdBrowseResult

`typedef struct otPlatDnssdBrowseResult otPlatDnssdBrowseResult`

**Description:**

Represents a browse result.

###### otPlatDnssdBrowseCallback

`typedef void(* otPlatDnssdBrowseCallback) (otInstance *aInstance, const otPlatDnssdBrowseResult *aResult)`

**Description:**

Represents the callback function used to report a browse result.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aInstance|The OpenThread instance.|
||[in]|aResult|The browse result.|

**Details:**

###### otPlatDnssdBrowser

`typedef struct otPlatDnssdBrowser otPlatDnssdBrowser`

**Description:**

Represents a service browser.

###### otPlatDnssdSrvResult

`typedef struct otPlatDnssdSrvResult otPlatDnssdSrvResult`

**Description:**

Represents an SRV resolver result.

###### otPlatDnssdSrvCallback

`typedef void(* otPlatDnssdSrvCallback) (otInstance *aInstance, const otPlatDnssdSrvResult *aResult)`

**Description:**

Represents the callback function used to report an SRV resolve result.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aInstance|The OpenThread instance.|
||[in]|aResult|The SRV resolve result.|

**Details:**

###### otPlatDnssdSrvResolver

`typedef struct otPlatDnssdSrvResolver otPlatDnssdSrvResolver`

**Description:**

Represents an SRV service resolver.

###### otPlatDnssdTxtResult

`typedef struct otPlatDnssdTxtResult otPlatDnssdTxtResult`

**Description:**

Represents a TXT resolver result.

###### otPlatDnssdTxtCallback

`typedef void(* otPlatDnssdTxtCallback) (otInstance *aInstance, const otPlatDnssdTxtResult *aResult)`

**Description:**

Represents the callback function used to report a TXT resolve result.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aInstance|The OpenThread instance.|
||[in]|aResult|The TXT resolve result.|

**Details:**

###### otPlatDnssdTxtResolver

`typedef struct otPlatDnssdTxtResolver otPlatDnssdTxtResolver`

**Description:**

Represents a TXT service resolver.

###### otPlatDnssdAddressAndTtl

`typedef struct otPlatDnssdAddressAndTtl otPlatDnssdAddressAndTtl`

**Description:**

Represents a discovered host address and its TTL.

###### otPlatDnssdAddressResult

`typedef struct otPlatDnssdAddressResult otPlatDnssdAddressResult`

**Description:**

Represents address resolver result.

###### otPlatDnssdAddressCallback

`typedef void(* otPlatDnssdAddressCallback) (otInstance *aInstance, const otPlatDnssdAddressResult *aResult)`

**Description:**

Represents the callback function use to report a IPv6/IPv4 address resolve result.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aInstance|The OpenThread instance.|
||[in]|aResult|The address resolve result.|

**Details:**

###### otPlatDnssdAddressResolver

`typedef struct otPlatDnssdAddressResolver otPlatDnssdAddressResolver`

**Description:**

Represents an address resolver.

###### otPlatDnssdRecordResult

`typedef struct otPlatDnssdRecordResult otPlatDnssdRecordResult`

**Description:**

Represents a record query result.

###### otPlatDnssdRecordCallback

`typedef void(* otPlatDnssdRecordCallback) (otInstance *aInstance, const otPlatDnssdRecordResult *aResult)`

**Description:**

Represents the callback function used to report a record querier result.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
||[in]|aInstance|The OpenThread instance.|
||[in]|aResult|The record querier result.|

**Details:**

###### otPlatDnssdRecordQuerier

`typedef struct otPlatDnssdRecordQuerier otPlatDnssdRecordQuerier`

**Description:**

Represents a record querier.

##### Functions

###### otPlatDnssdStateHandleStateChange

`void otPlatDnssdStateHandleStateChange(otInstance *aInstance)`

**Description:** Callback to notify state changes of the DNS-SD platform.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

The OpenThread stack will call `otPlatDnssdGetState()` (from this callback or later) to get the new state. The platform MUST therefore ensure that the returned state from `otPlatDnssdGetState()` is updated before calling this.

When the platform signals a state change to `OT_PLAT_DNSSD_STOPPED` using this callback, all active browsers and resolvers are considered to be stopped, and any previously registered host, service, key entries as removed.

###### otPlatDnssdGetState

`otPlatDnssdState otPlatDnssdGetState(otInstance *aInstance)`

**Description:** Gets the current state of the DNS-SD module.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|

The platform MUST notify the OpenThread stack whenever its state gets changed by invoking `otPlatDnssdStateHandleStateChange()`.

**Returns**

- The current state of the DNS-SD module.

###### otPlatDnssdRegisterService

`void otPlatDnssdRegisterService(otInstance *aInstance, const otPlatDnssdService *aService, otPlatDnssdRequestId aRequestId, otPlatDnssdRegisterCallback aCallback)`

**Description:** Registers or updates a service on the infrastructure network's DNS-SD module.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otPlatDnssdService](ot-plat-dnssd-service) *|[in]|aService|Information about the service to register.|
|[otPlatDnssdRequestId](plat-dns-sd#ot-plat-dnssd-request-id)|[in]|aRequestId|The ID associated with this request.|
|[otPlatDnssdRegisterCallback](plat-dns-sd#ot-plat-dnssd-register-callback)|[in]|aCallback|The callback function pointer to report the outcome (may be NULL if no callback needed).|

The `aService` and all its contained information (strings and buffers) are only valid during this call. The platform MUST save a copy of the information if it wants to retain the information after returning from this function.

The fields in `aService` follow these rules:

- The `mServiceInstance` and `mServiceType` fields specify the service instance label and service type name, respectively. They are never NULL.
- The `mHostName` field specifies the host name of the service if it is not NULL. Otherwise, if it is NULL, it indicates that this service is for the device itself and leaves the host name selection to DNS-SD platform.
- The `mSubTypeLabels` is an array of strings representing sub-types associated with the service. It can be NULL if there are no sub-types. Otherwise, the array length is specified by `mSubTypeLabelsLength`.
- The `mTxtData` and `mTxtDataLength` fields specify the encoded TXT data.
- The `mPort`, `mWeight`, and `mPriority` fields specify the service's parameters (as specified in DNS SRV record).
- The `mTtl` field specifies the TTL if non-zero. If zero, the platform can choose the TTL to use.
- The `mInfraIfIndex` field, if non-zero, specifies the infrastructure network interface index to use for this request. If zero, the platform implementation can decided the interface.

When the `mHostName` field in `aService` is not NULL (indicating that this registration is on behalf of another host), the OpenThread stack will ensure that `otPlatDnssdRegisterHost()` is also called for the same host before any service registration requests for the same host.

Once the registration request is finished, either successfully or failed, the platform reports the outcome by invoking the `aCallback` and passing the same `aRequestId` in the callback. The `aCallback` function pointer can be NULL, which indicates that the OpenThread stack does not need to be notified of the outcome of the request. If the outcome is determined, the platform implementation may invoke the `aCallback` before returning from this function. The OpenThread stack will ensure to handle such a situation.

On success, the `aCallback` MUST be called (if non-NULL) with `OT_ERROR_NONE` as the `aError` input parameter. If the registration causes a name conflict on DNS-SD domain (the service instance name is already claimed by another host), the `OT_ERROR_DUPLICATED` error MUST be used. The platform implementation can use other `OT_ERROR` types for other types of errors.

The platform implementation MUST not assume that the `aRequestId` used in subsequent requests will be different. OpenThread may reuse the same request ID again for a different request.

The OpenThread stack will not register the same service (with no changes) that was registered successfully earlier. Therefore, the platform implementation does not need to check for duplicate/same service and can assume that calls to this function are either registering a new entry or changing some parameter in a previously registered item. As a result, these changes always need to be synced on the infrastructure DNS-SD module.

The OpenThread stack does not require the platform implementation to always invoke the `aCallback` function. The OpenThread stack has its own mechanism to time out an aged request with no response. This relaxes the requirement for platform implementations.

###### otPlatDnssdUnregisterService

`void otPlatDnssdUnregisterService(otInstance *aInstance, const otPlatDnssdService *aService, otPlatDnssdRequestId aRequestId, otPlatDnssdRegisterCallback aCallback)`

**Description:** Unregisters a service on the infrastructure network's DNS-SD module.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otPlatDnssdService](ot-plat-dnssd-service) *|[in]|aService|Information about the service to unregister.|
|[otPlatDnssdRequestId](plat-dns-sd#ot-plat-dnssd-request-id)|[in]|aRequestId|The ID associated with this request.|
|[otPlatDnssdRegisterCallback](plat-dns-sd#ot-plat-dnssd-register-callback)|[in]|aCallback|The callback function pointer to report the outcome (may be NULL if no callback needed).|

The `aService` and all its contained information (strings and buffers) are only valid during this call. The platform MUST save a copy of the information if it wants to retain the information after returning from this function.

The fields in `aService` follow these rules:

- The `mServiceInstance` and `mServiceType` fields specify the service instance label and service type name, respectively. They are never NULL.
- The `mHostName` field specifies the host name of the service if it is not NULL. Otherwise, if it is NULL, it indicates that this service is for the device itself and leaves the host name selection to DNS-SD platform.
- The `mInfraIfIndex` field, if non-zero, specifies the infrastructure network interface index to use for this request. If zero, the platform implementation can decided the interface.
- The rest of the fields in `aService` structure MUST be ignored in `otPlatDnssdUnregisterService()` call and may be set to zero by the OpenThread stack.

Regarding the invocation of the `aCallback` and the reuse of the `aRequestId`, this function follows the same rules as described in `otPlatDnssdRegisterService()`.

The OpenThread stack may request the unregistration of a service that was not previously registered, and the platform implementation MUST handle this case. In such a case, the platform can use either `OT_ERROR_NOT_FOUND` to indicate that there was no such registration, or `OT_ERROR_NONE` when invoking the `aCallback` function. The OpenThread stack will handle either case correctly.

###### otPlatDnssdRegisterHost

`void otPlatDnssdRegisterHost(otInstance *aInstance, const otPlatDnssdHost *aHost, otPlatDnssdRequestId aRequestId, otPlatDnssdRegisterCallback aCallback)`

**Description:** Registers or updates a host on the infrastructure network's DNS-SD module.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otPlatDnssdHost](ot-plat-dnssd-host) *|[in]|aHost|Information about the host to register.|
|[otPlatDnssdRequestId](plat-dns-sd#ot-plat-dnssd-request-id)|[in]|aRequestId|The ID associated with this request.|
|[otPlatDnssdRegisterCallback](plat-dns-sd#ot-plat-dnssd-register-callback)|[in]|aCallback|The callback function pointer to report the outcome (may be NULL if no callback needed).|

The `aHost` and all its contained information (strings and arrays) are only valid during this call. The platform MUST save a copy of the information if it wants to retain the information after returning from this function.

The fields in `aHost` follow these rules:

- The `mHostName` field specifies the host name to register. It is never NULL.
- The `mAddresses` field is an array of IPv6 addresses to register with the host. `mAddressesLength` field provides the number of entries in `mAddresses` array. The platform implementation MUST not filter or remove any of addresses in the list. The OpenThread stack will already ensure that the given addresses are externally reachable. For example, when registering host from an SRP registration, link-local or mesh-local addresses associated with the host which are intended for use within Thread mesh are not included in `mAddresses` array passed to this API. The `mAddresses` array can be empty with zero `mAddressesLength`. In such a case, the platform MUST stop advertising any addresses for this host name on the infrastructure DNS-SD.
- The `mTtl` field specifies the TTL if non-zero. If zero, the platform can choose the TTL to use.
- The `mInfraIfIndex` field, if non-zero, specifies the infrastructure network interface index to use for this request. If zero, the platform implementation can decided the interface.

Regarding the invocation of the `aCallback` and the reuse of the `aRequestId`, this function follows the same rules as described in `otPlatDnssdRegisterService()`.

The OpenThread stack will not register the same host (with no changes) that was registered successfully earlier. Therefore, the platform implementation does not need to check for duplicate/same host and can assume that calls to this function are either registering a new entry or changing some parameter in a previously registered item. As a result, these changes always need to be synced on the infrastructure DNS-SD module.

###### otPlatDnssdUnregisterHost

`void otPlatDnssdUnregisterHost(otInstance *aInstance, const otPlatDnssdHost *aHost, otPlatDnssdRequestId aRequestId, otPlatDnssdRegisterCallback aCallback)`

**Description:** Unregisters a host on the infrastructure network's DNS-SD module.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otPlatDnssdHost](ot-plat-dnssd-host) *|[in]|aHost|Information about the host to unregister.|
|[otPlatDnssdRequestId](plat-dns-sd#ot-plat-dnssd-request-id)|[in]|aRequestId|The ID associated with this request.|
|[otPlatDnssdRegisterCallback](plat-dns-sd#ot-plat-dnssd-register-callback)|[in]|aCallback|The callback function pointer to report the outcome (may be NULL if no callback needed).|

The `aHost` and all its contained information (strings and arrays) are only valid during this call. The platform MUST save a copy of the information if it wants to retain the information after returning from this function.

The fields in `aHost` follow these rules:

- The `mHostName` field specifies the host name to unregister. It is never NULL.
- The `mInfraIfIndex` field, if non-zero, specifies the infrastructure network interface index to use for this request. If zero, the platform implementation can decided the interface.
- The rest of the fields in `aHost` structure MUST be ignored in `otPlatDnssdUnregisterHost()` call and may be set to zero by the OpenThread stack.

Regarding the invocation of the `aCallback` and the reuse of the `aRequestId`, this function follows the same rules as described in `otPlatDnssdRegisterService()`.

The OpenThread stack may request the unregistration of a host that was not previously registered, and the platform implementation MUST handle this case. In such a case, the platform can use either `OT_ERROR_NOT_FOUND` to indicate that there was no such registration, or `OT_ERROR_NONE` when invoking the `aCallback` function. OpenThread stack will handle either case correctly.

When unregistering a host, the OpenThread stack will also unregister any previously registered services associated with the same host (by calling `otPlatDnssdUnregisterService()`). However, the platform implementation MAY assume that unregistering a host also unregisters all its associated services.

###### otPlatDnssdRegisterKey

`void otPlatDnssdRegisterKey(otInstance *aInstance, const otPlatDnssdKey *aKey, otPlatDnssdRequestId aRequestId, otPlatDnssdRegisterCallback aCallback)`

**Description:** Registers or updates a key record on the infrastructure network's DNS-SD module.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otPlatDnssdKey](ot-plat-dnssd-key) *|[in]|aKey|Information about the key record to register.|
|[otPlatDnssdRequestId](plat-dns-sd#ot-plat-dnssd-request-id)|[in]|aRequestId|The ID associated with this request.|
|[otPlatDnssdRegisterCallback](plat-dns-sd#ot-plat-dnssd-register-callback)|[in]|aCallback|The callback function pointer to report the outcome (may be NULL if no callback needed).|

The `aKey` and all its contained information (strings and arrays) are only valid during this call. The platform MUST save a copy of the information if it wants to retain the information after returning from this function.

The fields in `aKey` follow these rules:

- If the key is associated with a host, `mName` field specifies the host name and `mServiceType` will be NULL.
- If the key is associated with a service, `mName` field specifies the service instance label and `mServiceType` field specifies the service type. In this case the DNS name for key record is `{mName}.{mServiceTye}`.
- The `mKeyData` field contains the key record's data with `mKeyDataLength` as its length in byes. It is never NULL.
- The `mClass` fields specifies the resource record class to use when registering key record.
- The `mTtl` field specifies the TTL if non-zero. If zero, the platform can choose the TTL to use.
- The `mInfraIfIndex` field, if non-zero, specifies the infrastructure network interface index to use for this request. If zero, the platform implementation can decided the interface.

Regarding the invocation of the `aCallback` and the reuse of the `aRequestId`, this function follows the same rules as described in `otPlatDnssdRegisterService()`.

The OpenThread stack will not register the same key (with no changes) that was registered successfully earlier. Therefore, the platform implementation does not need to check for duplicate/same name and can assume that calls to this function are either registering a new key or changing the key data in a previously registered one. As a result, these changes always need to be synced on the infrastructure DNS-SD module.

###### otPlatDnssdUnregisterKey

`void otPlatDnssdUnregisterKey(otInstance *aInstance, const otPlatDnssdKey *aKey, otPlatDnssdRequestId aRequestId, otPlatDnssdRegisterCallback aCallback)`

**Description:** Unregisters a key record on the infrastructure network's DNS-SD module.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otPlatDnssdKey](ot-plat-dnssd-key) *|[in]|aKey|Information about the key to unregister.|
|[otPlatDnssdRequestId](plat-dns-sd#ot-plat-dnssd-request-id)|[in]|aRequestId|The ID associated with this request.|
|[otPlatDnssdRegisterCallback](plat-dns-sd#ot-plat-dnssd-register-callback)|[in]|aCallback|The callback function pointer to report the outcome (may be NULL if no callback needed).|

The `aKey` and all its contained information (strings and arrays) are only valid during this call. The platform MUST save a copy of the information if it wants to retain the information after returning from this function.

The fields in `aKey` follow these rules:

- If the key is associated with a host, `mName` field specifies the host name and `mServiceType` will be NULL.
- If the key is associated with a service, `mName` field specifies the service instance label and `mServiceType` field specifies the service type. In this case the DNS name for key record is `{mName}.{mServiceTye}`.
- The `mInfraIfIndex` field, if non-zero, specifies the infrastructure network interface index to use for this request. If zero, the platform implementation can decided the interface.
- The rest of the fields in `aKey` structure MUST be ignored in `otPlatDnssdUnregisterKey()` call and may be set to zero by the OpenThread stack.

Regarding the invocation of the `aCallback` and the reuse of the `aRequestId`, this function follows the same rules as described in `otPlatDnssdRegisterService()`.

The OpenThread stack may request the unregistration of a key that was not previously registered, and the platform implementation MUST handle this case. In such a case, the platform can use either `OT_ERROR_NOT_FOUND` to indicate that there was no such registration, or `OT_ERROR_NONE` when invoking the `aCallback` function. the OpenThread stack will handle either case correctly.

###### otPlatDnssdStartBrowser

`void otPlatDnssdStartBrowser(otInstance *aInstance, const otPlatDnssdBrowser *aBrowser)`

**Description:** Starts a service browser.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otPlatDnssdBrowser](ot-plat-dnssd-browser) *|[in]|aBrowser|The browser to be started.|

Initiates a continuous search for the specified `mServiceType` in `aBrowser`. For sub-type services, `mSubTypeLabel` specifies the sub-type, for base services, `mSubTypeLabel` is set to NULL.

Discovered services should be reported through the `mCallback` function in `aBrowser`. Services that have been removed are reported with a TTL value of zero. The callback may be invoked immediately with cached information (if available) and potentially before this function returns. When cached results are used, the reported TTL value should reflect the original TTL from the last received response.

Multiple browsers can be started for the same service, provided they use different callback functions.

The `aBrowser` and all its contained information (strings) are only valid during this call. The platform MUST save a copy of the information if it wants to retain the information after returning from this function.

###### otPlatDnssdStopBrowser

`void otPlatDnssdStopBrowser(otInstance *aInstance, const otPlatDnssdBrowser *aBrowser)`

**Description:** Stops a service browser.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otPlatDnssdBrowser](ot-plat-dnssd-browser) *|[in]|aBrowser|The browser to stop.|

No action is performed if no matching browser with the same service and callback is currently active.

The `aBrowser` and all its contained information (strings) are only valid during this call. The platform MUST save a copy of the information if it wants to retain the information after returning from this function.

###### otPlatDnssdStartSrvResolver

`void otPlatDnssdStartSrvResolver(otInstance *aInstance, const otPlatDnssdSrvResolver *aResolver)`

**Description:** Starts an SRV record resolver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otPlatDnssdSrvResolver](ot-plat-dnssd-srv-resolver) *|[in]|aResolver|The resolver to be started.|

Initiates a continuous SRV record resolver for the specified service in `aResolver`.

Discovered information should be reported through the `mCallback` function in `aResolver`. When the service is removed it is reported with a TTL value of zero. In this case, `mHostName` may be NULL and other result fields (such as `mPort`) will be ignored by the OpenThread stack.

The callback may be invoked immediately with cached information (if available) and potentially before this function returns. When cached result is used, the reported TTL value should reflect the original TTL from the last received response.

Multiple resolvers can be started for the same service, provided they use different callback functions.

The `aResolver` and all its contained information (strings) are only valid during this call. The platform MUST save a copy of the information if it wants to retain the information after returning from this function.

###### otPlatDnssdStopSrvResolver

`void otPlatDnssdStopSrvResolver(otInstance *aInstance, const otPlatDnssdSrvResolver *aResolver)`

**Description:** Stops an SRV record resolver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otPlatDnssdSrvResolver](ot-plat-dnssd-srv-resolver) *|[in]|aResolver|The resolver to stop.|

No action is performed if no matching resolver with the same service and callback is currently active.

The `aResolver` and all its contained information (strings) are only valid during this call. The platform MUST save a copy of the information if it wants to retain the information after returning from this function.

###### otPlatDnssdStartTxtResolver

`void otPlatDnssdStartTxtResolver(otInstance *aInstance, const otPlatDnssdTxtResolver *aResolver)`

**Description:** Starts a TXT record resolver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otPlatDnssdTxtResolver](ot-plat-dnssd-txt-resolver) *|[in]|aResolver|The resolver to be started.|

Initiates a continuous TXT record resolver for the specified service in `aResolver`.

Discovered information should be reported through the `mCallback` function in `aResolver`. When the TXT record is removed it is reported with a TTL value of zero. In this case, `mTxtData` may be NULL, and other result fields (such as `mTxtDataLength`) will be ignored by the OpenThread stack.

The callback may be invoked immediately with cached information (if available) and potentially before this function returns. When cached result is used, the reported TTL value should reflect the original TTL from the last received response.

Multiple resolvers can be started for the same service, provided they use different callback functions.

The `aResolver` and all its contained information (strings) are only valid during this call. The platform MUST save a copy of the information if it wants to retain the information after returning from this function.

###### otPlatDnssdStopTxtResolver

`void otPlatDnssdStopTxtResolver(otInstance *aInstance, const otPlatDnssdTxtResolver *aResolver)`

**Description:** Stops a TXT record resolver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otPlatDnssdTxtResolver](ot-plat-dnssd-txt-resolver) *|[in]|aResolver|The resolver to stop.|

No action is performed if no matching resolver with the same service and callback is currently active.

The `aResolver` and all its contained information (strings) are only valid during this call. The platform MUST save a copy of the information if it wants to retain the information after returning from this function.

###### otPlatDnssdStartIp6AddressResolver

`void otPlatDnssdStartIp6AddressResolver(otInstance *aInstance, const otPlatDnssdAddressResolver *aResolver)`

**Description:** Starts an IPv6 address resolver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otPlatDnssdAddressResolver](ot-plat-dnssd-address-resolver) *|[in]|aResolver|The resolver to be started.|

Initiates a continuous IPv6 address resolver for the specified host name in `aResolver`.

Discovered addresses should be reported through the `mCallback` function in `aResolver`. The callback should be invoked whenever addresses are added or removed, providing an updated list. If all addresses are removed, the callback should be invoked with an empty list (`mAddressesLength` set to zero).

The callback may be invoked immediately with cached information (if available) and potentially before this function returns. When cached result is used, the reported TTL values should reflect the original TTL from the last received response.

Multiple resolvers can be started for the same host name, provided they use different callback functions.

The `aResolver` and all its contained information (strings) are only valid during this call. The platform MUST save a copy of the information if it wants to retain the information after returning from this function.

###### otPlatDnssdStopIp6AddressResolver

`void otPlatDnssdStopIp6AddressResolver(otInstance *aInstance, const otPlatDnssdAddressResolver *aResolver)`

**Description:** Stops an IPv6 address resolver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otPlatDnssdAddressResolver](ot-plat-dnssd-address-resolver) *|[in]|aResolver|The resolver to stop.|

No action is performed if no matching resolver with the same host name and callback is currently active.

The `aResolver` and all its contained information (strings) are only valid during this call. The platform MUST save a copy of the information if it wants to retain the information after returning from this function.

###### otPlatDnssdStartIp4AddressResolver

`void otPlatDnssdStartIp4AddressResolver(otInstance *aInstance, const otPlatDnssdAddressResolver *aResolver)`

**Description:** Starts an IPv4 address resolver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otPlatDnssdAddressResolver](ot-plat-dnssd-address-resolver) *|[in]|aResolver|The resolver to be started.|

Initiates a continuous IPv4 address resolver for the specified host name in `aResolver`.

Discovered addresses should be reported through the `mCallback` function in `aResolver`. The IPv4 addresses are represented using the IPv4-mapped IPv6 address format in `mAddresses` array. The callback should be invoked whenever addresses are added or removed, providing an updated list. If all addresses are removed, the callback should be invoked with an empty list (`mAddressesLength` set to zero).

The callback may be invoked immediately with cached information (if available) and potentially before this function returns. When cached result is used, the reported TTL values will reflect the original TTL from the last received response.

Multiple resolvers can be started for the same host name, provided they use different callback functions.

The `aResolver` and all its contained information (strings) are only valid during this call. The platform MUST save a copy of the information if it wants to retain the information after returning from this function.

###### otPlatDnssdStopIp4AddressResolver

`void otPlatDnssdStopIp4AddressResolver(otInstance *aInstance, const otPlatDnssdAddressResolver *aResolver)`

**Description:** Stops an IPv4 address resolver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otPlatDnssdAddressResolver](ot-plat-dnssd-address-resolver) *|[in]|aResolver|The resolver to stop.|

No action is performed if no matching resolver with the same host name and callback is currently active.

The `aResolver` and all its contained information (strings) are only valid during this call. The platform MUST save a copy of the information if it wants to retain the information after returning from this function.

###### otPlatDnssdStartRecordQuerier

`void otPlatDnssdStartRecordQuerier(otInstance *aInstance, const otPlatDnssdRecordQuerier *aQuerier)`

**Description:** Starts a record querier.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otPlatDnssdRecordQuerier](ot-plat-dnssd-record-querier) *|[in]|aQuerier|The record querier to be started.|

Initiates a continuous query for a given `mRecordType` as specified in `aQuerier`. The queried name is specified by the combination of `mFirstLabel` and `mNextLabels` (optional rest of the labels) in `aQuerier`. The `mFirstLabel` is always non-NULL but `mNextLabels` can be `NULL` if there are no other labels. The `mNextLabels does not include the domain name. The reason for a separate first label is to allow it to include a dot`.` character (as allowed for service instance labels).

Discovered results should be reported through the `mCallback` function in `aQuerier`, providing the raw record data bytes. A removed record data is indicated with a TTL value of zero. The callback may be invoked immediately with cached information (if available) and potentially before this function returns. When cached results are used, the reported TTL value should reflect the original TTL from the last received response.

Multiple querier instances can be started for the same name, provided they use different callback functions.

OpenThread will only use a record querier for types other than PTR, SRV, TXT, A, and AAAA. For those, specific browsers or resolvers are used. The platform implementation, therefore, can choose to restrict its implementation.

The `aQuerier` and all its contained information (strings) are only valid during this call. The platform MUST save a copy of the information if it wants to retain the information after returning from this function.

###### otPlatDnssdStopRecordQuerier

`void otPlatDnssdStopRecordQuerier(otInstance *aInstance, const otPlatDnssdRecordQuerier *aQuerier)`

**Description:** Stops a record querier.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otPlatDnssdRecordQuerier](ot-plat-dnssd-record-querier) *|[in]|aQuerier|The record querier to be stopped.|

No action is performed if no matching querier with the same name, record type and callback is currently active.

The `aQuerier` and all its contained information (strings) are only valid during this call. The platform MUST save a copy of the information if it wants to retain the information after returning from this function.

Represents a DNS-SD service. 

See `otPlatDnssdRegisterService()`, `otPlatDnssdUnregisterService()` for more details about fields in each case. 

###### Public Attributes

###### mHostName (heading level 7)

```
const char* otPlatDnssdService::mHostName
```

**Description:** The host name (does not include domain name).

###### mServiceInstance (heading level 7)

```
const char* otPlatDnssdService::mServiceInstance
```

**Description:** The service instance name label (not the full name).

###### mServiceType (heading level 7)

```
const char* otPlatDnssdService::mServiceType
```

**Description:** The service type (e.g., "_mt._udp", does not include domain name).

###### mSubTypeLabels (heading level 7)

```
const char* const* otPlatDnssdService::mSubTypeLabels
```

**Description:** Array of sub-type labels (can be NULL if no label).

###### mSubTypeLabelsLength (heading level 7)

```
uint16_t otPlatDnssdService::mSubTypeLabelsLength
```

**Description:** Length of array of sub-type labels.

###### mTxtData (heading level 7)

```
const uint8_t* otPlatDnssdService::mTxtData
```

**Description:** Encoded TXT data bytes.

###### mTxtDataLength (heading level 7)

```
uint16_t otPlatDnssdService::mTxtDataLength
```

**Description:** Length of TXT data.

###### mPort (heading level 7)

```
uint16_t otPlatDnssdService::mPort
```

**Description:** The service port number.

###### mPriority (heading level 7)

```
uint16_t otPlatDnssdService::mPriority
```

**Description:** The service priority.

###### mWeight (heading level 7)

```
uint16_t otPlatDnssdService::mWeight
```

**Description:** The service weight.

###### mTtl (heading level 7)

```
uint32_t otPlatDnssdService::mTtl
```

**Description:** The service TTL in seconds.

###### mInfraIfIndex (heading level 7)

```
uint32_t otPlatDnssdService::mInfraIfIndex
```

**Description:** The infrastructure network interface index.

Represents a DNS-SD host. 

See `otPlatDnssdRegisterHost()`, `otPlatDnssdUnregisterHost()` for more details about fields in each case. 

###### Public Attributes

###### mHostName (heading level 7)

```
const char* otPlatDnssdHost::mHostName
```

**Description:** The host name (does not include domain name).

###### mAddresses (heading level 7)

```
const otIp6Address* otPlatDnssdHost::mAddresses
```

**Description:** Array of IPv6 host addresses.

###### mAddressesLength (heading level 7)

```
uint16_t otPlatDnssdHost::mAddressesLength
```

**Description:** Number of entries in `mAddresses` array.

###### mTtl (heading level 7)

```
uint32_t otPlatDnssdHost::mTtl
```

**Description:** The host TTL in seconds.

###### mInfraIfIndex (heading level 7)

```
uint32_t otPlatDnssdHost::mInfraIfIndex
```

**Description:** The infrastructure network interface index.

Represents a DNS-SD key record. 

See `otPlatDnssdRegisterKey()`, `otPlatDnssdUnregisterKey()` for more details about fields in each case. 

###### Public Attributes

###### mName (heading level 7)

```
const char* otPlatDnssdKey::mName
```

**Description:** A host or a service instance name (does not include domain name).

###### mServiceType (heading level 7)

```
const char* otPlatDnssdKey::mServiceType
```

**Description:** The service type if key is for a service (does not include domain name).

###### mKeyData (heading level 7)

```
const uint8_t* otPlatDnssdKey::mKeyData
```

**Description:** Byte array containing the key record data.

###### mKeyDataLength (heading level 7)

```
uint16_t otPlatDnssdKey::mKeyDataLength
```

**Description:** Length of `mKeyData` in bytes.

###### mClass (heading level 7)

```
uint16_t otPlatDnssdKey::mClass
```

**Description:** The resource record class.

###### mTtl (heading level 7)

```
uint32_t otPlatDnssdKey::mTtl
```

**Description:** The TTL in seconds.

###### mInfraIfIndex (heading level 7)

```
uint32_t otPlatDnssdKey::mInfraIfIndex
```

**Description:** The infrastructure network interface index.

Represents a browse result. 

###### Public Attributes

###### mServiceType (heading level 7)

```
const char* otPlatDnssdBrowseResult::mServiceType
```

**Description:** The service type (e.g., "_mt._udp").

###### mSubTypeLabel (heading level 7)

```
const char* otPlatDnssdBrowseResult::mSubTypeLabel
```

**Description:** The sub-type label if browsing for sub-type, NULL otherwise.

###### mServiceInstance (heading level 7)

```
const char* otPlatDnssdBrowseResult::mServiceInstance
```

**Description:** Service instance label.

###### mTtl (heading level 7)

```
uint32_t otPlatDnssdBrowseResult::mTtl
```

**Description:** TTL in seconds. Zero TTL indicates that service is removed.

###### mInfraIfIndex (heading level 7)

```
uint32_t otPlatDnssdBrowseResult::mInfraIfIndex
```

**Description:** The infrastructure network interface index.

Represents a service browser. 

###### Public Attributes

###### mServiceType (heading level 7)

```
const char* otPlatDnssdBrowser::mServiceType
```

**Description:** The service type (e.g., "_mt._udp"). MUST NOT include domain name.

###### mSubTypeLabel (heading level 7)

```
const char* otPlatDnssdBrowser::mSubTypeLabel
```

**Description:** The sub-type label if browsing for sub-type, NULL otherwise.

###### mInfraIfIndex (heading level 7)

```
uint32_t otPlatDnssdBrowser::mInfraIfIndex
```

**Description:** The infrastructure network interface index.

###### mCallback (heading level 7)

```
otPlatDnssdBrowseCallback otPlatDnssdBrowser::mCallback
```

**Description:** The callback to report result.

Represents an SRV resolver result. 

###### Public Attributes

###### mServiceInstance (heading level 7)

```
const char* otPlatDnssdSrvResult::mServiceInstance
```

**Description:** The service instance name label.

###### mServiceType (heading level 7)

```
const char* otPlatDnssdSrvResult::mServiceType
```

**Description:** The service type.

###### mHostName (heading level 7)

```
const char* otPlatDnssdSrvResult::mHostName
```

**Description:** The host name (e.g., "myhost"). Can be NULL when `mTtl` is zero.

###### mPort (heading level 7)

```
uint16_t otPlatDnssdSrvResult::mPort
```

**Description:** The service port number.

###### mPriority (heading level 7)

```
uint16_t otPlatDnssdSrvResult::mPriority
```

**Description:** The service priority.

###### mWeight (heading level 7)

```
uint16_t otPlatDnssdSrvResult::mWeight
```

**Description:** The service weight.

###### mTtl (heading level 7)

```
uint32_t otPlatDnssdSrvResult::mTtl
```

**Description:** The service TTL in seconds. Zero TTL indicates SRV record is removed.

###### mInfraIfIndex (heading level 7)

```
uint32_t otPlatDnssdSrvResult::mInfraIfIndex
```

**Description:** The infrastructure network interface index.

Represents an SRV service resolver. 

###### Public Attributes

###### mServiceInstance (heading level 7)

```
const char* otPlatDnssdSrvResolver::mServiceInstance
```

**Description:** The service instance label.

###### mServiceType (heading level 7)

```
const char* otPlatDnssdSrvResolver::mServiceType
```

**Description:** The service type.

###### mInfraIfIndex (heading level 7)

```
uint32_t otPlatDnssdSrvResolver::mInfraIfIndex
```

**Description:** The infrastructure network interface index.

###### mCallback (heading level 7)

```
otPlatDnssdSrvCallback otPlatDnssdSrvResolver::mCallback
```

**Description:** The callback to report result.

Represents a TXT resolver result. 

###### Public Attributes

###### mServiceInstance (heading level 7)

```
const char* otPlatDnssdTxtResult::mServiceInstance
```

**Description:** The service instance name label.

###### mServiceType (heading level 7)

```
const char* otPlatDnssdTxtResult::mServiceType
```

**Description:** The service type.

###### mTxtData (heading level 7)

```
const uint8_t* otPlatDnssdTxtResult::mTxtData
```

**Description:** Encoded TXT data bytes. Can be NULL when `mTtl` is zero.

###### mTxtDataLength (heading level 7)

```
uint16_t otPlatDnssdTxtResult::mTxtDataLength
```

**Description:** Length of TXT data.

###### mTtl (heading level 7)

```
uint32_t otPlatDnssdTxtResult::mTtl
```

**Description:** The TXT data TTL in seconds. Zero TTL indicates record is removed.

###### mInfraIfIndex (heading level 7)

```
uint32_t otPlatDnssdTxtResult::mInfraIfIndex
```

**Description:** The infrastructure network interface index.

Represents a TXT service resolver. 

###### Public Attributes

###### mServiceInstance (heading level 7)

```
const char* otPlatDnssdTxtResolver::mServiceInstance
```

**Description:** Service instance label.

###### mServiceType (heading level 7)

```
const char* otPlatDnssdTxtResolver::mServiceType
```

**Description:** Service type.

###### mInfraIfIndex (heading level 7)

```
uint32_t otPlatDnssdTxtResolver::mInfraIfIndex
```

**Description:** The infrastructure network interface index.

###### mCallback (heading level 7)

```
otPlatDnssdTxtCallback otPlatDnssdTxtResolver::mCallback
```

Represents a discovered host address and its TTL. 

###### Public Attributes

###### mAddress (heading level 7)

```
otIp6Address otPlatDnssdAddressAndTtl::mAddress
```

**Description:** The IPv6 address. For IPv4 address the IPv4-mapped IPv6 address format is used.

###### mTtl (heading level 7)

```
uint32_t otPlatDnssdAddressAndTtl::mTtl
```

**Description:** The TTL in seconds.

Represents address resolver result. 

###### Public Attributes

###### mHostName (heading level 7)

```
const char* otPlatDnssdAddressResult::mHostName
```

**Description:** The host name.

###### mAddresses (heading level 7)

```
const otPlatDnssdAddressAndTtl* otPlatDnssdAddressResult::mAddresses
```

**Description:** Array of host addresses and their TTL. Can be NULL if empty.

###### mAddressesLength (heading level 7)

```
uint16_t otPlatDnssdAddressResult::mAddressesLength
```

**Description:** Number of entries in `mAddresses` array.

###### mInfraIfIndex (heading level 7)

```
uint32_t otPlatDnssdAddressResult::mInfraIfIndex
```

**Description:** The infrastructure network interface index.

Represents an address resolver. 

###### Public Attributes

###### mHostName (heading level 7)

```
const char* otPlatDnssdAddressResolver::mHostName
```

**Description:** The host name (e.g., "myhost"). MUST NOT contain domain name.

###### mInfraIfIndex (heading level 7)

```
uint32_t otPlatDnssdAddressResolver::mInfraIfIndex
```

**Description:** The infrastructure network interface index.

###### mCallback (heading level 7)

```
otPlatDnssdAddressCallback otPlatDnssdAddressResolver::mCallback
```

**Description:** The callback to report result.

Represents a record query result. 

###### Public Attributes

###### mFirstLabel (heading level 7)

```
const char* otPlatDnssdRecordResult::mFirstLabel
```

**Description:** The first label of the name to be queried.

###### mNextLabels (heading level 7)

```
const char* otPlatDnssdRecordResult::mNextLabels
```

**Description:** The rest of the name labels. Does not include domain name. Can be NULL.

###### mRecordType (heading level 7)

```
uint16_t otPlatDnssdRecordResult::mRecordType
```

**Description:** The record type.

###### mRecordData (heading level 7)

```
const uint8_t* otPlatDnssdRecordResult::mRecordData
```

**Description:** The record data bytes.

###### mRecordDataLength (heading level 7)

```
uint16_t otPlatDnssdRecordResult::mRecordDataLength
```

**Description:** Number of bytes in record data.

###### mTtl (heading level 7)

```
uint32_t otPlatDnssdRecordResult::mTtl
```

**Description:** TTL in seconds. Zero TTL indicates removal the data.

###### mInfraIfIndex (heading level 7)

```
uint32_t otPlatDnssdRecordResult::mInfraIfIndex
```

**Description:** The infrastructure network interface index.

Represents a record querier. 

###### Public Attributes

###### mFirstLabel (heading level 7)

```
const char* otPlatDnssdRecordQuerier::mFirstLabel
```

**Description:** The first label of the name to be queried. MUST NOT be NULL.

###### mNextLabels (heading level 7)

```
const char* otPlatDnssdRecordQuerier::mNextLabels
```

**Description:** The rest of name labels, excluding domain name. Can be NULL.

###### mRecordType (heading level 7)

```
uint16_t otPlatDnssdRecordQuerier::mRecordType
```

**Description:** The record type to query.

###### mInfraIfIndex (heading level 7)

```
uint32_t otPlatDnssdRecordQuerier::mInfraIfIndex
```

**Description:** The infrastructure network interface index.

###### mCallback (heading level 7)

```
otPlatDnssdRecordCallback otPlatDnssdRecordQuerier::mCallback
```

**Description:** The callback to report result.

#### Infrastructure Interface

This module includes the platform abstraction for the adjacent infrastructure network interface. 

##### Modules

[otPlatInfraIfLinkLayerAddress](ot-plat-infra-if-link-layer-address)

##### Typedefs

###### otPlatInfraIfLinkLayerAddress

`typedef struct otPlatInfraIfLinkLayerAddress otPlatInfraIfLinkLayerAddress`

**Description:**

Represents an InfraIf Link-Layer Address.

##### Functions

###### otPlatInfraIfHasAddress

`bool otPlatInfraIfHasAddress(otInstance *aInstance, uint32_t aInfraIfIndex, const otIp6Address *aAddress)`

**Description:** Tells whether an infra interface has the given IPv6 address assigned.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|uint32_t|[in]|aInfraIfIndex|The index of the infra interface.|
|const [otIp6Address](ot-ip6-address) *|[in]|aAddress|The IPv6 address.|

**Returns**

- TRUE if the infra interface has given IPv6 address assigned, FALSE otherwise.

###### otPlatInfraIfSendIcmp6Nd

`otError otPlatInfraIfSendIcmp6Nd(otInstance *aInstance, uint32_t aInfraIfIndex, const otIp6Address *aDestAddress, const uint8_t *aBuffer, uint16_t aBufferLength)`

**Description:** Sends an ICMPv6 Neighbor Discovery message on given infrastructure interface.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|uint32_t|[in]|aInfraIfIndex|The index of the infrastructure interface this message is sent to.|
|const [otIp6Address](ot-ip6-address) *|[in]|aDestAddress|The destination address this message is sent to.|
|const uint8_t *|[in]|aBuffer|The ICMPv6 message buffer. The ICMPv6 checksum is left zero and the platform should do the checksum calculate.|
|uint16_t|[in]|aBufferLength|The length of the message buffer.|

See RFC 4861: [https://tools.ietf.org/html/rfc4861](https://tools.ietf.org/html/rfc4861).

**Note**

- Per RFC 4861, the implementation should send the message with IPv6 link-local source address of interface `aInfraIfIndex` and IP Hop Limit 255.

###### otPlatInfraIfRecvIcmp6Nd

`void otPlatInfraIfRecvIcmp6Nd(otInstance *aInstance, uint32_t aInfraIfIndex, const otIp6Address *aSrcAddress, const uint8_t *aBuffer, uint16_t aBufferLength)`

**Description:** The infra interface driver calls this method to notify OpenThread that an ICMPv6 Neighbor Discovery message is received.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint32_t|[in]|aInfraIfIndex|The index of the infrastructure interface on which the ICMPv6 message is received.|
|const [otIp6Address](ot-ip6-address) *|[in]|aSrcAddress|The source address this message is received from.|
|const uint8_t *|[in]|aBuffer|The ICMPv6 message buffer.|
|uint16_t|[in]|aBufferLength|The length of the ICMPv6 message buffer.|

See RFC 4861: [https://tools.ietf.org/html/rfc4861](https://tools.ietf.org/html/rfc4861).

**Note**

- Per RFC 4861, the caller should enforce that the source address MUST be a IPv6 link-local address and the IP Hop Limit MUST be 255.

###### otPlatInfraIfStateChanged

`otError otPlatInfraIfStateChanged(otInstance *aInstance, uint32_t aInfraIfIndex, bool aIsRunning)`

**Description:** The infra interface driver calls this method to notify OpenThread of the interface state changes.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint32_t|[in]|aInfraIfIndex|The index of the infrastructure interface.|
|bool|[in]|aIsRunning|A boolean that indicates whether the infrastructure interface is running.|

It is fine for the platform to call to method even when the running state of the interface hasn't changed. In this case, the Routing Manager state is not affected.

###### otPlatInfraIfDiscoverNat64Prefix

`otError otPlatInfraIfDiscoverNat64Prefix(otInstance *aInstance, uint32_t aInfraIfIndex)`

**Description:** Send a request to discover the NAT64 prefix on the infrastructure interface with `aInfraIfIndex`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|uint32_t|[in]|aInfraIfIndex|The index of the infrastructure interface to discover the NAT64 prefix.|

OpenThread will call this method periodically to monitor the presence or change of NAT64 prefix.

**Note**

- This function requests the platform to discover a NAT64 prefix (e.g. using RFC 7050 DNS-based discovery). The priority of the discovered prefix is lower than that of the prefix discovered via Router Advertisements PREF64 option (RFC 8781).

###### otPlatInfraIfDiscoverNat64PrefixDone

`void otPlatInfraIfDiscoverNat64PrefixDone(otInstance *aInstance, uint32_t aInfraIfIndex, const otIp6Prefix *aIp6Prefix)`

**Description:** The infra interface driver calls this method to notify OpenThread that the discovery of NAT64 prefix is done.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint32_t|[in]|aInfraIfIndex|The index of the infrastructure interface on which the NAT64 prefix is discovered.|
|const [otIp6Prefix](ot-ip6-prefix) *|[in]|aIp6Prefix|A pointer to NAT64 prefix.|

Is expected to be invoked after calling otPlatInfraIfDiscoverNat64Prefix. If no NAT64 prefix is discovered, `aIp6Prefix` shall point to an empty prefix with zero length.

**Note**

- This function is used to report a NAT64 prefix discovered by the platform (e.g. using RFC 7050 DNS-based discovery). The priority of the discovered prefix is lower than that of the prefix discovered via Router Advertisements PREF64 option (RFC 8781).

###### otPlatGetInfraIfLinkLayerAddress

`otError otPlatGetInfraIfLinkLayerAddress(otInstance *aInstance, uint32_t aIfIndex, otPlatInfraIfLinkLayerAddress *aInfraIfLinkLayerAddress)`

**Description:** Get the link-layer address of the infrastructure interface.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint32_t|[in]|aIfIndex|The index of the infrastructure interface.|
|[otPlatInfraIfLinkLayerAddress](ot-plat-infra-if-link-layer-address) *|[out]|aInfraIfLinkLayerAddress|A pointer to infrastructure interface link-layer address.|

OpenThread invokes this method when the address is required, for example: when generating an ND6 message which includes a source link-layer address option.

###### otPlatInfraIfDhcp6PdClientSetListeningEnabled

`void otPlatInfraIfDhcp6PdClientSetListeningEnabled(otInstance *aInstance, bool aEnable, uint32_t aInfraIfIndex)`

**Description:** Enables or disables listening for DHCPv6 Prefix Delegation (PD) messages on client.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|bool|[in]|aEnable|A boolean to enable (`true`) or disable (`false`) listening.|
|uint32_t|[in]|aInfraIfIndex|The index of the infrastructure interface to operate on.|

This function is only used when `OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_CLIENT_ENABLE` is enabled.

When enabled, the platform must open a UDP socket on the specified infrastructure interface, binding to the DHCPv6 client port 546 to receive messages from DHCPv6 servers.

###### otPlatInfraIfDhcp6PdClientSend

`void otPlatInfraIfDhcp6PdClientSend(otInstance *aInstance, otMessage *aMessage, otIp6Address *aDestAddress, uint32_t aInfraIfIndex)`

**Description:** Sends a DHCPv6 message to a unicast or multicast destination address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|An `otMessage` containing the DHCPv6 payload. Ownership is passed to the platform layer.|
|[otIp6Address](ot-ip6-address) *|[in]|aDestAddress|The IPv6 multicast or unicast destination address.|
|uint32_t|[in]|aInfraIfIndex|The index of the infrastructure interface from which to send the message.|

This function is only used when `OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_CLIENT_ENABLE` is enabled.

The platform is responsible for constructing a UDP datagram with the given DHCPv6 message as its payload. The datagram must be sent from the DHCPv6 client port (546) to the server port (547) on the specified infrastructure interface. The destination IPv6 address can be a unicast address or the multicast `All_DHCP_Relay_Agents_and_Servers` address (`ff02::1:2`).

This function passes the ownership of `aMessage` to the platform layer. Platform MUST then free the message when no longer needed.

###### otPlatInfraIfDhcp6PdClientHandleReceived

`void otPlatInfraIfDhcp6PdClientHandleReceived(otInstance *aInstance, otMessage *aMessage, uint32_t aInfraIfIndex)`

**Description:** Callback from the platform to notify the OpenThread stack of a received DHCPv6 message.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|The `otMessage` containing the received DHCPv6 payload. Ownership is passed to OT stack.|
|uint32_t|[in]|aInfraIfIndex|The index of the infrastructure interface from which the message was received..|

This function is provided when `OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_CLIENT_ENABLE` is enabled.

The platform calls this function whenever a DHCPv6 message is received on the client port (546) while listening on this port is enabled (refer to `otPlatInfraIfDhcp6PdClientSetListeningEnabled()`).

The platform is responsible for allocating the `otMessage` to pass the received UDP payload. Ownership of the `aMessage` is passed to the OpenThread stack (which will free it once no longer needed).

##### Macros

`#define OT_PLAT_INFRA_IF_MAX_LINK_LAYER_ADDR_LENGTH 16`

**Description**: Maximum InfraIf Link-layer address length.

Represents an InfraIf Link-Layer Address. 

###### Public Attributes

###### mAddress (heading level 7)

```
uint8_t otPlatInfraIfLinkLayerAddress::mAddress[OT_PLAT_INFRA_IF_MAX_LINK_LAYER_ADDR_LENGTH]
```

**Description:** The link-layer address bytes.

###### mLength (heading level 7)

```
uint8_t otPlatInfraIfLinkLayerAddress::mLength
```

**Description:** The address length (number of bytes).

#### Message Pool

This module includes the platform abstraction for the message pool. 

##### Modules

[otMessageBuffer](ot-message-buffer)

##### Typedefs

###### otMessageBuffer

`typedef struct otMessageBuffer otMessageBuffer`

**Description:**

Represents an OpenThread message buffer.

##### Functions

###### otPlatMessagePoolInit

`void otPlatMessagePoolInit(otInstance *aInstance, uint16_t aMinNumFreeBuffers, size_t aBufferSize)`

**Description:** Initialize the platform implemented message pool.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|uint16_t|[in]|aMinNumFreeBuffers|An uint16 containing the minimum number of free buffers desired by OpenThread.|
|size_t|[in]|aBufferSize|The size in bytes of a buffer object.|

Is used when `OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT` is enabled.

###### otPlatMessagePoolNew

`otMessageBuffer * otPlatMessagePoolNew(otInstance *aInstance)`

**Description:** Allocate a buffer from the platform managed buffer pool.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

Is used when `OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT` is enabled.

The returned buffer instance MUST have at least `aBufferSize` bytes (as specified in `otPlatMessagePoolInit()`).

**Returns**

- A pointer to the buffer or NULL if no buffers are available.

###### otPlatMessagePoolFree

`void otPlatMessagePoolFree(otInstance *aInstance, otMessageBuffer *aBuffer)`

**Description:** Is used to free a buffer back to the platform managed buffer pool.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|
|[otMessageBuffer](ot-message-buffer) *|[in]|aBuffer|The buffer to free.|

Is used when `OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT` is enabled.

###### otPlatMessagePoolNumFreeBuffers

`uint16_t otPlatMessagePoolNumFreeBuffers(otInstance *aInstance)`

**Description:** Get the number of free buffers.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

Is used when `OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT` is enabled.

**Returns**

- The number of buffers currently free and available to OpenThread.

###### otPlatMessagePoolDeinit

`void otPlatMessagePoolDeinit(otInstance *aInstance)`

**Description:** Deinitialize the platform implemented message pool.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance.|

Is used when `OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT` is enabled.

Represents an OpenThread message buffer. 

###### Public Attributes

###### mNext (heading level 7)

```
struct otMessageBuffer* otMessageBuffer::mNext
```

**Description:** Pointer to the next buffer.

#### Multicast DNS

This module defines platform APIs for Multicast DNS (mDNS) socket. 

##### Modules

[otPlatMdnsAddressInfo](ot-plat-mdns-address-info)

##### Typedefs

###### otPlatMdnsAddressInfo

`typedef struct otPlatMdnsAddressInfo otPlatMdnsAddressInfo`

**Description:**

Represents a socket address info.

##### Functions

###### otPlatMdnsSetListeningEnabled

`otError otPlatMdnsSetListeningEnabled(otInstance *aInstance, bool aEnable, uint32_t aInfraIfIndex)`

**Description:** Enables or disables listening for mDNS messages sent to mDNS port 5353.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpernThread instance.|
|bool|[in]|aEnable|Indicate whether to enable or disable.|
|uint32_t|[in]|aInfraIfIndex|The infrastructure network interface index.|

When listening is enabled, the platform MUST listen for multicast messages sent to UDP destination port 5353 at the mDNS link-local multicast address `224.0.0.251` and its IPv6 equivalent `ff02::fb`.

The platform SHOULD also listen for any unicast messages sent to UDP destination port 5353. If this is not possible, then OpenThread mDNS module can be configured to not use any "QU" questions requesting unicast response.

While enabled, all received messages MUST be reported back using `otPlatMdnsHandleReceive()` callback.

When enabled, the platform MUST also monitor and report all IPv4 and IPv6 addresses assigned to the network interface using the `otPlatMdnsHandleHostAddressEvent()` callback function. Refer to the documentation of this callback for detailed information on the callback's usage and parameters.

###### otPlatMdnsSendMulticast

`void otPlatMdnsSendMulticast(otInstance *aInstance, otMessage *aMessage, uint32_t aInfraIfIndex)`

**Description:** Sends an mDNS message as multicast.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|The mDNS message to multicast. Ownership is transferred to the platform layer.|
|uint32_t|[in]|aInfraIfIndex|The infrastructure network interface index.|

The platform MUST multicast the prepared mDNS message in `aMessage` as a UDP message using the mDNS well-known port number 5353 for both source and destination ports. The message MUST be sent to the mDNS link-local multicast address `224.0.0.251` and/or its IPv6 equivalent `ff02::fb`.

`aMessage` contains the mDNS message starting with DNS header at offset zero. It does not include IP or UDP headers. This function passes the ownership of `aMessage` to the platform layer and platform implementation MUST free `aMessage` once sent and no longer needed.

The platform MUST allow multicast loopback, i.e., the multicast message `aMessage` MUST also be received and passed back to OpenThread stack using `otPlatMdnsHandleReceive()` callback. This behavior is essential for the OpenThread mDNS stack to process and potentially respond to its own queries, while allowing other mDNS receivers to also receive the query and its response.

###### otPlatMdnsSendUnicast

`void otPlatMdnsSendUnicast(otInstance *aInstance, otMessage *aMessage, const otPlatMdnsAddressInfo *aAddress)`

**Description:** Sends an mDNS message as unicast.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|The mDNS message to multicast. Ownership is transferred to platform layer.|
|const [otPlatMdnsAddressInfo](ot-plat-mdns-address-info) *|[in]|aAddress|The destination address info.|

The platform MUST send the prepared mDNS message in `aMessage` as a UDP message using source UDP port 5353 to the destination address and port number specified by `aAddress`.

`aMessage` contains the DNS message starting with the DNS header at offset zero. It does not include IP or UDP headers. This function passes the ownership of `aMessage` to the platform layer and platform implementation MUST free `aMessage` once sent and no longer needed.

The `aAddress` fields are as follows:

- `mAddress` specifies the destination address. IPv4-mapped IPv6 format is used to represent an IPv4 destination.
- `mPort` specifies the destination port.
- `mInfraIndex` specifies the interface index.

If the @aAddress matches this devices address, the platform MUST ensure to receive and pass the message back to the OpenThread stack using `otPlatMdnsHandleReceive()` for processing.

###### otPlatMdnsHandleReceive

`void otPlatMdnsHandleReceive(otInstance *aInstance, otMessage *aMessage, bool aIsUnicast, const otPlatMdnsAddressInfo *aAddress)`

**Description:** Callback to notify OpenThread mDNS module of a received message on UDP port 5353.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|[otMessage](api-message#ot-message) *|[in]|aMessage|The received mDNS message. Ownership is transferred to the OpenThread stack.|
|bool|[in]|aIsUnicast|Indicates whether the received message is unicast or multicast.|
|const [otPlatMdnsAddressInfo](ot-plat-mdns-address-info) *|[in]|aAddress|The sender's address info.|

`aMessage` MUST contain DNS message starting with the DNS header at offset zero. This function passes the ownership of `aMessage` from the platform layer to the OpenThread stack. The OpenThread stack will free the message once processed.

The `aAddress` fields are as follows:

- `mAddress` specifies the sender's address. IPv4-mapped IPv6 format is used to represent an IPv4 destination.
- `mPort` specifies the sender's port.
- `mInfraIndex` specifies the interface index.

###### otPlatMdnsHandleHostAddressEvent

`void otPlatMdnsHandleHostAddressEvent(otInstance *aInstance, const otIp6Address *aAddress, bool aAdded, uint32_t aInfraIfIndex)`

**Description:** Callback to notify OpenThread mDNS module of host address changes.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otIp6Address](ot-ip6-address) *|[in]|aAddress|IP Address. IPv4-mapped IPv6 format is used to represent an IPv4 address.|
|bool|[in]|aAdded|Boolean to indicate whether the address added (`TRUE`) or removed (`FALSE`).|
|uint32_t|[in]|aInfraIfIndex|The interface index.|

When `otPlatMdnsSetListeningEnabled()` enables mDNS listening on an `aInfraIfIndex`, the platform MUST monitor and report ALL IPv4 and IPv6 addresses assigned to this network interface.

When mDNS is enabled:

- The platform MUST retrieve ALL currently assigned IPv4 and IPv6 addresses on the specified interface.
- For each retrieved address, the platform MUST call `otPlatMdnsHandleHostAddressEvent()` to add the address.
- The IPv4 addresses are represented using IPv4-mapped IPv6 format.

Ongoing monitoring (while enabled):

- The platform MUST continuously monitor the specified interface for address changes.
- When the address list changes, the platform MUST notify the OpenThread stack of the change using one of the following methods: A. Call this callback for each affected address, indicating the change (addition or removal using `aAdded`). B. Alternatively, call the `otPlatMdnsHandleHostAddressRemoveAll()` callback once, immediately followed by invoking this callback for every currently assigned IPv4 and IPv6 address on the interface adding them (`aAdded` set to `TRUE`), providing the completed updated address list.
- These two approaches offer flexibility for platforms with varying capabilities, such as different operating systems and network stacks. Some network stacks may provide mechanisms to identify the added or removed addresses, while others may only provide the new list upon a change.  
  When mDNS is disabled:
- The platform MUST cease monitoring for address changes on the interface.
- The platform does NOT need to explicitly signal the removal of addresses upon disable. The OpenThread stack automatically clears its internal address list.
- If address monitoring is re-enabled later, the platform MUST repeat the "enable" steps again, retrieving and reporting ALL current addresses.

The OpenThread stack maintains an internal list of host addresses. It updates this list automatically upon receiving calls to `otPlatMdnsHandleHostAddressEvent()`.

- OpenThread's mDNS implementation uses a short guard time (4 msec) before taking action (e.g., announcing new addresses). This allows multiple changes to be grouped and announced together.
- OpenThread's mDNS implementation also handles transient changes, e.g., an address is removed and then quickly re-added. It ensures that announcements are only made when there is a change to the list (from what was announced before). This simplifies the platform's responsibility as it can simply report all observed changes.

###### otPlatMdnsHandleHostAddressRemoveAll

`void otPlatMdnsHandleHostAddressRemoveAll(otInstance *aInstance, uint32_t aInfraIfIndex)`

**Description:** Callback to notify OpenThread mDNS module to remove all previously added host IPv4 and IPv6 addresses.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|uint32_t|[in]|aInfraIfIndex|The interface index.|

See documentation of `otPlatMdnsHandleHostAddressEvent()` for how this callback MUST be used.

Represents a socket address info. 

###### Public Attributes

###### mAddress (heading level 7)

```
otIp6Address otPlatMdnsAddressInfo::mAddress
```

**Description:** IP address. IPv4-mapped IPv6 format should be used to represent IPv4 address.

###### mPort (heading level 7)

```
uint16_t otPlatMdnsAddressInfo::mPort
```

**Description:** Port number.

###### mInfraIfIndex (heading level 7)

```
uint32_t otPlatMdnsAddressInfo::mInfraIfIndex
```

**Description:** Interface index.

#### Radio

This module includes the platform abstraction for radio communication. 

##### Modules

[Radio Types](radio-types)

[Radio Configuration](radio-config)

[Radio Operation](radio-operation)

[Radio Extension](radio-extension)

##### Radio Configuration

This module includes the platform abstraction for radio configuration. 

###### Functions

###### otPlatRadioGetCaps (heading level 7)

`otRadioCaps otPlatRadioGetCaps(otInstance *aInstance)`

**Description:** Get the radio capabilities.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

**Returns**

- The radio capability bit vector (see `OT_RADIO_CAP_*` definitions).

###### otPlatRadioGetVersionString (heading level 7)

`const char * otPlatRadioGetVersionString(otInstance *aInstance)`

**Description:** Get the radio version string.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

This is an optional radio driver platform function. If not provided by platform radio driver, OpenThread uses the OpenThread version instead (**See Also**

- [otGetVersionString()](api-instance#ot-get-version-string)).

**Returns**

- A pointer to the OpenThread radio version.

###### otPlatRadioGetReceiveSensitivity (heading level 7)

`int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance)`

**Description:** Get the radio receive sensitivity value.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

**Returns**

- The radio receive sensitivity value in dBm.

###### otPlatRadioGetIeeeEui64 (heading level 7)

`void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64)`

**Description:** Gets the factory-assigned IEEE EUI-64 for this interface.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint8_t *|[out]|aIeeeEui64|A pointer to the factory-assigned IEEE EUI-64.|

###### otPlatRadioSetPanId (heading level 7)

`void otPlatRadioSetPanId(otInstance *aInstance, otPanId aPanId)`

**Description:** Set the PAN ID for address filtering.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otPanId](radio-types#ot-pan-id)|[in]|aPanId|The IEEE 802.15.4 PAN ID.|

###### otPlatRadioSetExtendedAddress (heading level 7)

`void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aExtAddress)`

**Description:** Set the Extended Address for address filtering.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|const [otExtAddress](ot-ext-address) *|[in]|aExtAddress|A pointer to the IEEE 802.15.4 Extended Address stored in little-endian byte order.|

###### otPlatRadioSetShortAddress (heading level 7)

`void otPlatRadioSetShortAddress(otInstance *aInstance, otShortAddress aShortAddress)`

**Description:** Set the Short Address for address filtering.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otShortAddress](radio-types#ot-short-address)|[in]|aShortAddress|The IEEE 802.15.4 Short Address.|

###### otPlatRadioSetAlternateShortAddress (heading level 7)

`void otPlatRadioSetAlternateShortAddress(otInstance *aInstance, otShortAddress aShortAddress)`

**Description:** Set the alternate short address.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otShortAddress](radio-types#ot-short-address)|[in]|aShortAddress|The alternate IEEE 802.15.4 short address. `OT_RADIO_INVALID_SHORT_ADDR` to clear.|

This is an optional radio platform API. The radio platform MUST indicate support for this API by including the capability `OT_RADIO_CAPS_ALT_SHORT_ADDR` in `otPlatRadioGetCaps()`.

When supported, the radio should accept received frames destined to the specified alternate short address in addition to the short address provided in `otPlatRadioSetShortAddress()`.

The `aShortAddress` can be set to `OT_RADIO_INVALID_SHORT_ADDR` (0xfffe) to clear any previously set alternate short address.

This function is used by OpenThread stack during child-to-router role transitions, allowing the device to continue receiving frames addressed to its previous short address for a short period.

###### otPlatRadioGetTransmitPower (heading level 7)

`otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower)`

**Description:** Get the radio's transmit power in dBm.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|int8_t *|[out]|aPower|The transmit power in dBm.|

**Note**

- The transmit power returned will be no larger than the power specified in the max power table for the current channel.

###### otPlatRadioSetTransmitPower (heading level 7)

`otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower)`

**Description:** Set the radio's transmit power in dBm for all channels.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|int8_t|[in]|aPower|The transmit power in dBm.|

**Note**

- The real transmit power will be no larger than the power specified in the max power table for the current channel that was configured by `otPlatRadioSetChannelMaxTransmitPower()`.

###### otPlatRadioGetCcaEnergyDetectThreshold (heading level 7)

`otError otPlatRadioGetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t *aThreshold)`

**Description:** Get the radio's CCA ED threshold in dBm measured at antenna connector per IEEE 802.15.4 - 2015 section 10.1.4.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|int8_t *|[out]|aThreshold|The CCA ED threshold in dBm.|

###### otPlatRadioSetCcaEnergyDetectThreshold (heading level 7)

`otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t aThreshold)`

**Description:** Set the radio's CCA ED threshold in dBm measured at antenna connector per IEEE 802.15.4 - 2015 section 10.1.4.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|int8_t|[in]|aThreshold|The CCA ED threshold in dBm.|

###### otPlatRadioGetFemLnaGain (heading level 7)

`otError otPlatRadioGetFemLnaGain(otInstance *aInstance, int8_t *aGain)`

**Description:** Gets the external FEM's Rx LNA gain in dBm.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|int8_t *|[out]|aGain|The external FEM's Rx LNA gain in dBm.|

###### otPlatRadioSetFemLnaGain (heading level 7)

`otError otPlatRadioSetFemLnaGain(otInstance *aInstance, int8_t aGain)`

**Description:** Sets the external FEM's Rx LNA gain in dBm.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|int8_t|[in]|aGain|The external FEM's Rx LNA gain in dBm.|

###### otPlatRadioGetPromiscuous (heading level 7)

`bool otPlatRadioGetPromiscuous(otInstance *aInstance)`

**Description:** Get the status of promiscuous mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

###### otPlatRadioSetPromiscuous (heading level 7)

`void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)`

**Description:** Enable or disable promiscuous mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|bool|[in]|aEnable|TRUE to enable or FALSE to disable promiscuous mode.|

###### otPlatRadioSetRxOnWhenIdle (heading level 7)

`void otPlatRadioSetRxOnWhenIdle(otInstance *aInstance, bool aEnable)`

**Description:** Sets the rx-on-when-idle state to the radio platform.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|bool|[in]|aEnable|TRUE to keep radio in Receive state, FALSE to put to Sleep state during idle periods.|

There are a few situations that the radio can enter sleep state if the device is in rx-off-when-idle state but it's hard and costly for the SubMac to identify these situations and instruct the radio to enter sleep:

- Finalization of a regular frame reception task, provided that:  
  - The frame is received without errors and passes the filtering and it's not an spurious ACK.  
  - ACK is not requested or transmission of ACK is not possible due to internal conditions.
- Finalization of a frame transmission or transmission of an ACK frame, when ACK is not requested in the transmitted frame.
- Finalization of the reception operation of a requested ACK due to:  
  - ACK timeout expiration.  
  - Reception of an invalid ACK or not an ACK frame.  
  - Reception of the proper ACK, unless the transmitted frame was a Data Request Command and the frame pending bit on the received ACK is set to true. In this case the radio platform implementation SHOULD keep the receiver on until a determined timeout which triggers an idle period start.`OPENTHREAD_CONFIG_MAC_DATA_POLL_TIMEOUT` can be taken as a reference for this.
- Finalization of a stand alone CCA task.
- Finalization of a CCA operation with busy result during CSMA/CA procedure.
- Finalization of an Energy Detection task.
- Finalization of a radio reception window scheduled with `otPlatRadioReceiveAt`.

If a platform supports `OT_RADIO_CAPS_RX_ON_WHEN_IDLE` it must also support `OT_RADIO_CAPS_CSMA_BACKOFF` and handle idle periods after CCA as described above.

Upon the transition of the "RxOnWhenIdle" flag from TRUE to FALSE, the radio platform should enter sleep mode. If the radio is currently in receive mode, it should enter sleep mode immediately. Otherwise, it should enter sleep mode after the current operation is completed.

###### otPlatRadioSetMacKey (heading level 7)

`void otPlatRadioSetMacKey(otInstance *aInstance, uint8_t aKeyIdMode, uint8_t aKeyId, const otMacKeyMaterial *aPrevKey, const otMacKeyMaterial *aCurrKey, const otMacKeyMaterial *aNextKey, otRadioKeyType aKeyType)`

**Description:** Update MAC keys and key index.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint8_t|[in]|aKeyIdMode|The key ID mode.|
|uint8_t|[in]|aKeyId|Current MAC key index.|
|const [otMacKeyMaterial](ot-mac-key-material) *|[in]|aPrevKey|A pointer to the previous MAC key.|
|const [otMacKeyMaterial](ot-mac-key-material) *|[in]|aCurrKey|A pointer to the current MAC key.|
|const [otMacKeyMaterial](ot-mac-key-material) *|[in]|aNextKey|A pointer to the next MAC key.|
|[otRadioKeyType](radio-types#ot-radio-key-type)|[in]|aKeyType|Key Type used.|

Is used when radio provides OT_RADIO_CAPS_TRANSMIT_SEC capability.

The radio platform should reset the current security MAC frame counter tracked by the radio on this call. While this is highly recommended, the OpenThread stack, as a safeguard, will also reset the frame counter using the `otPlatRadioSetMacFrameCounter()` before calling this API.

###### otPlatRadioSetMacFrameCounter (heading level 7)

`void otPlatRadioSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCounter)`

**Description:** Sets the current MAC frame counter value.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aMacFrameCounter|The MAC frame counter value.|

Is used when radio provides `OT_RADIO_CAPS_TRANSMIT_SEC` capability.

###### otPlatRadioSetMacFrameCounterIfLarger (heading level 7)

`void otPlatRadioSetMacFrameCounterIfLarger(otInstance *aInstance, uint32_t aMacFrameCounter)`

**Description:** Sets the current MAC frame counter value only if the new given value is larger than the current value.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|
|uint32_t|[in]|aMacFrameCounter|The MAC frame counter value.|

Is used when radio provides `OT_RADIO_CAPS_TRANSMIT_SEC` capability.

###### otPlatRadioGetNow (heading level 7)

`uint64_t otPlatRadioGetNow(otInstance *aInstance)`

**Description:** Get the current time in microseconds referenced to a continuous monotonic local radio clock (64 bits width).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

The radio clock SHALL NOT wrap during the device's uptime. Implementations SHALL therefore identify and compensate for internal counter overflows. The clock does not have a defined epoch and it SHALL NOT introduce any continuous or discontinuous adjustments (e.g. leap seconds). Implementations SHALL compensate for any sleep times of the device.

Implementations MAY choose to discipline the radio clock and compensate for sleep times by any means (e.g. by combining a high precision/low power RTC with a high resolution counter) as long as the exposed combined clock provides continuous monotonic microsecond resolution ticks within the accuracy limits announced by [otPlatRadioGetCslAccuracy](radio-operation#ot-plat-radio-get-csl-accuracy).

**Returns**

- The current time in microseconds. UINT64_MAX when platform does not support or radio time is not ready.

###### otPlatRadioGetBusSpeed (heading level 7)

`uint32_t otPlatRadioGetBusSpeed(otInstance *aInstance)`

**Description:** Get the bus speed in bits/second between the host and the radio chip.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The bus speed in bits/second between the host and the radio chip. Return 0 when the MAC and above layer and Radio layer resides on the same chip.

###### otPlatRadioGetBusLatency (heading level 7)

`uint32_t otPlatRadioGetBusLatency(otInstance *aInstance)`

**Description:** Get the bus latency in microseconds between the host and the radio chip.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

**Returns**

- The bus latency in microseconds between the host and the radio chip. Return 0 when the MAC and above layer and Radio layer resides on the same chip.

##### Radio Operation

This module includes the platform abstraction for radio operations. 

###### Functions

###### otPlatRadioGetState (heading level 7)

`otRadioState otPlatRadioGetState(otInstance *aInstance)`

**Description:** Get current state of the radio.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

Is not required by OpenThread. It may be used for debugging and/or application-specific purposes.

**Note**

- This function may be not implemented. It does not affect OpenThread.

**Returns**

- Current state of the radio.

###### otPlatRadioEnable (heading level 7)

`otError otPlatRadioEnable(otInstance *aInstance)`

**Description:** Enable the radio.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

###### otPlatRadioDisable (heading level 7)

`otError otPlatRadioDisable(otInstance *aInstance)`

**Description:** Disable the radio.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

###### otPlatRadioIsEnabled (heading level 7)

`bool otPlatRadioIsEnabled(otInstance *aInstance)`

**Description:** Check whether radio is enabled or not.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

**Returns**

- TRUE if the radio is enabled, FALSE otherwise.

###### otPlatRadioSleep (heading level 7)

`otError otPlatRadioSleep(otInstance *aInstance)`

**Description:** Transition the radio from Receive to Sleep (turn off the radio).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

###### otPlatRadioReceive (heading level 7)

`otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)`

**Description:** Transition the radio from Sleep to Receive (turn on the radio).

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint8_t|[in]|aChannel|The channel to use for receiving.|

###### otPlatRadioReceiveAt (heading level 7)

`otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, uint32_t aStart, uint32_t aDuration)`

**Description:** Schedule a radio reception window at a specific time and duration.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The radio channel on which to receive.|
|uint8_t|[in]|aChannel|The receive window start time relative to the local radio clock, see `otPlatRadioGetNow`. The radio receiver SHALL be on and ready to receive the first symbol of a frame's SHR at the window start time.|
|uint32_t|[in]|aStart|The receive window duration, in microseconds, as measured by the local radio clock. The radio SHOULD be turned off (or switched to TX mode if an ACK frame needs to be sent) after that duration unless it is still actively receiving a frame. In the latter case the radio SHALL be kept in reception mode until frame reception has either succeeded or failed.|
|uint32_t|N/A|aDuration||

After a radio reception is successfully scheduled for a future time and duration, a subsequent call to this function MUST be handled as follows:

- If the start time of the previously scheduled reception window has not yet been reached, the new call to `otPlatRadioReceiveAt()` MUST cancel the previous schedule, effectively replacing it.
- If the start of the previous window has already passed, the previous receive schedule is already being executed by the radio and MUST NOT be replaced or impacted. The new call to `otPlatRadioReceiveAt()` would then schedule a new future receive window. In particular, if the new `otPlatRadioReceiveAt()` call occurs after the start but while still within the previous reception window, the ongoing reception window MUST NOT be impacted.

###### otPlatRadioReceiveDone (heading level 7)

`void otPlatRadioReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError)`

**Description:** The radio driver calls this function to notify OpenThread of a received frame.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otRadioFrame](ot-radio-frame) *|[in]|aFrame|A pointer to the received frame or NULL if the receive operation failed.|
|[otError](api-error#ot-error)|[in]|aError|OT_ERROR_NONE when successfully received a frame, OT_ERROR_ABORT when reception was aborted and a frame was not received, OT_ERROR_NO_BUFS when a frame could not be received due to lack of rx buffer space.|

###### otPlatDiagRadioReceiveDone (heading level 7)

`void otPlatDiagRadioReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError)`

**Description:** The radio driver calls this function to notify OpenThread diagnostics module of a received frame.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otRadioFrame](ot-radio-frame) *|[in]|aFrame|A pointer to the received frame or NULL if the receive operation failed.|
|[otError](api-error#ot-error)|[in]|aError|OT_ERROR_NONE when successfully received a frame, OT_ERROR_ABORT when reception was aborted and a frame was not received, OT_ERROR_NO_BUFS when a frame could not be received due to lack of rx buffer space.|

**Note**

- This function is deprecated and will be removed in the future. It is recommended to use the function `otPlatRadioReceiveDone()`.

Is used when diagnostics is enabled.

###### otPlatRadioGetTransmitBuffer (heading level 7)

`otRadioFrame * otPlatRadioGetTransmitBuffer(otInstance *aInstance)`

**Description:** Get the radio transmit frame buffer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

OpenThread forms the IEEE 802.15.4 frame in this buffer then calls `otPlatRadioTransmit()` to request transmission.

**Returns**

- A pointer to the transmit frame buffer.

###### otPlatRadioTransmit (heading level 7)

`otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)`

**Description:** Begin the transmit sequence on the radio.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otRadioFrame](ot-radio-frame) *|[in]|aFrame|A pointer to the frame to be transmitted.|

The caller must form the IEEE 802.15.4 frame in the buffer provided by `otPlatRadioGetTransmitBuffer()` before requesting transmission. The channel and transmit power are also included in the [otRadioFrame](ot-radio-frame) structure.

The transmit sequence consists of:

1. Transitioning the radio to Transmit from one of the following states:  
   - Receive if RX is on when the device is idle or OT_RADIO_CAPS_SLEEP_TO_TX is not supported  
   - Sleep if RX is off when the device is idle and OT_RADIO_CAPS_SLEEP_TO_TX is supported.
2. Transmits the psdu on the given channel and at the given transmit power.

###### otPlatRadioTxStarted (heading level 7)

`void otPlatRadioTxStarted(otInstance *aInstance, otRadioFrame *aFrame)`

**Description:** The radio driver calls this function to notify OpenThread that the transmission has started.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to the OpenThread instance structure.|
|[otRadioFrame](ot-radio-frame) *|[in]|aFrame|A pointer to the frame that is being transmitted.|

**Note**

- This function should be called by the same thread that executes all of the other OpenThread code. It should not be called by ISR or any other task.

###### otPlatRadioTxDone (heading level 7)

`void otPlatRadioTxDone(otInstance *aInstance, otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError)`

**Description:** The radio driver calls this function to notify OpenThread that the transmit operation has completed, providing both the transmitted frame and, if applicable, the received ack frame.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otRadioFrame](ot-radio-frame) *|[in]|aFrame|A pointer to the frame that was transmitted.|
|[otRadioFrame](ot-radio-frame) *|[in]|aAckFrame|A pointer to the ACK frame, NULL if no ACK was received.|
|[otError](api-error#ot-error)|[in]|aError|OT_ERROR_NONE when the frame was transmitted, OT_ERROR_NO_ACK when the frame was transmitted but no ACK was received, OT_ERROR_CHANNEL_ACCESS_FAILURE tx could not take place due to activity on the channel, OT_ERROR_ABORT when transmission was aborted for other reasons.|

When radio provides `OT_RADIO_CAPS_TRANSMIT_SEC` capability, radio platform layer updates `aFrame` with the security frame counter and key index values maintained by the radio.

###### otPlatDiagRadioTransmitDone (heading level 7)

`void otPlatDiagRadioTransmitDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError)`

**Description:** The radio driver calls this function to notify OpenThread diagnostics module that the transmission has completed.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otRadioFrame](ot-radio-frame) *|[in]|aFrame|A pointer to the frame that was transmitted.|
|[otError](api-error#ot-error)|[in]|aError|OT_ERROR_NONE when the frame was transmitted, OT_ERROR_CHANNEL_ACCESS_FAILURE tx could not take place due to activity on the channel, OT_ERROR_ABORT when transmission was aborted for other reasons.|

**Note**

- This function is deprecated and will be removed in the future. It is recommended to use the function `otPlatRadioTxDone()`.

Is used when diagnostics is enabled.

###### otPlatRadioGetRssi (heading level 7)

`int8_t otPlatRadioGetRssi(otInstance *aInstance)`

**Description:** Return a recent RSSI measurement when the radio is in receive state.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

If the radio is not in receive state, then `OT_RADIO_RSSI_INVALID` MUST be returned. If the radio is in receive state, then a single RSSI measurement is taken on the current receive channel and returned.

**Returns**

- The RSSI in dBm when it is valid. `OT_RADIO_RSSI_INVALID` when RSSI is invalid.

###### otPlatRadioEnergyScan (heading level 7)

`otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration)`

**Description:** Begin the energy scan sequence on the radio.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint8_t|[in]|aScanChannel|The channel to perform the energy scan on.|
|uint16_t|[in]|aScanDuration|The duration, in milliseconds, for the channel to be scanned.|

Is used when radio provides OT_RADIO_CAPS_ENERGY_SCAN capability.

###### otPlatRadioEnergyScanDone (heading level 7)

`void otPlatRadioEnergyScanDone(otInstance *aInstance, int8_t aEnergyScanMaxRssi)`

**Description:** The radio driver calls this function to notify OpenThread that the energy scan is complete.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|int8_t|[in]|aEnergyScanMaxRssi|The maximum RSSI encountered on the scanned channel.|

Is used when radio provides OT_RADIO_CAPS_ENERGY_SCAN capability.

###### otPlatRadioBusLatencyChanged (heading level 7)

`void otPlatRadioBusLatencyChanged(otInstance *aInstance)`

**Description:** The radio driver calls this function to notify OpenThread that the spinel bus latency has been changed.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

###### otPlatRadioEnableSrcMatch (heading level 7)

`void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable)`

**Description:** Enable/Disable source address match feature.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|bool|[in]|aEnable|Enable/disable source address match feature.|

The source address match feature controls how the radio layer decides the "frame pending" bit for acks sent in response to data request commands from children.

If disabled, the radio layer must set the "frame pending" on all acks to data request commands.

If enabled, the radio layer uses the source address match table to determine whether to set or clear the "frame
pending" bit in an ack to a data request command.

The source address match table provides the list of children for which there is a pending frame. Either a short address or an extended/long address can be added to the source address match table.

###### otPlatRadioAddSrcMatchShortEntry (heading level 7)

`otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, otShortAddress aShortAddress)`

**Description:** Add a short address to the source address match table.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otShortAddress](radio-types#ot-short-address)|[in]|aShortAddress|The short address to be added.|

###### otPlatRadioAddSrcMatchExtEntry (heading level 7)

`otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress)`

**Description:** Add an extended address to the source address match table.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|const [otExtAddress](ot-ext-address) *|[in]|aExtAddress|The extended address to be added stored in little-endian byte order.|

###### otPlatRadioClearSrcMatchShortEntry (heading level 7)

`otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, otShortAddress aShortAddress)`

**Description:** Remove a short address from the source address match table.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otShortAddress](radio-types#ot-short-address)|[in]|aShortAddress|The short address to be removed.|

###### otPlatRadioClearSrcMatchExtEntry (heading level 7)

`otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress)`

**Description:** Remove an extended address from the source address match table.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|const [otExtAddress](ot-ext-address) *|[in]|aExtAddress|The extended address to be removed stored in little-endian byte order.|

###### otPlatRadioClearSrcMatchShortEntries (heading level 7)

`void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance)`

**Description:** Clear all short addresses from the source address match table.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

###### otPlatRadioClearSrcMatchExtEntries (heading level 7)

`void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance)`

**Description:** Clear all the extended/long addresses from source address match table.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

###### otPlatRadioGetSupportedChannelMask (heading level 7)

`uint32_t otPlatRadioGetSupportedChannelMask(otInstance *aInstance)`

**Description:** Get the radio supported channel mask that the device is allowed to be on.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

**Returns**

- The radio supported channel mask.

###### otPlatRadioGetPreferredChannelMask (heading level 7)

`uint32_t otPlatRadioGetPreferredChannelMask(otInstance *aInstance)`

**Description:** Gets the radio preferred channel mask that the device prefers to form on.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

**Returns**

- The radio preferred channel mask.

###### otPlatRadioSetCoexEnabled (heading level 7)

`otError otPlatRadioSetCoexEnabled(otInstance *aInstance, bool aEnabled)`

**Description:** Enable the radio coex.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|bool|[in]|aEnabled|TRUE to enable the radio coex, FALSE otherwise.|

Is used when feature OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE is enabled.

###### otPlatRadioIsCoexEnabled (heading level 7)

`bool otPlatRadioIsCoexEnabled(otInstance *aInstance)`

**Description:** Check whether radio coex is enabled or not.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

Is used when feature OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE is enabled.

**Returns**

- TRUE if the radio coex is enabled, FALSE otherwise.

###### otPlatRadioGetCoexMetrics (heading level 7)

`otError otPlatRadioGetCoexMetrics(otInstance *aInstance, otRadioCoexMetrics *aCoexMetrics)`

**Description:** Get the radio coexistence metrics.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otRadioCoexMetrics](ot-radio-coex-metrics) *|[out]|aCoexMetrics|A pointer to the coexistence metrics structure.|

Is used when feature OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE is enabled.

###### otPlatRadioEnableCsl (heading level 7)

`otError otPlatRadioEnableCsl(otInstance *aInstance, uint32_t aCslPeriod, otShortAddress aShortAddr, const otExtAddress *aExtAddr)`

**Description:** Enable or disable CSL receiver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint32_t|[in]|aCslPeriod|CSL period, 0 for disabling CSL. CSL period is in unit of 10 symbols.|
|[otShortAddress](radio-types#ot-short-address)|[in]|aShortAddr|The short source address of CSL receiver's peer.|
|const [otExtAddress](ot-ext-address) *|[in]|aExtAddr|The extended source address of CSL receiver's peer. The `aExtAddr` assumes big-endian byte order.|

Regarding `aExtAddr`, this function assumes big-endian byte order. Note that this differs from `otPlatRadioSetExtendedAddress()`, `otPlatRadioAddSrcMatchExtEntry()`, and `otPlatRadioClearSrcMatchExtEntry()`, which use little-endian byte order for the Extended MAC address.

**Note**

- Platforms should use CSL peer addresses to include CSL IE when generating enhanced acks.

###### otPlatRadioResetCsl (heading level 7)

`otError otPlatRadioResetCsl(otInstance *aInstance)`

**Description:** Reset CSL receiver in the platform.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

**Note**

- Defaults to `otPlatRadioEnableCsl(aInstance,0, Mac::kShortAddrInvalid, NULL);`

###### otPlatRadioUpdateCslSampleTime (heading level 7)

`void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime)`

**Description:** Update CSL sample time in radio driver.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint32_t|[in]|aCslSampleTime|The next sample time, in microseconds. It is the time when the first symbol of the MHR of the frame is expected.|

Sample time is stored in radio driver as a copy to calculate phase when sending ACK with CSL IE. The CSL sample (window) of the CSL receiver extends before and after the sample time. The CSL sample time marks a timestamp in the CSL sample window when a frame should be received in "ideal conditions" if there would be no inaccuracy/clock-drift.

###### otPlatRadioGetCslAccuracy (heading level 7)

`uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance)`

**Description:** Get the current estimated worst case accuracy (maximum ± deviation from the nominal frequency) of the local radio clock in units of PPM.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

This is the clock used to schedule CSL operations.

**Note**

- Implementations MAY estimate this value based on current operating conditions (e.g. temperature).

In case the implementation does not estimate the current value but returns a fixed value, this value MUST be the worst-case accuracy over all possible foreseen operating conditions (temperature, pressure, etc) of the implementation.

**Returns**

- The current CSL rx/tx scheduling drift, in PPM.

###### otPlatRadioGetCslUncertainty (heading level 7)

`uint8_t otPlatRadioGetCslUncertainty(otInstance *aInstance)`

**Description:** The fixed uncertainty (i.e.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|A pointer to an OpenThread instance.|

random jitter) of the arrival time of CSL transmissions received by this device in units of 10 microseconds.

This designates the worst case constant positive or negative deviation of the actual arrival time of a transmission from the transmission time calculated relative to the local radio clock independent of elapsed time. In addition to uncertainty accumulated over elapsed time, the CSL channel sample ("RX window") must be extended by twice this deviation such that an actual transmission is guaranteed to be detected by the local receiver in the presence of random arrival time jitter.

**Returns**

- The CSL Uncertainty in units of 10 us.

###### otPlatRadioSetChannelMaxTransmitPower (heading level 7)

`otError otPlatRadioSetChannelMaxTransmitPower(otInstance *aInstance, uint8_t aChannel, int8_t aMaxPower)`

**Description:** Set the max transmit power for a specific channel.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint8_t|[in]|aChannel|The radio channel.|
|int8_t|[in]|aMaxPower|The max power in dBm, passing OT_RADIO_RSSI_INVALID will disable this channel.|

**Note**

- This function will be deprecated in October 2027. It is recommended to use the function `otPlatRadioSetChannelTargetPower()`.

###### otPlatRadioSetRegion (heading level 7)

`otError otPlatRadioSetRegion(otInstance *aInstance, uint16_t aRegionCode)`

**Description:** Set the region code.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint16_t|[in]|aRegionCode|The radio region code. The `aRegionCode >> 8` is first ascii char and the `aRegionCode & 0xff` is the second ascii char.|

The radio region format is the 2-bytes ascii representation of the ISO 3166 alpha-2 code.

###### otPlatRadioGetRegion (heading level 7)

`otError otPlatRadioGetRegion(otInstance *aInstance, uint16_t *aRegionCode)`

**Description:** Get the region code.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint16_t *|[out]|aRegionCode|The radio region.|

The radio region format is the 2-bytes ascii representation of the ISO 3166 alpha-2 code.

###### otPlatRadioConfigureEnhAckProbing (heading level 7)

`otError otPlatRadioConfigureEnhAckProbing(otInstance *aInstance, otLinkMetrics aLinkMetrics, otShortAddress aShortAddress, const otExtAddress *aExtAddress)`

**Description:** Enable/disable or update Enhanced-ACK Based Probing in radio for a specific Initiator.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|[otLinkMetrics](ot-link-metrics)|[in]|aLinkMetrics|This parameter specifies what metrics to query. Per spec 4.11.3.4.4.6, at most 2 metrics can be specified. The probing would be disabled if ``aLinkMetrics`` is bitwise 0.|
|[otShortAddress](radio-types#ot-short-address)|[in]|aShortAddress|The short address of the Probing Initiator.|
|const [otExtAddress](ot-ext-address) *|[in]|aExtAddress|The extended source address of the Probing Initiator. `aExtAddress` MUST NOT be `NULL`. The `aExtAddress` assumes big-endian byte order.|

After Enhanced-ACK Based Probing is configured by a specific Probing Initiator, the Enhanced-ACK sent to that node should include Vendor-Specific IE containing Link Metrics data. This function informs the radio to start/stop to collect Link Metrics data and include Vendor-Specific IE that containing the data in Enhanced-ACK sent to that Probing Initiator.

Regarding `aExtAddress`, this function assumes big-endian byte order. Note that this differs from `otPlatRadioSetExtendedAddress()`, `otPlatRadioAddSrcMatchExtEntry()`, and `otPlatRadioClearSrcMatchExtEntry()`, which use little-endian byte order for the Extended MAC address.

###### otPlatRadioAddCalibratedPower (heading level 7)

`otError otPlatRadioAddCalibratedPower(otInstance *aInstance, uint8_t aChannel, int16_t aActualPower, const uint8_t *aRawPowerSetting, uint16_t aRawPowerSettingLength)`

**Description:** Add a calibrated power of the specified channel to the power calibration table.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint8_t|[in]|aChannel|The radio channel.|
|int16_t|[in]|aActualPower|The actual power in 0.01dBm.|
|const uint8_t *|[in]|aRawPowerSetting|A pointer to the raw power setting byte array.|
|uint16_t|[in]|aRawPowerSettingLength|The length of the `aRawPowerSetting`.|

**Note**

- This API is an optional radio platform API. It's up to the platform layer to implement it.

The `aActualPower` is the actual measured output power when the parameters of the radio hardware modules are set to the `aRawPowerSetting`.

The raw power setting is an opaque byte array. OpenThread doesn't define the format of the raw power setting. Its format is radio hardware related and it should be defined by the developers in the platform radio driver. For example, if the radio hardware contains both the radio chip and the FEM chip, the raw power setting can be a combination of the radio power register and the FEM gain value.

###### otPlatRadioClearCalibratedPowers (heading level 7)

`otError otPlatRadioClearCalibratedPowers(otInstance *aInstance)`

**Description:** Clear all calibrated powers from the power calibration table.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

**Note**

- This API is an optional radio platform API. It's up to the platform layer to implement it.

###### otPlatRadioSetChannelTargetPower (heading level 7)

`otError otPlatRadioSetChannelTargetPower(otInstance *aInstance, uint8_t aChannel, int16_t aTargetPower)`

**Description:** Set the target power for the given channel.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint8_t|[in]|aChannel|The radio channel.|
|int16_t|[in]|aTargetPower|The target power in 0.01dBm.|

**Note**

- This API is an optional radio platform API. It's up to the platform layer to implement it. If this function and `otPlatRadioSetTransmitPower()` are implemented at the same time:  
  - If neither of these two functions is called, the radio outputs the platform-defined default power.  
  - If both functions are called, the last one to be called takes effect.

The radio driver should set the actual output power to be less than or equal to the `aTargetPower` and as close as possible to the `aTargetPower`. If the `aTargetPower` is lower than the minimum output power supported by the platform, the output power should be set to the minimum output power supported by the platform. If the `aTargetPower` is higher than the maximum output power supported by the platform, the output power should be set to the maximum output power supported by the platform. If the `aTargetPower` is set to `INT16_MAX`, the corresponding channel is disabled.

###### otPlatRadioGetRawPowerSetting (heading level 7)

`otError otPlatRadioGetRawPowerSetting(otInstance *aInstance, uint8_t aChannel, uint8_t *aRawPowerSetting, uint16_t *aRawPowerSettingLength)`

**Description:** Get the raw power setting for the given channel.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint8_t|[in]|aChannel|The radio channel.|
|uint8_t *|[out]|aRawPowerSetting|A pointer to the raw power setting byte array.|
|uint16_t *|[inout]|aRawPowerSettingLength|On input, a pointer to the size of `aRawPowerSetting`. On output, a pointer to the length of the raw power setting data.|

**Note**

- OpenThread `src/core/utils` implements a default implementation of the API `otPlatRadioAddCalibratedPower()`, `otPlatRadioClearCalibratedPowers()` and `otPlatRadioSetChannelTargetPower()`. This API is provided by the default implementation to get the raw power setting for the given channel. If the platform doesn't use the default implementation, it can ignore this API.

Platform radio layer should parse the raw power setting based on the radio layer defined format and set the parameters of each radio hardware module.

##### Radio Extension

This module includes the Silicon Labs extension to the openthread platform radio interface. 

The functions in this modules provide an API that can be called from SoC or host based openthread applications.

**Note**

- Many of the functions defined in this module are wrappers on top of the Silicon Labs RAIL API. For additional information on the RAIl API please refer to the `Silicon Labs RAIL API Reference Guide`. Those functions that are wrappers to RAIL functions include a reference to the underlying RAIL function.

###### Enumerations

###### otPlatRadioExtensionCoexEvent_t (heading level 7)

```
enum otPlatRadioExtensionCoexEvent_t {
    OT_PLAT_RADIO_EXTENSION_COEX_EVENT_LO_PRI_REQUESTED
    OT_PLAT_RADIO_EXTENSION_COEX_EVENT_HI_PRI_REQUESTED
    OT_PLAT_RADIO_EXTENSION_COEX_EVENT_LO_PRI_DENIED
    OT_PLAT_RADIO_EXTENSION_COEX_EVENT_HI_PRI_DENIED
    OT_PLAT_RADIO_EXTENSION_COEX_EVENT_LO_PRI_TX_ABORTED
    OT_PLAT_RADIO_EXTENSION_COEX_EVENT_HI_PRI_TX_ABORTED
    OT_PLAT_RADIO_EXTENSION_COEX_EVENT_COUNT
}
```

**Description:**

This enumeration defines the coex event counters and can be used as an index into the `aCoexCounters` table returned in a call to [otPlatRadioExtensionGetCoexCounters](radio-extension#ot-plat-radio-extension-get-coex-counters).

**Enumerator:**

|   |   |
|---|---|
|OT_PLAT_RADIO_EXTENSION_COEX_EVENT_LO_PRI_REQUESTED|Low priority request initiated.|
|OT_PLAT_RADIO_EXTENSION_COEX_EVENT_HI_PRI_REQUESTED|High priority request initiated.|
|OT_PLAT_RADIO_EXTENSION_COEX_EVENT_LO_PRI_DENIED|Low priority request denied.|
|OT_PLAT_RADIO_EXTENSION_COEX_EVENT_HI_PRI_DENIED|High priority request denied.|
|OT_PLAT_RADIO_EXTENSION_COEX_EVENT_LO_PRI_TX_ABORTED|Low priority transmission aborted mid packet.|
|OT_PLAT_RADIO_EXTENSION_COEX_EVENT_HI_PRI_TX_ABORTED|High priority transmission aborted mid packet.|
|OT_PLAT_RADIO_EXTENSION_COEX_EVENT_COUNT|Number of coexistence events.|

###### Functions

###### otPlatRadioExtensionGetTxAntennaMode (heading level 7)

`otError otPlatRadioExtensionGetTxAntennaMode(uint8_t *aMode)`

**Description:** Get the antenna diversity transmit antenna mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t *|[out]|aMode|A pointer to the location where the current transmit antenna mode will be returned. Antenna modes are defined by the RAIL `sl_rail_util_antenna_mode_t` enumeration.|

Requires the `ot_ant_div` component.

**See Also**

- RAIL API: **sl_rail_util_ant_div_get_tx_antenna_mode()**

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully obtained the transmit antenna mode.
- OT_ERROR_INVALID_ARGS: The `aMode` parameter is NULL.
- OT_ERROR_NOT_IMPLEMENTED: Antenna diversity is not implemented.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionSetTxAntennaMode (heading level 7)

`otError otPlatRadioExtensionSetTxAntennaMode(uint8_t aMode)`

**Description:** Set the antenna diversity transmit antenna mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t|[in]|aMode|The antenna mode to use for transmit. Antenna modes are defined by the RAIL `sl_rail_util_antenna_mode_t` enumeration.|

Requires the `ot_ant_div` component.

**See Also**

- RAIL API: **sl_rail_util_ant_div_set_tx_antenna_mode()**

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully set the transmit antenna mode.
- OT_ERROR_FAILED: The specified transmit antenna mode is not supported.
- OT_ERROR_NOT_IMPLEMENTED: Antenna diversity is not implemented.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionGetRxAntennaMode (heading level 7)

`otError otPlatRadioExtensionGetRxAntennaMode(uint8_t *aMode)`

**Description:** Get the antenna diversity receive antenna mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t *|[out]|aMode|A pointer to the location where the current receive antenna mode will be returned. Antenna modes are defined by the RAIL `sl_rail_util_antenna_mode_t` enumeration.|

Requires the `ot_ant_div` component.

**See Also**

- RAIL API: **sl_rail_util_ant_div_get_rx_antenna_mode()**

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully obtained the receive antenna mode.
- OT_ERROR_INVALID_ARGS: The `aMode` parameter is NULL.
- OT_ERROR_NOT_IMPLEMENTED: Antenna diversity is not implemented.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionSetRxAntennaMode (heading level 7)

`otError otPlatRadioExtensionSetRxAntennaMode(uint8_t aMode)`

**Description:** Set the antenna diversity receive antenna mode.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t|[in]|aMode|The antenna mode to use for receive. Antenna modes are defined by the RAIL `sl_rail_util_antenna_mode_t` enumeration.|

Requires the `ot_ant_div` component.

**See Also**

- RAIL API: **sl_rail_util_ant_div_set_rx_antenna_mode()**

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully set the receive antenna mode.
- OT_ERROR_FAILED: The specified receive antenna mode is not supported.
- OT_ERROR_NOT_IMPLEMENTED: Antenna diversity is not implemented.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionGetActivePhy (heading level 7)

`otError otPlatRadioExtensionGetActivePhy(uint8_t *aActivePhy)`

**Description:** Get the antenna diversity active phy state.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t *|[out]|aActivePhy|A pointer to the location where the current phy state will be returned. Phy states are defined by the RAIL `sl_rail_ieee802154_phy_t` enumeration.|

Requires the `ot_ant_div` component.

**See Also**

- RAIL API: **sl_rail_util_ieee802154_get_active_radio_config()**

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully obtained the phy state.
- OT_ERROR_INVALID_ARGS: The `aActivePhy` parameter is NULL.
- OT_ERROR_NOT_IMPLEMENTED: Antenna diversity is not implemented.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionGetDpState (heading level 7)

`otError otPlatRadioExtensionGetDpState(uint8_t *aDpPulse)`

**Description:** Get the coexistence directional priority state and pulse width.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t *|[out]|aDpPulse|A pointer to the location where the current directional priority state will be returned. If `aDpPulse` is 0 then directional priority is disabled. If `aDpPulse` is not 0 then directional priority is enabled and the value is the pulse width in microseconds.|

Requires the `ot_coex` component.

**See Also**

- RAIL API: **sl_rail_util_coex_get_directional_priority_pulse_width()**

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully obtained the directional priority.
- OT_ERROR_INVALID_ARGS: The `aDpPulse` parameter is NULL.
- OT_ERROR_NOT_IMPLEMENTED: Coexistence is not implemented.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionSetDpState (heading level 7)

`otError otPlatRadioExtensionSetDpState(uint8_t aDpPulse)`

**Description:** Set the coexistence directional priority state and pulse width.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t|[in]|aDpPulse|The directional priority state to set. If `aDpPulse` is 0 then directional priority will be disabled. If `aDpPulse` is not 0 then directional priority will be enabled and the value will be the pulse width to use in microseconds.|

Requires the `ot_coex` component.

**See Also**

- RAIL API: **sl_rail_util_coex_set_directional_priority_pulse_width()**

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully set the directional priority.
- OT_ERROR_FAILED: The `aDpPulse` is invalid.
- OT_ERROR_NOT_IMPLEMENTED: Coexistence is not implemented.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionGetGpioInputOverride (heading level 7)

`otError otPlatRadioExtensionGetGpioInputOverride(uint8_t aGpioIndex, bool *aEnabled)`

**Description:** Get the override input value of a GPIO.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t|[in]|aGpioIndex|The GPIO index<br/><br/>- 0x00 = Radio Holdoff GPIO index<br/>- 0x01 = Request GPIO index<br/>- 0x02 = Grant GPIO index<br/>- 0x03 = PHY Select index|
|bool *|[out]|aEnabled|A pointer to the location where the boolean override input value will be returned. A TRUE value indicating the override input value is enabled, FALSE disabled. The return is inverted if the selected GPIO is active low.|

Requires the `ot_coex` component.

**See Also**

- RAIL API: **sl_rail_util_coex_get_gpio_input_override()**

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully obtained the override input value.
- OT_ERROR_INVALID_ARGS: The `aGpioIndex` parameter is invalid and/or `aDpPulse` parameter is NULL.
- OT_ERROR_NOT_IMPLEMENTED: Coexistence is not implemented.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionSetGpioInputOverride (heading level 7)

`otError otPlatRadioExtensionSetGpioInputOverride(uint8_t aGpioIndex, bool aEnabled)`

**Description:** Set the override input value of a GPIO.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t|[in]|aGpioIndex|The GPIO index<br/><br/>- 0x00 = Radio Holdoff GPIO index<br/>- 0x01 = Request GPIO index<br/>- 0x02 = Grant GPIO index<br/>- 0x03 = PHY Select index|
|bool|[in]|aEnabled|The boolean override input value. A TRUE value indicating the override input value is enabled, FALSE disabled.|

Requires the `ot_coex` component.

**See Also**

- RAIL API: **sl_rail_util_coex_set_gpio_input_override()**

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully set the override input value.
- OT_ERROR_INVALID_ARGS: The `aGpioIndex` parameter is invalid.
- OT_ERROR_FAILED: The override input value of the specified GPIO can not be set.
- OT_ERROR_NOT_IMPLEMENTED: Coexistence is not implemented.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionGetActiveRadio (heading level 7)

`otError otPlatRadioExtensionGetActiveRadio(uint8_t *aActivePhy)`

**Description:** Get the coexistence active phy state.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t *|[out]|aActivePhy|A pointer to the location where the current phy state will be returned. Phy states are defined by the RAIL `sl_rail_ieee802154_phy_t` enumeration.|

Requires the `ot_coex` component.

**See Also**

- RAIL API: **sl_rail_util_ieee802154_get_active_radio_config()**

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully obtained the phy state.
- OT_ERROR_INVALID_ARGS: The `aActivePhy` parameter is NULL.
- OT_ERROR_NOT_IMPLEMENTED: Coexistence is not implemented.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionGetPhySelectTimeout (heading level 7)

`otError otPlatRadioExtensionGetPhySelectTimeout(uint8_t *aTimeout)`

**Description:** Get the coexistence phy select state and timeout.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t *|[out]|aTimeout|A pointer to the location where the current phy select state will be returned. If `aTimeout` is 0 then phy select is disabled. If `aTimeout` is not 0 then phy select is enabled and the value is the timeout in milliseconds.|

Requires the `ot_coex` component.

**See Also**

- RAIL API: **sl_rail_util_coex_get_phy_select_timeout()**

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully obtained the phy select state.
- OT_ERROR_INVALID_ARGS: The `aTimeout` parameter is NULL.
- OT_ERROR_NOT_IMPLEMENTED: Coexistence is not implemented.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionSetPhySelectTimeout (heading level 7)

`otError otPlatRadioExtensionSetPhySelectTimeout(uint8_t aTimeout)`

**Description:** Set the coexistence phy select state and timeout.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t|[in]|aTimeout|The phy select state to set. If `aTimeout` is 0 then phy select will be disabled. If `aTimeout` is not 0 then phy select will be enabled and the value will be the timeout to use in milliseconds.|

Requires the `ot_coex` component.

**See Also**

- RAIL API: **sl_rail_util_coex_set_phy_select_timeout()**

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully set the directional priority.
- OT_ERROR_FAILED: The `aTimeout` is invalid.
- OT_ERROR_NOT_IMPLEMENTED: Coexistence is not implemented.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionGetCoexOptions (heading level 7)

`otError otPlatRadioExtensionGetCoexOptions(uint32_t *aPtaOptions)`

**Description:** Get the coexistence bitmask of features.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint32_t *|[out]|aPtaOptions|A pointer to the location where the coexistence feature bitmask will be returned. The feature bitmask is defined by the set of macros making up the RAIL `sl_rail_util_coex_options_t` type.|

Requires the `ot_coex` component.

**See Also**

- RAIL API: **sl_rail_util_coex_get_options()**

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully obtained the bitmask of features.
- OT_ERROR_INVALID_ARGS: The `aPtaOptions` parameter is NULL.
- OT_ERROR_NOT_IMPLEMENTED: Coexistence is not implemented.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionSetCoexOptions (heading level 7)

`otError otPlatRadioExtensionSetCoexOptions(uint32_t aPtaOptions)`

**Description:** Set the coexistence bitmask of features.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint32_t|[in]|aPtaOptions|The coexistence feature bitmask to set. The feature bitmask is defined by the set of macros making up the RAIL `sl_rail_util_coex_options_t` type.|

Requires the `ot_coex` component.

**See Also**

- RAIL API: **sl_rail_util_coex_set_options()**

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully set the bitmask of features.
- OT_ERROR_INVALID_ARGS: The `aPtaOptions` is invalid.
- OT_ERROR_FAILED: The bitmask of features can not be set.
- OT_ERROR_NOT_IMPLEMENTED: Coexistence is not implemented.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionGetCoexConstantOptions (heading level 7)

`otError otPlatRadioExtensionGetCoexConstantOptions(uint32_t *aPtaOptions)`

**Description:** Get the coexistence bitmask of constant PTA features that can not be modified using public APIs.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint32_t *|[out]|aPtaOptions|A pointer to the location where the coexistence constant PTA feature bitmask will be returned. The feature bitmask is defined by the set of macros making up the RAIL `sl_rail_util_coex_options_t` type.|

Requires the `ot_coex` component.

**See Also**

- RAIL API: **sl_rail_util_coex_get_constant_options()**

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully obtained the bitmask of of constant PTA features.
- OT_ERROR_INVALID_ARGS: The `aPtaOptions` parameter is NULL.
- OT_ERROR_NOT_IMPLEMENTED: Coexistence is not implemented.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionIsCoexEnabled (heading level 7)

`otError otPlatRadioExtensionIsCoexEnabled(bool *aPtaState)`

**Description:** Get the coexistence enabled status.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|bool *|[out]|aPtaState|A pointer to the location where the coexistence enabled status will be returned.|

Requires the `ot_coex` component.

**See Also**

- RAIL API: **sl_rail_util_coex_is_enabled()**

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully obtained the coexistence status.
- OT_ERROR_INVALID_ARGS: The `aPtaState` parameter is NULL.
- OT_ERROR_NOT_IMPLEMENTED: Coexistence is not implemented.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionSetCoexEnable (heading level 7)

`otError otPlatRadioExtensionSetCoexEnable(bool aPtaState)`

**Description:** Set the coexistence enabled status.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|bool|[in]|aPtaState|The coexistence enabled status.|

Requires the `ot_coex` component.

**See Also**

- RAIL API: **sl_rail_util_coex_set_enable()**

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully set the coexistence status.
- OT_ERROR_FAILED: The coexistence status can not be set.
- OT_ERROR_NOT_IMPLEMENTED: Coexistence is not implemented.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionGetRequestPwmArgs (heading level 7)

`otError otPlatRadioExtensionGetRequestPwmArgs(uint8_t *aPwmReq, uint8_t *aPwmDutyCycle, uint8_t *aPwmPeriodHalfMs)`

**Description:** Get the coexistence PWM configuration.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t *|[out]|aPwmReq|A pointer to the location where the coexistence PWM request is returned. The value is defined as a bitmap using shift values from the RAIL `COEX_Req_t` enumeration.|
|uint8_t *|[out]|aPwmDutyCycle|A pointer to the location where the coexistence PWM duty cycle value is returned.|
|uint8_t *|[out]|aPwmPeriodHalfMs|A pointer to the location where the coexistence PWM period half MS value is returned.|

Requires the `ot_coex` component.

**See Also**

- RAIL API: **sl_rail_util_coex_get_request_pwm_args()**

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully obtained the PWM configuration.
- OT_ERROR_INVALID_ARGS: One or more of the parameters `aPwmReq`, `aPwmDutyCycle`, or `aPwmPeriodHalfMs` is NULL.
- OT_ERROR_NOT_IMPLEMENTED: Coexistence is not implemented.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionSetRequestPwmArgs (heading level 7)

`otError otPlatRadioExtensionSetRequestPwmArgs(uint8_t aPwmReq, uint8_t aPwmDutyCycle, uint8_t aPwmPeriodHalfMs)`

**Description:** Set the coexistence PWM configuration.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t|[in]|aPwmReq|The coexistence PWM request. The value is defined as a bitmap using shift values from the RAIL `COEX_Req_t` enumeration.|
|uint8_t|[in]|aPwmDutyCycle|The coexistence PWM duty cycle.|
|uint8_t|[in]|aPwmPeriodHalfMs|The coexistencec PWM period half MS.|

Requires the `ot_coex` component.

**See Also**

- RAIL API: **sl_rail_util_coex_set_request_pwm()**

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully set the PWM configuration.
- OT_ERROR_FAILED: The coexistence radio PWM configuration can not be set/.
- OT_ERROR_NOT_IMPLEMENTED: Coexistence is not implemented.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionClearCoexCounters (heading level 7)

`otError otPlatRadioExtensionClearCoexCounters(void)`

**Description:** Clear the coexistence counters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

Requires the `ot_coex` component.

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully cleared the coexistence counters.
- OT_ERROR_NOT_IMPLEMENTED: Coexistence is not implemented or `SL_OPENTHREAD_COEX_COUNTER_ENABLE` is not enabled.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionGetCoexCounters (heading level 7)

`otError otPlatRadioExtensionGetCoexCounters(uint8_t aNumEntries, uint32_t aCoexCounters[])`

**Description:** Get the coexistence counters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|uint8_t|[in]|aNumEntries|The number of entries in `aCoexCounters` array where counters will be returned.|
|uint32_t|[out]|aCoexCounters|A pointer to an array where the coexistence counters will be returned. See [otPlatRadioExtensionCoexEvent_t](radio-extension#ot-plat-radio-extension-coex-event-t) which defines what coexistence counter each array element stores.|

Requires the `ot_coex` component.

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully obtained the coexistence counters.
- OT_ERROR_INVALID_ARGS: `aNumEntries` is not `OT_PLAT_RADIO_EXTENSION_COEX_EVENT_COUNT` or `aCoexCounters` is NULL.
- OT_ERROR_NOT_IMPLEMENTED: Coexistence is not implemented or `SL_OPENTHREAD_COEX_COUNTER_ENABLE` is not enabled.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionSetRadioHoldoff (heading level 7)

`otError otPlatRadioExtensionSetRadioHoldoff(bool aEnabled)`

**Description:** Set the coexistence radio holdoff status.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|bool|[in]|aEnabled|The coexistence radio holdoff status.|

Requires the `ot_coex` component.

**See Also**

- RAIL API: **sl_rail_util_coex_set_radio_holdoff()**

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully set the coexistence radio holdoff status.
- OT_ERROR_FAILED: The coexistence radio holdoff status can not be set.
- OT_ERROR_NOT_IMPLEMENTED: Coexistence is not implemented.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionGetRadioCounters (heading level 7)

`otError otPlatRadioExtensionGetRadioCounters(efr32RadioCounters *aCounters)`

**Description:** Get RAIL debug counter values.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|efr32RadioCounters *|[out]|aCounters|Pointer to struct to store counter values.|

Requires the `ot_efr32_custom_cli` component.

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully obtained radio counter values.
- OT_ERROR_INVALID_ARGS: `aCounters` is NULL.
- OT_ERROR_NOT_IMPLEMENTED: RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT is not enabled.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

###### otPlatRadioExtensionClearRadioCounters (heading level 7)

`otError otPlatRadioExtensionClearRadioCounters(void)`

**Description:** Clear the RAIL debug counters.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|void|N/A|undefined||

Requires the `ot_efr32_custom_cli` component.

**Returns**

- Error code indicating success of the function call.

**Return values**

- OT_ERROR_NONE: Successfully cleared radio counter values.
- OT_ERROR_NOT_IMPLEMENTED: RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT is not enabled.
- OT_ERROR_NOT_FOUND: Returned to host applications when the coprocessor is not built with the required component.

##### Radio Types

This module includes the platform abstraction for a radio frame. 

###### Modules

[otExtAddress](ot-ext-address)

[otMacKey](ot-mac-key)

[otMacKeyMaterial](ot-mac-key-material)

[otRadioIeInfo](ot-radio-ie-info)

[otRadioFrame](ot-radio-frame)

[otRadioCoexMetrics](ot-radio-coex-metrics)

[otLinkMetrics](ot-link-metrics)

###### Enumerations

###### @8 (heading level 7)

```
enum @8 {
    OT_RADIO_FRAME_MAX_SIZE = 127
    OT_RADIO_FRAME_MIN_SIZE = 3
    OT_RADIO_SYMBOLS_PER_OCTET = 2
    OT_RADIO_BIT_RATE = 250000
    OT_RADIO_BITS_PER_OCTET = 8
    OT_RADIO_SYMBOL_RATE = 62500
    OT_RADIO_SYMBOL_TIME = 1000000 * 1 / OT_RADIO_SYMBOL_RATE
    OT_RADIO_TEN_SYMBOLS_TIME = 10 * OT_RADIO_SYMBOL_TIME
    OT_RADIO_LQI_NONE = 0
    OT_RADIO_RSSI_INVALID = 127
    OT_RADIO_POWER_INVALID = 127
    OT_RADIO_INVALID_SHORT_ADDR = 0xfffe
    OT_RADIO_BROADCAST_SHORT_ADDR = 0xffff
}
```

**Enumerator:**

|   |   |
|---|---|
|OT_RADIO_FRAME_MAX_SIZE|aMaxPHYPacketSize (IEEE 802.15.4-2006)|
|OT_RADIO_FRAME_MIN_SIZE|Minimal size of frame FCS + CONTROL.|
|OT_RADIO_SYMBOLS_PER_OCTET|2.4 GHz IEEE 802.15.4-2006|
|OT_RADIO_BIT_RATE|2.4 GHz IEEE 802.15.4 (bits per second)|
|OT_RADIO_BITS_PER_OCTET|Number of bits per octet.|
|OT_RADIO_SYMBOL_RATE|The O-QPSK PHY symbol rate when operating in the 780MHz, 915MHz, 2380MHz, 2450MHz.|
|OT_RADIO_SYMBOL_TIME|Symbol duration time in unit of microseconds.|
|OT_RADIO_TEN_SYMBOLS_TIME|Time for 10 symbols in unit of microseconds.|
|OT_RADIO_LQI_NONE|LQI measurement not supported.|
|OT_RADIO_RSSI_INVALID|Invalid or unknown RSSI value.|
|OT_RADIO_POWER_INVALID|Invalid or unknown power value.|
|OT_RADIO_INVALID_SHORT_ADDR|Invalid short address.|
|OT_RADIO_BROADCAST_SHORT_ADDR|Broadcast short address.|

###### @9 (heading level 7)

```
enum @9 {
    OT_RADIO_CHANNEL_PAGE_0 = 0
    OT_RADIO_CHANNEL_PAGE_0_MASK = (1U << OT_RADIO_CHANNEL_PAGE_0)
    OT_RADIO_CHANNEL_PAGE_2 = 2
    OT_RADIO_CHANNEL_PAGE_2_MASK = (1U << OT_RADIO_CHANNEL_PAGE_2)
}
```

**Description:**

Defines the channel page.

**Enumerator:**

|   |   |
|---|---|
|OT_RADIO_CHANNEL_PAGE_0|2.4 GHz IEEE 802.15.4-2006|
|OT_RADIO_CHANNEL_PAGE_0_MASK|2.4 GHz IEEE 802.15.4-2006|
|OT_RADIO_CHANNEL_PAGE_2|915 MHz IEEE 802.15.4-2006|
|OT_RADIO_CHANNEL_PAGE_2_MASK|915 MHz IEEE 802.15.4-2006|

###### @10 (heading level 7)

```
enum @10 {
    OT_RADIO_915MHZ_OQPSK_CHANNEL_MIN = 1
    OT_RADIO_915MHZ_OQPSK_CHANNEL_MAX = 10
    OT_RADIO_915MHZ_OQPSK_CHANNEL_MASK = 0x3ff << OT_RADIO_915MHZ_OQPSK_CHANNEL_MIN
    OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN = 11
    OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX = 26
    OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MASK = 0xffff << OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN
}
```

**Description:**

Defines the frequency band channel range.

**Enumerator:**

|   |   |
|---|---|
|OT_RADIO_915MHZ_OQPSK_CHANNEL_MIN|915 MHz IEEE 802.15.4-2006|
|OT_RADIO_915MHZ_OQPSK_CHANNEL_MAX|915 MHz IEEE 802.15.4-2006|
|OT_RADIO_915MHZ_OQPSK_CHANNEL_MASK|915 MHz IEEE 802.15.4-2006|
|OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN|2.4 GHz IEEE 802.15.4-2006|
|OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX|2.4 GHz IEEE 802.15.4-2006|
|OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MASK|2.4 GHz IEEE 802.15.4-2006|

###### @11 (heading level 7)

```
enum @11 {
    OT_RADIO_CAPS_NONE = 0
    OT_RADIO_CAPS_ACK_TIMEOUT = 1 << 0
    OT_RADIO_CAPS_ENERGY_SCAN = 1 << 1
    OT_RADIO_CAPS_TRANSMIT_RETRIES = 1 << 2
    OT_RADIO_CAPS_CSMA_BACKOFF = 1 << 3
    OT_RADIO_CAPS_SLEEP_TO_TX = 1 << 4
    OT_RADIO_CAPS_TRANSMIT_SEC = 1 << 5
    OT_RADIO_CAPS_TRANSMIT_TIMING = 1 << 6
    OT_RADIO_CAPS_RECEIVE_TIMING = 1 << 7
    OT_RADIO_CAPS_RX_ON_WHEN_IDLE = 1 << 8
    OT_RADIO_CAPS_TRANSMIT_FRAME_POWER = 1 << 9
    OT_RADIO_CAPS_ALT_SHORT_ADDR = 1 << 10
}
```

**Description:**

Defines constants that are used to indicate different radio capabilities.

**Details:**

See `otRadioCaps`.

**Enumerator:**

|   |   |
|---|---|
|OT_RADIO_CAPS_NONE|Radio supports no capability.|
|OT_RADIO_CAPS_ACK_TIMEOUT|Radio supports AckTime event.|
|OT_RADIO_CAPS_ENERGY_SCAN|Radio supports Energy Scans.|
|OT_RADIO_CAPS_TRANSMIT_RETRIES|Radio supports tx retry logic with collision avoidance (CSMA).|
|OT_RADIO_CAPS_CSMA_BACKOFF|Radio supports CSMA backoff for frame tx (but no retry).|
|OT_RADIO_CAPS_SLEEP_TO_TX|Radio supports direct transition from sleep to TX with CSMA.|
|OT_RADIO_CAPS_TRANSMIT_SEC|Radio supports tx security.|
|OT_RADIO_CAPS_TRANSMIT_TIMING|Radio supports tx at specific time.|
|OT_RADIO_CAPS_RECEIVE_TIMING|Radio supports rx at specific time.|
|OT_RADIO_CAPS_RX_ON_WHEN_IDLE|Radio supports RxOnWhenIdle handling.|
|OT_RADIO_CAPS_TRANSMIT_FRAME_POWER|Radio supports setting per-frame transmit power.|
|OT_RADIO_CAPS_ALT_SHORT_ADDR|Radio supports setting alternate short address.|

###### @12 (heading level 7)

```
enum @12 {
    OT_IE_HEADER_SIZE = 2
    OT_CSL_IE_SIZE = 4
    OT_ACK_IE_MAX_SIZE = 16
    OT_ENH_PROBING_IE_DATA_MAX_SIZE = 2
}
```

**Description:**

Defines constants about size of header IE in ACK.

**Enumerator:**

|   |   |
|---|---|
|OT_IE_HEADER_SIZE|Size of IE header in bytes.|
|OT_CSL_IE_SIZE|Size of CSL IE content in bytes.|
|OT_ACK_IE_MAX_SIZE|Max length for header IE in ACK.|
|OT_ENH_PROBING_IE_DATA_MAX_SIZE|Max length of Link Metrics data in Vendor-Specific IE.|

###### otRadioKeyType (heading level 7)

```
enum otRadioKeyType {
    OT_KEY_TYPE_LITERAL_KEY = 0
    OT_KEY_TYPE_KEY_REF = 1
}
```

**Description:**

Defines constants about key types.

**Enumerator:**

|   |   |
|---|---|
|OT_KEY_TYPE_LITERAL_KEY|Use Literal Keys.|
|OT_KEY_TYPE_KEY_REF|Use Reference to Key.|

###### otRadioState (heading level 7)

```
enum otRadioState {
    OT_RADIO_STATE_DISABLED = 0
    OT_RADIO_STATE_SLEEP = 1
    OT_RADIO_STATE_RECEIVE = 2
    OT_RADIO_STATE_TRANSMIT = 3
    OT_RADIO_STATE_INVALID = 255
}
```

**Description:**

Represents the state of a radio.

**Details:**

Initially, a radio is in the Disabled state.

**Enumerator:**

|   |   |
|---|---|
|OT_RADIO_STATE_DISABLED||
|OT_RADIO_STATE_SLEEP||
|OT_RADIO_STATE_RECEIVE||
|OT_RADIO_STATE_TRANSMIT||
|OT_RADIO_STATE_INVALID||

###### Typedefs

###### otRadioCaps (heading level 7)

`typedef uint16_t otRadioCaps`

**Description:**

Represents radio capabilities.

**Details:**

The value is a bit-field indicating the capabilities supported by the radio. See `OT_RADIO_CAPS_*` definitions.

###### otPanId (heading level 7)

`typedef uint16_t otPanId`

**Description:**

Represents the IEEE 802.15.4 PAN ID.

###### otShortAddress (heading level 7)

`typedef uint16_t otShortAddress`

**Description:**

Represents the IEEE 802.15.4 Short Address.

###### otExtAddress (heading level 7)

`typedef struct otExtAddress otExtAddress`

**Description:**

Represents the IEEE 802.15.4 Extended Address.

###### otMacKey (heading level 7)

`typedef struct otMacKey otMacKey`

**Description:**

Represents a MAC Key.

###### otMacKeyRef (heading level 7)

`typedef otCryptoKeyRef otMacKeyRef`

**Description:**

Represents a MAC Key Ref used by PSA.

###### otMacKeyMaterial (heading level 7)

`typedef struct otMacKeyMaterial otMacKeyMaterial`

###### otRadioIeInfo (heading level 7)

`typedef struct otRadioIeInfo otRadioIeInfo`

**Description:**

Represents the IEEE 802.15.4 Header IE (Information Element) related information of a radio frame.

###### otRadioFrame (heading level 7)

`typedef struct otRadioFrame otRadioFrame`

**Description:**

Represents an IEEE 802.15.4 radio frame.

###### otRadioState (heading level 7)

`typedef enum otRadioState otRadioState`

**Description:**

Represents the state of a radio.

**Details:**

Initially, a radio is in the Disabled state.

###### otRadioCoexMetrics (heading level 7)

`typedef struct otRadioCoexMetrics otRadioCoexMetrics`

**Description:**

The following are valid radio state transitions:

**Details:**

```c
                               (Radio ON)

```

 +-------—+ Enable() +----—+ Receive() +------—+ Transmit() +-------—+ | |--------—>| |--------—>| |-----------—>| | | Disabled | | Sleep | | Receive | | Transmit | | |<--------—| |<--------—| |<-----------—| | +-------—+ Disable() +----—+ Sleep() +------—+ Receive() +-------—+ (Radio OFF) or signal TransmitDone

During the IEEE 802.15.4 data request command the transition Sleep->Receive->Transmit can be shortened to direct transition from Sleep to Transmit if the platform supports the OT_RADIO_CAPS_SLEEP_TO_TX capability. Represents radio coexistence metrics.

###### otLinkMetrics (heading level 7)

`typedef struct otLinkMetrics otLinkMetrics`

**Description:**

Represents what metrics are specified to query.

###### Variables

###### OT_TOOL_PACKED_END (heading level 7)

```
OT_TOOL_PACKED_BEGIN struct otMacKey OT_TOOL_PACKED_END
```

###### Macros

`#define OT_PANID_BROADCAST 0xffff`

**Description**: IEEE 802.15.4 Broadcast PAN ID.

`#define OT_EXT_ADDRESS_SIZE 8`

**Description**: Size of an IEEE 802.15.4 Extended Address (bytes)

`#define CSL_IE_HEADER_BYTES_LO 0x04`

**Description**: Fixed CSL IE header first byte.

`#define CSL_IE_HEADER_BYTES_HI 0x0d`

**Description**: Fixed CSL IE header second byte.

`#define OT_MAC_KEY_SIZE 16`

**Description**: Size of the MAC Key in bytes.

`#define OT_TOOL_PACKED_END `

**Description**: Compiler-specific indication at the end of a byte packed class or struct.

Represents the IEEE 802.15.4 Extended Address. 

###### Public Attributes (heading level 7)

###### m8 (heading level 8)

```
uint8_t otExtAddress::m8[OT_EXT_ADDRESS_SIZE]
```

**Description:** IEEE 802.15.4 Extended Address bytes.

Represents a MAC Key. 

###### Public Attributes (heading level 7)

###### m8 (heading level 8)

```
uint8_t otMacKey::m8[OT_MAC_KEY_SIZE]
```

**Description:** MAC Key bytes.

Represents a MAC Key. 

###### Public Attributes (heading level 7)

###### mKeyRef (heading level 8)

```
otMacKeyRef otMacKeyMaterial::mKeyRef
```

**Description:** Reference to the key stored.

###### mKey (heading level 8)

```
otMacKey otMacKeyMaterial::mKey
```

**Description:** Key stored as literal.

###### mKeyMaterial (heading level 8)

```
union otMacKeyMaterial::@13 otMacKeyMaterial::mKeyMaterial
```

Represents the IEEE 802.15.4 Header IE (Information Element) related information of a radio frame. 

###### Public Attributes (heading level 7)

###### mNetworkTimeOffset (heading level 8)

```
int64_t otRadioIeInfo::mNetworkTimeOffset
```

**Description:** The time offset to the Thread network time.

###### mTimeIeOffset (heading level 8)

```
uint8_t otRadioIeInfo::mTimeIeOffset
```

**Description:** The Time IE offset from the start of PSDU.

###### mTimeSyncSeq (heading level 8)

```
uint8_t otRadioIeInfo::mTimeSyncSeq
```

**Description:** The Time sync sequence.

Represents an IEEE 802.15.4 radio frame. 

###### Public Attributes (heading level 7)

###### mPsdu (heading level 8)

```
uint8_t* otRadioFrame::mPsdu
```

**Description:** The PSDU.

###### mLength (heading level 8)

```
uint16_t otRadioFrame::mLength
```

**Description:** Length of the PSDU.

###### mChannel (heading level 8)

```
uint8_t otRadioFrame::mChannel
```

**Description:** Channel used to transmit/receive the frame.

###### mRadioType (heading level 8)

```
uint8_t otRadioFrame::mRadioType
```

**Description:** Radio link type - should be ignored by radio driver.

###### mAesKey (heading level 8)

```
const otMacKeyMaterial* otRadioFrame::mAesKey
```

**Description:** The key material used for AES-CCM frame security.

###### mIeInfo (heading level 8)

```
otRadioIeInfo* otRadioFrame::mIeInfo
```

**Description:** The pointer to the Header IE(s) related information.

###### mTxDelayBaseTime (heading level 8)

```
uint32_t otRadioFrame::mTxDelayBaseTime
```

**Description:** The base time in microseconds for scheduled transmissions relative to the local radio clock, see `otPlatRadioGetNow` and `mTxDelay`.

**Details:** If this field is non-zero, `mMaxCsmaBackoffs` should be ignored.

This field does not affect CCA behavior which is controlled by `mCsmaCaEnabled`.

###### mTxDelay (heading level 8)

```
uint32_t otRadioFrame::mTxDelay
```

**Description:** The delay time in microseconds for this transmission referenced to `mTxDelayBaseTime`.

**Details:** Note: `mTxDelayBaseTime` + `mTxDelay` SHALL point to the point in time when the end of the SFD will be present at the local antenna, relative to the local radio clock.

If this field is non-zero, `mMaxCsmaBackoffs` should be ignored.

This field does not affect CCA behavior which is controlled by `mCsmaCaEnabled`.

###### mMaxCsmaBackoffs (heading level 8)

```
uint8_t otRadioFrame::mMaxCsmaBackoffs
```

**Description:** Maximum number of CSMA backoff attempts before declaring channel access failure.

**Details:** This is applicable and MUST be used when radio platform provides the `OT_RADIO_CAPS_CSMA_BACKOFF` and/or `OT_RADIO_CAPS_TRANSMIT_RETRIES`.

This field MUST be ignored if `mCsmaCaEnabled` is set to `false` (CCA is disabled) or either `mTxDelayBaseTime` or `mTxDelay` is non-zero (frame transmission is expected at a specific time).

It can be set to `0` to skip backoff mechanism (note that CCA MUST still be performed assuming `mCsmaCaEnabled` is `true`).

###### mMaxFrameRetries (heading level 8)

```
uint8_t otRadioFrame::mMaxFrameRetries
```

**Description:** Maximum number of retries allowed after a transmission failure.

###### mRxChannelAfterTxDone (heading level 8)

```
uint8_t otRadioFrame::mRxChannelAfterTxDone
```

**Description:** The RX channel after frame TX is done (after all frame retries - ack received, or timeout, or abort).

**Details:** Radio platforms can choose to fully ignore this. OT stack will make sure to call `otPlatRadioReceive()` with the desired RX channel after a frame TX is done and signaled in `otPlatRadioTxDone()` callback. Radio platforms that don't provide `OT_RADIO_CAPS_TRANSMIT_RETRIES` must always ignore this.

This is intended for situations where there may be delay in interactions between OT stack and radio, as an example this is used in RCP/host architecture to make sure RCP switches to PAN channel more quickly. In particular, this can help with CSL tx to a sleepy child, where the child may use a different channel for CSL than the PAN channel. After frame tx, we want the radio/RCP to go back to the PAN channel quickly to ensure that parent does not miss tx from child afterwards, e.g., child responding to the earlier CSL transmitted frame from parent using PAN channel while radio still staying on CSL channel.

The switch to the RX channel MUST happen after the frame TX is fully done, i.e., after all retries and when ack is received (when "Ack Request" flag is set on the TX frame) or ack timeout. Note that ack is expected on the same channel that frame is sent on.

###### mTxPower (heading level 8)

```
int8_t otRadioFrame::mTxPower
```

**Description:** The transmit power in dBm.

**Details:** If the platform layer does not provide `OT_RADIO_CAPS_TRANSMIT_FRAME_POWER` capability, it can ignore this value.

If the value is OT_RADIO_POWER_INVALID, then the platform should ignore this value and transmit the frame with its default transmit power.

Otherwise, the platform should transmit this frame with the maximum power no larger than minimal of the following values:

1. mTxPower,
2. The power limit set by [otPlatRadioSetChannelTargetPower()](radio-operation#ot-plat-radio-set-channel-target-power),
3. The power limit set by [otPlatRadioSetChannelMaxTransmitPower()](radio-operation#ot-plat-radio-set-channel-max-transmit-power),
4. The power limit set by [otPlatRadioSetRegion()](radio-operation#ot-plat-radio-set-region).

###### mIsHeaderUpdated (heading level 8)

```
bool otRadioFrame::mIsHeaderUpdated
```

**Description:** Indicates whether frame counter and CSL IEs are properly updated in the header.

**Details:** If the platform layer does not provide `OT_RADIO_CAPS_TRANSMIT_SEC` capability, it can ignore this flag.

If the platform provides `OT_RADIO_CAPS_TRANSMIT_SEC` capability, then platform is expected to handle tx security processing and assignment of frame counter. In this case the following behavior is expected:

When `mIsHeaderUpdated` is set, it indicates that OpenThread core has already set the frame counter and CSL IEs (if security is enabled) in the prepared frame. The counter is ensured to match the counter value from the previous attempts of the same frame. The platform should not assign or change the frame counter (but may still need to perform security processing depending on `mIsSecurityProcessed` flag).

If `mIsHeaderUpdated` is not set, then the frame counter and key CSL IE not set in the frame by OpenThread core and it is the responsibility of the radio platform to assign them. The platform must update the frame header (assign counter and CSL IE values) before sending the frame over the air, however if the the transmission gets aborted and the frame is never sent over the air (e.g., channel access error) the platform may choose to not update the header. If the platform updates the header, it must also set this flag before passing the frame back from the `otPlatRadioTxDone()` callback.

###### mIsARetx (heading level 8)

```
bool otRadioFrame::mIsARetx
```

**Description:** Indicates whether the frame is a retransmission or not.

###### mCsmaCaEnabled (heading level 8)

```
bool otRadioFrame::mCsmaCaEnabled
```

**Description:** Set to true to enable CSMA-CA for this packet, false to disable both CSMA backoff and CCA.

**Details:** When it is set to `false`, the frame MUST be sent without performing CCA. In this case `mMaxCsmaBackoffs` MUST also be ignored.

###### mCslPresent (heading level 8)

```
bool otRadioFrame::mCslPresent
```

**Description:** Set to true if CSL header IE is present.

###### mIsSecurityProcessed (heading level 8)

```
bool otRadioFrame::mIsSecurityProcessed
```

**Description:** True if SubMac should skip the AES processing of this frame.

###### mTimestamp (heading level 8)

```
uint64_t otRadioFrame::mTimestamp
```

**Description:** The time of the local radio clock in microseconds when the end of the SFD was present at the local antenna.

**Details:** The platform should update this field before [otPlatRadioTxStarted()](radio-operation#ot-plat-radio-tx-started) is fired for each transmit attempt.

###### mTxInfo (heading level 8)

```
struct otRadioFrame::@14::@15 otRadioFrame::mTxInfo
```

**Description:** Structure representing radio frame transmit information.

###### mAckFrameCounter (heading level 8)

```
uint32_t otRadioFrame::mAckFrameCounter
```

**Description:** ACK security frame counter (applicable when `mAckedWithSecEnhAck` is set).

###### mAckKeyId (heading level 8)

```
uint8_t otRadioFrame::mAckKeyId
```

**Description:** ACK security key index (applicable when `mAckedWithSecEnhAck` is set).

###### mRssi (heading level 8)

```
int8_t otRadioFrame::mRssi
```

**Description:** Received signal strength indicator in dBm for received frames.

###### mLqi (heading level 8)

```
uint8_t otRadioFrame::mLqi
```

**Description:** Link Quality Indicator for received frames.

###### mAckedWithFramePending (heading level 8)

```
bool otRadioFrame::mAckedWithFramePending
```

**Description:** This indicates if this frame was acknowledged with frame pending set.

###### mAckedWithSecEnhAck (heading level 8)

```
bool otRadioFrame::mAckedWithSecEnhAck
```

**Description:** This indicates if this frame was acknowledged with secured enhance ACK.

###### mRxInfo (heading level 8)

```
struct otRadioFrame::@14::@16 otRadioFrame::mRxInfo
```

**Description:** Structure representing radio frame receive information.

###### mInfo (heading level 8)

```
union otRadioFrame::@14 otRadioFrame::mInfo
```

**Description:** The union of transmit and receive information for a radio frame.

The following are valid radio state transitions: 

```c
                               (Radio ON)

```

 +-------—+ Enable() +----—+ Receive() +------—+ Transmit() +-------—+ | |--------—>| |--------—>| |-----------—>| | | Disabled | | Sleep | | Receive | | Transmit | | |<--------—| |<--------—| |<-----------—| | +-------—+ Disable() +----—+ Sleep() +------—+ Receive() +-------—+ (Radio OFF) or signal TransmitDone

During the IEEE 802.15.4 data request command the transition Sleep->Receive->Transmit can be shortened to direct transition from Sleep to Transmit if the platform supports the OT_RADIO_CAPS_SLEEP_TO_TX capability. Represents radio coexistence metrics. 

###### Public Attributes (heading level 7)

###### mNumGrantGlitch (heading level 8)

```
uint32_t otRadioCoexMetrics::mNumGrantGlitch
```

**Description:** Number of grant glitches.

###### mNumTxRequest (heading level 8)

```
uint32_t otRadioCoexMetrics::mNumTxRequest
```

**Description:** Number of tx requests.

###### mNumTxGrantImmediate (heading level 8)

```
uint32_t otRadioCoexMetrics::mNumTxGrantImmediate
```

**Description:** Number of tx requests while grant was active.

###### mNumTxGrantWait (heading level 8)

```
uint32_t otRadioCoexMetrics::mNumTxGrantWait
```

**Description:** Number of tx requests while grant was inactive.

###### mNumTxGrantWaitActivated (heading level 8)

```
uint32_t otRadioCoexMetrics::mNumTxGrantWaitActivated
```

**Description:** Number of tx requests while grant was inactive that were ultimately granted.

###### mNumTxGrantWaitTimeout (heading level 8)

```
uint32_t otRadioCoexMetrics::mNumTxGrantWaitTimeout
```

**Description:** Number of tx requests while grant was inactive that timed out.

###### mNumTxGrantDeactivatedDuringRequest (heading level 8)

```
uint32_t otRadioCoexMetrics::mNumTxGrantDeactivatedDuringRequest
```

**Description:** Number of tx that were in progress when grant was deactivated.

###### mNumTxDelayedGrant (heading level 8)

```
uint32_t otRadioCoexMetrics::mNumTxDelayedGrant
```

**Description:** Number of tx requests that were not granted within 50us.

###### mAvgTxRequestToGrantTime (heading level 8)

```
uint32_t otRadioCoexMetrics::mAvgTxRequestToGrantTime
```

**Description:** Average time in usec from tx request to grant.

###### mNumRxRequest (heading level 8)

```
uint32_t otRadioCoexMetrics::mNumRxRequest
```

**Description:** Number of rx requests.

###### mNumRxGrantImmediate (heading level 8)

```
uint32_t otRadioCoexMetrics::mNumRxGrantImmediate
```

**Description:** Number of rx requests while grant was active.

###### mNumRxGrantWait (heading level 8)

```
uint32_t otRadioCoexMetrics::mNumRxGrantWait
```

**Description:** Number of rx requests while grant was inactive.

###### mNumRxGrantWaitActivated (heading level 8)

```
uint32_t otRadioCoexMetrics::mNumRxGrantWaitActivated
```

**Description:** Number of rx requests while grant was inactive that were ultimately granted.

###### mNumRxGrantWaitTimeout (heading level 8)

```
uint32_t otRadioCoexMetrics::mNumRxGrantWaitTimeout
```

**Description:** Number of rx requests while grant was inactive that timed out.

###### mNumRxGrantDeactivatedDuringRequest (heading level 8)

```
uint32_t otRadioCoexMetrics::mNumRxGrantDeactivatedDuringRequest
```

**Description:** Number of rx that were in progress when grant was deactivated.

###### mNumRxDelayedGrant (heading level 8)

```
uint32_t otRadioCoexMetrics::mNumRxDelayedGrant
```

**Description:** Number of rx requests that were not granted within 50us.

###### mAvgRxRequestToGrantTime (heading level 8)

```
uint32_t otRadioCoexMetrics::mAvgRxRequestToGrantTime
```

**Description:** Average time in usec from rx request to grant.

###### mNumRxGrantNone (heading level 8)

```
uint32_t otRadioCoexMetrics::mNumRxGrantNone
```

**Description:** Number of rx requests that completed without receiving grant.

###### mStopped (heading level 8)

```
bool otRadioCoexMetrics::mStopped
```

**Description:** Stats collection stopped due to saturation.

Represents what metrics are specified to query. 

###### Public Attributes (heading level 7)

###### mPduCount (heading level 8)

```
bool otLinkMetrics::mPduCount
```

**Description:** Pdu count.

###### mLqi (heading level 8)

```
bool otLinkMetrics::mLqi
```

**Description:** Link Quality Indicator.

###### mLinkMargin (heading level 8)

```
bool otLinkMetrics::mLinkMargin
```

**Description:** Link Margin.

###### mRssi (heading level 8)

```
bool otLinkMetrics::mRssi
```

**Description:** Received Signal Strength Indicator.

###### mReserved (heading level 8)

```
bool otLinkMetrics::mReserved
```

**Description:** Reserved, this is for reference device.

#### TREL - Platform

This module includes the platform abstraction for Thread Radio Encapsulation Link (TREL) using DNS-SD and UDP/IPv6. 

##### Modules

[otPlatTrelPeerInfo](ot-plat-trel-peer-info)

[otPlatTrelCounters](ot-plat-trel-counters)

##### Typedefs

###### otPlatTrelPeerInfo

`typedef struct otPlatTrelPeerInfo otPlatTrelPeerInfo`

**Description:**

Represents a TREL peer info discovered using DNS-SD browse on the service name "_trel._udp".

###### otPlatTrelCounters

`typedef struct otPlatTrelCounters otPlatTrelCounters`

**Description:**

Represents a group of TREL related counters in the platform layer.

##### Functions

###### otPlatTrelEnable

`void otPlatTrelEnable(otInstance *aInstance, uint16_t *aUdpPort)`

**Description:** Initializes and enables TREL platform layer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|uint16_t *|[out]|aUdpPort|A pointer to return the selected port number by platform layer.|

Upon this call, the platform layer MUST perform the following:

1) TREL platform layer MUST open a UDP socket to listen for and receive TREL messages from peers. The socket is bound to an ephemeral port number chosen by the platform layer. The port number MUST be returned in `aUdpPort`. The socket is also bound to network interface(s) on which TREL is to be supported. The socket and the chosen port should stay valid while TREL is enabled.

2) If `OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE` is enabled, the OpenThread core TREL implementation itself will handle mDNS (DNS-SD) TREL service registration and peer discovery. Otherwise the platform layer MUST initiate an ongoing DNS-SD browse on the service name "_trel._udp" within the local browsing domain to discover other devices supporting TREL. The ongoing browse will produce two different types of events: "add" events and "remove" events. When the browse is started, it should produce an "add" event for every TREL peer currently present on the network. Whenever a TREL peer goes offline, a "remove" event should be produced. "remove" events are not guaranteed, however. When a TREL service instance is discovered, a new ongoing DNS-SD query for an AAAA record should be started on the hostname indicated in the SRV record of the discovered instance. If multiple host IPv6 addressees are discovered for a peer, one with highest scope among all addresses MUST be reported (if there are multiple address at same scope, one must be selected randomly). TREL platform MUST signal back the discovered peer info using `otPlatTrelHandleDiscoveredPeerInfo()` callback. This callback MUST be invoked when a new peer is discovered, when there is a change in an existing entry (e.g., new TXT record or new port number or new IPv6 address), or when the peer is removed.

###### otPlatTrelDisable

`void otPlatTrelDisable(otInstance *aInstance)`

**Description:** Disables TREL platform layer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|N/A|aInstance||

After this call, the platform layer MUST stop DNS-SD browse on the service name "_trel._udp", stop advertising the TREL DNS-SD service (from `otPlatTrelRegisterService()`) and MUST close the UDP socket used to receive TREL messages.

@pram[in] aInstance The OpenThread instance. 

###### otPlatTrelHandleDiscoveredPeerInfo

`void otPlatTrelHandleDiscoveredPeerInfo(otInstance *aInstance, const otPlatTrelPeerInfo *aInfo)`

**Description:** This is a callback function from platform layer to report a discovered TREL peer info.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otPlatTrelPeerInfo](ot-plat-trel-peer-info) *|[in]|aInfo|A pointer to the TREL peer info.|

This is only applicable when `OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE` is disabled.

**Note**

- The `aInfo` structure and its content (e.g., the `mTxtData` buffer) does not need to persist after returning from this call. OpenThread code will make a copy of all the info it needs.

###### otPlatTrelNotifyPeerSocketAddressDifference

`void otPlatTrelNotifyPeerSocketAddressDifference(otInstance *aInstance, const otSockAddr *aPeerSockAddr, const otSockAddr *aRxSockAddr)`

**Description:** Notifies platform that a TREL packet is received from a peer using a different socket address than the one reported earlier from `otPlatTrelHandleDiscoveredPeerInfo()`.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|const [otSockAddr](ot-sock-addr) *|[in]|aPeerSockAddr|The address of the peer, reported from `otPlatTrelHandleDiscoveredPeerInfo()` call.|
|const [otSockAddr](ot-sock-addr) *|[in]|aRxSockAddr|The address of received packet from the same peer (differs from `aPeerSockAddr`).|

This is only applicable when `OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE` is disabled.

Ideally the platform underlying DNS-SD should detect changes to advertised port and addresses by peers, however, there are situations where this is not detected reliably. This function signals to the platform layer than we received a packet from a peer with it using a different port or address. This can be used by the playroom layer to restart/confirm the DNS-SD service/address resolution for the peer service and/or take any other relevant actions.

###### otPlatTrelRegisterService

`void otPlatTrelRegisterService(otInstance *aInstance, uint16_t aPort, const uint8_t *aTxtData, uint8_t aTxtLength)`

**Description:** Registers a new service to be advertised using DNS-SD [RFC6763].

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance.|
|uint16_t|[in]|aPort|The port number to include in the SRV record of the advertised service.|
|const uint8_t *|[in]|aTxtData|A pointer to the TXT record data (encoded) to be include in the advertised service.|
|uint8_t|[in]|aTxtLength|The length of `aTxtData` (number of bytes).|

This is only applicable when `OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE` is disabled.

The service name is "_trel._udp". The platform should use its own hostname, which when combined with the service name and the local DNS-SD domain name will produce the full service instance name, for example "example-host._trel._udp.local.".

The domain under which the service instance name appears will be 'local' for mDNS, and will be whatever domain is used for service registration in the case of a non-mDNS local DNS-SD service.

A subsequent call to this function updates the previous service. It is used to update the TXT record data and/or the port number.

The `aTxtData` buffer is not persisted after the return from this function. The platform layer MUST NOT keep the pointer and instead copy the content if needed.

###### otPlatTrelSend

`void otPlatTrelSend(otInstance *aInstance, const uint8_t *aUdpPayload, uint16_t aUdpPayloadLen, const otSockAddr *aDestSockAddr)`

**Description:** Requests a TREL UDP packet to be sent to a given destination.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|const uint8_t *|[in]|aUdpPayload|A pointer to UDP payload.|
|uint16_t|[in]|aUdpPayloadLen|The payload length (number of bytes).|
|const [otSockAddr](ot-sock-addr) *|[in]|aDestSockAddr|The destination socket address.|

###### otPlatTrelHandleReceived

`void otPlatTrelHandleReceived(otInstance *aInstance, uint8_t *aBuffer, uint16_t aLength, const otSockAddr *aSenderAddr)`

**Description:** Is a callback from platform to notify of a received TREL UDP packet.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|
|uint8_t *|[in]|aBuffer|A buffer containing the received UDP payload.|
|uint16_t|[in]|aLength|UDP payload length (number of bytes).|
|const [otSockAddr](ot-sock-addr) *|[in]|aSenderAddr|The sender address.|

**Note**

- The buffer content (up to its specified length) may get changed during processing by OpenThread core (e.g., decrypted in place), so the platform implementation should expect that after returning from this function the `aBuffer` content may have been altered.

###### otPlatTrelGetCounters

`const otPlatTrelCounters * otPlatTrelGetCounters(otInstance *aInstance)`

**Description:** Gets the pointer to the TREL counters in the platform layer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

###### otPlatTrelResetCounters

`void otPlatTrelResetCounters(otInstance *aInstance)`

**Description:** Resets the TREL counters in the platform layer.

**Parameters:**

|Type|Direction|Argument Name|Description|
|----|---------|-------------|-----------|
|[otInstance](api-instance#ot-instance) *|[in]|aInstance|The OpenThread instance structure.|

Represents a TREL peer info discovered using DNS-SD browse on the service name "_trel._udp". 

###### Public Attributes

###### mRemoved (heading level 7)

```
bool otPlatTrelPeerInfo::mRemoved
```

**Description:** This boolean flag indicates whether the entry is being removed or added.

**Details:** - TRUE indicates that peer is removed.
- FALSE indicates that it is a new entry or an update to an existing entry.

###### mTxtData (heading level 7)

```
const uint8_t* otPlatTrelPeerInfo::mTxtData
```

**Description:** The TXT record data (encoded as specified by DNS-SD) from the SRV record of the discovered TREL peer service instance.

###### mTxtLength (heading level 7)

```
uint16_t otPlatTrelPeerInfo::mTxtLength
```

**Description:** Number of bytes in `mTxtData` buffer.

###### mSockAddr (heading level 7)

```
otSockAddr otPlatTrelPeerInfo::mSockAddr
```

**Description:** The TREL peer socket address (IPv6 address and port number).

**Details:** The port number is determined from the SRV record of the discovered TREL peer service instance. The IPv6 address is determined from the DNS-SD query for AAAA records on the hostname indicated in the SRV record of the discovered service instance. If multiple host IPv6 addressees are discovered, one with highest scope is used.

Represents a group of TREL related counters in the platform layer. 

###### Public Attributes

###### mTxPackets (heading level 7)

```
uint64_t otPlatTrelCounters::mTxPackets
```

**Description:** Number of packets successfully transmitted through TREL.

###### mTxBytes (heading level 7)

```
uint64_t otPlatTrelCounters::mTxBytes
```

**Description:** Sum of size of packets successfully transmitted through TREL.

###### mTxFailure (heading level 7)

```
uint64_t otPlatTrelCounters::mTxFailure
```

**Description:** Number of packet transmission failures through TREL.

###### mRxPackets (heading level 7)

```
uint64_t otPlatTrelCounters::mRxPackets
```

**Description:** Number of packets received through TREL.

###### mRxBytes (heading level 7)

```
uint64_t otPlatTrelCounters::mRxBytes
```

**Description:** Sum of size of packets received through TREL.

### Config Variables

This module lists all OpenThread configuration variables. 

#### Modules

[Announce Sender](config-announce-sender)

[Backbone Router Services](config-backbone-router)

[Border Agent](config-border-agent)

[Border Router](config-border-router)

[Border Routing Manager](config-border-routing)

[Channel Manager](config-channel-manager)

[Channel Monitor](config-channel-monitor)

[Child Supervision](config-child-supervision)

[CoAP](config-coap)

[Commissioner](config-commissioner)

[Crypto Backend Library](config-crypto)

[Dataset Updater](config-dataset-updater)

[DHCPv6 Client](config-dhcpv6-client)

[DHCPv6 Server](config-dhcpv6-server)

[DIAG Service](config-diag)

[DNS Client](config-dns-client)

[DNS Stateful Operations](config-dns-dso)

[DNS-SD Server](config-dnssd-server)

[History Tracker](config-history-tracker)

[IP6 Service](config-ip6)

[Joiner](config-joiner)

[Link Metrics Manager](config-link-metrics-manager)

[Link Quality](config-link-quality)

[Link Raw Service](config-link-raw)

[Logging Service](config-logging)

[MAC](config-mac)

[Mesh Diagnostic](config-mesh-diag)

[Mesh Forwarder](config-mesh-forwarder)

[Miscellaneous Constants](config-misc)

[MLE Service](config-mle)

[Multicast DNS](config-mdns)

[NAT64](config-nat64)

[Network Data Publisher](config-netdata-publisher)

[Network Diagnostics](config-network-diagnostic)

[Peer-to-Peer](config-p2p)

[Parent Search](config-parent-search)

[Ping Sender](config-ping-sender)

[Platform Specific Services](config-platform)

[Power Calibration](config-power-calibration)

[Radio Links](config-radio-link)

[Secure Transport](config-secure-transport)

[SNTP Client](config-sntp-client)

[SRP Client](config-srp-client)

[SRP Server](config-srp-server)

[Time Sync Service](config-time-sync)

[Thread Management Framework Service](config-tmf)

[TREL](config-trel)

[Wake-up](config-wakeup)

#### Announce Sender

#### Backbone Router Services

#### Border Agent

#### Border Router

#### Border Routing Manager

#### Channel Manager

#### Channel Monitor

#### Child Supervision

#### CoAP

#### Commissioner

#### Crypto Backend Library

#### Dataset Updater

#### DHCPv6 Client

#### DHCPv6 Server

#### DIAG Service

#### DNS Client

#### DNS Stateful Operations

#### DNS-SD Server

#### History Tracker

#### IP6 Service

#### Joiner

#### Link Metrics Manager

#### Link Quality

#### Link Raw Service

#### Logging Service

#### MAC

#### Mesh Diagnostic

#### Mesh Forwarder

#### Miscellaneous Constants

#### MLE Service

#### Multicast DNS

#### NAT64

#### Network Data Publisher

#### Network Diagnostics

#### Peer-to-Peer

#### Parent Search

#### Ping Sender

#### Platform Specific Services

#### Power Calibration

#### Radio Links

#### Secure Transport

#### SNTP Client

#### SRP Client

#### SRP Server

#### Time Sync Service

#### Thread Management Framework Service

#### TREL

#### Wake-up