connectivity/networking/conn_mgr/implementation.html · connectivity/networking/conn_mgr/implementation.rst 공식 원문 ↗

Zephyr 3.7.0 · Connectivity · Connection Manager

Connectivity Implementations

Technology-specific implementation을 정의·binding하고 context, state, timeout과 persistence 규격을 일관되게 구현합니다.

Official pathconnectivity/networking/conn_mgr/implementation.html
Source fileconnectivity/networking/conn_mgr/implementation.rst
Source versionZephyr 3.7.0
TranslationFull · reviewed

본문 기준: Zephyr 3.7.0 LTS3 · 이후 유지보수 패치는 별도입니다.

Part 1

요약·해설

Implementation macro와 binding architecture, 16개 작성 guideline 및 persistent/non-persistent technology edge case를 설명합니다.

Binding

Implementation · Iface · Context

Callbacks

Non-blocking · Locked

Behavior

Timeout · Persistence

Part 2

접을 수 있는 영어 원문 전체

영어 원문 전체 펼치기
원문 SHA-256 5C4336E7EF4F58980F4C10190E1DFC7C28D0A6203490805AA689CA815CC2079E
.. _conn_mgr_impl:

Connectivity Implementations
############################

.. _conn_mgr_impl_overview:

Overview
========

Connectivity implementations are technology-specific modules that allow specific Zephyr ifaces to support :ref:`Connectivity Control <conn_mgr_control>`.
They are responsible for translating generic :ref:`connectivity control API <conn_mgr_control_api>` calls into hardware-specific operations.
They are also responsible for implementing standardized :ref:`persistence and timeout <conn_mgr_control_persistence_timeouts>` behaviors.

See the :ref:`implementation guidelines <conn_mgr_impl_guidelines>` for details on writing conformant connectivity implementations.

.. _conn_mgr_impl_architecture:

Architecture
============

The :ref:`implementation API <conn_mgr_impl_api>` allows connectivity implementations to be :ref:`defined <conn_mgr_impl_defining>` at build time using :c:macro:`CONN_MGR_CONN_DEFINE`.

This creates a static instance of the :c:struct:`conn_mgr_conn_impl` struct, which then stores a reference to the passed in :c:struct:`conn_mgr_conn_api` struct (which should be populated with implementation callbacks).

Once defined, you can reference implementations by name and bind them to any unbound iface using :c:macro:`CONN_MGR_BIND_CONN`.
Make sure not to accidentally bind two connectivity implementations to a single iface.

Once the iface is bound, :ref:`connectivity control API <conn_mgr_control_api>` functions can be called on the iface, and they will be translated to the corresponding implementation functions in :c:struct:`conn_mgr_conn_api`.

Binding an iface does not directly modify its :c:struct:`iface struct <net_if>`.

Instead, an instance of :c:struct:`conn_mgr_conn_binding` is created and appended an internal :ref:`iterable section <iterable_sections_api>`.

This binding structure will contain a reference to the bound iface, the connectivity implementation it is bound to, as well as a pointer to a per-iface :ref:`context pointer <conn_mgr_impl_ctx>`.

This iterable section can then be iterated over to find out what (if any) connectivity implementation has been bound to a given iface.
This search process is used by most of the functions in the :ref:`connectivity control API <conn_mgr_control_api>`.
As such, these functions should be called sparingly, due to their relatively high search cost.

A single connectivity implementation may be bound to multiple ifaces.
See :ref:`conn_mgr_impl_guidelines_no_instancing` for more details.

.. _conn_mgr_integration_diagram_detailed:

.. figure:: ../../../../../connectivity/networking/conn_mgr/figures/integration_diagram_detailed.svg
    :alt: A detailed view of how Connection Manager integrates with Zephyr and the application.
    :figclass: align-center

    A detailed view of how Connection Manager integrates with Zephyr and the application.

    See :ref:`here <conn_mgr_integration_diagram_simple>` for a simplified version.

.. _conn_mgr_impl_ctx:

Context Pointer
===============

Since a single connectivity implementation may be shared by several Zephyr ifaces, each binding instantiates a context container (of :ref:`configurable type <conn_mgr_impl_declaring>`) unique to that binding.
Each binding is then instantiated with a reference to that container, which implementations can then use to access per-iface state information.

See also :ref:`conn_mgr_impl_guidelines_binding_access` and :ref:`conn_mgr_impl_guidelines_no_instancing`.

.. _conn_mgr_impl_defining:

Defining an implementation
==========================

A connectivity implementation may be defined as follows:

