1.. _conn_mgr_impl: 2 3Connectivity Implementations 4############################ 5 6.. _conn_mgr_impl_overview: 7 8Overview 9======== 10 11Connectivity implementations are technology-specific modules that allow specific Zephyr ifaces to support :ref:`Connectivity Control <conn_mgr_control>`. 12They are responsible for translating generic :ref:`connectivity control API <conn_mgr_control_api>` calls into hardware-specific operations. 13They are also responsible for implementing standardized :ref:`persistence and timeout <conn_mgr_control_persistence_timeouts>` behaviors. 14 15See the :ref:`implementation guidelines <conn_mgr_impl_guidelines>` for details on writing conformant connectivity implementations. 16 17.. _conn_mgr_impl_architecture: 18 19Architecture 20============ 21 22The :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`. 23 24This 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). 25 26Once defined, you can reference implementations by name and bind them to any unbound iface using :c:macro:`CONN_MGR_BIND_CONN`. 27Make sure not to accidentally bind two connectivity implementations to a single iface. 28 29Once 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`. 30 31Binding an iface does not directly modify its :c:struct:`iface struct <net_if>`. 32 33Instead, an instance of :c:struct:`conn_mgr_conn_binding` is created and appended an internal :ref:`iterable section <iterable_sections_api>`. 34 35This 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>`. 36 37This iterable section can then be iterated over to find out what (if any) connectivity implementation has been bound to a given iface. 38This search process is used by most of the functions in the :ref:`connectivity control API <conn_mgr_control_api>`. 39As such, these functions should be called sparingly, due to their relatively high search cost. 40 41A single connectivity implementation may be bound to multiple ifaces. 42See :ref:`conn_mgr_impl_guidelines_no_instancing` for more details. 43 44.. _conn_mgr_integration_diagram_detailed: 45 46.. figure:: figures/integration_diagram_detailed.svg 47 :alt: A detailed view of how Connection Manager integrates with Zephyr and the application. 48 :figclass: align-center 49 50 A detailed view of how Connection Manager integrates with Zephyr and the application. 51 52 See :ref:`here <conn_mgr_integration_diagram_simple>` for a simplified version. 53 54.. _conn_mgr_impl_ctx: 55 56Context Pointer 57=============== 58 59Since 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. 60Each binding is then instantiated with a reference to that container, which implementations can then use to access per-iface state information. 61 62See also :ref:`conn_mgr_impl_guidelines_binding_access` and :ref:`conn_mgr_impl_guidelines_no_instancing`. 63 64.. _conn_mgr_impl_defining: 65 66Defining an implementation 67========================== 68 69A connectivity implementation may be defined as follows: 70 71.. code-block:: c 72 73 /* Create the API implementation functions */ 74 int my_connect_impl(struct conn_mgr_conn_binding *const binding) { 75 /* Cause your underlying technology to associate */ 76 } 77 int my_disconnect_impl(struct conn_mgr_conn_binding *const binding) { 78 /* Cause your underlying technology to disassociate */ 79 } 80 void my_init_impl(struct conn_mgr_conn_binding *const binding) { 81 /* Perform any required initialization for your underlying technology */ 82 } 83 84 /* Declare the API struct */ 85 static struct conn_mgr_conn_api my_impl_api = { 86 .connect = my_connect_impl, 87 .disconnect = my_disconnect_impl, 88 .init = my_init_impl, 89 /* ... so on */ 90 }; 91 92 /* Define the implementation (named MY_CONNECTIVITY_IMPL) */ 93 CONN_MGR_CONN_DEFINE(MY_CONNECTIVITY_IMPL, &my_impl_api); 94 95.. note:: 96 This does not work unless you also :ref:`declare the context pointer type <conn_mgr_impl_declaring_ctx>`. 97 98.. _conn_mgr_impl_declaring: 99 100Declaring an implementation publicly 101==================================== 102 103Once defined, you can make a connectivity implementation available to other compilation units by declaring it (in a header file) as follows: 104 105.. code-block:: c 106 :caption: ``my_connectivity_header.h`` 107 108 CONN_MGR_CONN_DECLARE_PUBLIC(MY_CONNECTIVITY_IMPL); 109 110The header file that contains this declaration must be included in any compilation units that need to reference the implementation. 111 112.. _conn_mgr_impl_declaring_ctx: 113 114Declaring a context type 115======================== 116 117For :c:macro:`CONN_MGR_CONN_DEFINE` to work, you must declare a corresponding context pointer type. 118This is because all connectivity bindings contain a :ref:`conn_mgr_impl_ctx` of their associated context pointer type. 119 120If you are using :c:macro:`CONN_MGR_CONN_DECLARE_PUBLIC`, declare this type alongside the declaration: 121 122.. code-block:: c 123 :caption: ``my_connectivity_impl.h`` 124 125 #define MY_CONNECTIVITY_IMPL_CTX_TYPE struct my_context_type * 126 CONN_MGR_CONN_DECLARE_PUBLIC(MY_CONNECTIVITY_IMPL); 127 128Then, make sure to include the header file before calling :c:macro:`CONN_MGR_CONN_DEFINE`: 129 130.. code-block:: c 131 :caption: ``my_connectivity_impl.c`` 132 133 #include "my_connectivity_impl.h" 134 135 CONN_MGR_CONN_DEFINE(MY_CONNECTIVITY_IMPL, &my_impl_api); 136 137Otherwise, it is sufficient to simply declare the context pointer type immediately before the call to :c:macro:`CONN_MGR_CONN_DEFINE`: 138 139.. code-block:: c 140 141 #define MY_CONNECTIVITY_IMPL_CTX_TYPE struct my_context_type * 142 CONN_MGR_CONN_DEFINE(MY_CONNECTIVITY_IMPL, &my_impl_api); 143 144.. note:: 145 146 Naming is important. 147 Your context pointer type declaration must use the same name as your implementation declaration, but with ``_CTX_TYPE`` appended. 148 149 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. 150 151If your connectivity implementation does not need a context pointer, simply declare the type as void: 152 153.. code-block:: c 154 155 #define MY_CONNECTIVITY_IMPL_CTX_TYPE void * 156 157.. _conn_mgr_impl_binding: 158 159Binding an iface to an implementation 160===================================== 161 162A defined connectivity implementation may be bound to an iface by calling :c:macro:`CONN_MGR_BIND_CONN` anywhere after the iface's device definition: 163 164.. code-block:: c 165 166 /* Define an iface */ 167 NET_DEVICE_INIT(my_iface, 168 /* ... the specifics here don't matter ... */ 169 ); 170 171 /* Now bind MY_CONNECTIVITY_IMPL to that iface -- 172 * the name used should match with the above 173 */ 174 CONN_MGR_BIND_CONN(my_iface, MY_CONNECTIVITY_IMPL); 175 176.. _conn_mgr_impl_guidelines: 177 178Connectivity implementation guidelines 179====================================== 180 181Rather than implement all features centrally, Connection Manager relies on each connectivity implementation to implement many behaviors and features individually. 182 183This approach allows Connection Manager to remain lean, and allows each connectivity implementation to choose the most appropriate approach to these behaviors for itself. 184However, it relies on trust that all connectivity implementations will faithfully implement the features that have been delegated to them. 185 186To maintain consistency between all connectivity implementations, observe the following guidelines when writing your own implementation: 187 188.. _conn_mgr_impl_guidelines_timeout_persistence: 189 190*Completely implement timeout and persistence* 191---------------------------------------------- 192 193All 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. 194In 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. 195 196See :ref:`conn_mgr_impl_timeout_persistence` for a detailed technical discussion on implementing timeouts and persistence. 197 198.. _conn_mgr_impl_guidelines_conformity: 199 200*Conform to API specifications* 201------------------------------- 202 203Each :c:struct:`implementation API function <conn_mgr_conn_api>` you implement should behave as-described in the corresponding connectivity control API function. 204 205For example, your implementation of :c:member:`conn_mgr_conn_api.connect` should conform to the behavior described for :c:func:`conn_mgr_if_connect`. 206 207.. _conn_mgr_impl_guidelines_preconfig: 208 209*Allow connectivity pre-configuration* 210-------------------------------------- 211 212Connectivity 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`. 213It 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>`. 214 215.. _conn_mgr_impl_guidelines_await_config: 216 217*Await valid connectivity configuration* 218---------------------------------------- 219 220If 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. 221 222In other words, the connectivity implementation should not give up on the connection attempt, even if valid connection parameters have not been configured. 223 224Instead, 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. 225 226.. _conn_mgr_impl_guidelines_iface_state_reporting: 227 228*Implement iface state reporting* 229--------------------------------- 230 231All connectivity implementations must keep bound iface state up to date. 232 233To be specific: 234 235* Set the iface to dormant, carrier-down, or both during :c:member:`binding init <conn_mgr_conn_api.init>`. 236 237 * See :ref:`net_if_interface_state_management` for details regarding iface carrier and dormant states. 238 239* 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. 240* Set the iface either to dormant or to carrier-down as soon as interruption of service is detected. 241 242 * It is acceptable to gate this behind a small timeout (separate from the connection timeout) for network technologies where service is commonly intermittent. 243 244* If the technology also handles IP assignment, ensure those IP addresses are :ref:`assigned to the iface <net_if_interface_ip_management>`. 245 246.. note:: 247 248 iface state updates do not necessarily need to be performed directly by connectivity implementations. 249 250 For instance: 251 252 * IP assignment is not necessary if :ref:`DHCP <dhcpv4_interface>` is used for the iface. 253 * The connectivity implementation does not need to update iface dormancy if the underlying :ref:`L2 implementation <net_l2_interface>` already does so. 254 255.. _conn_mgr_impl_guidelines_iface_state_writeonly: 256 257*Do not use iface state as implementation state* 258------------------------------------------------ 259 260Zephyr ifaces may be accessed from other threads without respecting the binding mutex. 261As such, Zephyr iface state may change unpredictably during connectivity implementation callbacks. 262 263Therefore, do not base implementation behaviors on iface state. 264 265Keep iface state updated to reflect network availability, but do not read iface state for any purpose. 266 267If 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>`. 268 269.. _conn_mgr_impl_guidelines_non_interference: 270 271*Remain non-interferent* 272------------------------ 273 274Connectivity implementations should not prevent applications from interacting directly with associated technology-specific APIs. 275 276In other words, it should be possible for an application to directly use your underlying technology without breaking your connectivity implementation. 277 278If exceptions to this are absolutely necessary, they should be constrained to specific API calls and should be documented. 279 280.. note:: 281 282 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. 283 284 For instance, if an application directly instructs an underlying technology to dissassociate, it would be acceptable for the connectivity implementation to interpret this as an unexpected connection loss and immediately attempt to re-associate. 285 286.. _conn_mgr_impl_guidelines_non_blocking: 287 288*Remain non-blocking* 289--------------------- 290 291All connectivity implementation callbacks should be non-blocking. 292 293For instance, calls to :c:member:`conn_mgr_conn_api.connect` should initiate a connection process and return immediately. 294 295One exception is :c:member:`conn_mgr_conn_api.init`, whose implementations are permitted to block. 296 297However, bear in mind that blocking during this callback will delay system init, so still consider offloading time-consuming tasks to a background thread. 298 299.. _conn_mgr_impl_guidelines_immediate_api_readiness: 300 301*Make API immediately ready* 302---------------------------- 303 304Connectivity implementations must be ready to receive API calls immediately after :c:member:`conn_mgr_conn_api.init`. 305 306For 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`. 307 308If 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. 309 310.. _conn_mgr_impl_guidelines_context_pointer: 311 312*Do not store state information outside the context pointer* 313------------------------------------------------------------ 314 315Connection Manager provides a context pointer to each binding. 316 317Connectivity implementations should store all state information in this context pointer. 318 319The only exception is connectivity implementations that are meant to be bound to only a single iface. 320Such implementations may use statically declared state instead. 321 322See also :ref:`conn_mgr_impl_guidelines_no_instancing`. 323 324.. _conn_mgr_impl_guidelines_iface_access: 325 326*Access ifaces only through binding structs* 327-------------------------------------------- 328 329Do not use statically declared ifaces or externally acquire references to ifaces. 330 331For example, do not use :c:func:`net_if_get_default` under the assumption that the bound iface will be the default iface. 332 333Instead, always use the :c:member:`iface pointer <conn_mgr_conn_binding.iface>` provided by the relevant :c:struct:`binding struct <conn_mgr_conn_binding>`. 334See also :ref:`conn_mgr_impl_guidelines_binding_access`. 335 336.. _conn_mgr_impl_guidelines_bindings_optional: 337 338*Make implementations optional at compile-time* 339----------------------------------------------- 340 341Connectivity implementations should provide a Kconfig option to enable or disable the implementation without affecting bound iface availability. 342 343In 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. 344 345.. _conn_mgr_impl_guidelines_no_instancing: 346 347*Do not instance implementations* 348--------------------------------- 349 350Do not declare a separate connectivity implementation for every iface you are going to bind to. 351 352Instead, bind one global connectivity implementation to all of your ifaces, and use the context pointer to store state relevant to individual ifaces. 353 354See also :ref:`conn_mgr_impl_guidelines_binding_access` and :ref:`conn_mgr_impl_guidelines_iface_access`. 355 356.. _conn_mgr_impl_guidelines_binding_access: 357 358*Do not access bindings without locking them* 359--------------------------------------------- 360 361Bindings 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. 362 363This applies to all descendents of the binding, including anything in the :ref:`context container <conn_mgr_impl_ctx>`. 364 365Make sure to :c:func:`unlock <conn_mgr_binding_unlock>` the binding when you are done accessing it. 366 367.. note:: 368 369 A possible exception to this rule is if the resource in question is inherently thread-safe. 370 371 However, be careful taking advantage of this exception. 372 It may still be possible to create a race condition, for instance when accessing multiple thread-safe resources simultaneously. 373 374 Therefore, it is recommended to simply always lock the binding, whether or not the resource being accessed is inherently thread-safe. 375 376.. _conn_mgr_impl_guidelines_support_builtins: 377 378*Do not disable built-in features* 379---------------------------------- 380 381Do not attempt to prevent the use of built-in features (such as :ref:`conn_mgr_control_persistence_timeouts` or :ref:`conn_mgr_control_automations`). 382 383All connectivity implementations must fully support these features. 384Implementations must not attempt to force certain features to be always enabled or always disabled. 385 386.. _conn_mgr_impl_guidelines_trigger_events: 387 388*Trigger connectivity control events* 389------------------------------------- 390 391Connectivity control :ref:`network management <net_mgmt_interface>` events are not triggered automatically by Connection Manager. 392 393Connectivity implementations must trigger these events themselves. 394 395Trigger :c:macro:`NET_EVENT_CONN_CMD_IF_TIMEOUT` when a connection :ref:`timeout <conn_mgr_control_timeouts>` occurs. 396See :ref:`conn_mgr_control_events_timeout` for details. 397 398Trigger :c:macro:`NET_EVENT_CONN_IF_FATAL_ERROR` when a fatal (non-recoverable) connection error occurs. 399See :ref:`conn_mgr_control_events_fatal_error` for details. 400 401See :ref:`net_mgmt_interface` for details on firing network management events. 402 403.. _conn_mgr_impl_timeout_persistence: 404 405Implementing timeouts and persistence 406===================================== 407 408First, see :ref:`conn_mgr_control_persistence_timeouts` for a high-level description of the expected behavior of timeouts and persistence. 409 410Connectivity implementations must fully conform to that description, regardless of the behavior of the underlying connectivity technology. 411 412Sometimes this means writing extra logic in the connectivity implementation to fake certain behaviors. 413The following sections discuss various common edge-cases and nuances and how to handle them. 414 415.. _conn_mgr_impl_tp_inherent_persistence: 416 417*Inherently persistent technologies* 418------------------------------------ 419 420If 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. 421 422For example: 423 424 * 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. 425 * 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. 426 427.. _conn_mgr_impl_tp_inherent_nonpersistence: 428 429*Technologiess that give up on connection attempts* 430--------------------------------------------------- 431 432If 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. 433 434* If your underlying technology is not persistent, you must manually trigger reconnect attempts when persistence is enabled. 435* If your underlying technology does not support a timeout, you must manually cancel connection attempts if the timeout is enabled. 436* 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. 437 438.. _conn_mgr_impl_tp_assoc_retry: 439 440*Technologies with association retry* 441------------------------------------- 442 443Many underlying technologies do not usually associate in a single attempt. 444 445Instead, these underlying technologies may need to make multiple back-to-back association attempts in a row, usually with a small delay. 446 447In these situations, the connectivity implementation should treat this series of back-to-back association sub-attempts as a single unified connection attempt. 448 449For 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. 450See also :ref:`conn_mgr_impl_tp_persistence_during_connect`. 451 452At 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. 453 454If the connection attempt crosses this threshold, but the configured timeout has not yet elapsed, or there is no timeout, sub-attempts should continue. 455 456.. _conn_mgr_impl_tp_persistence_during_connect: 457 458*Persistence during connection attempts* 459---------------------------------------- 460 461Persistence should not affect any aspect of implementation behavior during a connection attempt. 462Persistence should only affect whether or not connection attempts are automatically triggered after a connection loss. 463 464The configured timeout should fully determine whether connection retry should be performed. 465 466.. _conn_mgr_impl_api: 467 468Implementation API 469================== 470 471Include header file :file:`include/zephyr/net/conn_mgr_connectivity_impl.h` to access these. 472 473Only for use by connectivity implementations. 474 475.. doxygengroup:: conn_mgr_connectivity_impl 476