1 /*
2  * Copyright (c) 2022 René Beckmann
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /** @file mqtt_sn.h
8  *
9  * @brief MQTT-SN Client Implementation
10  *
11  * @details
12  * MQTT-SN Client's Application interface is defined in this header.
13  * Targets protocol version 1.2.
14  *
15  * @defgroup mqtt_sn_socket MQTT-SN Client library
16  * @since 3.3
17  * @version 0.1.0
18  * @ingroup networking
19  * @{
20  */
21 
22 #ifndef ZEPHYR_INCLUDE_NET_MQTT_SN_H_
23 #define ZEPHYR_INCLUDE_NET_MQTT_SN_H_
24 
25 #include <stddef.h>
26 
27 #include <zephyr/net_buf.h>
28 #include <zephyr/types.h>
29 
30 #include <sys/types.h>
31 
32 #ifdef CONFIG_MQTT_SN_TRANSPORT_UDP
33 #include <zephyr/net/net_ip.h>
34 #endif
35 
36 #ifdef __cplusplus
37 extern "C" {
38 #endif
39 
40 /**
41  * Quality of Service. QoS 0-2 work the same as basic MQTT, QoS -1 is an MQTT-SN addition.
42  * QOS -1 is not supported yet.
43  */
44 enum mqtt_sn_qos {
45 	MQTT_SN_QOS_0, /**< QOS 0 */
46 	MQTT_SN_QOS_1, /**< QOS 1 */
47 	MQTT_SN_QOS_2, /**< QOS 2 */
48 	MQTT_SN_QOS_M1 /**< QOS -1 */
49 };
50 
51 /**
52  * MQTT-SN topic types.
53  */
54 enum mqtt_sn_topic_type {
55 	/**
56 	 * Normal topic.
57 	 * It allows usage of any valid UTF-8 string as a topic name.
58 	 */
59 	MQTT_SN_TOPIC_TYPE_NORMAL,
60 	/**
61 	 * Pre-defined topic.
62 	 * It allows usage of a two-byte identifier representing a topic name for
63 	 * which the corresponding topic name is known in advance by both the client
64 	 * and the gateway/server.
65 	 */
66 	MQTT_SN_TOPIC_TYPE_PREDEF,
67 	/**
68 	 * Short topic.
69 	 * It allows usage of a two-byte string as a topic name.
70 	 */
71 	MQTT_SN_TOPIC_TYPE_SHORT
72 };
73 
74 /**
75  * MQTT-SN return codes.
76  */
77 enum mqtt_sn_return_code {
78 	MQTT_SN_CODE_ACCEPTED = 0x00,            /**< Accepted */
79 	MQTT_SN_CODE_REJECTED_CONGESTION = 0x01, /**< Rejected: congestion */
80 	MQTT_SN_CODE_REJECTED_TOPIC_ID = 0x02,   /**< Rejected: Invalid Topic ID */
81 	MQTT_SN_CODE_REJECTED_NOTSUP = 0x03,     /**< Rejected: Not Supported */
82 };
83 
84 /** @brief Abstracts memory buffers. */
85 struct mqtt_sn_data {
86 	const uint8_t *data; /**< Pointer to data. */
87 	size_t size;         /**< Size of data, in bytes. */
88 };
89 
90 /**
91  * @brief Initialize memory buffer from C literal string.
92  *
93  * Use it as follows:
94  *
95  * struct mqtt_sn_data topic = MQTT_SN_DATA_STRING_LITERAL("/zephyr");
96  *
97  * @param[in] literal Literal string from which to generate mqtt_sn_data object.
98  */
99 #define MQTT_SN_DATA_STRING_LITERAL(literal) ((struct mqtt_sn_data){literal, sizeof(literal) - 1})
100 
101 /**
102  * @brief Initialize memory buffer from single bytes.
103  *
104  * Use it as follows:
105  *
106  * struct mqtt_sn_data data = MQTT_SN_DATA_BYTES(0x13, 0x37);
107  */
108 #define MQTT_SN_DATA_BYTES(...)                                                                    \
109 	((struct mqtt_sn_data){(uint8_t[]){__VA_ARGS__}, sizeof((uint8_t[]){__VA_ARGS__})})
110 
111 /**
112  * Event types that can be emitted by the library.
113  */
114 enum mqtt_sn_evt_type {
115 	MQTT_SN_EVT_CONNECTED,    /**< Connected to a gateway */
116 	MQTT_SN_EVT_DISCONNECTED, /**< Disconnected */
117 	MQTT_SN_EVT_ASLEEP,       /**< Entered ASLEEP state */
118 	MQTT_SN_EVT_AWAKE,        /**< Entered AWAKE state */
119 	MQTT_SN_EVT_PUBLISH,      /**< Received a PUBLISH message */
120 	MQTT_SN_EVT_PINGRESP,     /**< Received a PINGRESP */
121 	MQTT_SN_EVT_ADVERTISE,    /**< Received a ADVERTISE */
122 	MQTT_SN_EVT_GWINFO,       /**< Received a GWINFO */
123 	MQTT_SN_EVT_SEARCHGW      /**< Received a SEARCHGW */
124 };
125 
126 /**
127  * Event metadata.
128  */
129 union mqtt_sn_evt_param {
130 	/** Structure holding publish event details */
131 	struct {
132 		/** The payload data associated with the event */
133 		struct mqtt_sn_data data;
134 		/** The type of topic for the event */
135 		enum mqtt_sn_topic_type topic_type;
136 		/** The identifier for the topic of the event */
137 		uint16_t topic_id;
138 	} publish;
139 };
140 
141 /**
142  * MQTT-SN event structure to be handled by the event callback.
143  */
144 struct mqtt_sn_evt {
145 	/** Event type */
146 	enum mqtt_sn_evt_type type;
147 	/** Event parameters */
148 	union mqtt_sn_evt_param param;
149 };
150 
151 struct mqtt_sn_client;
152 
153 /**
154  * @brief Asynchronous event notification callback registered by the
155  *        application.
156  *
157  * @param[in] client Identifies the client for which the event is notified.
158  * @param[in] evt Event description along with result and associated
159  *                parameters (if any).
160  */
161 typedef void (*mqtt_sn_evt_cb_t)(struct mqtt_sn_client *client, const struct mqtt_sn_evt *evt);
162 
163 /**
164  * @brief Structure to describe an MQTT-SN transport.
165  *
166  * MQTT-SN does not require transports to be reliable or to hold a connection.
167  * Transports just need to be frame-based, so you can use UDP, ZigBee, or even
168  * a simple UART, given some kind of framing protocol is used.
169  */
170 struct mqtt_sn_transport {
171 	/**
172 	 * @brief Will be called once on client init to initialize the transport.
173 	 *
174 	 * Use this to open sockets or similar. May be NULL.
175 	 */
176 	int (*init)(struct mqtt_sn_transport *transport);
177 
178 	/**
179 	 * @brief Will be called on client deinit
180 	 *
181 	 * Use this to close sockets or similar. May be NULL.
182 	 */
183 	void (*deinit)(struct mqtt_sn_transport *transport);
184 
185 	/**
186 	 * @brief Will be called by the library when it wants to send a message.
187 	 *
188 	 * Implementations should follow sendto conventions with exceptions.
189 	 * When dest_addr == NULL, message should be broadcast with addrlen being
190 	 * the broadcast radius. This should also handle setting up/destroying
191 	 * connections as required when the address changes.
192 	 *
193 	 * @return ENOERR on connection+transmission success, Negative values
194 	 *		signal errors.
195 	 */
196 	int (*sendto)(struct mqtt_sn_client *client, void *buf, size_t sz, const void *dest_addr,
197 		      size_t addrlen);
198 
199 	/**
200 	 * @brief Will be called by the library when it wants to receive a message.
201 	 *
202 	 * Implementations should follow recvfrom conventions with the exception
203 	 * of a NULL src_addr being a broadcast message.
204 	 */
205 	ssize_t (*recvfrom)(struct mqtt_sn_client *client, void *rx_buf, size_t rx_len,
206 			    void *src_addr, size_t *addrlen);
207 
208 	/**
209 	 * @brief Check if incoming data is available.
210 	 *
211 	 * If poll() returns a positive number, recv must not block.
212 	 *
213 	 * May be NULL, but recv should not block then either.
214 	 *
215 	 * @return Positive number if data is available, or zero if there is none.
216 	 * Negative values signal errors.
217 	 */
218 	int (*poll)(struct mqtt_sn_client *client);
219 };
220 
221 #ifdef CONFIG_MQTT_SN_TRANSPORT_UDP
222 /**
223  * Transport struct for UDP based transport.
224  */
225 struct mqtt_sn_transport_udp {
226 	/** Parent struct */
227 	struct mqtt_sn_transport tp;
228 
229 	/** Socket FD */
230 	int sock;
231 
232 	/** Address of broadcasts */
233 	struct sockaddr bcaddr;
234 	socklen_t bcaddrlen;
235 };
236 
237 #define UDP_TRANSPORT(transport) CONTAINER_OF(transport, struct mqtt_sn_transport_udp, tp)
238 
239 /**
240  * @brief Initialize the UDP transport.
241  *
242  * @param[in] udp The transport to be initialized
243  * @param[in] gwaddr Pre-initialized gateway address
244  * @param[in] addrlen Size of the gwaddr structure.
245  */
246 int mqtt_sn_transport_udp_init(struct mqtt_sn_transport_udp *udp, struct sockaddr *gwaddr,
247 			       socklen_t addrlen);
248 #endif
249 
250 /**
251  * Structure describing an MQTT-SN client.
252  */
253 struct mqtt_sn_client {
254 	/** 1-23 character unique client ID */
255 	struct mqtt_sn_data client_id;
256 
257 	/** Topic for Will message.
258 	 * Must be initialized before connecting with will=true
259 	 */
260 	struct mqtt_sn_data will_topic;
261 
262 	/** Will message.
263 	 * Must be initialized before connecting with will=true
264 	 */
265 	struct mqtt_sn_data will_msg;
266 
267 	/** Quality of Service for the Will message */
268 	enum mqtt_sn_qos will_qos;
269 
270 	/** Flag indicating if the will message should be retained by the broker */
271 	bool will_retain;
272 
273 	/** Underlying transport to be used by the client */
274 	struct mqtt_sn_transport *transport;
275 
276 	/** Buffer for outgoing data */
277 	struct net_buf_simple tx;
278 
279 	/** Buffer for incoming data */
280 	struct net_buf_simple rx;
281 
282 	/** Buffer for incoming data sender address */
283 	struct net_buf_simple rx_addr;
284 
285 	/** Event callback */
286 	mqtt_sn_evt_cb_t evt_cb;
287 
288 	/** Message ID for the next message to be sent */
289 	uint16_t next_msg_id;
290 
291 	/** List of pending publish messages */
292 	sys_slist_t publish;
293 
294 	/** List of registered topics */
295 	sys_slist_t topic;
296 
297 	/** List of found gateways */
298 	sys_slist_t gateway;
299 
300 	/** Current state of the MQTT-SN client */
301 	int state;
302 
303 	/** Timestamp of the last ping request */
304 	int64_t last_ping;
305 
306 	/** Number of retries for failed ping attempts */
307 	uint8_t ping_retries;
308 
309 	/** Timestamp of the next SEARCHGW transmission */
310 	int64_t ts_searchgw;
311 
312 	/** Timestamp of the next GWINFO transmission */
313 	int64_t ts_gwinfo;
314 
315 	/** Radius of the next GWINFO transmission */
316 	int64_t radius_gwinfo;
317 
318 	/** Delayable work structure for processing MQTT-SN events */
319 	struct k_work_delayable process_work;
320 };
321 
322 /**
323  * @brief Initialize a client.
324  *
325  * @param client        The MQTT-SN client to initialize.
326  * @param client_id     The ID to be used by the client.
327  * @param transport     The transport to be used by the client.
328  * @param evt_cb        The event callback function for the client.
329  * @param tx            Pointer to the transmit buffer.
330  * @param txsz          Size of the transmit buffer.
331  * @param rx            Pointer to the receive buffer.
332  * @param rxsz          Size of the receive buffer.
333  *
334  * @return 0 or a negative error code (errno.h) indicating reason of failure.
335  */
336 int mqtt_sn_client_init(struct mqtt_sn_client *client, const struct mqtt_sn_data *client_id,
337 			struct mqtt_sn_transport *transport, mqtt_sn_evt_cb_t evt_cb, void *tx,
338 			size_t txsz, void *rx, size_t rxsz);
339 
340 /**
341  * @brief Deinitialize the client.
342  *
343  * This removes all topics and publishes, and also de-inits the transport.
344  *
345  * @param client        The MQTT-SN client to deinitialize.
346  */
347 void mqtt_sn_client_deinit(struct mqtt_sn_client *client);
348 
349 /**
350  * @brief Manually add a Gateway, bypasing the normal search process.
351  *
352  * This function manually creates a gateway that is stored internal to the library.
353  *
354  * @param client      The MQTT-SN client to connect.
355  * @param gw_id       Single byte Gateway Identifier
356  * @param gw_addr     Address data structure to be used by the transport layer.
357  *
358  * @return 0 or a negative error code (errno.h) indicating reason of failure.
359  */
360 int mqtt_sn_add_gw(struct mqtt_sn_client *client, uint8_t gw_id, struct mqtt_sn_data gw_addr);
361 
362 /**
363  * @brief Initiate the MQTT-SN GW Search process.
364  *
365  * @param client     The MQTT-SN client to connect.
366  * @param radius     Broadcast radius for the search message.
367  *
368  * @return 0 or a negative error code (errno.h) indicating reason of failure.
369  */
370 int mqtt_sn_search(struct mqtt_sn_client *client, uint8_t radius);
371 
372 /**
373  * @brief Connect the client.
374  *
375  * @param client            The MQTT-SN client to connect.
376  * @param will              Flag indicating if a Will message should be sent.
377  * @param clean_session     Flag indicating if a clean session should be started.
378  *
379  * @return 0 or a negative error code (errno.h) indicating reason of failure.
380  */
381 int mqtt_sn_connect(struct mqtt_sn_client *client, bool will, bool clean_session);
382 
383 /**
384  * @brief Disconnect the client.
385  *
386  * @param client            The MQTT-SN client to disconnect.
387  *
388  * @return 0 or a negative error code (errno.h) indicating reason of failure.
389  */
390 int mqtt_sn_disconnect(struct mqtt_sn_client *client);
391 
392 /**
393  * @brief Set the client into sleep state.
394  *
395  * @param client            The MQTT-SN client to be put to sleep.
396  * @param duration          Sleep duration (in seconds).
397  *
398  * @return 0 on success, negative errno code on failure.
399  */
400 int mqtt_sn_sleep(struct mqtt_sn_client *client, uint16_t duration);
401 
402 /**
403  * @brief Subscribe to a given topic.
404  *
405  * @param client            The MQTT-SN client that should subscribe.
406  * @param qos               The desired quality of service for the subscription.
407  * @param topic_name        The name of the topic to subscribe to.
408  *
409  * @return 0 or a negative error code (errno.h) indicating reason of failure.
410  */
411 int mqtt_sn_subscribe(struct mqtt_sn_client *client, enum mqtt_sn_qos qos,
412 		      struct mqtt_sn_data *topic_name);
413 
414 /**
415  * @brief Unsubscribe from a topic.
416  *
417  * @param client            The MQTT-SN client that should unsubscribe.
418  * @param qos               The quality of service used when subscribing.
419  * @param topic_name        The name of the topic to unsubscribe from.
420  *
421  * @return 0 or a negative error code (errno.h) indicating reason of failure.
422  */
423 int mqtt_sn_unsubscribe(struct mqtt_sn_client *client, enum mqtt_sn_qos qos,
424 			struct mqtt_sn_data *topic_name);
425 
426 /**
427  * @brief Publish a value.
428  *
429  * If the topic is not yet registered with the gateway, the library takes care of it.
430  *
431  * @param client            The MQTT-SN client that should publish.
432  * @param qos               The desired quality of service for the publish.
433  * @param topic_name        The name of the topic to publish to.
434  * @param retain            Flag indicating if the message should be retained by the broker.
435  * @param data              The data to be published.
436  *
437  * @return 0 or a negative error code (errno.h) indicating reason of failure.
438  */
439 int mqtt_sn_publish(struct mqtt_sn_client *client, enum mqtt_sn_qos qos,
440 		    struct mqtt_sn_data *topic_name, bool retain, struct mqtt_sn_data *data);
441 
442 /**
443  * @brief Check the transport for new incoming data.
444  *
445  * Call this function periodically, or if you have good reason to believe there is any data.
446  * If the client's transport struct contains a poll-function, this function is non-blocking.
447  *
448  * @param client            The MQTT-SN client to check for incoming data.
449  *
450  * @return 0 or a negative error code (errno.h) indicating reason of failure.
451  */
452 int mqtt_sn_input(struct mqtt_sn_client *client);
453 
454 /**
455  * @brief Get topic name by topic ID.
456  *
457  * @param[in] client The MQTT-SN client that uses this topic.
458  * @param[in] id Topic identifier.
459  * @param[out] topic_name Will be assigned to topic name.
460  *
461  * @return 0 on success, -ENOENT if topic ID doesn't exist,
462  * or -EINVAL on invalid arguments.
463  */
464 int mqtt_sn_get_topic_name(struct mqtt_sn_client *client, uint16_t id,
465 			   struct mqtt_sn_data *topic_name);
466 
467 #ifdef __cplusplus
468 }
469 #endif
470 
471 #endif /* ZEPHYR_INCLUDE_NET_MQTT_SN_H_ */
472 
473 /**@}  */
474