.. code-block:: c

   /* Create the API implementation functions */
   int my_connect_impl(struct conn_mgr_conn_binding *const binding) {
           /* Cause your underlying technology to associate */
   }
   int my_disconnect_impl(struct conn_mgr_conn_binding *const binding) {
           /* Cause your underlying technology to disassociate */
   }
   void my_init_impl(struct conn_mgr_conn_binding *const binding) {
           /* Perform any required initialization for your underlying technology */
   }

   /* Declare the API struct */
   static struct conn_mgr_conn_api my_impl_api = {
           .connect = my_connect_impl,
           .disconnect = my_disconnect_impl,
           .init = my_init_impl,
           /* ... so on */
   };

   /* Define the implementation (named MY_CONNECTIVITY_IMPL) */
   CONN_MGR_CONN_DEFINE(MY_CONNECTIVITY_IMPL, &my_impl_api);

.. note::
   This does not work unless you also :ref:`declare the context pointer type <conn_mgr_impl_declaring_ctx>`.

.. _conn_mgr_impl_declaring:

Declaring an implementation publicly
====================================

Once defined, you can make a connectivity implementation available to other compilation units by declaring it (in a header file) as follows:

.. code-block:: c
   :caption: ``my_connectivity_header.h``

   CONN_MGR_CONN_DECLARE_PUBLIC(MY_CONNECTIVITY_IMPL);

The header file that contains this declaration must be included in any compilation units that need to reference the implementation.

.. _conn_mgr_impl_declaring_ctx:

Declaring a context type
========================

For :c:macro:`CONN_MGR_CONN_DEFINE` to work, you must declare a corresponding context pointer type.
This is because all connectivity bindings contain a :ref:`conn_mgr_impl_ctx` of their associated context pointer type.

If you are using :c:macro:`CONN_MGR_CONN_DECLARE_PUBLIC`, declare this type alongside the declaration:

.. code-block:: c
   :caption: ``my_connectivity_impl.h``

   #define MY_CONNECTIVITY_IMPL_CTX_TYPE struct my_context_type *
   CONN_MGR_CONN_DECLARE_PUBLIC(MY_CONNECTIVITY_IMPL);

Then, make sure to include the header file before calling :c:macro:`CONN_MGR_CONN_DEFINE`:

.. code-block:: c
   :caption: ``my_connectivity_impl.c``

   #include "my_connectivity_impl.h"

   CONN_MGR_CONN_DEFINE(MY_CONNECTIVITY_IMPL, &my_impl_api);

Otherwise, it is sufficient to simply declare the context pointer type immediately before the call to :c:macro:`CONN_MGR_CONN_DEFINE`:

.. code-block:: c

   #define MY_CONNECTIVITY_IMPL_CTX_TYPE struct my_context_type *
   CONN_MGR_CONN_DEFINE(MY_CONNECTIVITY_IMPL, &my_impl_api);

.. note::

   Naming is important.
   Your context pointer type declaration must use the same name as your implementation declaration, but with ``_CTX_TYPE`` appended.

   In the previous example, the context type is named ``MY_CONNECTIVITY_IMPL_CTX_TYPE``, because ``MY_CONNECTIVITY_IMPL`` was used as the connectivity implementation name.

If your connectivity implementation does not need a context pointer, simply declare the type as void:

.. code-block:: c

   #define MY_CONNECTIVITY_IMPL_CTX_TYPE void *

.. _conn_mgr_impl_binding:

Binding an iface to an implementation
=====================================

A defined connectivity implementation may be bound to an iface by calling :c:macro:`CONN_MGR_BIND_CONN` anywhere after the iface's device definition:

.. code-block:: c

	/* Define an iface */
	NET_DEVICE_INIT(my_iface,
		/* ... the specifics here don't matter ... */
	);

	/* Now bind MY_CONNECTIVITY_IMPL to that iface --
	 * the name used should match with the above
	 */
	CONN_MGR_BIND_CONN(my_iface, MY_CONNECTIVITY_IMPL);

.. _conn_mgr_impl_guidelines:

Connectivity implementation guidelines
======================================

Rather than implement all features centrally, Connection Manager relies on each connectivity implementation to implement many behaviors and features individually.

This approach allows Connection Manager to remain lean, and allows each connectivity implementation to choose the most appropriate approach to these behaviors for itself.
However, it relies on trust that all connectivity implementations will faithfully implement the features that have been delegated to them.

To maintain consistency between all connectivity implementations, observe the following guidelines when writing your own implementation:

.. _conn_mgr_impl_guidelines_timeout_persistence:

*Completely implement timeout and persistence*
----------------------------------------------

All connectivity implementations must offer complete support for :ref:`timeout and persistence <conn_mgr_control_persistence_timeouts>`, such that a user can disable or enable these features, regardless of the inherent behavior of the underlying technology.
In other words, no matter how the underlying technology behaves, your implementation must make it appear to the end user to behave exactly as specified in the :ref:`conn_mgr_control_persistence_timeouts` section.

