zephyr/doc/services/device_mgmt/mcumgr_handlers.rst
Jamie McCrae d52b634610 doc: mgmt: mcumgr: Add details on making handlers
Adds a guide on how to make out-of-tree MCUmgr function handlers
and groups, with an example showing a test implementation.

Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
2023-07-20 16:22:31 +02:00

204 lines
7.9 KiB
ReStructuredText

.. _mcumgr_handlers:
MCUmgr handlers
###############
Overview
********
MCUmgr functions by having group handlers which identify a group of functions relating to a
specific management area, which is addressed with a 16-bit identification value,
:c:enum:`mcumgr_group_t` contains the management groups available in Zephyr with their
corresponding group ID values. The group ID is included in SMP headers to identify which
group a command belongs to, there is also an 8-bit command ID which identifies the function of
that group to execute - see :ref:`mcumgr_smp_protocol_specification` for details on the SMP
protocol and header. There can only be one registered group per unique ID.
Implementation
**************
MCUmgr handlers can be added externally by application code or by module code, they do not have
to reside in the upstream Zephyr tree to be usable. The first step to creating a handler is to
create the folder structure for it, the typical Zephyr MCUmgr group layout is as follows:
.. code-block:: none
<dir>/grp/<grp_name>_mgmt/
├── CMakeLists.txt
├── Kconfig
├── include
├──── <grp_name>_mgmt.h
├──── <grp_name>_mgmt_callbacks.h
├── src
└──── <grp_name>_mgmt.c
Note that the header files in upstream Zephyr MCUmgr handlers reside in the
``zephyr/include/zephyr/mgmt/mcumgr/grp/<grp_name>_mgmt`` directory to allow the files to be
globally included by applications.
Initial header <grp_name>_mgmt.h
================================
The purpose of the header file is to provide defines which can be used by the MCUmgr handler
itself and application code, e.g. to reference the command IDs for executing functions. An example
file would look similar to:
.. literalinclude:: ../../../tests/subsys/mgmt/mcumgr/handler_demo/example_as_module/include/example_mgmt.h
:language: c
:linenos:
This provides the defines for 2 command ``test`` and ``other`` and sets up the SMP version 2 error
responses (which have unique error codes per group as opposed to the legacy SMP version 1 error
responses that return a :c:enum:`mcumgr_err_t` - there should always be an OK error code with the
value 0 and an unknown error code with the value 1. The above example then adds an error code of
``not wanted`` with value 2. In addition, the group ID is set to be
:c:enum:`MGMT_GROUP_ID_PERUSER`, which is the start group ID for user-defined groups, note that
group IDs need to be unique so other custom groups should use different values, a central index
header file (as upstream Zephyr has) can be used to distribute group IDs more easily.
Initial header <grp_name>_mgmt_callbacks.h
==========================================
The purpose of the header file is to provide defines which can be used by the MCUmgr handler
itself and application code, e.g. to reference the command IDs for executing functions. An example
file would look similar to:
.. literalinclude:: ../../../tests/subsys/mgmt/mcumgr/handler_demo/example_as_module/include/example_mgmt_callbacks.h
:language: c
:linenos:
This sets up a single event which application (or module) code can register for to receive a
callback when the function handler is executed, which allows the flow of the handler to be
changed (i.e. to return an error instead of continuing). The event group ID is set to
:c:enum:`MGMT_EVT_GRP_USER_CUSTOM_START`, which is the start event ID for user-defined groups,
note that event IDs need to be unique so other custom groups should use different values, a
central index header file (as upstream Zephyr has) can be used to distribute event IDs more
easily.
Initial source <grp_name>_mgmt.c
================================
The purpose of this source file is to handle the incoming MCUmgr commands, provide responses, and
register the transport with MCUmgr so that commands will be sent to it. An example file would
look similar to:
.. literalinclude:: ../../../tests/subsys/mgmt/mcumgr/handler_demo/example_as_module/src/example_mgmt.c
:language: c
:linenos:
The above code creates 2 function handlers, ``test`` which supports read requests and takes 2
required parameters, and ``other`` which supports write requests and takes 1 optional parameter,
this function handler has an optional notification callback feature that allows other parts of
the code to listen for the event and take any required actions that are necessary or prevent
further execution of the function by returning an error, further details on MCUmgr callback
functionality can be found on :ref:`mcumgr_callbacks`.
Note that other code referencing callbacks for custom MCUmgr handlers needs to include both the
base Zephyr callback include file and the custom handler callback file, only in-tree Zephyr
handler headers are included when including the upstream Zephyr callback header file.
Initial Kconfig
===============
The purpose of the Kconfig file is to provide options which users can enable or change relating
to the functionality of the handler being implemented. An example file would look similar to:
.. literalinclude:: ../../../tests/subsys/mgmt/mcumgr/handler_demo/Kconfig
:language: kconfig
Initial CMakeLists.txt
======================
The CMakeLists.txt file is used by the build system to setup files to compile, include
directories to add and specify options that can be changed. A basic file only need to include the
source files if the Kconfig options are enabled. An example file would look similar to:
.. tabs::
.. group-tab:: Zephyr module
.. literalinclude:: ../../../tests/subsys/mgmt/mcumgr/handler_demo/example_as_module/CMakeLists.txt
:language: cmake
.. group-tab:: Application
.. literalinclude:: ../../../tests/subsys/mgmt/mcumgr/handler_demo/CMakeLists.txt
:language: cmake
:start-after: Include handler files
Including from application
**************************
Application-specific MCUmgr handlers can be added by creating/editing application build files.
Example modifications are shown below.
Example CMakeLists.txt
======================
The application ``CMakeLists.txt`` file can load the CMake file for the example MCUmgr handler by
adding the following:
.. code-block:: cmake
add_subdirectory(mcumgr/grp/<grp_name>)
Example Kconfig
===============
The application Kconfig file can include the Kconfig file for the example MCUmgr handler by adding
the following to the ``Kconfig`` file in the application directory (or creating it if it does not
exist):
.. code-block:: kconfig
rsource "mcumgr/grp/<grp_name>/Kconfig"
# Include Zephyr's Kconfig
source "Kconfig.zephyr"
Including from Zephyr Module
****************************
Zephyr :ref:`modules` can be used to add custom MCUmgr handlers to multiple different applications
without needing to duplicate the code in each application's source tree, see :ref:`module-yml` for
details on how to set up the module files. Example files are shown below.
Example zephyr/module.yml
=========================
This is an example file which can be used to load the Kconfig and CMake files from the root of the
module directory, and would be placed at ``zephyr/module.yml``:
.. code-block:: yaml
build:
kconfig: Kconfig
cmake: .
Example CMakeLists.txt
======================
This is an example CMakeLists.txt file which loads the CMake file for the example MCUmgr handler,
and would be placed at ``CMakeLists.txt``:
.. code-block:: cmake
add_subdirectory(mcumgr/grp/<grp_name>)
Example Kconfig
===============
This is an example Kconfig file which loads the Kconfig file for the example MCUmgr handler, and
would be placed at ``Kconfig``:
.. code-block:: kconfig
rsource "mcumgr/grp/<grp_name>/Kconfig"
Demonstration handler
*********************
There is a demonstration project which includes configuration for both application and zephyr
module-MCUmgr handlers which can be used as a basis for created your own in
:zephyr_file:`tests/subsys/mgmt/mcumgr/handler_demo/`.