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