See :ref:`conn_mgr_impl_timeout_persistence` for a detailed technical discussion on implementing timeouts and persistence.

.. _conn_mgr_impl_guidelines_conformity:

*Conform to API specifications*
-------------------------------

Each :c:struct:`implementation API function <conn_mgr_conn_api>` you implement should behave as-described in the corresponding connectivity control API function.

For example, your implementation of :c:member:`conn_mgr_conn_api.connect` should conform to the behavior described for :c:func:`conn_mgr_if_connect`.

.. _conn_mgr_impl_guidelines_preconfig:

*Allow connectivity pre-configuration*
--------------------------------------

Connectivity implementations should provide means for applications to pre-configure all necessary connection parameters (for example, network SSID, or PSK, if applicable), before the call to :c:func:`conn_mgr_if_connect`.
It should not be necessary to provide this information as part of, or following the :c:func:`conn_mgr_if_connect` call, although implementations :ref:`should await this information if it is not provided <conn_mgr_impl_guidelines_await_config>`.

.. _conn_mgr_impl_guidelines_await_config:

*Await valid connectivity configuration*
----------------------------------------

If network association fails because the application pre-configured invalid connection parameters, or did not configure connection parameters at all, this should be treated as a network failure.

In other words, the connectivity implementation should not give up on the connection attempt, even if valid connection parameters have not been configured.

Instead, the connectivity implementation should asynchronously wait for valid connection parameters to be configured, either indefinitely, or until the configured :ref:`connectivity timeout <conn_mgr_control_timeouts>` elapses.

.. _conn_mgr_impl_guidelines_iface_state_reporting:

*Implement iface state reporting*
---------------------------------

All connectivity implementations must keep bound iface state up to date.

To be specific:

* Set the iface to dormant, carrier-down, or both during :c:member:`binding init <conn_mgr_conn_api.init>`.

  *  See :ref:`net_if_interface_state_management` for details regarding iface carrier and dormant states.

* Update dormancy and carrier state so that the iface is non-dormant and carrier-up whenever (and only when) association is complete and connectivity is ready.
* Set the iface either to dormant or to carrier-down as soon as interruption of service is detected.

  * It is acceptable to gate this behind a small timeout (separate from the connection timeout) for network technologies where service is commonly intermittent.

* If the technology also handles IP assignment, ensure those IP addresses are :ref:`assigned to the iface <net_if_interface_ip_management>`.

.. note::

   iface state updates do not necessarily need to be performed directly by connectivity implementations.

   For instance:

   * IP assignment is not necessary if :ref:`DHCP <dhcpv4_interface>` is used for the iface.
   * The connectivity implementation does not need to update iface dormancy if the underlying :ref:`L2 implementation <net_l2_interface>` already does so.

.. _conn_mgr_impl_guidelines_iface_state_writeonly:

*Do not use iface state as implementation state*
------------------------------------------------

Zephyr ifaces may be accessed from other threads without respecting the binding mutex.
As such, Zephyr iface state may change unpredictably during connectivity implementation callbacks.

Therefore, do not base implementation behaviors on iface state.

Keep iface state updated to reflect network availability, but do not read iface state for any purpose.

If you need to keep track of dormancy or IP assignment, use a separate state variable stored in the :ref:`context pointer <conn_mgr_impl_ctx>`.

.. _conn_mgr_impl_guidelines_non_interference:

*Remain non-interferent*
------------------------

Connectivity implementations should not prevent applications from interacting directly with associated technology-specific APIs.

In other words, it should be possible for an application to directly use your underlying technology without breaking your connectivity implementation.

If exceptions to this are absolutely necessary, they should be constrained to specific API calls and should be documented.

.. note::

   While connectivity implementations must not break, it is acceptable for implementations to have potentially unexpected behavior if applications attempt to directly control the association state.

   For instance, if an application directly instructs an underlying technology to disassociate, it would be acceptable for the connectivity implementation to interpret this as an unexpected connection loss and immediately attempt to re-associate.

.. _conn_mgr_impl_guidelines_non_blocking:

*Remain non-blocking*
---------------------

All connectivity implementation callbacks should be non-blocking.

For instance, calls to :c:member:`conn_mgr_conn_api.connect` should initiate a connection process and return immediately.

One exception is :c:member:`conn_mgr_conn_api.init`, whose implementations are permitted to block.

However, bear in mind that blocking during this callback will delay system init, so still consider offloading time-consuming tasks to a background thread.

.. _conn_mgr_impl_guidelines_immediate_api_readiness:

*Make API immediately ready*
----------------------------

Connectivity implementations must be ready to receive API calls immediately after :c:member:`conn_mgr_conn_api.init`.

