1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief API for defining conn_mgr connectivity implementations (allowing ifaces to be used with
10  * conn_mgr_connectivity).
11  */
12 
13 #ifndef ZEPHYR_INCLUDE_CONN_MGR_CONNECTIVITY_IMPL_H_
14 #define ZEPHYR_INCLUDE_CONN_MGR_CONNECTIVITY_IMPL_H_
15 
16 #include <zephyr/device.h>
17 #include <zephyr/net/net_if.h>
18 #include <zephyr/sys/iterable_sections.h>
19 #include <zephyr/net/net_mgmt.h>
20 #include <zephyr/net/conn_mgr_connectivity.h>
21 
22 #ifdef __cplusplus
23 extern "C" {
24 #endif
25 
26 /**
27  * @brief Connection Manager Connectivity Implementation API
28  * @defgroup conn_mgr_connectivity_impl Connection Manager Connectivity Implementation API
29  * @since 3.4
30  * @version 0.1.0
31  * @ingroup conn_mgr_connectivity
32  * @{
33  */
34 
35 /* Forward declaration */
36 struct conn_mgr_conn_binding;
37 
38 /**
39  * @brief Connectivity Manager Connectivity API structure
40  *
41  * Used to provide generic access to network association parameters and procedures
42  */
43 struct conn_mgr_conn_api {
44 	/**
45 	 * @brief When called, the connectivity implementation should start attempting to
46 	 * establish connectivity (association with a network) for the bound iface pointed
47 	 * to by if_conn->iface.
48 	 *
49 	 * Must be non-blocking.
50 	 *
51 	 * Called by @ref conn_mgr_if_connect.
52 	 */
53 	int (*connect)(struct conn_mgr_conn_binding *const binding);
54 
55 	/**
56 	 * @brief When called, the connectivity implementation should disconnect (disassociate), or
57 	 * stop any in-progress attempts to associate to a network, the bound iface pointed to by
58 	 * if_conn->iface.
59 	 *
60 	 * Must be non-blocking.
61 	 *
62 	 * Called by @ref conn_mgr_if_disconnect.
63 	 */
64 	int (*disconnect)(struct conn_mgr_conn_binding *const binding);
65 
66 	/**
67 	 * @brief Called once for each iface that has been bound to a connectivity implementation
68 	 * using this API.
69 	 *
70 	 * Connectivity implementations should use this callback to perform any required
71 	 * per-bound-iface initialization.
72 	 *
73 	 * Implementations may choose to gracefully handle invalid buffer lengths with partial
74 	 * writes, rather than raise errors, if deemed appropriate.
75 	 */
76 	void (*init)(struct conn_mgr_conn_binding *const binding);
77 
78 	/**
79 	 * @brief Implementation callback for conn_mgr_if_set_opt.
80 	 *
81 	 * Used to set implementation-specific connectivity settings.
82 	 *
83 	 * Calls to conn_mgr_if_set_opt on an iface will result in calls to this callback with
84 	 * the conn_mgr_conn_binding struct bound to that iface.
85 	 *
86 	 * It is up to the connectivity implementation to interpret optname. Options can be
87 	 * specific to the bound iface (pointed to by if_conn->iface), or can apply to the whole
88 	 * connectivity implementation.
89 	 *
90 	 * See the description of conn_mgr_if_set_opt for more details.
91 	 * set_opt implementations should conform to that description.
92 	 *
93 	 * Implementations may choose to gracefully handle invalid buffer lengths with partial
94 	 * reads, rather than raise errors, if deemed appropriate.
95 	 */
96 	int (*set_opt)(struct conn_mgr_conn_binding *const binding,
97 		       int optname, const void *optval, size_t optlen);
98 
99 	/**
100 	 * @brief Implementation callback for conn_mgr_if_get_opt.
101 	 *
102 	 * Used to retrieve implementation-specific connectivity settings.
103 	 *
104 	 * Calls to conn_mgr_if_get_opt on an iface will result in calls to this callback with
105 	 * the conn_mgr_conn_binding struct bound to that iface.
106 	 *
107 	 * It is up to the connectivity implementation to interpret optname. Options can be
108 	 * specific to the bound iface (pointed to by if_conn->iface), or can apply to the whole
109 	 * connectivity implementation.
110 	 *
111 	 * See the description of conn_mgr_if_get_opt for more details.
112 	 * get_opt implementations should conform to that description.
113 	 */
114 	int (*get_opt)(struct conn_mgr_conn_binding *const binding,
115 		       int optname, void *optval, size_t *optlen);
116 };
117 
118 /** @cond INTERNAL_HIDDEN */
119 #define CONN_MGR_CONN_IMPL_GET_NAME(conn_id)		__conn_mgr_conn_##conn_id
120 #define CONN_MGR_CONN_IMPL_GET_CTX_TYPE(conn_id)	conn_id##_CTX_TYPE
121 /** @endcond */
122 
123 /**
124  * @brief Connectivity Implementation struct
125  *
126  * Declares a conn_mgr connectivity layer implementation with the provided API
127  */
128 struct conn_mgr_conn_impl {
129 	/** The connectivity API used by the implementation */
130 	struct conn_mgr_conn_api *api;
131 };
132 
133 /**
134  * @brief Define a conn_mgr connectivity implementation that can be bound to network devices.
135  *
136  * @param conn_id The name of the new connectivity implementation
137  * @param conn_api A pointer to a conn_mgr_conn_api struct
138  */
139 #define CONN_MGR_CONN_DEFINE(conn_id, conn_api)						\
140 	const struct conn_mgr_conn_impl CONN_MGR_CONN_IMPL_GET_NAME(conn_id) = {	\
141 		.api = conn_api,							\
142 	};
143 
144 /**
145  * @brief Helper macro to make a conn_mgr connectivity implementation publicly available.
146  */
147 #define CONN_MGR_CONN_DECLARE_PUBLIC(conn_id)						\
148 	extern const struct conn_mgr_conn_impl CONN_MGR_CONN_IMPL_GET_NAME(conn_id)
149 
150 /** @cond INTERNAL_HIDDEN */
151 #define CONN_MGR_CONN_BINDING_GET_NAME(dev_id, sfx)	__conn_mgr_bndg_##dev_id##_##sfx
152 #define CONN_MGR_CONN_BINDING_GET_DATA(dev_id, sfx)	__conn_mgr_bndg_data_##dev_id##_##sfx
153 #define CONN_MGR_CONN_BINDING_GET_MUTEX(dev_id, sfx)	__conn_mgr_bndg_mutex_##dev_id##_##sfx
154 /** @endcond */
155 
156 /**
157  * @brief Connectivity Manager network interface binding structure
158  *
159  * Binds a conn_mgr connectivity implementation to an iface / network device.
160  * Stores per-iface state for the connectivity implementation.
161  */
162 struct conn_mgr_conn_binding {
163 	/** The network interface the connectivity implementation is bound to */
164 	struct net_if *iface;
165 
166 	/** The connectivity implementation the network device is bound to */
167 	const struct conn_mgr_conn_impl *impl;
168 
169 	/** Pointer to private, per-iface connectivity context */
170 	void *ctx;
171 
172 	/**
173 	 * @name Generic connectivity state
174 	 * @{
175 	 */
176 
177 	/**
178 	 * Connectivity flags
179 	 *
180 	 * Public boolean state and configuration values supported by all bindings.
181 	 * See conn_mgr_if_flag for options.
182 	 */
183 	uint32_t flags;
184 
185 	/**
186 	 * Timeout (seconds)
187 	 *
188 	 * Indicates to the connectivity implementation how long it should attempt to
189 	 * establish connectivity for during a connection attempt before giving up.
190 	 *
191 	 * The connectivity implementation should give up on establishing connectivity after this
192 	 * timeout, even if persistence is enabled.
193 	 *
194 	 * Set to @ref CONN_MGR_IF_NO_TIMEOUT to indicate that no timeout should be used.
195 	 */
196 	int timeout;
197 
198 	/** @} */
199 
200 /** @cond INTERNAL_HIDDEN */
201 	/* Internal-use mutex for protecting access to the binding and API functions. */
202 	struct k_mutex *mutex;
203 /** @endcond */
204 };
205 
206 /**
207  * @brief Associate a connectivity implementation with an existing network device instance
208  *
209  * @param dev_id Network device id.
210  * @param inst Network device instance.
211  * @param conn_id Name of the connectivity implementation to associate.
212  */
213 #define CONN_MGR_BIND_CONN_INST(dev_id, inst, conn_id)						\
214 	K_MUTEX_DEFINE(CONN_MGR_CONN_BINDING_GET_MUTEX(dev_id, inst));				\
215 	static CONN_MGR_CONN_IMPL_GET_CTX_TYPE(conn_id)						\
216 					CONN_MGR_CONN_BINDING_GET_DATA(dev_id, inst);		\
217 	static STRUCT_SECTION_ITERABLE(conn_mgr_conn_binding,					\
218 					CONN_MGR_CONN_BINDING_GET_NAME(dev_id, inst)) = {	\
219 		.iface = NET_IF_GET(dev_id, inst),						\
220 		.impl = &(CONN_MGR_CONN_IMPL_GET_NAME(conn_id)),				\
221 		.ctx = &(CONN_MGR_CONN_BINDING_GET_DATA(dev_id, inst)),				\
222 		.mutex = &(CONN_MGR_CONN_BINDING_GET_MUTEX(dev_id, inst))			\
223 	};
224 
225 /**
226  * @brief Associate a connectivity implementation with an existing network device
227  *
228  * @param dev_id Network device id.
229  * @param conn_id Name of the connectivity implementation to associate.
230  */
231 #define CONN_MGR_BIND_CONN(dev_id, conn_id)		\
232 	CONN_MGR_BIND_CONN_INST(dev_id, 0, conn_id)
233 
234 /**
235  * @brief Retrieves the conn_mgr binding struct for a provided iface if it exists.
236  *
237  * Bindings for connectivity implementations with missing API structs are ignored.
238  *
239  * For use only by connectivity implementations.
240  *
241  * @param iface - bound network interface to obtain the binding struct for.
242  * @return struct conn_mgr_conn_binding* Pointer to the retrieved binding struct if it exists,
243  *	   NULL otherwise.
244  */
conn_mgr_if_get_binding(struct net_if * iface)245 static inline struct conn_mgr_conn_binding *conn_mgr_if_get_binding(struct net_if *iface)
246 {
247 	STRUCT_SECTION_FOREACH(conn_mgr_conn_binding, binding) {
248 		if (iface == binding->iface) {
249 			if (binding->impl->api) {
250 				return binding;
251 			}
252 			return NULL;
253 		}
254 	}
255 	return NULL;
256 }
257 
258 /**
259  * @brief Lock the passed-in binding, making it safe to access.
260  *
261  * Call this whenever accessing binding data, unless inside a conn_mgr_conn_api callback, where it
262  * is called automatically by conn_mgr.
263  *
264  * Reentrant.
265  *
266  * For use only by connectivity implementations.
267  *
268  * @param binding - Binding to lock
269  */
conn_mgr_binding_lock(struct conn_mgr_conn_binding * binding)270 static inline void conn_mgr_binding_lock(struct conn_mgr_conn_binding *binding)
271 {
272 	(void)k_mutex_lock(binding->mutex, K_FOREVER);
273 }
274 
275 /**
276  * @brief Unlocks the passed-in binding.
277  *
278  * Call this after any call to @ref conn_mgr_binding_lock once done accessing binding data.
279  *
280  * Reentrant.
281  *
282  * For use only by connectivity implementations.
283  *
284  * @param binding - Binding to unlock
285  */
conn_mgr_binding_unlock(struct conn_mgr_conn_binding * binding)286 static inline void conn_mgr_binding_unlock(struct conn_mgr_conn_binding *binding)
287 {
288 	(void)k_mutex_unlock(binding->mutex);
289 }
290 
291 /**
292  * @brief Set the value of the specified connectivity flag for the provided binding
293  *
294  * Can be used from any thread or callback without calling @ref conn_mgr_binding_lock.
295  *
296  * For use only by connectivity implementations
297  *
298  * @param binding The binding to check
299  * @param flag The flag to check
300  * @param value New value for the specified flag
301  */
conn_mgr_binding_set_flag(struct conn_mgr_conn_binding * binding,enum conn_mgr_if_flag flag,bool value)302 static inline void conn_mgr_binding_set_flag(struct conn_mgr_conn_binding *binding,
303 					     enum conn_mgr_if_flag flag, bool value)
304 {
305 	conn_mgr_binding_lock(binding);
306 
307 	binding->flags &= ~BIT(flag);
308 	if (value) {
309 		binding->flags |= BIT(flag);
310 	}
311 
312 	conn_mgr_binding_unlock(binding);
313 }
314 
315 /**
316  * @brief Check the value of the specified connectivity flag for the provided binding
317  *
318  * Can be used from any thread or callback without calling @ref conn_mgr_binding_lock.
319  *
320  * For use only by connectivity implementations
321  *
322  * @param binding The binding to check
323  * @param flag The flag to check
324  * @return bool The value of the specified flag
325  */
conn_mgr_binding_get_flag(struct conn_mgr_conn_binding * binding,enum conn_mgr_if_flag flag)326 static inline bool conn_mgr_binding_get_flag(struct conn_mgr_conn_binding *binding,
327 					     enum conn_mgr_if_flag flag)
328 {
329 	bool value = false;
330 
331 	conn_mgr_binding_lock(binding);
332 
333 	value = !!(binding->flags & BIT(flag));
334 
335 	conn_mgr_binding_unlock(binding);
336 
337 	return value;
338 }
339 
340 /**
341  * @}
342  */
343 
344 #ifdef __cplusplus
345 }
346 #endif
347 
348 #endif /* ZEPHYR_INCLUDE_CONN_MGR_CONNECTIVITY_IMPL_H_ */
349