For instance, a call to :c:member:`conn_mgr_conn_api.connect` must eventually lead to an association attempt, even if called immediately after :c:member:`conn_mgr_conn_api.init`.

If the underlying technology cannot be made ready for connect commands immediately when :c:member:`conn_mgr_conn_api.init` is called, calls to :c:member:`conn_mgr_conn_api.connect` must be queued in a non-blocking fashion, and then executed later when ready.

.. _conn_mgr_impl_guidelines_context_pointer:

*Do not store state information outside the context pointer*
------------------------------------------------------------

Connection Manager provides a context pointer to each binding.

Connectivity implementations should store all state information in this context pointer.

The only exception is connectivity implementations that are meant to be bound to only a single iface.
Such implementations may use statically declared state instead.

See also :ref:`conn_mgr_impl_guidelines_no_instancing`.

.. _conn_mgr_impl_guidelines_iface_access:

*Access ifaces only through binding structs*
--------------------------------------------

Do not use statically declared ifaces or externally acquire references to ifaces.

For example, do not use :c:func:`net_if_get_default` under the assumption that the bound iface will be the default iface.

Instead, always use the :c:member:`iface pointer <conn_mgr_conn_binding.iface>` provided by the relevant :c:struct:`binding struct <conn_mgr_conn_binding>`.
See also :ref:`conn_mgr_impl_guidelines_binding_access`.

.. _conn_mgr_impl_guidelines_bindings_optional:

*Make implementations optional at compile-time*
-----------------------------------------------

Connectivity implementations should provide a Kconfig option to enable or disable the implementation without affecting bound iface availability.

In other words, it should be possible to configure builds that include Connectivity Manager, as well as the iface that would have been bound to the implementation, but not the implementation itself, nor its binding.

.. _conn_mgr_impl_guidelines_no_instancing:

*Do not instance implementations*
---------------------------------

Do not declare a separate connectivity implementation for every iface you are going to bind to.

Instead, bind one global connectivity implementation to all of your ifaces, and use the context pointer to store state relevant to individual ifaces.

See also :ref:`conn_mgr_impl_guidelines_binding_access` and :ref:`conn_mgr_impl_guidelines_iface_access`.

.. _conn_mgr_impl_guidelines_binding_access:

*Do not access bindings without locking them*
---------------------------------------------

Bindings may be accessed and modified at random by multiple threads, so modifying or reading from a binding without first :c:func:`locking it <conn_mgr_binding_lock>` may lead to unpredictable behavior.

This applies to all descendents of the binding, including anything in the :ref:`context container <conn_mgr_impl_ctx>`.

Make sure to :c:func:`unlock <conn_mgr_binding_unlock>` the binding when you are done accessing it.

.. note::

   A possible exception to this rule is if the resource in question is inherently thread-safe.

   However, be careful taking advantage of this exception.
   It may still be possible to create a race condition, for instance when accessing multiple thread-safe resources simultaneously.

   Therefore, it is recommended to simply always lock the binding, whether or not the resource being accessed is inherently thread-safe.

.. _conn_mgr_impl_guidelines_support_builtins:

*Do not disable built-in features*
----------------------------------

Do not attempt to prevent the use of built-in features (such as :ref:`conn_mgr_control_persistence_timeouts` or :ref:`conn_mgr_control_automations`).

All connectivity implementations must fully support these features.
Implementations must not attempt to force certain features to be always enabled or always disabled.

.. _conn_mgr_impl_guidelines_trigger_events:

*Trigger connectivity control events*
-------------------------------------

Connectivity control :ref:`network management <net_mgmt_interface>` events are not triggered automatically by Connection Manager.

Connectivity implementations must trigger these events themselves.

Trigger :c:macro:`NET_EVENT_CONN_CMD_IF_TIMEOUT` when a connection :ref:`timeout <conn_mgr_control_timeouts>` occurs.
See :ref:`conn_mgr_control_events_timeout` for details.

Trigger :c:macro:`NET_EVENT_CONN_IF_FATAL_ERROR` when a fatal (non-recoverable) connection error occurs.
See :ref:`conn_mgr_control_events_fatal_error` for details.

See :ref:`net_mgmt_interface` for details on firing network management events.

.. _conn_mgr_impl_timeout_persistence:

Implementing timeouts and persistence
=====================================

First, see :ref:`conn_mgr_control_persistence_timeouts` for a high-level description of the expected behavior of timeouts and persistence.

Connectivity implementations must fully conform to that description, regardless of the behavior of the underlying connectivity technology.

Sometimes this means writing extra logic in the connectivity implementation to fake certain behaviors.
The following sections discuss various common edge-cases and nuances and how to handle them.

.. _conn_mgr_impl_tp_inherent_persistence:

*Inherently persistent technologies*
------------------------------------

If the underlying technology automatically attempts to reconnect or retry connection after connection loss or failure, the connectivity implementation must manually cancel such attempts when they are in conflict with timeout or persistence settings.

For example:

  * If the underlying technology automatically attempts to reconnect after losing connection, and persistence is disabled for the iface, the connectivity implementation should immediately cancel this reconnection attempt.
  * If a connection attempt times out on an iface whose underlying technology does not have a built-in timeout, the connectivity implementation must simulate a timeout by cancelling the connection attempt manually.

.. _conn_mgr_impl_tp_inherent_nonpersistence:

*Technologiess that give up on connection attempts*
---------------------------------------------------

If the underlying technology has no mechanism to retry connection attempts, or would give up on them before the user-configured timeout, or would not reconnect after connection loss, the connectivity implementation must manually re-request connection to counteract these deviances.

* If your underlying technology is not persistent, you must manually trigger reconnect attempts when persistence is enabled.
* If your underlying technology does not support a timeout, you must manually cancel connection attempts if the timeout is enabled.
* If your underlying technology forces a timeout, you must manually trigger a new connection attempts if that timeout is shorter than the Connection Manager timeout.

.. _conn_mgr_impl_tp_assoc_retry:

*Technologies with association retry*
-------------------------------------

Many underlying technologies do not usually associate in a single attempt.

Instead, these underlying technologies may need to make multiple back-to-back association attempts in a row, usually with a small delay.

In these situations, the connectivity implementation should treat this series of back-to-back association sub-attempts as a single unified connection attempt.

For instance, after a sub-attempt failure, persistence being disabled should not prevent further sub-attempts, since they all count as one single overall connection attempt.
See also :ref:`conn_mgr_impl_tp_persistence_during_connect`.

At which point a series of failed sub-attempts should be considered a failure of the connection attempt as a whole is up to each implementation to decide.

If the connection attempt crosses this threshold, but the configured timeout has not yet elapsed, or there is no timeout, sub-attempts should continue.

.. _conn_mgr_impl_tp_persistence_during_connect:

*Persistence during connection attempts*
----------------------------------------

Persistence should not affect any aspect of implementation behavior during a connection attempt.
Persistence should only affect whether or not connection attempts are automatically triggered after a connection loss.

The configured timeout should fully determine whether connection retry should be performed.

.. _conn_mgr_impl_api:

Implementation API
==================

Include header file :file:`include/zephyr/net/conn_mgr_connectivity_impl.h` to access these.

Only for use by connectivity implementations.

.. doxygengroup:: conn_mgr_connectivity_impl

Part 3

한국어 전문 번역

개요

Connectivity implementation은 특정 Zephyr interface가 Connectivity Control을 지원하도록 만드는 technology-specific module입니다. Generic connectivity control API call을 hardware-specific operation으로 변환하고, 표준화된 persistence와 timeout 동작도 구현할 책임이 있습니다.

규격을 준수하는 connectivity implementation 작성법은 아래 implementation guideline을 참고하십시오.

Architecture

Implementation API를 사용하면 build time에 CONN_MGR_CONN_DEFINE으로 connectivity implementation을 정의할 수 있습니다.

이 macro는 struct conn_mgr_conn_impl의 static instance를 만들고, 전달된 struct conn_mgr_conn_api structure를 참조합니다. 이 API structure에는 implementation callback을 채워야 합니다.

정의한 implementation은 name으로 참조하고 CONN_MGR_BIND_CONN으로 아직 binding되지 않은 interface에 연결할 수 있습니다. 한 interface에 두 connectivity implementation을 실수로 binding하지 않도록 주의하십시오.

Binding이 끝나면 interface에 connectivity control API function을 호출할 수 있고, call은 struct conn_mgr_conn_api의 대응 implementation function으로 변환됩니다.

iface를 binding해도 struct net_if 자체를 직접 수정하지 않습니다. 대신 struct conn_mgr_conn_binding instance를 만들어 internal iterable section에 append합니다.

Binding structure에는 연결된 interface, 그 interface에 연결한 connectivity implementation, interface별 context pointer를 가리키는 pointer가 들어 있습니다.

Iterable section을 순회하면 특정 interface에 어떤 connectivity implementation이 binding됐는지 확인할 수 있습니다. Connectivity control API 대부분이 이 search를 사용하므로 비용이 비교적 높으며 해당 function은 아껴 호출해야 합니다.

하나의 connectivity implementation을 여러 interface에 binding할 수 있습니다. 자세한 내용은 implementation을 instance화하지 말라는 guideline을 참고하십시오.

Connection Manager가 Zephyr 및 application과 통합되는 상세 구조
Connection Manager와 application·Zephyr 구성 요소의 상세 integration 구조. 단순화한 도식은 Connection Manager 본문을 참고하십시오.

Context Pointer

하나의 connectivity implementation을 여러 Zephyr interface가 공유할 수 있으므로 각 binding은 해당 binding에 고유한 configurable type의 context container를 만듭니다. Binding은 이 container의 reference와 함께 생성되고 implementation은 이를 통해 interface별 state information에 접근합니다.

Binding access와 implementation instancing 관련 guideline도 함께 참고하십시오.

Implementation 정의

Connectivity implementation은 다음처럼 정의할 수 있습니다.

/* Create the API implementation functions */
int my_connect_impl(struct conn_mgr_conn_binding *const binding) {
        /* Cause your underlying technology to associate */
}
int my_disconnect_impl(struct conn_mgr_conn_binding *const binding) {
        /* Cause your underlying technology to disassociate */
}
void my_init_impl(struct conn_mgr_conn_binding *const binding) {
        /* Perform any required initialization for your underlying technology */
}

/* Declare the API struct */
static struct conn_mgr_conn_api my_impl_api = {
        .connect = my_connect_impl,
        .disconnect = my_disconnect_impl,
        .init = my_init_impl,
        /* ... so on */
};

/* Define the implementation (named MY_CONNECTIVITY_IMPL) */
CONN_MGR_CONN_DEFINE(MY_CONNECTIVITY_IMPL, &my_impl_api);

Implementation을 public으로 선언

정의한 connectivity implementation을 다른 compilation unit에서 사용할 수 있게 하려면 header file에서 다음처럼 선언합니다.

CONN_MGR_CONN_DECLARE_PUBLIC(MY_CONNECTIVITY_IMPL);

Implementation을 참조해야 하는 모든 compilation unit에 이 declaration을 담은 header file을 include해야 합니다.

Context type 선언

CONN_MGR_CONN_DEFINE이 동작하려면 대응하는 context pointer type을 선언해야 합니다. 모든 connectivity binding이 연결된 context pointer type의 context를 포함하기 때문입니다.

CONN_MGR_CONN_DECLARE_PUBLIC을 사용한다면 declaration 옆에 type을 함께 선언합니다.

#define MY_CONNECTIVITY_IMPL_CTX_TYPE struct my_context_type *
CONN_MGR_CONN_DECLARE_PUBLIC(MY_CONNECTIVITY_IMPL);

그 다음 CONN_MGR_CONN_DEFINE을 호출하기 전에 header file을 include해야 합니다.

#include "my_connectivity_impl.h"

CONN_MGR_CONN_DEFINE(MY_CONNECTIVITY_IMPL, &my_impl_api);

CONN_MGR_CONN_DECLARE_PUBLIC을 쓰지 않는다면 CONN_MGR_CONN_DEFINE 바로 앞에 context pointer type을 선언해도 충분합니다.

#define MY_CONNECTIVITY_IMPL_CTX_TYPE struct my_context_type *
CONN_MGR_CONN_DEFINE(MY_CONNECTIVITY_IMPL, &my_impl_api);

Connectivity implementation에 context pointer가 필요 없다면 type을 void로 선언합니다.

#define MY_CONNECTIVITY_IMPL_CTX_TYPE void *

Interface를 implementation에 binding

정의한 connectivity implementation은 interface의 device definition 뒤 어느 위치에서든 CONN_MGR_BIND_CONN을 호출해 binding할 수 있습니다.

/* Define an iface */
NET_DEVICE_INIT(my_iface,
	/* ... the specifics here don't matter ... */
);

/* Now bind MY_CONNECTIVITY_IMPL to that iface --
 * the name used should match with the above
 */
CONN_MGR_BIND_CONN(my_iface, MY_CONNECTIVITY_IMPL);

Connectivity implementation guideline

Connection Manager는 모든 feature를 중앙에서 구현하지 않고 각 connectivity implementation이 여러 동작과 feature를 개별적으로 구현하도록 합니다.

이 접근은 Connection Manager를 가볍게 유지하고 각 implementation이 해당 technology에 가장 알맞은 방식을 선택하게 합니다. 대신 위임받은 feature를 모든 implementation이 충실히 구현한다는 신뢰가 필요합니다. 일관성을 위해 다음 guideline을 지키십시오.

Timeout과 persistence를 완전히 구현

모든 connectivity implementation은 underlying technology의 고유 동작과 무관하게 사용자가 timeout과 persistence를 끄거나 켤 수 있도록 완전히 지원해야 합니다. End user에게는 Connection Manager specification에 적힌 동작과 정확히 같아 보이도록 구현해야 합니다.

API specification 준수

구현하는 각 conn_mgr_conn_api function은 대응 connectivity control API function의 설명대로 동작해야 합니다. 예를 들어 conn_mgr_conn_api.connect 구현은 conn_mgr_if_connect()의 동작을 따라야 합니다.

Connectivity 사전 설정 허용

Application이 conn_mgr_if_connect() 호출 전에 network SSID나 PSK 같은 필요한 connection parameter를 모두 사전 설정할 수단을 제공해야 합니다. 이 정보는 connect call의 일부로 또는 호출 뒤에 제공할 필요가 없어야 하지만, 제공되지 않았다면 implementation은 정보를 기다려야 합니다.

유효한 connectivity configuration 대기

Application이 잘못된 connection parameter를 사전 설정했거나 전혀 설정하지 않아 network association이 실패하면 network failure로 다뤄야 합니다.

유효한 parameter가 없더라도 connection attempt를 포기하지 마십시오. 유효한 parameter가 설정될 때까지 비동기로 무기한 기다리거나 설정된 connectivity timeout이 끝날 때까지 기다려야 합니다.

Interface state reporting 구현

모든 connectivity implementation은 binding된 interface state를 최신 상태로 유지해야 합니다. 구체적으로 다음을 수행합니다.

  • Binding initialization(conn_mgr_conn_api.init) 중 interface를 dormant 또는 carrier-down, 혹은 둘 다로 설정합니다.
  • Association이 완료되고 connectivity가 준비된 경우에만 interface가 non-dormant이고 carrier-up이 되도록 dormancy와 carrier state를 갱신합니다.
  • Service interruption을 감지하는 즉시 interface를 dormant 또는 carrier-down으로 설정합니다. Service가 흔히 intermittent한 technology에서는 connection timeout과 별도의 짧은 timeout 뒤에 적용해도 됩니다.
  • Technology가 IP assignment도 처리한다면 그 IP address가 interface에 할당되도록 합니다.

Interface state를 implementation state로 사용하지 않기

다른 thread가 binding mutex를 존중하지 않고 Zephyr interface에 접근할 수 있으므로 connectivity implementation callback 중에도 interface state가 예측할 수 없이 바뀔 수 있습니다.

따라서 implementation 동작을 interface state에 근거하지 마십시오. Network availability를 반영하도록 state를 써서 갱신하되 어떤 목적으로도 읽지 마십시오. Dormancy나 IP assignment를 추적해야 하면 context pointer에 별도 state variable을 저장합니다.

Non-interferent 유지

Connectivity implementation은 application이 관련 technology-specific API와 직접 상호작용하는 것을 막아서는 안 됩니다. Application이 underlying technology를 직접 사용해도 implementation이 깨지지 않아야 합니다.

예외가 꼭 필요하다면 특정 API call에만 한정하고 문서화해야 합니다.

Non-blocking 유지

모든 connectivity implementation callback은 non-blocking이어야 합니다. 예를 들어 conn_mgr_conn_api.connect call은 connection process를 시작하고 즉시 반환해야 합니다.

conn_mgr_conn_api.init 구현은 예외적으로 block할 수 있습니다. 다만 이 callback에서 block하면 system init이 지연되므로 시간이 오래 걸리는 task는 background thread로 offload하는 방안을 고려하십시오.

API를 즉시 ready 상태로 만들기

Connectivity implementation은 conn_mgr_conn_api.init 직후부터 API call을 받을 준비가 돼야 합니다. Init 직후 conn_mgr_conn_api.connect를 호출해도 결국 association attempt로 이어져야 합니다.

Init 시점에 underlying technology를 connect command에 즉시 준비시킬 수 없다면 connect call을 non-blocking 방식으로 queue에 넣고 나중에 준비됐을 때 실행해야 합니다.

Context pointer 밖에 state 저장 금지

Connection Manager는 각 binding에 context pointer를 제공합니다. Connectivity implementation은 모든 state information을 이 context pointer에 저장해야 합니다. 단 하나의 interface에만 binding하도록 만든 implementation은 예외적으로 static state를 사용할 수 있습니다.

Binding structure를 통해서만 interface 접근

net_if_get_default()로 binding된 interface가 default interface일 것이라 가정하는 식으로 static interface를 사용하거나 외부에서 interface reference를 얻지 마십시오.

항상 해당 struct conn_mgr_conn_binding이 제공하는 conn_mgr_conn_binding.iface pointer를 사용하십시오.

Compile time에 implementation을 optional로 만들기

Binding될 interface의 availability에 영향을 주지 않고 implementation을 켜거나 끌 수 있는 Kconfig option을 제공해야 합니다. Connection Manager와 interface는 포함하지만 implementation과 binding은 포함하지 않는 build도 구성할 수 있어야 합니다.

Implementation을 instance화하지 않기

Binding할 interface마다 별도 connectivity implementation을 선언하지 마십시오. 하나의 global implementation을 모든 interface에 binding하고 context pointer에 interface별 state를 저장하십시오.

Lock 없이 binding 접근 금지

여러 thread가 binding을 임의로 접근·수정할 수 있으므로 먼저 conn_mgr_binding_lock()으로 lock하지 않고 binding을 읽거나 수정하면 동작을 예측할 수 없습니다. Context container를 포함한 binding의 모든 descendant에도 적용됩니다. 접근이 끝나면 conn_mgr_binding_unlock()으로 unlock하십시오.

Resource 자체가 thread-safe이면 예외일 수 있지만 여러 thread-safe resource를 동시에 접근할 때도 race condition이 생길 수 있습니다. Resource의 thread safety와 관계없이 항상 binding을 lock하는 방식을 권장합니다.

Built-in feature 비활성화 금지

Timeout·persistence나 automation 같은 built-in feature 사용을 막지 마십시오. 모든 connectivity implementation은 이 feature를 완전히 지원해야 하며 특정 feature를 항상 켜거나 항상 끄도록 강제해서는 안 됩니다.

Connectivity control event trigger

Connectivity control network management event는 Connection Manager가 자동으로 trigger하지 않습니다. Connectivity implementation이 직접 trigger해야 합니다.

Connection timeout이 발생하면 NET_EVENT_CONN_CMD_IF_TIMEOUT을 trigger합니다. 복구할 수 없는 fatal connection error가 발생하면 NET_EVENT_CONN_IF_FATAL_ERROR를 trigger합니다. Network management event 발생 방법은 Network Management 문서를 참고하십시오.

Timeout과 persistence 구현

먼저 Connection Manager 본문의 timeout·persistence 기대 동작을 확인하십시오. Underlying connectivity technology의 동작과 무관하게 implementation은 그 설명을 완전히 준수해야 합니다.

일부 경우에는 특정 동작을 흉내 내는 추가 logic이 필요합니다. 다음 절은 흔한 edge case와 처리 방법을 설명합니다.

본질적으로 persistent한 technology

Underlying technology가 connection loss나 failure 뒤에 자동으로 reconnect 또는 retry한다면 timeout·persistence 설정과 충돌하는 attempt를 implementation이 수동으로 취소해야 합니다.

  • Connection을 잃은 뒤 technology가 자동 reconnect하고 interface의 persistence가 꺼져 있다면 reconnect attempt를 즉시 취소합니다.
  • Built-in timeout이 없는 technology에서 connection attempt가 timeout되면 attempt를 수동 취소해 timeout을 simulate합니다.

Connection attempt를 포기하는 technology

Underlying technology가 retry mechanism이 없거나 user-configured timeout 전에 포기하거나 connection loss 뒤 reconnect하지 않는다면 implementation이 connection을 다시 요청해 이 차이를 보완해야 합니다.

  • Underlying technology가 persistent하지 않고 persistence가 켜져 있으면 reconnect attempt를 수동 trigger합니다.
  • Underlying technology가 timeout을 지원하지 않고 Connection Manager timeout이 켜져 있으면 connection attempt를 수동 취소합니다.
  • Technology가 timeout을 강제하고 그 값이 Connection Manager timeout보다 짧으면 새 connection attempt를 수동 trigger합니다.

Association retry가 있는 technology

많은 underlying technology는 한 번의 attempt만으로 association하지 못하고, 보통 짧은 delay를 두고 여러 association sub-attempt를 연속 수행해야 합니다.

이 경우 연속 sub-attempt 전체를 하나의 통합 connection attempt로 다뤄야 합니다. Sub-attempt 하나가 실패해도 persistence가 꺼져 있다는 이유로 다음 sub-attempt를 막아서는 안 됩니다. 모두 하나의 전체 attempt에 속하기 때문입니다.

여러 실패한 sub-attempt를 언제 전체 connection attempt의 실패로 볼지는 implementation이 정합니다. 그 threshold를 넘었더라도 configured timeout이 끝나지 않았거나 timeout이 없다면 sub-attempt는 계속돼야 합니다.

Connection attempt 중 persistence

Persistence는 connection attempt 도중 implementation 동작의 어떤 부분에도 영향을 주면 안 됩니다. Persistence는 connection loss 뒤 connection attempt를 자동으로 trigger할지 여부에만 영향을 줍니다.

Connection retry 수행 여부는 configured timeout이 전적으로 결정해야 합니다.

Implementation API

include/zephyr/net/conn_mgr_connectivity_impl.h header를 include해 API에 접근합니다. Connectivity implementation 전용 API입니다.

Doxygen group conn_mgr_connectivity_impl이 implementation API reference를 제공합니다.

Source

출처

영어 원문에는 문단, 문서 지시문, 표, 코드, 코드 심벌, 경로를 그대로 보존했습니다.