diff --git a/scripts/import_wiseconnect.py b/scripts/import_wiseconnect.py index fd59b709e..4803f402a 100755 --- a/scripts/import_wiseconnect.py +++ b/scripts/import_wiseconnect.py @@ -207,6 +207,10 @@ "components/device/silabs/si91x/wireless/crypto/wrap/src/sl_si91x_psa_wrap.c", "components/device/silabs/si91x/wireless/crypto/wrap/src/sl_si91x_wrap.c", "components/device/silabs/si91x/wireless/host_mcu/si91x/siwx917_soc_ncp_host.c", + "components/device/silabs/si91x/wireless/icmp/sl_net_ping.c", + "components/device/silabs/si91x/wireless/icmp/sl_net_ping.h", + "components/device/silabs/si91x/wireless/inc/http_client/inc/sl_si91x_http_client_callback_framework.h", + "components/device/silabs/si91x/wireless/inc/http_client/src/sl_si91x_http_client_callback_framework.c", "components/device/silabs/si91x/wireless/inc/sl_rsi_utility.h", "components/device/silabs/si91x/wireless/inc/sl_si91x_constants.h", "components/device/silabs/si91x/wireless/inc/sl_si91x_core_utilities.h", @@ -216,12 +220,18 @@ "components/device/silabs/si91x/wireless/inc/sl_si91x_status.h", "components/device/silabs/si91x/wireless/inc/sl_si91x_types.h", "components/device/silabs/si91x/wireless/inc/sl_wifi_device.h", + "components/device/silabs/si91x/wireless/inc//mqtt/inc/si91x_mqtt_client_callback_framework.h", + "components/device/silabs/si91x/wireless/inc//mqtt/inc/si91x_mqtt_client_types.h", + "components/device/silabs/si91x/wireless/inc//mqtt/inc/si91x_mqtt_client_utility.h", + "components/device/silabs/si91x/wireless/inc/sntp/si91x_sntp_client_callback_framework.h", + "components/device/silabs/si91x/wireless/inc/sntp/si91x_sntp_client_types.h", "components/device/silabs/si91x/wireless/memory/malloc_buffers.c", "components/device/silabs/si91x/wireless/sl_net/inc/sli_net_utility.h", "components/device/silabs/si91x/wireless/sl_net/inc/sl_net_si91x.h", "components/device/silabs/si91x/wireless/sl_net/inc/sl_net_si91x_integration_handler.h", "components/device/silabs/si91x/wireless/sl_net/src/sli_net_si91x_utility.c", "components/device/silabs/si91x/wireless/sl_net/src/sl_net_rsi_utility.c", + "components/device/silabs/si91x/wireless/sl_net/src/sl_net_si91x.c", "components/device/silabs/si91x/wireless/sl_net/src/sl_net_si91x_callback_framework.c", "components/device/silabs/si91x/wireless/sl_net/src/sl_net_si91x_integration_handler.c", "components/device/silabs/si91x/wireless/sl_net/src/sl_si91x_net_internal_stack.c", @@ -244,6 +254,13 @@ "components/protocol/wifi/src/sl_wifi_basic_credentials.c", "components/protocol/wifi/src/sl_wifi_callback_framework.c", "components/service/bsd_socket/si91x_socket/sl_si91x_socket_support.h", + "components/service/http_client/http_client/si91x_socket/sl_http_client.c", + "components/service/http_client/inc/sl_http_client.h", + "components/service/mdns/inc/sl_mdns.h", + "components/service/mdns/si91x/sl_mdns.c", + "components/service/mqtt/inc/sl_mqtt_client.h", + "components/service/mqtt/inc/sl_mqtt_client_types.h", + "components/service/mqtt/si91x/sl_mqtt_client.c", "components/service/network_manager/inc/sli_net_common_utility.h", "components/service/network_manager/inc/sli_net_constants.h", "components/service/network_manager/inc/sl_net_constants.h", @@ -256,6 +273,13 @@ "components/service/network_manager/src/sl_net_basic_profiles.c", "components/service/network_manager/src/sl_net.c", "components/service/network_manager/src/sl_net_credentials.c", + "components/service/sntp/inc/sl_sntp.h", + "components/service/sntp/si91x/sl_sntp.c", + "components/service/sl_websocket_client/inc/sl_websocket_client.h", + "components/service/sl_websocket_client/inc/sl_websocket_client_types.h", + "components/service/sl_websocket_client/inc/sli_websocket_client_sync.h", + "components/service/sl_websocket_client/src/sl_websocket_client.c", + "components/service/sl_websocket_client/src/sli_websocket_client_sync.c", "components/sli_si91x_wifi_event_handler/src/sli_si91x_wifi_event_handler.c", "components/sli_wifi_command_engine/inc/sli_wifi_command_engine.h", "components/sli_wifi_command_engine/inc/sli_wifi_event_handler.h", diff --git a/wiseconnect/components/device/silabs/si91x/wireless/icmp/sl_net_ping.c b/wiseconnect/components/device/silabs/si91x/wireless/icmp/sl_net_ping.c new file mode 100644 index 000000000..70e0ce9fd --- /dev/null +++ b/wiseconnect/components/device/silabs/si91x/wireless/icmp/sl_net_ping.c @@ -0,0 +1,98 @@ +/***************************************************************************/ /** + * @file sl_net_ping.c + ******************************************************************************* + * # License + * Copyright 2025 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ +#include "sl_status.h" +#include "sl_constants.h" +#include "sl_net_ping.h" +#include "sl_si91x_driver.h" +#include + +/****************************************************** + * Macros + ******************************************************/ + +// This macro must be used while sending ping timeout in sli_si91x_ping_request_t +#define SLI_CONVERT_TO_SI91X_PING_TIMEOUT(timeout) (timeout / 100) + +/****************************************************** + * Constants + ******************************************************/ +#define SLI_PING_RESPONSE_TIMEOUT_MS 1000 // timeout in ms + +/****************************************************** + * Static Function Declarations + ******************************************************/ + +/****************************************************** + * Extern Variable + ******************************************************/ +extern bool device_initialized; + +/****************************************************** + * Global Variable + ******************************************************/ + +/****************************************************** + * Function Definitions + ******************************************************/ + +sl_status_t sl_si91x_send_ping(sl_ip_address_t ip_address, uint16_t ping_size) +{ + sl_status_t status = SL_STATUS_OK; + sli_si91x_ping_request_t request = { 0 }; + + if (!device_initialized) { + return SL_STATUS_NOT_INITIALIZED; + } + + if ((ip_address.type != SL_IPV4) && (ip_address.type != SL_IPV6)) { + return SL_STATUS_INVALID_PARAMETER; + } + + if (ip_address.type == SL_IPV6) { + request.ip_version = SL_IPV6_VERSION; + memcpy(&request.ping_address.ipv6_address, &ip_address.ip.v6, SL_IPV6_ADDRESS_LENGTH); // Copy IPv6 address + } else { + request.ip_version = SL_IPV4_VERSION; + memcpy(&request.ping_address.ipv4_address, &ip_address.ip.v4, SL_IPV4_ADDRESS_LENGTH); // Copy IPv4 address + } + + request.ping_size = ping_size; // Copy Ping size + request.timeout = SLI_CONVERT_TO_SI91X_PING_TIMEOUT(SLI_PING_RESPONSE_TIMEOUT_MS); // Copy Ping timeout + + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_PING_PACKET, + SLI_SI91X_NETWORK_CMD, + &request, + sizeof(sli_si91x_ping_request_t), + SLI_SI91X_RETURN_IMMEDIATELY, + NULL, + NULL); + VERIFY_STATUS_AND_RETURN(status); + + return status; +} diff --git a/wiseconnect/components/device/silabs/si91x/wireless/icmp/sl_net_ping.h b/wiseconnect/components/device/silabs/si91x/wireless/icmp/sl_net_ping.h new file mode 100644 index 000000000..52ef36062 --- /dev/null +++ b/wiseconnect/components/device/silabs/si91x/wireless/icmp/sl_net_ping.h @@ -0,0 +1,64 @@ +/***************************************************************************/ /** + * @file + * @brief + ******************************************************************************* + * # License + * Copyright 2019 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ +#pragma once + +#include "sl_status.h" +#include "sl_net_types.h" + +/** \addtogroup SL_PING_FUNCTIONS Functions + * \ingroup SL_PING_APIS + * @{ */ + +/***************************************************************************/ /** + * @brief + * Send an ICMP ping request to a specific IP address. + * @details + * This function sends an ICMP ping request to a specific IP address (IPv4 or IPv6) with a user defined ping packet size. + * It verifies the device’s initialization status and validates the IP address type before sending the request to the specific IP address. + * @pre Pre-condition: + * - [sl_net_up](../wiseconnect-api-reference-guide-nwk-mgmt/net-interface-functions#sl-net-up) should be called before this API. + * @param[in] ip_address + * The destination IP address should be of type [sl_ip_address_type_t](../wiseconnect-api-reference-guide-common/sl-ip-address-t) either IPv4 or IPv6. + * @param[in] ping_packet_size + * The size of the ping packet must fall within the valid range of [0, 300]. + * @return + * [sl_status_t](https://docs.silabs.com/gecko-platform/latest/platform-common/status) - Status of the operation. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_NOT_INITIALIZED: Device is not initialized. + * - SL_STATUS_INVALID_PARAMETER: Invalid IP address type. + * + * @note + * 1. This API is asynchronous. The ping response is delivered through the registered [sl_net_event_handler_t](../wiseconnect-api-reference-guide-nwk-mgmt/sl-net-types#sl-net-event-handler-t) callback, + * using the event type [SL_NET_PING_RESPONSE_EVENT](../wiseconnect-api-reference-guide-nwk-mgmt/sl-net-constants#sl-net-event-t). + * To receive this event, the sl_net_event_handler_t must be registered via the [sl_net_init()](../wiseconnect-api-reference-guide-nwk-mgmt/net-interface-functions#sl-net-init) API. + ******************************************************************************/ +sl_status_t sl_si91x_send_ping(sl_ip_address_t ip_address, uint16_t ping_packet_size); + +/** @} */ diff --git a/wiseconnect/components/device/silabs/si91x/wireless/inc/http_client/inc/sl_si91x_http_client_callback_framework.h b/wiseconnect/components/device/silabs/si91x/wireless/inc/http_client/inc/sl_si91x_http_client_callback_framework.h new file mode 100644 index 000000000..2e3f47744 --- /dev/null +++ b/wiseconnect/components/device/silabs/si91x/wireless/inc/http_client/inc/sl_si91x_http_client_callback_framework.h @@ -0,0 +1,46 @@ +/***************************************************************************/ /** + * @file + * @brief sl_si91x_http_client_callback_framework.h + ******************************************************************************* + * # License + * Copyright 2019 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ + +#include "sl_http_client.h" +#include "sl_si91x_core_utilities.h" +#include "sl_si91x_constants.h" +#include "sl_si91x_driver.h" +#include "sl_si91x_protocol_types.h" + +/****************************************************** + * Function Declarations + ******************************************************/ +sl_status_t sli_http_client_register_callback(sl_http_client_event_t event, + sl_http_client_t client_handle, + sl_http_client_event_handler_t function); + +sl_status_t sli_http_client_default_event_handler(sl_http_client_event_t event, + sl_wifi_buffer_t *buffer, + void *sdk_context); diff --git a/wiseconnect/components/device/silabs/si91x/wireless/inc/mqtt/inc/si91x_mqtt_client_callback_framework.h b/wiseconnect/components/device/silabs/si91x/wireless/inc/mqtt/inc/si91x_mqtt_client_callback_framework.h new file mode 100644 index 000000000..8ebae8e25 --- /dev/null +++ b/wiseconnect/components/device/silabs/si91x/wireless/inc/mqtt/inc/si91x_mqtt_client_callback_framework.h @@ -0,0 +1,52 @@ +/***************************************************************************/ /** + * @file si91x_mqtt_client_callback_framework.h + ******************************************************************************* + * # License + * Copyright 2025 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ + +#pragma once + +#include "sl_mqtt_client.h" +#include "sl_mqtt_client_types.h" +#include "sl_si91x_types.h" +#include "sl_status.h" + +/** + * @addtogroup SERVICE_MQTT_TYPES + * @{ + */ +typedef struct { + sl_mqtt_client_event_t event; + sl_mqtt_client_t *client; + void *user_context; + void *sdk_data; +} sl_si91x_mqtt_client_context_t; + +sl_status_t sli_si91x_mqtt_event_handler(sl_status_t status, + sl_si91x_mqtt_client_context_t *sdk_context, + sl_wifi_packet_t *rx_packet); + +/** @} */ \ No newline at end of file diff --git a/wiseconnect/components/device/silabs/si91x/wireless/inc/mqtt/inc/si91x_mqtt_client_types.h b/wiseconnect/components/device/silabs/si91x/wireless/inc/mqtt/inc/si91x_mqtt_client_types.h new file mode 100644 index 000000000..6decb46e1 --- /dev/null +++ b/wiseconnect/components/device/silabs/si91x/wireless/inc/mqtt/inc/si91x_mqtt_client_types.h @@ -0,0 +1,173 @@ +/***************************************************************************/ /** + * @file si91x_mqtt_client_types.h + ******************************************************************************* + * # License + * Copyright 2025 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ + +#pragma once +#include "stdint.h" +#include "sl_common.h" + +//Note: Please go through http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html + +#define SI91X_MQTT_CLIENT_TOPIC_MAXIMUM_LENGTH 202 // This size is including the NULL terminating character. +#define SI91X_MQTT_CLIENT_WILL_TOPIC_MAXIMUM_LENGTH 202 // This size is including the NULL terminating character. +#define SLI_SI91X_MQTT_CLIENT_MESSAGE_MAXIMUM_LENGTH 100 + +#define SLI_SI91X_MQTT_CLIENT_ID_MAXIMUM_LENGTH 62 // This size is including the NULL terminating character. +#define SI91X_MQTT_CLIENT_USERNAME_MAXIMUM_LENGTH 122 // This size is including the NULL terminating character. +#define SI91X_MQTT_CLIENT_PASSWORD_MAXIMUM_LENGTH 62 // This size is including the NULL terminating character. + +#define SLI_SI91X_MQTT_CLIENT_INIT_COMMAND 1 +#define SLI_SI91X_MQTT_CLIENT_CONNECT_COMMAND 2 +#define SLI_SI91X_MQTT_CLIENT_SUBSCRIBE_COMMAND 3 +#define SLI_SI91X_MQTT_CLIENT_PUBLISH_COMMAND 4 +#define SLI_SI91X_MQTT_CLIENT_UNSUBSCRIBE_COMMAND 5 +#define SLI_SI91X_MQTT_CLIENT_DISCONNECT_COMMAND 8 +#define SLI_SI91X_MQTT_CLIENT_DEINIT_COMMAND 9 + +#define SLI_SI91X_MQTT_CLIENT_TOPIC_DELIMITER "/" +#define SLI_SI91X_MQTT_CLIENT_SINGLE_LEVEL_WILD_CARD "+" +#define SLI_SI91X_MQTT_CLIENT_MULTI_LEVEL_WILD_CARD "#" + +typedef struct { + // IP version + uint32_t ip_version; + union { + uint8_t ipv4_address[4]; + uint8_t ipv6_address[16]; + } server_ip_address; +} sli_si91x_mqtt_client_ip_address_t; + +typedef struct SL_ATTRIBUTE_PACKED { + uint32_t command_type; + // MQTT server IP address + sli_si91x_mqtt_client_ip_address_t server_ip; + // MQTT server port + uint32_t server_port; + // Client ID Length + uint8_t client_id_len; + // client ID, should be unique + int8_t client_id[SLI_SI91X_MQTT_CLIENT_ID_MAXIMUM_LENGTH]; + // keep alive interval (s) + uint16_t keep_alive_interval; + // username Length + uint8_t username_len; + // user name + uint8_t user_name[SI91X_MQTT_CLIENT_USERNAME_MAXIMUM_LENGTH]; + // password Length + uint8_t password_len; + // password + uint8_t password[SI91X_MQTT_CLIENT_PASSWORD_MAXIMUM_LENGTH]; + // clean session(0-1) + uint8_t clean; + // 0 : TCP , 1 : SSl + uint8_t encrypt; + // MQTT Client port + uint32_t client_port; +#if defined(SLI_SI917) || defined(SLI_SI915) + //! Capping tcp retransmission timeout + uint8_t tcp_max_retransmission_cap_for_emb_mqtt; +#endif + //! MQTT ping retries. + uint16_t keep_alive_retries; +} si91x_mqtt_client_init_request_t; + +typedef struct SL_ATTRIBUTE_PACKED { + uint32_t command_type; + // whether to use username (0-1) + uint8_t is_username_present; + // whether to use pwdFlag (0-1) + uint8_t is_password_present; + // whether to set willmsg (0-1) + uint8_t will_flag; + // retained flag(0-1) + uint8_t will_retain; + // message Qos(0-2) + uint8_t will_qos; + // length of topic + uint8_t will_topic_len; + // topic name of will + uint8_t will_topic[SI91X_MQTT_CLIENT_WILL_TOPIC_MAXIMUM_LENGTH]; + // Length of Will message + uint8_t will_message_len; + // message of will + uint8_t will_msg[SLI_SI91X_MQTT_CLIENT_MESSAGE_MAXIMUM_LENGTH]; + +} sli_si91x_mqtt_client_connect_request_t; + +typedef struct SL_ATTRIBUTE_PACKED { + uint32_t command_type; + // length of TOPIC + uint8_t topic_len; + // topic of subscribe message + int8_t topic[SI91X_MQTT_CLIENT_TOPIC_MAXIMUM_LENGTH]; + // message Qos, can be 0, 1, or 2 + int8_t qos; + +} sli_si91x_mqtt_client_subscribe_t; + +typedef struct SL_ATTRIBUTE_PACKED { + uint32_t command_type; + // length of TOPIC + uint8_t topic_len; + // topic of unsubscribe message + uint8_t topic[SI91X_MQTT_CLIENT_TOPIC_MAXIMUM_LENGTH]; + // message Qos, can be 0, 1, or 2 + uint8_t qos; + // retained flag, can be 0 or 1 + uint8_t retained; + // duplicate flag, can be 0 or 1 + uint8_t dup; + // length of publish message(option), if set to 0 or + //omitted, will be parsed in text format, else + //hexidecimai format + uint16_t msg_len; + // publish message + int8_t *msg; + +} sli_si91x_mqtt_client_publish_request_t; + +typedef struct SL_ATTRIBUTE_PACKED { + uint32_t command_type; + // length of TOPIC + uint8_t topic_len; + // topic of unsubscribe message + uint8_t topic[SI91X_MQTT_CLIENT_TOPIC_MAXIMUM_LENGTH]; + +} sli_si91x_mqtt_client_unsubscribe_request_t; + +typedef struct { + uint32_t command_type; + +} sli_si91x_mqtt_client_command_request_t; + +typedef struct SL_ATTRIBUTE_PACKED { + uint16_t mqtt_flags; + uint16_t current_chunk_length; + uint16_t topic_length; + uint8_t data[]; +} sli_si91x_mqtt_client_received_message_t; diff --git a/wiseconnect/components/device/silabs/si91x/wireless/inc/mqtt/inc/si91x_mqtt_client_utility.h b/wiseconnect/components/device/silabs/si91x/wireless/inc/mqtt/inc/si91x_mqtt_client_utility.h new file mode 100644 index 000000000..506df19c6 --- /dev/null +++ b/wiseconnect/components/device/silabs/si91x/wireless/inc/mqtt/inc/si91x_mqtt_client_utility.h @@ -0,0 +1,68 @@ +/***************************************************************************/ /** + * @file si91x_mqtt_client_utility.h + ******************************************************************************* + * # License + * Copyright 2025 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ + +#pragma once + +#include "si91x_mqtt_client_callback_framework.h" + +/** + * An internal helper function to build sl_mqtt_client_event_t in case an API is called in asynchronous mode. + * @param event Event that needs to be stored in context. + * @param client Client address that needs to be stored in context. + * @param user_context user_context provided by user while calling API. + * @param timeout timeout which determines if API needs to be executed in sync or async mode. + * @param si91x_mqtt_client_context_t** A double pointer to client context.This function allocates memory and initializes members if timeout is greater than 0 else pointer is initialized with null. + * @return return SL_STATUS_OK in case of success or appropriate status. + */ +sl_status_t sli_si91x_build_mqtt_sdk_context_if_async(sl_mqtt_client_event_t event, + sl_mqtt_client_t *client, + void *user_context, + void *sdk_data, + uint32_t timeout, + sl_si91x_mqtt_client_context_t **context); + +/** + * An internal helper function to get current mqtt client. + * @param client current mqtt client + * + * Note: client would be NULL if this function is called before sl_mqtt_client_init or after sl_mqtt_client_deint + */ +void sli_si91x_get_mqtt_client(sl_mqtt_client_t **client); + +/** + * Cleans up resources used by the MQTT client. + * + * This function is responsible for releasing any resources allocated to the MQTT client, + * ensuring a clean shutdown of the client instance. It should be called when the MQTT + * client is no longer needed, typically at the end of the application or before a restart + * of the MQTT client. + * + * Note: It is important to call this function to prevent resource leaks. + */ +void sli_mqtt_client_cleanup(); diff --git a/wiseconnect/components/device/silabs/si91x/wireless/inc/sntp/si91x_sntp_client_callback_framework.h b/wiseconnect/components/device/silabs/si91x/wireless/inc/sntp/si91x_sntp_client_callback_framework.h new file mode 100644 index 000000000..15b24e9d4 --- /dev/null +++ b/wiseconnect/components/device/silabs/si91x/wireless/inc/sntp/si91x_sntp_client_callback_framework.h @@ -0,0 +1,33 @@ +/***************************************************************************/ /** + * @file si91x_sntp_client_callback_framework.h + ******************************************************************************* + * # License + * Copyright 2025 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ +#pragma once + +#include "sl_si91x_types.h" + +sl_status_t sli_si91x_sntp_event_handler(sli_si91x_queue_packet_t *data); \ No newline at end of file diff --git a/wiseconnect/components/device/silabs/si91x/wireless/inc/sntp/si91x_sntp_client_types.h b/wiseconnect/components/device/silabs/si91x/wireless/inc/sntp/si91x_sntp_client_types.h new file mode 100644 index 000000000..28cc766a7 --- /dev/null +++ b/wiseconnect/components/device/silabs/si91x/wireless/inc/sntp/si91x_sntp_client_types.h @@ -0,0 +1,77 @@ +/***************************************************************************/ /** + * @file si91x_sntp_client_types.h + ******************************************************************************* + * # License + * Copyright 2025 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ +#pragma once +#include "stdint.h" + +/****************************************************** + * * Macros + * ******************************************************/ +//SNTP client feature +#define SLI_SI91X_SNTP_CLIENT_CREATE 1 +#define SLI_SI91X_SNTP_CLIENT_GETTIME 2 +#define SLI_SI91X_SNTP_CLIENT_GETTIME_DATE 3 +#define SLI_SI91X_SNTP_CLIENT_GETSERVER_ADDRESS 4 +#define SLI_SI91X_SNTP_CLIENT_DELETE 5 +#define SLI_SI91X_SNTP_CLIENT_GET_SERVER_INFO 6 +#define SLI_SI91X_SNTP_CLIENT_SERVER_ASYNC_RSP 7 + +/****************************************************** + * * Type Definitions + * ******************************************************/ + +// Define for SNTP client initialization +typedef struct { + uint8_t command_type; + uint8_t ip_version; + union { + uint8_t ipv4_address[4]; + uint8_t ipv6_address[16]; + } server_ip_address; + uint8_t sntp_method; + uint8_t sntp_timeout[2]; +} sli_si91x_sntp_client_t; + +typedef struct { + uint8_t command_type; + uint8_t ip_version; + union { + uint8_t ipv4_address[4]; + uint8_t ipv6_address[16]; + } server_ip_address; + uint8_t sntp_method; +} sli_si91x_sntp_server_info_rsp_t; + +typedef struct { + uint8_t ip_version; + union { + uint8_t ipv4_address[4]; + uint8_t ipv6_address[16]; + } server_ip_address; + uint8_t sntp_method; +} sli_si91x_sntp_server_rsp_t; \ No newline at end of file diff --git a/wiseconnect/components/device/silabs/si91x/wireless/sl_net/src/sl_net_si91x.c b/wiseconnect/components/device/silabs/si91x/wireless/sl_net/src/sl_net_si91x.c new file mode 100644 index 000000000..d2939ab1d --- /dev/null +++ b/wiseconnect/components/device/silabs/si91x/wireless/sl_net/src/sl_net_si91x.c @@ -0,0 +1,475 @@ +/***************************************************************************/ /** + * @file + * @brief + ******************************************************************************* + * # License + * Copyright 2019 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ +#include "sl_status.h" +#include "sl_wifi_types.h" +#include "sl_net.h" +#include "stddef.h" +#include "sl_status.h" +#include "sl_utility.h" +#include "sl_net.h" +#include "sl_wifi.h" +#include "sl_net_wifi_types.h" +#include "sl_net_si91x.h" +#include "sl_si91x_host_interface.h" +#include "sl_si91x_driver.h" +#include "sl_rsi_utility.h" +#include "sli_net_utility.h" +#include "sl_si91x_core_utilities.h" +#include "sli_net_common_utility.h" +#include +#include +#include "sl_wifi_callback_framework.h" +#include "sl_net_dns.h" +#include "sli_wifi_constants.h" + +typedef enum { SLI_SI91X_CLIENT = 0, SLI_SI91X_AP = 1, SLI_SI91X_MAX_INTERFACES = 2 } sli_si91x_interfaces_t; +sl_status_t sl_net_dns_resolve_hostname(const char *host_name, + const uint32_t timeout, + const sl_net_dns_resolution_ip_type_t dns_resolution_ip, + sl_ip_address_t *sl_ip_address); +static bool sli_si91x_get_dns_mode(const sl_net_dns_address_t *address); +extern bool device_initialized; +static sl_status_t sli_si91x_send_multicast_request(sl_wifi_interface_t interface, + const sl_ip_address_t *ip_address, + uint8_t command_type); +sl_status_t sli_si91x_configure_ip_address(sl_net_ip_configuration_t *ip_config, + uint8_t virtual_ap_id, + const uint32_t timeout); +static sl_wifi_interface_t nmap_ap_interface_with_band[MAX_NET_AP_INTERFACES] = { 0 }; +static sl_wifi_interface_t nmap_client_interface_with_band[MAX_NET_CLIENT_INTERFACES] = { 0 }; +static sl_ip_management_t dhcp_type[SLI_SI91X_MAX_INTERFACES] = { 0 }; + +sl_status_t sl_net_wifi_client_init(sl_net_interface_t interface, + const void *configuration, + void *context, + sl_net_event_handler_t event_handler) +{ + UNUSED_PARAMETER(interface); + UNUSED_PARAMETER(context); + sl_status_t status = SL_STATUS_FAIL; + + // Set the user-defined event handler for client mode + sl_si91x_register_event_handler(event_handler); + + status = sl_wifi_init(configuration, NULL, sl_wifi_default_event_handler); + // Verify the initialization status and return it + VERIFY_STATUS_AND_RETURN(status); + return status; +} + +sl_status_t sl_net_wifi_client_deinit(sl_net_interface_t interface) +{ + UNUSED_PARAMETER(interface); + return sl_wifi_deinit(); +} + +sl_status_t sl_net_wifi_client_up(sl_net_interface_t interface, sl_net_profile_id_t profile_id) +{ + //Check whether the Interfaces are valid + if (interface != SL_NET_WIFI_CLIENT_1_INTERFACE && interface != SL_NET_WIFI_CLIENT_2_INTERFACE) { + return SL_STATUS_INVALID_PARAMETER; + } + + sl_status_t status; + sl_net_wifi_client_profile_t profile; + sl_wifi_interface_t client_interface = 0; + + // Connect to the Wi-Fi network + if (profile_id == SL_NET_AUTO_JOIN) { + return sli_handle_auto_join(interface, &profile); + } + + // Get the client profile using the provided profile_id + status = sl_net_get_profile(interface, profile_id, &profile); + VERIFY_STATUS_AND_RETURN(status); + + // Configure the client interface based on the band + if (profile.config.channel.band == SL_WIFI_BAND_2_4GHZ || profile.config.channel.band == SL_WIFI_AUTO_BAND) { + client_interface = SL_WIFI_CLIENT_2_4GHZ_INTERFACE; + } else if (profile.config.channel.band == SL_WIFI_BAND_5GHZ) { + client_interface = SL_WIFI_CLIENT_5GHZ_INTERFACE; + } else if (profile.config.channel.band == SL_WIFI_BAND_DUAL) { + client_interface = SL_WIFI_CLIENT_DUAL_INTERFACE; + } + + // Connect to the Wi-Fi network + status = sl_wifi_connect(client_interface, &profile.config, SLI_WIFI_CONNECT_TIMEOUT); + VERIFY_STATUS_AND_RETURN(status); + + // Configure the IP address settings + if (interface == SL_NET_WIFI_CLIENT_1_INTERFACE) { + nmap_client_interface_with_band[0] = client_interface; //Map the Interface with the band + status = sl_si91x_configure_ip_address(&profile.ip, SL_WIFI_CLIENT_VAP_ID); + VERIFY_STATUS_AND_RETURN(status); + } else if (interface == SL_NET_WIFI_CLIENT_2_INTERFACE) { + nmap_client_interface_with_band[1] = client_interface; //Map the Interface with the band + status = sl_si91x_configure_ip_address(&profile.ip, SL_WIFI_CLIENT_VAP_ID_1); + VERIFY_STATUS_AND_RETURN(status); + } + dhcp_type[SLI_SI91X_CLIENT] = profile.ip.mode; + + // Set the client profile + status = sl_net_set_profile(interface, profile_id, &profile); + return status; +} + +sl_status_t sl_net_wifi_client_down(sl_net_interface_t interface) +{ + //Check whether the Interfaces are valid + if (interface != SL_NET_WIFI_CLIENT_1_INTERFACE && interface != SL_NET_WIFI_CLIENT_2_INTERFACE) { + return SL_STATUS_INVALID_PARAMETER; + } + + sl_wifi_interface_t client_interface = (interface == SL_NET_WIFI_CLIENT_1_INTERFACE) + ? nmap_client_interface_with_band[0] + : nmap_client_interface_with_band[1]; + + // Disconnect from the Wi-Fi network + return sl_wifi_disconnect(client_interface); +} + +sl_status_t sl_net_wifi_ap_init(sl_net_interface_t interface, + const void *configuration, + const void *workspace, + sl_net_event_handler_t event_handler) +{ + UNUSED_PARAMETER(interface); + UNUSED_PARAMETER(workspace); + sl_status_t status = SL_STATUS_FAIL; + + // Set the user-defined event handler for AP mode + sl_si91x_register_event_handler(event_handler); + + status = sl_wifi_init(configuration, NULL, sl_wifi_default_event_handler); + VERIFY_STATUS_AND_RETURN(status); + return status; +} + +sl_status_t sl_net_wifi_ap_deinit(sl_net_interface_t interface) +{ + UNUSED_PARAMETER(interface); + return sl_wifi_deinit(); +} + +sl_status_t sl_net_wifi_ap_up(sl_net_interface_t interface, sl_net_profile_id_t profile_id) +{ + //Check whether the Interfaces are valid + if (interface != SL_NET_WIFI_AP_1_INTERFACE && interface != SL_NET_WIFI_AP_2_INTERFACE) { + return SL_STATUS_INVALID_PARAMETER; + } + sl_status_t status; + sl_net_wifi_ap_profile_t profile; + sl_wifi_interface_t ap_interface = 0; + + status = sl_net_get_profile(interface, profile_id, &profile); + VERIFY_STATUS_AND_RETURN(status); + + // Validate if profile configuration is valid + // AP + DHCP client not supported + // AP + link local not supported + if (profile.ip.mode != SL_IP_MANAGEMENT_STATIC_IP) { + return SL_STATUS_INVALID_CONFIGURATION; + } + if (interface == SL_NET_WIFI_AP_1_INTERFACE) { + status = sl_si91x_configure_ip_address(&profile.ip, SL_WIFI_AP_VAP_ID); + VERIFY_STATUS_AND_RETURN(status); + } else if (interface == SL_NET_WIFI_AP_2_INTERFACE) { + status = sl_si91x_configure_ip_address(&profile.ip, SL_WIFI_AP_VAP_ID_1); + VERIFY_STATUS_AND_RETURN(status); + } + dhcp_type[SLI_SI91X_AP] = profile.ip.mode; + + // Set the AP profile + status = sl_net_set_profile(interface, profile_id, &profile); + VERIFY_STATUS_AND_RETURN(status); + + // Configure the ap_interface based on the band + if (profile.config.channel.band == SL_WIFI_BAND_2_4GHZ || profile.config.channel.band == SL_WIFI_AUTO_BAND) { + ap_interface = SL_WIFI_AP_2_4GHZ_INTERFACE; + } else if (profile.config.channel.band == SL_WIFI_BAND_5GHZ) { + ap_interface = SL_WIFI_AP_5GHZ_INTERFACE; + } else if (profile.config.channel.band == SL_WIFI_BAND_DUAL) { + ap_interface = SL_WIFI_AP_DUAL_INTERFACE; + } + // Map the AP Interface with band + if (interface == SL_NET_WIFI_AP_1_INTERFACE) { + nmap_ap_interface_with_band[0] = ap_interface; + } else if (interface == SL_NET_WIFI_AP_2_INTERFACE) { + nmap_ap_interface_with_band[1] = ap_interface; + } + + status = sl_wifi_start_ap(ap_interface, &profile.config); + VERIFY_STATUS_AND_RETURN(status); + + return status; +} + +sl_status_t sl_net_wifi_ap_down(sl_net_interface_t interface) +{ + //Check whether the Interfaces are valid + if (interface != SL_NET_WIFI_AP_1_INTERFACE && interface != SL_NET_WIFI_AP_2_INTERFACE) { + return SL_STATUS_INVALID_PARAMETER; + } + + sl_wifi_interface_t ap_interface = (interface == SL_NET_WIFI_AP_1_INTERFACE) ? nmap_ap_interface_with_band[0] + : nmap_ap_interface_with_band[1]; + + return sl_wifi_stop_ap(ap_interface); +} + +sl_status_t sl_net_join_multicast_address(sl_net_interface_t interface, const sl_ip_address_t *ip_address) +{ + return sli_si91x_send_multicast_request((sl_wifi_interface_t)interface, ip_address, SL_WIFI_MULTICAST_JOIN); +} + +sl_status_t sl_net_leave_multicast_address(sl_net_interface_t interface, const sl_ip_address_t *ip_address) +{ + return sli_si91x_send_multicast_request((sl_wifi_interface_t)interface, ip_address, SL_WIFI_MULTICAST_LEAVE); +} + +static sl_status_t sli_si91x_send_multicast_request(sl_wifi_interface_t interface, + const sl_ip_address_t *ip_address, + uint8_t command_type) +{ + UNUSED_PARAMETER(interface); + sli_si91x_req_multicast_t multicast = { 0 }; + sl_status_t status = SL_STATUS_OK; + + if (!device_initialized) { + return SL_STATUS_NOT_INITIALIZED; + } + + //Fill IP version and IP address + if (ip_address->type == SL_IPV6) { + multicast.ip_version[0] = 6; + memcpy(multicast.multicast_address.ipv6_address, ip_address->ip.v6.bytes, SLI_IP_ADDRESS_LEN * 4); + } else { + multicast.ip_version[0] = 4; + memcpy(multicast.multicast_address.ipv4_address, ip_address->ip.v4.bytes, SLI_IP_ADDRESS_LEN); + } + multicast.type[0] = command_type; + + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_MULTICAST, + SLI_SI91X_NETWORK_CMD, + &multicast, + sizeof(multicast), + SLI_SI91X_WAIT_FOR_COMMAND_SUCCESS, + NULL, + NULL); + + VERIFY_STATUS_AND_RETURN(status); + return SL_STATUS_OK; +} + +// Resolve a host name to an IP address using DNS +sl_status_t sl_net_dns_resolve_hostname(const char *host_name, + const uint32_t timeout, + const sl_net_dns_resolution_ip_type_t dns_resolution_ip, + sl_ip_address_t *sl_ip_address) +{ + // Check for a NULL pointer for sl_ip_address + SL_WIFI_ARGS_CHECK_NULL_POINTER(sl_ip_address); + + sl_status_t status; + sl_wifi_packet_t *packet; + sl_wifi_buffer_t *buffer = NULL; + const sli_si91x_dns_response_t *dns_response = { 0 }; + sli_si91x_dns_query_request_t dns_query_request = { 0 }; + + // Determine the wait period based on the timeout value + sli_si91x_wait_period_t wait_period = timeout == 0 ? SLI_SI91X_RETURN_IMMEDIATELY + : SL_SI91X_WAIT_FOR_RESPONSE(timeout); + // Determine the IP version to be used (IPv4 or IPv6) + dns_query_request.ip_version[0] = (dns_resolution_ip == SL_NET_DNS_TYPE_IPV4) ? 4 : 6; + memcpy(dns_query_request.url_name, host_name, sizeof(dns_query_request.url_name)); + + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_DNS_QUERY, + SLI_SI91X_NETWORK_CMD, + &dns_query_request, + sizeof(dns_query_request), + wait_period, + NULL, + &buffer); + + // Check if the command failed and free the buffer if it was allocated + if ((status != SL_STATUS_OK) && (buffer != NULL)) { + sli_si91x_host_free_buffer(buffer); + } + VERIFY_STATUS_AND_RETURN(status); + + // Extract the DNS response from the SI91X packet buffer + packet = sl_si91x_host_get_buffer_data(buffer, 0, NULL); + dns_response = (sli_si91x_dns_response_t *)packet->data; + + // Convert the SI91X DNS response to the sl_ip_address format + sli_convert_si91x_dns_response(sl_ip_address, dns_response); + sli_si91x_host_free_buffer(buffer); + return SL_STATUS_OK; +} + +sl_status_t sl_net_set_dns_server(sl_net_interface_t interface, const sl_net_dns_address_t *address) +{ + UNUSED_PARAMETER(interface); + sl_status_t status = 0; + sli_dns_server_add_request_t dns_server_add_request = { 0 }; + + if (!device_initialized) { + return SL_STATUS_NOT_INITIALIZED; + } + + //! Check for invalid parameters + if ((address->primary_server_address && address->primary_server_address->type != SL_IPV4 + && address->primary_server_address->type != SL_IPV6) + || (address->secondary_server_address && address->secondary_server_address->type != SL_IPV4 + && address->secondary_server_address->type != SL_IPV6)) { + //! Throw error in case of invalid parameters + return SL_STATUS_INVALID_PARAMETER; + } + + dns_server_add_request.dns_mode[0] = sli_si91x_get_dns_mode(address); + + if (address->primary_server_address && address->primary_server_address->type == SL_IPV4) { + dns_server_add_request.ip_version[0] = SL_IPV4_VERSION; + //! Fill Primary IP address + memcpy(dns_server_add_request.sli_ip_address1.primary_dns_ipv4, + address->primary_server_address->ip.v4.bytes, + SL_IPV4_ADDRESS_LENGTH); + } else if (address->primary_server_address && address->primary_server_address->type == SL_IPV6) { + dns_server_add_request.ip_version[0] = SL_IPV6_VERSION; + //! Fill Primary IP address + memcpy(dns_server_add_request.sli_ip_address1.primary_dns_ipv6, + address->primary_server_address->ip.v6.bytes, + SL_IPV6_ADDRESS_LENGTH); + } + + if (address->secondary_server_address && address->secondary_server_address->type == SL_IPV4) { + dns_server_add_request.ip_version[0] = SL_IPV4_VERSION; + //! Fill Secondary IP address + memcpy(dns_server_add_request.sli_ip_address2.secondary_dns_ipv4, + address->secondary_server_address->ip.v4.bytes, + SL_IPV4_ADDRESS_LENGTH); + } else if (address->secondary_server_address && address->secondary_server_address->type == SL_IPV6) { + dns_server_add_request.ip_version[0] = SL_IPV6_VERSION; + //! Fill Secondary IP address + memcpy(dns_server_add_request.sli_ip_address2.secondary_dns_ipv6, + address->secondary_server_address->ip.v6.bytes, + SL_IPV6_ADDRESS_LENGTH); + } + + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_DNS_SERVER_ADD, + SLI_SI91X_NETWORK_CMD, + &dns_server_add_request, + sizeof(dns_server_add_request), + SLI_SI91X_WAIT_FOR_COMMAND_SUCCESS, + NULL, + NULL); + + return status; +} + +sl_status_t sl_net_configure_ip(sl_net_interface_t interface, + const sl_net_ip_configuration_t *ip_config, + uint32_t timeout) +{ + uint8_t vap_id = 0; + sl_net_ip_configuration_t config = { 0 }; + + if (SL_NET_WIFI_CLIENT_INTERFACE == SL_NET_INTERFACE_TYPE(interface)) { + vap_id = SL_WIFI_CLIENT_VAP_ID; + dhcp_type[SLI_SI91X_CLIENT] = ip_config->mode; + } else if (SL_NET_WIFI_AP_INTERFACE == SL_NET_INTERFACE_TYPE(interface)) { + vap_id = SL_WIFI_AP_VAP_ID; + dhcp_type[SLI_SI91X_AP] = ip_config->mode; + } else { + return SL_STATUS_WIFI_UNSUPPORTED; + } + + memcpy(&config, ip_config, sizeof(sl_net_ip_configuration_t)); + return sli_si91x_configure_ip_address(&config, vap_id, timeout); +} + +sl_status_t sl_net_get_ip_address(sl_net_interface_t interface, sl_net_ip_address_t *ip_address, uint32_t timeout) +{ + uint8_t vap_id = 0; + sl_status_t status = 0; + sl_net_ip_configuration_t ip_config = { 0 }; + + if (SL_NET_WIFI_CLIENT_INTERFACE == SL_NET_INTERFACE_TYPE(interface)) { + vap_id = SL_WIFI_CLIENT_VAP_ID; + ip_address->mode = dhcp_type[SLI_SI91X_CLIENT]; + } else if (SL_NET_WIFI_AP_INTERFACE == SL_NET_INTERFACE_TYPE(interface)) { + ip_address->mode = dhcp_type[SLI_SI91X_AP]; + return SL_STATUS_OK; + } else { + return SL_STATUS_WIFI_UNSUPPORTED; + } + + ip_config.mode = SL_IP_MANAGEMENT_DHCP; +#ifdef SLI_SI91X_ENABLE_IPV6 + ip_config.type = SL_IPV6; +#else + ip_config.type = SL_IPV4; +#endif + status = sli_si91x_configure_ip_address(&ip_config, vap_id, timeout); + if (status != SL_STATUS_OK) { + return status; + } + + ip_address->type = ip_config.type; + // Copy the IPv4 addresses to the address structure + memcpy(ip_address->v4.ip_address.bytes, (const uint8_t *)ip_config.ip.v4.ip_address.bytes, sizeof(sl_ipv4_address_t)); + memcpy(ip_address->v4.netmask.bytes, (const uint8_t *)ip_config.ip.v4.netmask.bytes, sizeof(sl_ipv4_address_t)); + memcpy(ip_address->v4.gateway.bytes, (const uint8_t *)ip_config.ip.v4.gateway.bytes, sizeof(sl_ipv4_address_t)); + + // Copy the IPv6 addresses to the address structure + memcpy(&ip_address->v6.link_local_address.bytes, + (const uint8_t *)ip_config.ip.v6.link_local_address.bytes, + sizeof(sl_ipv6_address_t)); + memcpy(&ip_address->v6.global_address.bytes, + (const uint8_t *)ip_config.ip.v6.global_address.bytes, + sizeof(sl_ipv6_address_t)); + memcpy(&ip_address->v6.gateway.bytes, (const uint8_t *)ip_config.ip.v6.gateway.bytes, sizeof(sl_ipv6_address_t)); + + return SL_STATUS_OK; +} + +//! Set DNS mode based on the configuration of primary and secondary server addresses +static bool sli_si91x_get_dns_mode(const sl_net_dns_address_t *address) +{ + bool primary_is_zero = address->primary_server_address == NULL + || sli_wifi_is_ip_address_zero(address->primary_server_address); + bool secondary_is_zero = address->secondary_server_address == NULL + || sli_wifi_is_ip_address_zero(address->secondary_server_address); + if (primary_is_zero && secondary_is_zero) { + return SL_SI91X_DHCP; /* Set to DHCP mode if both addresses are zero or NULL */ + } else { + return SL_SI91X_STATIC; /* Set to static mode if at least one address is non-zero */ + } +} diff --git a/wiseconnect/components/device/silabs/si91x/wireless/socket/inc/sl_si91x_socket_constants.h b/wiseconnect/components/device/silabs/si91x/wireless/socket/inc/sl_si91x_socket_constants.h index 2efbb6bea..37ada4ad4 100644 --- a/wiseconnect/components/device/silabs/si91x/wireless/socket/inc/sl_si91x_socket_constants.h +++ b/wiseconnect/components/device/silabs/si91x/wireless/socket/inc/sl_si91x_socket_constants.h @@ -87,6 +87,14 @@ #define SLI_MAX_RETRANSMISSION_TIME_VALUE 32 + +#define SOL_TCP 0x0006 ///< This is used to denote that the options are applicable at the TCP level. +#define TCP_ULP 0x001f ///< Attach a ULP (Upper Layer Protocol) to a TCP connection. +#define TLS "tls" ///< Option value for default TLS version. +#define TLS_1_0 "tls_1_0" ///< Option value for TLS 1.0. +#define TLS_1_1 "tls_1_1" ///< Option value for TLS 1.1. +#define TLS_1_2 "tls_1_2" ///< Option value for TLS 1.2. +#define TLS_1_3 "tls_1_3" ///< Option value for TLS 1.3. /** * @addtogroup SI91X_SOCKET_OPTION_NAME SiWx91x Socket Option Name * @ingroup SI91X_SOCKET_FUNCTIONS diff --git a/wiseconnect/components/device/silabs/si91x/wireless/src/sl_si91x_http_client_callback_framework.c b/wiseconnect/components/device/silabs/si91x/wireless/src/sl_si91x_http_client_callback_framework.c new file mode 100644 index 000000000..6c77006e0 --- /dev/null +++ b/wiseconnect/components/device/silabs/si91x/wireless/src/sl_si91x_http_client_callback_framework.c @@ -0,0 +1,215 @@ +/***************************************************************************/ /** + * @file + * @brief sl_si91x_http_client_callback_framework.c + ******************************************************************************* + * # License + * Copyright 2019 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ + +#include "sl_si91x_http_client_callback_framework.h" +#include "sl_constants.h" +#include + +/****************************************************** + * Constants + ******************************************************/ +//! HTTP OFFSET +#define SLI_SI91X_HTTP_OFFSET 12 + +//! HTTP PUT OFFSET +#define SLI_SI91X_HTTP_PUT_OFFSET 16 + +/****************************************************** + * Structures + ******************************************************/ +/** + * Callback entry structure for sl_http_client events + */ +typedef struct { + sl_http_client_t client_handle; ///< HTTP client handle + sl_http_client_event_handler_t callback_function; ///< User provided callback function pointer +} sl_http_client_callback_entry_t; + +/****************************************************** + * Global Variables + ******************************************************/ +static sl_http_client_callback_entry_t registered_callback[SL_HTTP_CLIENT_MAX_EVENT] = { 0 }; + +/****************************************************** + * Function Declarations + ******************************************************/ +static sl_http_client_callback_entry_t *sli_get_http_client_callback_entry(sl_http_client_event_t event); +static sl_status_t sl_si91x_http_client_put_delete(void); + +/****************************************************** + * Function Definitions + ******************************************************/ +sl_status_t sli_http_client_register_callback(sl_http_client_event_t event, + sl_http_client_t client_handle, + sl_http_client_event_handler_t function) +{ + // Get the callback entry associated with the specified event + sl_http_client_callback_entry_t *entry = sli_get_http_client_callback_entry(event); + + // check if the entry is valid + if (entry != NULL) { + entry->client_handle = client_handle; + entry->callback_function = function; + return SL_STATUS_OK; + } + return SL_STATUS_FAIL; +} + +static sl_status_t sl_si91x_http_client_put_delete(void) +{ + sl_status_t status = SL_STATUS_OK; + + sl_si91x_http_client_put_request_t *request = + (sl_si91x_http_client_put_request_t *)malloc(sizeof(sl_si91x_http_client_put_request_t)); + if (request == NULL) { + return SL_STATUS_ALLOCATION_FAILED; + } + memset(request, 0, sizeof(sl_si91x_http_client_put_request_t)); + request->command_type = SLI_SI91X_HTTP_CLIENT_PUT_DELETE; + uint16_t packet_len = sizeof(sl_si91x_http_client_put_request_t) - SLI_SI91X_HTTP_CLIENT_PUT_MAX_BUFFER_LENGTH; + + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_HTTP_CLIENT_PUT, + SLI_SI91X_NETWORK_CMD, + request, + packet_len, + SLI_SI91X_WAIT_FOR_COMMAND_SUCCESS, + NULL, + NULL); + // Free the memory allocated + free(request); + + // Verify the status + VERIFY_STATUS_AND_RETURN(status); + return status; +} + +static sl_http_client_callback_entry_t *sli_get_http_client_callback_entry(sl_http_client_event_t event) +{ + return ®istered_callback[event]; +} + +sl_status_t sli_http_client_default_event_handler(sl_http_client_event_t event, + sl_wifi_buffer_t *buffer, + void *sdk_context) +{ + const sl_http_client_callback_entry_t *entry = sli_get_http_client_callback_entry(event); + + // Get the packet data from the buffer + sl_wifi_packet_t *packet = (sl_wifi_packet_t *)sl_si91x_host_get_buffer_data(buffer, 0, NULL); + + // Convert the firmware status to a library status + sl_status_t status = sli_convert_and_save_firmware_status(sli_get_si91x_frame_status(packet)); + + // Initialize an HTTP client response structure + sl_http_client_response_t http_response = { 0 }; + uint16_t end_of_data; + uint16_t http_server_response; + + http_response.status = status; + + //! TBD + http_response.response_headers = NULL; + + if ((entry == NULL) && (entry->callback_function == NULL)) { + return SL_STATUS_FAIL; + } + + SL_DEBUG_LOG("\r\n>>> %s : %x <<<\r\n", __func__, status); + + // Handle different HTTP client response types based on the packet's command + switch (packet->command) { + case SLI_WLAN_RSP_HTTP_CLIENT_GET: + case SLI_WLAN_RSP_HTTP_CLIENT_POST: + case SLI_WLAN_RSP_HTTP_CLIENT_POST_DATA: { + // Handle GET, POST, and POST_DATA responses + if (status == SL_STATUS_OK) { + // Extract http server response from packet + memcpy(&http_server_response, packet->data + 2, sizeof(uint16_t)); + + http_response.http_response_code = http_server_response; + + // Check http server response and fill other callback response data + // http server response - success codes 200 to 299, failure codes 300 to 499 + // http_server_response will be zero, if FW returns error for any http client cmd + if (http_server_response == 0 || (http_server_response >= 200 && http_server_response <= 499)) { + // Extract end of data indication from packet + memcpy(&end_of_data, packet->data, sizeof(uint16_t)); + + http_response.end_of_data = end_of_data; + http_response.data_buffer = &packet->data[SLI_SI91X_HTTP_OFFSET]; + http_response.data_length = packet->length - SLI_SI91X_HTTP_OFFSET; + } + } else if (status == SL_STATUS_SI91X_HTTP_GET_CMD_IN_PROGRESS) { + // Don't trigger the callback, If the HTTP GET execution is in progress + return status; + } + break; + } + + case SLI_WLAN_RSP_HTTP_CLIENT_PUT: { + // Handle PUT responses + uint8_t http_cmd_type = *packet->data; + const sli_si91x_http_client_put_pkt_rsp_t *response = (sli_si91x_http_client_put_pkt_rsp_t *)&packet->data; + const sli_si91x_http_put_pkt_server_rsp_t *server_rsp = (sli_si91x_http_put_pkt_server_rsp_t *)&packet->data; + + // Delete HTTP PUT client if PUT request fails + if (status != SL_STATUS_OK) { + sl_si91x_http_client_put_delete(); + break; + } + + // Check for HTTP_CLIENT_PUT_PKT command + if (http_cmd_type == SLI_SI91X_HTTP_CLIENT_PUT_PKT) { + http_response.data_length = 0; + http_response.end_of_data = response->end_of_file; + } + // Check for HTTP Client PUT response from server + else if (http_cmd_type == SLI_SI91X_HTTP_CLIENT_PUT_OFFSET_PKT) { + http_response.data_length = (uint16_t)server_rsp->data_len; + http_response.end_of_data = server_rsp->more; + } + + http_response.data_buffer = &packet->data[SLI_SI91X_HTTP_PUT_OFFSET]; + + // 917 does not support HTTP Server response for SL_HTTP_PUT request + http_response.http_response_code = 0; + + // Delete HTTP PUT client if end of data is 1 + if (http_response.end_of_data) { + sl_si91x_http_client_put_delete(); + } + + break; + } + default: + break; + } + return entry->callback_function(&entry->client_handle, event, &http_response, sdk_context); +} diff --git a/wiseconnect/components/service/http_client/inc/sl_http_client.h b/wiseconnect/components/service/http_client/inc/sl_http_client.h new file mode 100644 index 000000000..896035ee4 --- /dev/null +++ b/wiseconnect/components/service/http_client/inc/sl_http_client.h @@ -0,0 +1,555 @@ +/***************************************************************************/ /** + * @file sl_http_client.h + ******************************************************************************* + * # License + * Copyright 2025 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ +#pragma once + +#include "sl_status.h" +#include "sl_wifi_types.h" +#include "sl_ip_types.h" +#include "sl_net_constants.h" +#include "cmsis_os2.h" +#include "sl_si91x_socket_types.h" +#include "sl_si91x_socket_utility.h" +#include + +/****************************************************** + * Macros + ******************************************************/ + +/****************************************************** + * Constants + ******************************************************/ + +/** + * @addtogroup SERVICE_HTTP_CLIENT_CONSTANTS + * @{ + */ + +/** + * @def SL_HTTP_CLIENT_MAX_WRITE_BUFFER_LENGTH + * @brief + * Maximum buffer length for write data. + * + * @details + * This macro defines the maximum length, in bytes, of the buffer used for writing data in HTTP client operations. It ensures that the buffer allocated for outgoing data is sufficiently large to handle typical HTTP requests. + */ +#define SL_HTTP_CLIENT_MAX_WRITE_BUFFER_LENGTH 900 + +/** + * @def SL_HTTPS_CLIENT_DEFAULT_CERTIFICATE_INDEX + * @brief + * Default certificate index for HTTPS client. + * + * @details + * This macro defines the default index for the client certificate used in HTTPS connections. It is used to specify which certificate to use from the certificate store when establishing a secure connection. + */ +#define SL_HTTPS_CLIENT_DEFAULT_CERTIFICATE_INDEX 0 + +/** + * @def SL_HTTPS_CLIENT_CERTIFICATE_INDEX_1 + * @brief + * Certificate index for an HTTPS client certificate. + * + * @details + * This macro defines the index for an HTTPS client certificate used in secure connections. It specifies which certificate to use from the certificate store when establishing a secure connection. + */ +#define SL_HTTPS_CLIENT_CERTIFICATE_INDEX_1 1 + +/** + * @def SL_HTTPS_CLIENT_CERTIFICATE_INDEX_2 + * @brief + * Certificate index for the an HTTPS client certificate. + * + * @details + * This macro defines the index for an HTTPS client certificate used in secure connections. It specifies which certificate to use from the certificate store when establishing a secure connection. + */ +#define SL_HTTPS_CLIENT_CERTIFICATE_INDEX_2 2 + +/****************************************************** + * Enumerations + ******************************************************/ + +/** + * @brief + * Enumeration of HTTP client methods. + * + * @details + * This enumeration defines the HTTP methods that can be used by the HTTP client to perform various operations on the server. + * + * @note + * Current SDK does not support SL_HTTP_HEAD and SL_HTTP_DELETE methods. + */ +typedef enum { + SL_HTTP_GET = 0, ///< HTTP GET method, used to retrieve data from the server. + SL_HTTP_POST = 1, ///< HTTP POST method, used to send data to the server. + SL_HTTP_HEAD = 2, ///< HTTP HEAD method, used to retrieve headers from the server. + SL_HTTP_PUT = 3, ///< HTTP PUT method, used to update or create a resource on the server. + SL_HTTP_DELETE = 4 ///< HTTP DELETE method, used to delete a resource from the server. +} sl_http_client_method_type_t; + +/** + * @brief + * Enumeration of HTTPS client TLS versions. + * + * @details + * This enumeration defines the versions of the Transport Layer Security (TLS) protocol that can be used by the HTTPS client. Each version provides different levels of security and compatibility. + */ +typedef enum { + SL_TLS_V_1_0 = 0, ///< Use TLS Version 1.0 for HTTPS Client. + SL_TLS_V_1_1 = 1, ///< Use TLS Version 1.1 for HTTPS Client. + SL_TLS_V_1_2 = 2, ///< Use TLS Version 1.2 for HTTPS Client. + SL_TLS_V_1_3 = 3, ///< Use TLS Version 1.3 for HTTPS Client. + SL_TLS_DEFAULT_VERSION = -1 ///< Use the default TLS version for HTTPS Client. +} sl_http_client_tls_version_t; + +/** + * @brief + * Enumeration of HTTP protocol versions. + * + * @details + * This enumeration defines the versions of the HTTP protocol that can be used by the HTTP client. Each version corresponds to a specific set of features and behaviors defined by the HTTP standard. + */ +typedef enum { + SL_HTTP_V_1_0 = 0, ///< Use HTTP Protocol version 1.0 + SL_HTTP_V_1_1 = 1 ///< Use HTTP Protocol version 1.1 +} sl_http_client_version_t; + +/** + * @brief + * Enumeration of HTTP client events. + * + * @details + * This enumeration defines the various events that can occur during HTTP client operations. Each event corresponds to a specific type of HTTP response received by the client. + */ +typedef enum { + SL_HTTP_CLIENT_GET_RESPONSE_EVENT = 0, ///< Event for receiving a response to an HTTP GET request. + SL_HTTP_CLIENT_POST_RESPONSE_EVENT, ///< Event for receiving a response to an HTTP POST request. + SL_HTTP_CLIENT_PUT_RESPONSE_EVENT, ///< Event for receiving a response to an HTTP PUT request. + SL_HTTP_CLIENT_MAX_EVENT ///< Maximum number of HTTP client events. +} sl_http_client_event_t; + +/** @} */ + +/****************************************************** + * Type Definitions + ******************************************************/ + +/** + * @addtogroup SERVICE_HTTP_CLIENT_TYPES + * @{ + */ + +/** + * @brief + * Handle for the HTTP client. + * + * @details + * This type defines a handle used to identify and manage an HTTP client instance. It is used in various HTTP client operations to reference a specific client. + */ +typedef uint32_t sl_http_client_t; + +/** + * @typedef sl_http_client_event_handler_t + * @brief + * Callback function type for handling HTTP client events. + * + * @details + * This callback is invoked when an HTTP response is received. It provides the client handle, the event that occurred, the response data, and a user-defined context pointer. + * + * @param[out] client + * HTTP client handle of type @ref sl_http_client_t. + * @param[out] event + * HTTP event that has occurred, of type @ref sl_http_client_event_t. + * @param[out] data + * Pointer to the HTTP response data. + * @param[out] request_context + * User-defined context pointer. + * + * @return + * Returns an sl_status_t value indicating the result of the callback execution. See https://docs.silabs.com/gecko-platform/latest/platform-common/status for details. + */ +typedef sl_status_t (*sl_http_client_event_handler_t)(const sl_http_client_t *client, + sl_http_client_event_t event, + void *data, + void *request_context); + +/****************************************************** + * Structures + ******************************************************/ +/** + * @brief + * Structure to hold HTTP client credentials. + * + * @details + * This structure is used to store the credentials required for HTTP client authentication. It includes the lengths of the username ,and password as well as, a flexible array to store the actual username, and password data. + */ +typedef struct { + uint16_t username_length; ///< Length of the username. Maximum supported length is 139 bytes. + uint16_t password_length; ///< Length of the password. Maximum supported length is 139 bytes. + uint8_t data[]; ///< Flexible array to store both the username, and password. +} sl_http_client_credentials_t; + +/** + * @brief + * HTTP client configurations. + * + * @details + * This structure holds the configuration settings for the HTTP client, that includes security options, protocol versions, and network interface settings. + */ +typedef struct { + uint8_t certificate_index; ///< Index of the HTTPS client certificate. + sl_http_client_tls_version_t tls_version; ///< TLS version for the HTTP client. See @ref sl_http_client_tls_version_t. + sl_http_client_version_t http_version; ///< HTTP protocol version. See @ref sl_http_client_version_t. + bool https_enable; ///< Enable or disable HTTPS. + bool https_use_sni; ///< Enable or disable the use of Server Name Indication (SNI) extension in HTTPS. + sl_ip_address_type_t + ip_version; ///< IP version for the HTTP client. See [sl_ip_address_type_t](../wiseconnect-api-reference-guide-common/sl-ip-address-t). + sl_net_interface_t + network_interface; ///< Network interface for the HTTP client. See [sl_net_interface_t](../wiseconnect-api-reference-guide-nwk-mgmt/sl-net-constants#sl-net-interface-t). +} sl_http_client_configuration_t; + +/** + * @brief + * Structure representing an HTTP client extended header node. + * + * @details + * This structure is used to represent a single HTTP header in a linked list of headers. Each node contains a key-value pair for the header, and a pointer to the next header in the list. + */ +typedef struct sl_http_client_header_s { + struct sl_http_client_header_s *next; ///< Pointer to the next header node in the linked list. + char *key; ///< Key name of the HTTP header. + char *value; ///< Value of the HTTP header. +} sl_http_client_header_t; + +/** + * @brief + * HTTP client request configurations. + * + * @details + * This structure holds the configuration settings for an HTTP client request, that includes the HTTP method, server address, resource URL, headers, and other options. + */ +/** + * @brief + * Configuration settings for an HTTP client request. + * + * @details + * This structure contains all the necessary configuration parameters for making an HTTP client request, which includes the HTTP method, server address, resource URL, headers, and other options. + */ +typedef struct { + sl_http_client_method_type_t http_method_type; ///< HTTP request method. See @ref sl_http_client_method_type_t. + uint8_t * + ip_address; ///< IP address should contain the IP address of the HTTP server as a string (values in ASCII), for example, "192.168.1.1" for IPv4 or "2001:0db8:85a3:0000:0000:8a2e:0370:7334" for IPv6. + uint8_t * + resource; ///< Full URL string for the requested resource, including the scheme (for example, http, https), domain, port, path, query parameters, and fragment. The maximum supported HTTP URL is 2048 bytes when the SL_SI91X_FEAT_LONG_HTTP_URL bit is enabled in the feature_bit_map. If the SL_SI91X_FEAT_LONG_HTTP_URL bit is disabled, then the maximum supported length for the HTTP URL is (872 - (length of username + length of password) - length of hostname - length of IP address) bytes, excludes the delimiters. + uint16_t port; ///< Port number of the HTTP server. + sl_si91x_socket_type_length_value_t * + sni_extension; ///< SNI (Server Name Indication) extension to specify the hostname for servers hosting multiple domains on the same IP address of type [si91x_socket_type_length_value_t](../wiseconnect-api-reference-guide-sockets/si91x-socket-type-length-value-t). + uint8_t + *body; ///< HTTP body to be sent to the server. Setting this to NULL will process the request in chunked encoding. + uint32_t + body_length; ///< Length of the HTTP body data to be posted. In the case of a chunked request, body length should be equal to the total content length. + sl_http_client_header_t * + extended_header; ///< User-defined extended header. If NULL, the default extended header will be added internally. See @ref sl_http_client_header_t. + uint16_t timeout_ms; ///< HTTP request timeout period in milliseconds. (Si91x chipsets do not support this feature). + uint16_t + retry_count; ///< Maximum number of retry attempts after a timeout. (Si91x chipsets do not support this feature). + uint16_t + retry_period_ms; ///< Retry period in milliseconds after the maximum retry count is reached. (Si91x chipsets do not support this feature). + bool + tcp_connection_reuse; ///< Flag to indicate whether to reuse the same TCP socket for the connection. (Si91x chipsets do not support this feature). + void *context; ///< User-defined context for the request. + sl_http_client_event_handler_t + event_handler; ///< Callback function for handling HTTP client events. See @ref sl_http_client_event_handler_t. + uint8_t * + host_name; ///< Hostname of the HTTP server as specified in the request header. If NULL, the ip_address is used instead of the hostname. +} sl_http_client_request_t; + +/** + * @brief + * HTTP client response structure. + * + * @details + * This structure holds the response data received from an HTTP server, which includes the status, data buffer, data length, HTTP response code, and response headers. + */ +typedef struct { + uint32_t status; ///< Status of the HTTP request. + uint8_t *data_buffer; ///< Pointer to the buffer containing the response data. + uint16_t data_length; ///< Length of the received data in bytes. + uint32_t end_of_data; ///< Indicator for the end of data (0: more data, 1: end of data). + uint16_t + http_response_code; ///< HTTP response code from the server. (Si91x chipsets do not support this feature for SL_HTTP_PUT). + uint8_t version; ///< HTTP version used in the response. (Si91x chipsets do not support this feature). + sl_http_client_header_t * + response_headers; ///< Pointer to the HTTP response headers. See @ref sl_http_client_header_t. (Si91x chipsets do not support this feature). +} sl_http_client_response_t; + +/** @} */ + +/****************************************************** + * Function Declarations + ******************************************************/ + +/** + * @addtogroup SERVICE_HTTP_CLIENT_FUNCTIONS + * @{ + */ + +/***************************************************************************/ /** + * @brief + * Initializes an HTTP client. + * + * @details + * It prepares the client to send HTTP requests. You should call this function before making any HTTP requests to ensure the client is properly configured. + * + * @note + * - You can call this function multiple times to initialize multiple HTTP client resources. + * - You can use this function after calling `deinit()` to reinitialize the resource. + * + * @param[in] configuration + * Pointer to an @ref sl_http_client_configuration_t structure containing the configuration settings for the HTTP client. Must not be NULL. + * + * @param[out] client + * Pointer to an @ref sl_http_client_t object that would be initialized with the client handle. Must not be NULL. + * + * @return + * sl_status_t - Status of the operation. For more details, see https://docs.silabs.com/gecko-platform/latest/platform-common/status. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_INVALID_PARAMETER: One or more input parameters are NULL or invalid. + * - SL_STATUS_FAIL: Failed to initialize the HTTP client. + ******************************************************************************/ +sl_status_t sl_http_client_init(const sl_http_client_configuration_t *configuration, sl_http_client_t *client); + +/***************************************************************************/ +/** + * @brief + * Deinitializes an HTTP client and releases resources used by the HTTP client. + * + * @details + * This function deinitializes the HTTP client, freeing any resources that were allocated during its initialization and usage. + * It also deletes any extended headers that may exist. + * + * @pre + * The HTTP client must be initialized using @ref sl_http_client_init before calling this function. + * + * @param[in] client + * Pointer to an @ref sl_http_client_t object representing the HTTP client handle. Must not be NULL. + * + * @return + * sl_status_t - Status of the operation. For more details, see https://docs.silabs.com/gecko-platform/latest/platform-common/status. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_INVALID_PARAMETER: The provided client handle is NULL or invalid. + * - SL_STATUS_FAIL: Failed to deinitialize the HTTP client. + * + * @note + * The user must call this function to release resources once the HTTP client is no longer needed. + ******************************************************************************/ +sl_status_t sl_http_client_deinit(const sl_http_client_t *client); + +/***************************************************************************/ +/** + * @brief + * Initializes a callback function for the specified HTTP request. + * + * @details + * This function sets up a callback function to handle events for a specific HTTP request. It must be called after initializing the HTTP client. + * + * @pre + * - @ref sl_http_client_init should be called before this function. + * + * @param[in] request + * Pointer to an @ref sl_http_client_request_t structure containing the HTTP client request configuration. Must not be NULL. + * + * @param[in] event_handler + * Callback function of type @ref sl_http_client_event_handler_t to handle HTTP client events. Must not be NULL. + * + * @param[in] request_context + * User-defined context pointer. The memory space for this context must remain valid until the response is received. + * + * @return + * sl_status_t - Status of the operation. For more details, see https://docs.silabs.com/gecko-platform/latest/platform-common/status. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_INVALID_PARAMETER: One or more input parameters are NULL or invalid. + * - SL_STATUS_FAIL: Failed to initialize the HTTP client request. + ******************************************************************************/ +sl_status_t sl_http_client_request_init(sl_http_client_request_t *request, + sl_http_client_event_handler_t event_handler, + void *request_context); + +/***************************************************************************/ +/** + * @brief + * Adds an extended header with a key and value to the client request. + * + * @details + * This function adds an extended header to the specified HTTP client request. If the request does not already contain any extended headers, + * this function allocates memory for them. + * + * @pre + * - @ref sl_http_client_init should be called before this function. + * + * @param[in] request + * Pointer to an @ref sl_http_client_request_t structure representing the HTTP client request configuration. Must not be NULL. + * + * @param[in] key + * Pointer to a string containing the header key. Must not be NULL. + * + * @param[in] value + * Pointer to a string containing the header value. Must not be NULL. + * + * @return + * sl_status_t - Status of the operation. For more details, see https://docs.silabs.com/gecko-platform/latest/platform-common/status. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_INVALID_PARAMETER: One or more input parameters are NULL or invalid. + * - SL_STATUS_FAIL: Failed to add the header to the request. + * + * @note + * If the request does not contain any extended headers, this function allocates memory for them. + ******************************************************************************/ +sl_status_t sl_http_client_add_header(sl_http_client_request_t *request, const char *key, const char *value); + +/***************************************************************************/ +/** + * @brief + * Deletes a specified header field from the extended headers of an HTTP client request. + * + * @details + * This function removes a header field from the extended headers of the specified HTTP client request based on the provided key. + * + * @pre + * - @ref sl_http_client_add_header should be called before this function. + * + * @param[in] request + * Pointer to an @ref sl_http_client_request_t structure represents the HTTP client request configuration. Must not be NULL. + * + * @param[in] key + * Pointer to a string contains the header key to be deleted. Must not be NULL. + * + * @return + * sl_status_t - Status of the operation. For more details, see https://docs.silabs.com/gecko-platform/latest/platform-common/status. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_INVALID_PARAMETER: One or more input parameters are NULL or invalid. + * - SL_STATUS_FAIL: Failed to delete the header from the request. + ******************************************************************************/ +sl_status_t sl_http_client_delete_header(sl_http_client_request_t *request, const char *key); + +/***************************************************************************/ +/** + * @brief + * Deletes all headers from the extended headers of an HTTP client request. + * + * @details + * This function removes all headers from the extended headers of the specified HTTP client request. It frees up the memory allocated for these headers. + * + * @pre + * - @ref sl_http_client_add_header should be called before this function. + * + * @param[in] request + * Pointer to an @ref sl_http_client_request_t structure representing the HTTP client request configuration. Must not be NULL. + * + * @return + * sl_status_t - Status of the operation. For more details, see https://docs.silabs.com/gecko-platform/latest/platform-common/status. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_INVALID_PARAMETER: The provided request handle is NULL or invalid. + * - SL_STATUS_FAIL: Failed to delete the headers from the request. + * + * @note + * The user must call this function to free the memory allocated for the headers. + ******************************************************************************/ +sl_status_t sl_http_client_delete_all_headers(sl_http_client_request_t *request); + +/***************************************************************************/ +/** + * @brief + * Sends an HTTP request. + * + * @details + * This function sends an HTTP request using the specified client and request configuration. It must be called after initializing the request with + * @ref sl_http_client_request_init. + * + * @pre + * - @ref sl_http_client_request_init should be called before this function. + * + * @param[in] client + * Pointer to an @ref sl_http_client_t object representing the HTTP client handle. Must not be NULL. + * + * @param[in] request + * Pointer to an @ref sl_http_client_request_t object representing the HTTP client request configuration. Must not be NULL. + * + * @return + * sl_status_t - Status of the operation. For more details, see https://docs.silabs.com/gecko-platform/latest/platform-common/status. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_INVALID_PARAMETER: One or more input parameters are NULL or invalid. + * - SL_STATUS_FAIL: Failed to send the HTTP request. + * + * @note + * - HTTP HEAD and DELETE methods are not supported on Si91x specific chipsets. + * - The `body_length` header in the request is set internally by default on Si91x specific chipsets. + * - HTTP PUT does not support sending the body through this API; it is mandatory to call @ref sl_http_client_write_chunked_data on Si91x specific chipsets. + * - HTTP response status and response codes (e.g., 200, 201, 404) would be returned in the corresponding event handler registered during @ref sl_http_client_request_init. + * - If the `sni_extension` field in the `sl_http_client_request_t` structure is NULL, the `host_name` field will be used as the SNI, provided that `host_name` is not equal to `ip_address`. + ******************************************************************************/ +sl_status_t sl_http_client_send_request(const sl_http_client_t *client, const sl_http_client_request_t *request); + +/***************************************************************************/ +/** + * @brief + * Sends HTTP POST and PUT chunked data. + * + * @details + * This function sends a chunk of data as part of an HTTP POST or PUT request. It should be used after initiating the request with + * @ref sl_http_client_send_request. The data can be sent in multiple chunks by calling this function multiple times. + * + * @pre + * - @ref sl_http_client_send_request should be called before this function. + * + * @param[in] client + * Pointer to an @ref sl_http_client_t object representing the HTTP client handle. Must not be NULL. + * + * @param[in] data + * Pointer to the buffer containing the data to be written. Must not be NULL. + * + * @param[in] data_length + * Length of the data chunk to be sent. + * + * @param[in] flush_now + * Boolean flag indicating whether to flush the data immediately. Note that this feature is not supported on Si91x specific chipsets. + * + * @return + * sl_status_t - Status of the operation. For more details, see https://docs.silabs.com/gecko-platform/latest/platform-common/status. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_INVALID_PARAMETER: One or more input parameters are NULL or invalid. + * - SL_STATUS_FAIL: Failed to send the data chunk. + * + * @note + * The `flush_now` feature is not supported on Si91x specific chipsets. + ******************************************************************************/ +sl_status_t sl_http_client_write_chunked_data(const sl_http_client_t *client, + const uint8_t *data, + uint32_t data_length, + bool flush_now); +/** @} */ diff --git a/wiseconnect/components/service/http_client/si91x_socket/sl_http_client.c b/wiseconnect/components/service/http_client/si91x_socket/sl_http_client.c new file mode 100644 index 000000000..49529d250 --- /dev/null +++ b/wiseconnect/components/service/http_client/si91x_socket/sl_http_client.c @@ -0,0 +1,1131 @@ + /***************************************************************************/ /** + * @file sl_http_client.c + ******************************************************************************* + * # License + * Copyright 2025 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ + +#include "sl_http_client.h" +#include "sl_constants.h" +#include "sl_slist.h" +#include "sl_net.h" +#include "sl_net_constants.h" +#include "sl_wifi_constants.h" +#include "sl_si91x_constants.h" +#include "sl_si91x_driver.h" +#include "sl_si91x_protocol_types.h" +#include "sli_net_utility.h" +#include "sl_si91x_http_client_callback_framework.h" +#include + +/****************************************************** + * Macros + ******************************************************/ + +#define VERIFY_MALLOC_AND_FREE_IF_FAIL(ptr, ptr1, ptr2) \ + do { \ + if (ptr == NULL) { \ + if (ptr1 != NULL) \ + free(ptr1); \ + if (ptr2 != NULL) \ + free(ptr2); \ + return SL_STATUS_ALLOCATION_FAILED; \ + } \ + } while (0) + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +/****************************************************** + * Constants + ******************************************************/ +//! HTTP client state +typedef enum { + HTTP_STATE_DEINITIALIZED = 0, ///< HTTP client deinitialized state + HTTP_STATE_INITIALIZED, ///< HTTP client initialized state + HTTP_STATE_GET_REQUEST_INITIALIZED, ///< HTTP client state after GET request initialized + HTTP_STATE_POST_REQUEST_INITIALIZED, ///< HTTP client state after POST request initialized + HTTP_STATE_PUT_REQUEST_INITIALIZED, ///< HTTP client state after PUT request initialized + HTTP_STATE_REQUEST_SENT, ///< HTTP client state after GET/POST/PUT request sent + HTTP_STATE_CHUNKED_REQUEST_SENT ///< HTTP client state after sending chunked request +} sl_http_client_state_t; + +//! MAX supported length for Username and Password together +#define SI91X_MAX_SUPPORTED_HTTP_CREDENTIAL_LENGTH 278 +#define TEMP_STR_SIZE 7 +/****************************************************** + * Structures + ******************************************************/ +//! HTTP client internal context +typedef struct { + sl_http_client_credentials_t *client_credentials; ///< HTTP client credentials + sl_http_client_configuration_t configuration; ///< HTTP client configurations + sl_http_client_request_t request; ///< HTTP client request configurations + sl_http_client_state_t client_state; ///< HTTP client state +} sl_http_client_internal_t; + +/****************************************************** + * Global Variables + ******************************************************/ +static sl_http_client_internal_t http_client_handle = { 0 }; + +extern bool device_initialized; + +/****************************************************** + * Function Declarations + ******************************************************/ +// Validates HTTP clients request configurations +static sl_status_t sli_si91x_copy_ip_address_and_port(const sl_http_client_request_t *request); + +// Copy extended headers into HTTP request buffer +static void sli_si91x_load_extended_headers_into_request_buffer(uint8_t *buffer, + sl_http_client_header_t *extended_header, + uint16_t *http_buffer_offset); + +// Sends GET/POST request +static sl_status_t sli_si91x_send_http_client_request(sl_http_client_method_type_t send_request, + const sl_http_client_internal_t *client_internal, + const sl_http_client_request_t *request); + +// Abort ongoing HTTP client operation +static sl_status_t sli_si91x_http_client_abort(void); + +/****************************************************** + * Function Definitions + ******************************************************/ +sl_status_t sl_http_client_init(const sl_http_client_configuration_t *client_configuration, sl_http_client_t *client) +{ + if (!device_initialized) { + return SL_STATUS_NOT_INITIALIZED; + } + + SL_WIFI_ARGS_CHECK_NULL_POINTER(client_configuration); + SL_WIFI_ARGS_CHECK_NULL_POINTER(client); + + // Only one http client supported in current release + if (http_client_handle.client_state != HTTP_STATE_DEINITIALIZED) { + return SL_STATUS_ALREADY_EXISTS; + } + + // Check https configurations + if (client_configuration->certificate_index > SL_HTTPS_CLIENT_CERTIFICATE_INDEX_2) { + return SL_STATUS_INVALID_CONFIGURATION; + } + + // Check HTTP version + if ((client_configuration->http_version != SL_HTTP_V_1_0) && (client_configuration->http_version != SL_HTTP_V_1_1)) { + return SL_STATUS_INVALID_CONFIGURATION; + } + + // Check SSL/TLS version + switch (client_configuration->tls_version) { + case SL_TLS_DEFAULT_VERSION: + case SL_TLS_V_1_0: + case SL_TLS_V_1_1: + case SL_TLS_V_1_2: { + break; + } + case SL_TLS_V_1_3: { +#if defined(SLI_SI917) || defined(SLI_SI915) + break; +#else + return SL_STATUS_NOT_SUPPORTED; +#endif + } + default: { + return SL_STATUS_INVALID_CONFIGURATION; + } + } + + // Check for network interface + if ((client_configuration->network_interface != SL_NET_WIFI_CLIENT_INTERFACE) + && (client_configuration->network_interface != SL_NET_WIFI_AP_INTERFACE)) { + return SL_STATUS_INVALID_MODE; + } + + // Store client configurations into internal configurations + memcpy(&http_client_handle.configuration, client_configuration, sizeof(sl_http_client_configuration_t)); + + sl_net_credential_type_t type; + uint32_t max_credential_size = sizeof(sl_http_client_credentials_t) + SI91X_MAX_SUPPORTED_HTTP_CREDENTIAL_LENGTH; + + http_client_handle.client_credentials = (sl_http_client_credentials_t *)malloc(max_credential_size); + SLI_VERIFY_MALLOC_AND_RETURN(http_client_handle.client_credentials); + memset(http_client_handle.client_credentials, 0, max_credential_size); + sl_status_t status = sl_net_get_credential(SL_NET_HTTP_CLIENT_CREDENTIAL_ID(0), + &type, + http_client_handle.client_credentials, + &max_credential_size); + + if (status != SL_STATUS_OK || type != SL_NET_HTTP_CLIENT_CREDENTIAL) { + return status != SL_STATUS_OK ? status : SL_STATUS_INVALID_CREDENTIALS; + } + + // Set HTTP client state + http_client_handle.client_state = HTTP_STATE_INITIALIZED; + + // Copy address of HTTP client internal handle + *client = (sl_http_client_t)&http_client_handle; + + return SL_STATUS_OK; +} + +sl_status_t sl_http_client_deinit(const sl_http_client_t *client) +{ + SL_WIFI_ARGS_CHECK_NULL_POINTER(client); + + sl_status_t status = SL_STATUS_OK; + + if (http_client_handle.client_state == HTTP_STATE_DEINITIALIZED) { + return SL_STATUS_INVALID_STATE; + } + + if (*client != (sl_http_client_t)&http_client_handle) { + return SL_STATUS_INVALID_HANDLE; + } + + // Free HTTP client credentials + if (http_client_handle.client_credentials != NULL) { + free(http_client_handle.client_credentials); + } + + // Free extended headers + if (http_client_handle.request.extended_header != NULL) { + status = sl_http_client_delete_all_headers(&http_client_handle.request); + VERIFY_STATUS_AND_RETURN(status); + } + + memset(&http_client_handle, 0, sizeof(sl_http_client_internal_t)); + + status = sli_si91x_http_client_abort(); + + return status; +} + +static sl_status_t sli_si91x_copy_ip_address_and_port(const sl_http_client_request_t *request) +{ + // Validate host name + SL_WIFI_ARGS_CHECK_NULL_POINTER(request->ip_address); + + // Validate resource + SL_WIFI_ARGS_CHECK_NULL_POINTER(request->resource); + + // 917 does not support HTTP POST and PUT request with body_length as 0 in Alpha 3 release + // Bug in NWP, when SL_HTTP_POST is requested with body_length = 0, NWP sends SL_HTTP_GET + // Bug in NWP, when SL_HTTP_PUT is requested with body_length = 0, NWP doesn't send end of data to host so currently there is no way to handle put_delete() + // Below check should be removed once the above NWP bugs are fixed + if (request->http_method_type != SL_HTTP_GET && request->body_length == 0) { + return SL_STATUS_INVALID_PARAMETER; + } + + // Validate http request method + switch (request->http_method_type) { + case SL_HTTP_GET: + case SL_HTTP_POST: + case SL_HTTP_PUT: { + break; + } + case SL_HTTP_HEAD: + case SL_HTTP_DELETE: { + // Not supported in current release + return SL_STATUS_NOT_SUPPORTED; + } + default: { + return SL_STATUS_INVALID_PARAMETER; + } + } + + return SL_STATUS_OK; +} + +sl_status_t sl_http_client_request_init(sl_http_client_request_t *request, + sl_http_client_event_handler_t event_handler, + void *request_context) +{ + SL_WIFI_ARGS_CHECK_NULL_POINTER(request); + SL_WIFI_ARGS_CHECK_NULL_POINTER(event_handler); + + sl_status_t status = SL_STATUS_OK; + + // Validate HTTP client request + status = sli_si91x_copy_ip_address_and_port(request); + VERIFY_STATUS_AND_RETURN(status); + + request->event_handler = event_handler; + request->context = request_context; + + sl_http_client_event_t http_event; + + // Set HTTP client event for requested method + switch (request->http_method_type) { + case SL_HTTP_GET: { + http_event = SL_HTTP_CLIENT_GET_RESPONSE_EVENT; + http_client_handle.client_state = HTTP_STATE_GET_REQUEST_INITIALIZED; + break; + } + case SL_HTTP_POST: { + http_event = SL_HTTP_CLIENT_POST_RESPONSE_EVENT; + http_client_handle.client_state = HTTP_STATE_POST_REQUEST_INITIALIZED; + break; + } + case SL_HTTP_PUT: { + http_event = SL_HTTP_CLIENT_PUT_RESPONSE_EVENT; + http_client_handle.client_state = HTTP_STATE_PUT_REQUEST_INITIALIZED; + break; + } + default: { + return SL_STATUS_INVALID_PARAMETER; + } + } + + // Register callback function for the http event + status = sli_http_client_register_callback(http_event, (sl_http_client_t)&http_client_handle, event_handler); + + return status; +} + +sl_status_t sl_http_client_add_header(sl_http_client_request_t *request, const char *key, const char *value) +{ + SL_WIFI_ARGS_CHECK_NULL_POINTER(request); + SL_WIFI_ARGS_CHECK_NULL_POINTER(key); + SL_WIFI_ARGS_CHECK_NULL_POINTER(value); + + // Allocate memory for the new header + sl_http_client_header_t *new_header = (sl_http_client_header_t *)malloc(sizeof(sl_http_client_header_t)); + SLI_VERIFY_MALLOC_AND_RETURN(new_header); + + // Memset the heap chunk + memset(new_header, 0, sizeof(sl_http_client_header_t)); + + // Allocate memory for key + new_header->key = (char *)malloc(strlen(key) + 1); + VERIFY_MALLOC_AND_FREE_IF_FAIL(new_header->key, new_header, NULL); + + // Copy key in header + snprintf(new_header->key, strlen(key) + 1, "%s", key); + + // Allocate memory for value + new_header->value = (char *)malloc(strlen(value) + 1); + VERIFY_MALLOC_AND_FREE_IF_FAIL(new_header->value, new_header->key, new_header); + + // Copy value in header + snprintf(new_header->value, strlen(value) + 1, "%s", value); + + // Add new header at start of linked list + sl_slist_push((sl_slist_node_t **)&request->extended_header, (sl_slist_node_t *)new_header); + + return SL_STATUS_OK; +} + +sl_status_t sl_http_client_delete_header(sl_http_client_request_t *request, const char *key) +{ + SL_WIFI_ARGS_CHECK_NULL_POINTER(request); + SL_WIFI_ARGS_CHECK_NULL_POINTER(key); + + sl_http_client_header_t *current_header = request->extended_header; + + // Check if linked list is empty + if (request->extended_header == NULL) { + return SL_STATUS_EMPTY; + } + + // Search key in linked list + while ((current_header != NULL) && (strcmp(current_header->key, key) != 0)) { + current_header = current_header->next; + } + + // If key not present in linked list + if (current_header == request->extended_header) { + return SL_STATUS_INVALID_KEY; + } + + // Unlink node from the linked list + sl_slist_remove((sl_slist_node_t **)&request->extended_header, (sl_slist_node_t *)current_header); + + if (current_header != NULL) { + free(current_header->key); + free(current_header->value); + free(current_header); + } + + return SL_STATUS_OK; +} + +sl_status_t sl_http_client_delete_all_headers(sl_http_client_request_t *request) +{ + SL_WIFI_ARGS_CHECK_NULL_POINTER(request); + + sl_http_client_header_t *current_header = request->extended_header; + sl_http_client_header_t *next_header = NULL; + + // Check if linked list is empty + if (request->extended_header == NULL) { + return SL_STATUS_EMPTY; + } + + while (current_header != NULL) { + // Store next header link + next_header = current_header->next; + + // Remove node from list + sl_slist_remove((sl_slist_node_t **)&request->extended_header, (sl_slist_node_t *)current_header); + + // Free current node + free(current_header->key); + free(current_header->value); + free(current_header); + + // Traverse to next node + current_header = next_header; + } + + // Set head node of a linked list to NULL + request->extended_header = NULL; + http_client_handle.request.extended_header = NULL; + + return SL_STATUS_OK; +} +static void sli_si91x_load_extended_headers_into_request_buffer(uint8_t *buffer, + sl_http_client_header_t *extended_header, + uint16_t *http_buffer_offset) +{ + sl_http_client_header_t *current_header = extended_header; + + if (current_header == NULL) { + buffer[(*http_buffer_offset)] = '\0'; + (*http_buffer_offset)++; + } + + while (current_header != NULL) { + // Copy header key + *http_buffer_offset += snprintf((char *)(buffer + (*http_buffer_offset)), + SLI_SI91X_HTTP_BUFFER_LEN - (*http_buffer_offset), + "%s", + current_header->key); + + buffer[(*http_buffer_offset)] = ':'; + (*http_buffer_offset)++; + + // Copy header value + *http_buffer_offset += snprintf((char *)(buffer + (*http_buffer_offset)), + SLI_SI91X_HTTP_BUFFER_LEN - (*http_buffer_offset), + "%s", + current_header->value); + + // Add carriage return to buffer + buffer[(*http_buffer_offset)] = '\r'; + (*http_buffer_offset)++; + + // Add newline to buffer + buffer[(*http_buffer_offset)] = '\n'; + (*http_buffer_offset)++; + + // Check if current header is the last one + if (current_header->next == NULL) { + // Add null terminator to buffer + buffer[(*http_buffer_offset)] = '\0'; + (*http_buffer_offset)++; + break; + } else { + // Point to next header + current_header = current_header->next; + } + } +} + +static sl_status_t sli_si91x_send_http_client_request(sl_http_client_method_type_t send_request, + const sl_http_client_internal_t *client_internal, + const sl_http_client_request_t *request) +{ + sl_status_t status = SL_STATUS_OK; + uint32_t packet_length = 0; + uint8_t packet_identifier = 0; + uint16_t http_buffer_offset = 0; + sli_si91x_http_client_request_t *packet_buffer = NULL; + uint16_t offset = 0; + uint16_t rem_length = 0; + uint16_t chunk_size = SLI_SI91X_MAX_HTTP_CHUNK_SIZE; + // Allocate memory for request structure + sli_si91x_http_client_request_t *http_client_request = + (sli_si91x_http_client_request_t *)malloc(sizeof(sli_si91x_http_client_request_t)); + SLI_VERIFY_MALLOC_AND_RETURN(http_client_request); + + memset(http_client_request, 0, sizeof(sli_si91x_http_client_request_t)); + + // Fill IP version + if (client_internal->configuration.ip_version == SL_IPV6) { + http_client_request->ip_version = 6; + } else { + http_client_request->ip_version = 4; + } + + // Set default by NULL delimiter + http_client_request->https_enable |= SL_SI91X_ENABLE_NULL_DELIMETER; + + // Fill HTTPS feature + if (client_internal->configuration.https_enable) { + // Enable SSL/TLS + http_client_request->https_enable |= SL_SI91X_ENABLE_TLS; + + // Fill SSL/TLS version + switch (client_internal->configuration.tls_version) { + case SL_TLS_DEFAULT_VERSION: { + break; + } + case SL_TLS_V_1_0: { + http_client_request->https_enable |= SL_SI91X_TLS_V_1_0; + break; + } + case SL_TLS_V_1_1: { + http_client_request->https_enable |= SL_SI91X_TLS_V_1_1; + break; + } + case SL_TLS_V_1_2: { + http_client_request->https_enable |= SL_SI91X_TLS_V_1_2; + break; + } +#if defined(SLI_SI917) || defined(SLI_SI915) + case SL_TLS_V_1_3: { + http_client_request->https_enable |= SL_SI91X_TLS_V_1_3; + break; + } +#endif + default: + free(http_client_request); + return SL_STATUS_INVALID_CONFIGURATION; + } + + // Fill HTTPS certificate index bitmap + switch (client_internal->configuration.certificate_index) { + case SL_HTTPS_CLIENT_CERTIFICATE_INDEX_1: { + http_client_request->https_enable |= SL_SI91X_HTTPS_CERTIFICATE_INDEX_1; + break; + } + case SL_HTTPS_CLIENT_CERTIFICATE_INDEX_2: { + http_client_request->https_enable |= SL_SI91X_HTTPS_CERTIFICATE_INDEX_2; + break; + } + case SL_HTTPS_CLIENT_DEFAULT_CERTIFICATE_INDEX: { + break; + } + default: + break; + } + } + + if (client_internal->configuration.https_use_sni) { + http_client_request->https_enable |= SL_SI91X_HTTPS_USE_SNI; + + if (request->sni_extension != NULL) { + status = sli_si91x_set_sni_for_embedded_socket(request->sni_extension); + } else { + if (request->host_name != NULL && request->ip_address != request->host_name) { + + sl_si91x_socket_type_length_value_t *tls_sni = (sl_si91x_socket_type_length_value_t *)malloc( + sizeof(sl_si91x_socket_type_length_value_t) + strlen((char *)request->host_name)); + if (tls_sni == NULL) { + free(http_client_request); + return SL_STATUS_ALLOCATION_FAILED; + } + tls_sni->type = SL_SI91X_TLS_EXTENSION_SNI_TYPE; + + tls_sni->length = strlen((char *)(request->host_name)); + memcpy(tls_sni->value, request->host_name, tls_sni->length); + status = sli_si91x_set_sni_for_embedded_socket(tls_sni); + free(tls_sni); + } + } + if (status != SL_STATUS_OK) { + free(http_client_request); + return status; + } + } + + // Fill HTTP version + if (client_internal->configuration.http_version == SL_HTTP_V_1_1) { + http_client_request->https_enable |= SL_SI91X_HTTP_V_1_1; + } + + // Fill port number + http_client_request->port_number = request->port; + + // Fill username + memcpy(http_client_request->buffer, + &client_internal->client_credentials->data[0], + client_internal->client_credentials->username_length); + http_buffer_offset += client_internal->client_credentials->username_length; + http_client_request->buffer[http_buffer_offset] = '\0'; + http_buffer_offset++; + + // Fill password + memcpy(http_client_request->buffer + http_buffer_offset, + &client_internal->client_credentials->data[client_internal->client_credentials->username_length], + client_internal->client_credentials->password_length); + http_buffer_offset += client_internal->client_credentials->password_length; + http_client_request->buffer[http_buffer_offset] = '\0'; + http_buffer_offset++; + + // Check for HTTP_V_1.1 and Empty host name and fill IP address + if (client_internal->configuration.http_version == SL_HTTP_V_1_1 + && (request->host_name == NULL || strlen((char *)request->host_name) == 0)) { + http_buffer_offset += snprintf((char *)(http_client_request->buffer + http_buffer_offset), + SLI_SI91X_HTTP_BUFFER_LEN - http_buffer_offset, + "%s", + request->ip_address); + } else if (request->host_name != NULL) { + // Fill hostname + http_buffer_offset += snprintf((char *)(http_client_request->buffer + http_buffer_offset), + SLI_SI91X_HTTP_BUFFER_LEN - http_buffer_offset, + "%s", + request->host_name); + } + http_buffer_offset++; + + // Fill IP address + http_buffer_offset += snprintf((char *)(http_client_request->buffer + http_buffer_offset), + SLI_SI91X_HTTP_BUFFER_LEN - http_buffer_offset, + "%s", + request->ip_address); + http_buffer_offset++; + + // Fill URL resource + http_buffer_offset += snprintf((char *)(http_client_request->buffer + http_buffer_offset), + SLI_SI91X_HTTP_BUFFER_LEN - http_buffer_offset, + "%s", + request->resource); + http_buffer_offset++; + + // Fill extended header + if (request->extended_header != NULL) { + // Enable user given content type in extended header + http_client_request->https_enable |= SL_SI91X_HTTP_USER_DEFINED_CONTENT_TYPE; + + sli_si91x_load_extended_headers_into_request_buffer(http_client_request->buffer, + request->extended_header, + &http_buffer_offset); + } else { + http_client_request->buffer[http_buffer_offset] = '\0'; + http_buffer_offset++; + } + + if (send_request == SL_HTTP_POST) { + // Check for HTTP post data feature + if (request->body == NULL) { + // Enable HTTP Post big data feature + http_client_request->https_enable |= SL_SI91X_SUPPORT_HTTP_POST_DATA; + + // Copy total data length into buffer + uint8_t temp_str[TEMP_STR_SIZE] = { 0 }; + convert_itoa(request->body_length, temp_str); + size_t temp_str_len = strnlen((char *)temp_str, TEMP_STR_SIZE + 1); + memcpy(http_client_request->buffer + http_buffer_offset, temp_str, temp_str_len); + http_buffer_offset += temp_str_len; + } else { + // Fill HTTP post data + memcpy(http_client_request->buffer + http_buffer_offset, request->body, request->body_length); + http_buffer_offset += request->body_length; + } + } + + // Check if request buffer is overflowed or resource length is overflowed + if (http_buffer_offset > SLI_SI91X_HTTP_BUFFER_LEN + || strnlen((char *)request->resource, SLI_SI91X_MAX_HTTP_URL_SIZE + 1) > SLI_SI91X_MAX_HTTP_URL_SIZE) { + free(http_client_request); + return SL_STATUS_HAS_OVERFLOWED; + } + + // Fill total packet length + packet_length = sizeof(sli_si91x_http_client_request_t) - SLI_SI91X_HTTP_BUFFER_LEN + http_buffer_offset; + + // Copy the total http buffer size to the rem_length + rem_length = http_buffer_offset; + + // Check if the HTTP buffer size exceeds the limit + if (http_buffer_offset <= SLI_SI91X_MAX_HTTP_CHUNK_SIZE) { + + // Send HTTP request + if (send_request == SL_HTTP_POST) { + // HTTP Post request + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_HTTP_CLIENT_POST, + SLI_SI91X_NETWORK_CMD, + http_client_request, + packet_length, + SLI_SI91X_RETURN_IMMEDIATELY, + request->context, + NULL); + } else { + // HTTP Get request + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_HTTP_CLIENT_GET, + SLI_SI91X_NETWORK_CMD, + http_client_request, + packet_length, + SLI_SI91X_RETURN_IMMEDIATELY, + request->context, + NULL); + } + } else { + // Allocate memory for a new packet buffer + packet_buffer = malloc(sizeof(sli_si91x_http_client_request_t)); + SLI_VERIFY_MALLOC_AND_RETURN(packet_buffer); + + // Iterate through the length of the packet + while (rem_length) { + memset(packet_buffer, 0, sizeof(sli_si91x_http_client_request_t)); + + // Fill the packet identifier + if (rem_length > SLI_SI91X_MAX_HTTP_CHUNK_SIZE) { + if (!offset) { + packet_identifier = SLI_HTTP_GET_FIRST_PKT; + } else { + packet_identifier = SLI_HTTP_GET_MIDDLE_PKT; + } + } else { + packet_identifier = SLI_HTTP_GET_LAST_PKT; + chunk_size = rem_length; + } + + // Fill the HTTP params + packet_buffer->ip_version = http_client_request->ip_version; + packet_buffer->https_enable = http_client_request->https_enable; + packet_buffer->port_number = http_client_request->port_number; + + // Copy the original buffer into the new packet buffer with an offset and for a chunk size + memcpy(packet_buffer->buffer, (http_client_request->buffer + offset), chunk_size); + + if (send_request == SL_HTTP_GET) { + // HTTP Get request with custom driver command + status = sl_si91x_custom_driver_send_command( + SLI_WLAN_REQ_HTTP_CLIENT_GET, + SLI_SI91X_NETWORK_CMD, + packet_buffer, + (sizeof(sli_si91x_http_client_request_t) - SLI_SI91X_HTTP_BUFFER_LEN + chunk_size), + SLI_SI91X_RETURN_IMMEDIATELY, + request->context, + NULL, + packet_identifier); + } else if (send_request == SL_HTTP_POST) { + // HTTP POST request with custom driver command + status = sl_si91x_custom_driver_send_command( + SLI_WLAN_REQ_HTTP_CLIENT_POST, + SLI_SI91X_NETWORK_CMD, + packet_buffer, + (sizeof(sli_si91x_http_client_request_t) - SLI_SI91X_HTTP_BUFFER_LEN + chunk_size), + SLI_SI91X_RETURN_IMMEDIATELY, + request->context, + NULL, + packet_identifier); + } + + // Increase the offset by chunk_size + offset += chunk_size; + + // Decrease the rem_length by chunk_size + rem_length = rem_length - chunk_size; + } + + // Free packet buffer structure memory + free(packet_buffer); + } + + // Free request structure memory + free(http_client_request); + + return status; +} + +sl_status_t sl_http_client_send_request(const sl_http_client_t *client, const sl_http_client_request_t *request) +{ + SL_WIFI_ARGS_CHECK_NULL_POINTER(client); + SL_WIFI_ARGS_CHECK_NULL_POINTER(request); + + if (*client != (sl_http_client_t)&http_client_handle) { + return SL_STATUS_INVALID_HANDLE; + } + + // Validate HTTP client request + sl_status_t status = sli_si91x_copy_ip_address_and_port(request); + VERIFY_STATUS_AND_RETURN(status); + + uint32_t packet_length = 0; + uint16_t http_buffer_offset = 0; + + switch (request->http_method_type) { + case SL_HTTP_GET: { + // Validate HTTP client state + if (http_client_handle.client_state != HTTP_STATE_GET_REQUEST_INITIALIZED) { + return SL_STATUS_INVALID_STATE; + } + + //! Send HTTP client GET request + status = sli_si91x_send_http_client_request(SL_HTTP_GET, &http_client_handle, request); + break; + } + + case SL_HTTP_POST: { + // Validate HTTP client state + if (http_client_handle.client_state != HTTP_STATE_POST_REQUEST_INITIALIZED) { + return SL_STATUS_INVALID_STATE; + } + + //! Send HTTP client POST request + status = sli_si91x_send_http_client_request(SL_HTTP_POST, &http_client_handle, request); + break; + } + + case SL_HTTP_PUT: { + // Validate HTTP client state + if (http_client_handle.client_state != HTTP_STATE_PUT_REQUEST_INITIALIZED) { + return SL_STATUS_INVALID_STATE; + } + + // 917 does not support this feature for PUT request in Alpha 3 release + if (request->body != NULL) { + return SL_STATUS_NOT_SUPPORTED; + } + + // Allocate memory for request structure + sl_si91x_http_client_put_request_t *http_put_request = + (sl_si91x_http_client_put_request_t *)malloc(sizeof(sl_si91x_http_client_put_request_t)); + SLI_VERIFY_MALLOC_AND_RETURN(http_put_request); + + memset(http_put_request, 0, sizeof(sl_si91x_http_client_put_request_t)); + + sli_si91x_http_client_put_start_t *http_put_start = + &http_put_request->sli_http_client_put_struct.http_client_put_start; + + //! Create HTTP PUT client + // Fill command type + http_put_request->command_type = SLI_SI91X_HTTP_CLIENT_PUT_CREATE; + + // Fill payload length + // Since body is not sent in this request, we are subtracting max SLI_SI91X_HTTP_CLIENT_PUT_MAX_BUFFER_LENGTH + packet_length = sizeof(sl_si91x_http_client_put_request_t) - SLI_SI91X_HTTP_CLIENT_PUT_MAX_BUFFER_LENGTH; + + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_HTTP_CLIENT_PUT, + SLI_SI91X_NETWORK_CMD, + http_put_request, + packet_length, + SLI_SI91X_WAIT_FOR_COMMAND_SUCCESS, + NULL, + NULL); + if (status != SL_STATUS_OK) { + free(http_put_request); + return status; + } + + //! Start HTTP client PUT process + // Fill command type + http_put_request->command_type = SLI_SI91X_HTTP_CLIENT_PUT_START; + + // Fill IP version + if (http_client_handle.configuration.ip_version == SL_IPV6) { + http_put_start->ip_version = 6; + } else { + http_put_start->ip_version = 4; + } + + // Set default by NULL delimiter + http_put_start->https_enable |= SL_SI91X_ENABLE_NULL_DELIMETER; + + // Fill HTTPS feature + if (http_client_handle.configuration.https_enable) { + http_put_start->https_enable = SL_SI91X_ENABLE_TLS; + + // Fill SSL/TLS version + switch (http_client_handle.configuration.tls_version) { + case SL_TLS_DEFAULT_VERSION: { + break; + } + case SL_TLS_V_1_0: { + http_put_start->https_enable |= SL_SI91X_TLS_V_1_0; + break; + } + case SL_TLS_V_1_1: { + http_put_start->https_enable |= SL_SI91X_TLS_V_1_1; + break; + } + case SL_TLS_V_1_2: { + http_put_start->https_enable |= SL_SI91X_TLS_V_1_2; + break; + } +#if defined(SLI_SI917) || defined(SLI_SI915) + case SL_TLS_V_1_3: { + http_put_start->https_enable |= SL_SI91X_TLS_V_1_3; + break; + } +#endif + default: + return SL_STATUS_INVALID_CONFIGURATION; + } + + // Fill HTTPS certificate index bitmap + switch (http_client_handle.configuration.certificate_index) { + case SL_HTTPS_CLIENT_CERTIFICATE_INDEX_1: { + http_put_start->https_enable |= SL_SI91X_HTTPS_CERTIFICATE_INDEX_1; + break; + } + case SL_HTTPS_CLIENT_CERTIFICATE_INDEX_2: { + http_put_start->https_enable |= SL_SI91X_HTTPS_CERTIFICATE_INDEX_2; + break; + } + case SL_HTTPS_CLIENT_DEFAULT_CERTIFICATE_INDEX: { + break; + } + default: + break; + } + } + + // Fill HTTP version + if (http_client_handle.configuration.http_version == SL_HTTP_V_1_1) { + http_put_start->https_enable |= SL_SI91X_HTTP_V_1_1; + } + + // Fill port number + http_put_start->port_number = (uint32_t)request->port; + + // Fill Total resource content length + http_put_start->content_length = request->body_length; + + // Fill username + memcpy(http_put_request->http_put_buffer, + &http_client_handle.client_credentials->data[0], + http_client_handle.client_credentials->username_length); + http_buffer_offset += http_client_handle.client_credentials->username_length; + http_put_request->http_put_buffer[http_buffer_offset] = '\0'; + http_buffer_offset++; + + // Fill password + memcpy(http_put_request->http_put_buffer + http_buffer_offset, + &http_client_handle.client_credentials->data[http_client_handle.client_credentials->username_length], + http_client_handle.client_credentials->password_length); + http_buffer_offset += http_client_handle.client_credentials->password_length; + http_put_request->http_put_buffer[http_buffer_offset] = '\0'; + http_buffer_offset++; + + // Check for HTTP_V_1.1 and Empty host name + if (http_client_handle.configuration.http_version == SL_HTTP_V_1_1 + && (strlen((char *)request->host_name) == 0 || request->host_name == NULL)) { + memcpy((char *)request->host_name, + (char *)request->ip_address, + MIN(strlen((char *)request->host_name), strlen((char *)request->ip_address)) + 1); + } + + // Fill hostname + http_buffer_offset += snprintf((char *)(http_put_request->http_put_buffer + http_buffer_offset), + SLI_SI91X_HTTP_CLIENT_PUT_MAX_BUFFER_LENGTH - http_buffer_offset, + "%s", + request->host_name); + http_buffer_offset++; + + // Fill IP address + http_buffer_offset += snprintf((char *)(http_put_request->http_put_buffer + http_buffer_offset), + SLI_SI91X_HTTP_CLIENT_PUT_MAX_BUFFER_LENGTH - http_buffer_offset, + "%s", + request->ip_address); + http_buffer_offset++; + + // Fill URL resource + http_buffer_offset += snprintf((char *)(http_put_request->http_put_buffer + http_buffer_offset), + SLI_SI91X_HTTP_CLIENT_PUT_MAX_BUFFER_LENGTH - http_buffer_offset, + "%s", + request->resource); + http_buffer_offset++; + + // Fill extended header + if (request->extended_header != NULL) { + // Enable user given content type in extended header + http_put_start->https_enable |= SL_SI91X_HTTP_USER_DEFINED_CONTENT_TYPE; + + sli_si91x_load_extended_headers_into_request_buffer(http_put_request->http_put_buffer, + request->extended_header, + &http_buffer_offset); + } else { + http_put_request->http_put_buffer[http_buffer_offset] = '\0'; + http_buffer_offset++; + } + + // Check if request buffer is overflowed + if (http_buffer_offset > SLI_SI91X_HTTP_BUFFER_LEN) { + free(http_put_request); + return SL_STATUS_HAS_OVERFLOWED; + } + + // Fill data length + packet_length = + sizeof(sl_si91x_http_client_put_request_t) - SLI_SI91X_HTTP_CLIENT_PUT_MAX_BUFFER_LENGTH + http_buffer_offset; + + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_HTTP_CLIENT_PUT, + SLI_SI91X_NETWORK_CMD, + http_put_request, + packet_length, + SLI_SI91X_RETURN_IMMEDIATELY, + request->context, + NULL); + + // Free memory of request struture + free(http_put_request); + + break; + } + + default: { + return SL_STATUS_FAIL; + } + } + + // Store request configurations into client_internal structure + memcpy(&http_client_handle.request, request, sizeof(sl_http_client_request_t)); + + // Set HTTP client state to requested sent state + if (request->http_method_type != SL_HTTP_GET && request->body == NULL) { + http_client_handle.client_state = HTTP_STATE_CHUNKED_REQUEST_SENT; + } else { + http_client_handle.client_state = HTTP_STATE_REQUEST_SENT; + } + + return status; +} + +sl_status_t sl_http_client_write_chunked_data(const sl_http_client_t *client, + const uint8_t *data, + uint32_t data_length, + bool flush_now) +{ + UNUSED_PARAMETER(flush_now); + SL_WIFI_ARGS_CHECK_NULL_POINTER(client); + SL_WIFI_ARGS_CHECK_NULL_POINTER(data); + + if (*client != (sl_http_client_t)&http_client_handle) { + return SL_STATUS_INVALID_HANDLE; + } + + // Check for HTTP client requested state + if (http_client_handle.client_state != HTTP_STATE_CHUNKED_REQUEST_SENT) { + return SL_STATUS_INVALID_STATE; + } + + // Check for data length more than max buffer size + if (data_length > SLI_SI91X_HTTP_CLIENT_MAX_WRITE_BUFFER_LENGTH) { + return SL_STATUS_INVALID_PARAMETER; + } + + // Check for invalid data length + if ((data_length == 0) && (strlen((char *)data) == 0)) { + return SL_STATUS_INVALID_PARAMETER; + } + + sl_status_t status = SL_STATUS_OK; + uint16_t packet_length = 0; + + switch (http_client_handle.request.http_method_type) { + case SL_HTTP_POST: { + // Allocate memory for request structure + sli_si91x_http_client_post_data_request_t *http_post_data = + (sli_si91x_http_client_post_data_request_t *)malloc(sizeof(sli_si91x_http_client_post_data_request_t)); + SLI_VERIFY_MALLOC_AND_RETURN(http_post_data); + memset(http_post_data, 0, sizeof(sli_si91x_http_client_post_data_request_t)); + // Fill HTTP Post data current chunk length + http_post_data->current_length = (uint16_t)data_length; + + // Fill HTTP Post data + memcpy(http_post_data->http_post_data_buffer, data, data_length); + + // Fill total packet length + packet_length = (uint16_t)(sizeof(sli_si91x_http_client_post_data_request_t) + - SLI_SI91X_HTTP_CLIENT_POST_MAX_BUFFER_LENGTH + data_length); + + // Send HTTP Post Data request + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_HTTP_CLIENT_POST_DATA, + SLI_SI91X_NETWORK_CMD, + http_post_data, + packet_length, + SLI_SI91X_RETURN_IMMEDIATELY, + http_client_handle.request.context, + NULL); + + // Free memory of request struture + free(http_post_data); + + break; + } + + case SL_HTTP_PUT: { + // Allocate memory for request structure + sl_si91x_http_client_put_request_t *http_put_pkt_request = + (sl_si91x_http_client_put_request_t *)malloc(sizeof(sl_si91x_http_client_put_request_t)); + SLI_VERIFY_MALLOC_AND_RETURN(http_put_pkt_request); + memset(http_put_pkt_request, 0, sizeof(sl_si91x_http_client_put_request_t)); + sli_si91x_http_client_put_data_request_t *http_put_data = + &http_put_pkt_request->sli_http_client_put_struct.http_client_put_data_req; + + // Fill command type + http_put_pkt_request->command_type = SLI_SI91X_HTTP_CLIENT_PUT_PKT; + + // Fill HTTP Put packet current chunk length + http_put_data->current_length = (uint16_t)data_length; + + // Fill HTTP Put data + memcpy(http_put_pkt_request->http_put_buffer, data, data_length); + + // Fill total packet length + packet_length = (uint16_t)(sizeof(sl_si91x_http_client_put_request_t) + - SLI_SI91X_HTTP_CLIENT_PUT_MAX_BUFFER_LENGTH + data_length); + + // Send HTTP Put Data request + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_HTTP_CLIENT_PUT, + SLI_SI91X_NETWORK_CMD, + http_put_pkt_request, + packet_length, + SLI_SI91X_RETURN_IMMEDIATELY, + http_client_handle.request.context, + NULL); + + // Free memory of request struture + free(http_put_pkt_request); + + break; + } + + default: + return SL_STATUS_FAIL; + } + + return status; +} + +static sl_status_t sli_si91x_http_client_abort(void) +{ + sl_status_t status = sli_si91x_driver_send_command(SLI_WLAN_REQ_HTTP_ABORT, + SLI_SI91X_NETWORK_CMD, + NULL, + 0, + SLI_SI91X_WAIT_FOR_COMMAND_SUCCESS, + NULL, + NULL); + VERIFY_STATUS_AND_RETURN(status); + return status; +} diff --git a/wiseconnect/components/service/mdns/inc/sl_mdns.h b/wiseconnect/components/service/mdns/inc/sl_mdns.h new file mode 100644 index 000000000..0f79bfd71 --- /dev/null +++ b/wiseconnect/components/service/mdns/inc/sl_mdns.h @@ -0,0 +1,220 @@ +/***************************************************************************/ /** + * @file sl_mdns.h + ******************************************************************************* + * # License + * Copyright 2025 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ +#ifndef SL_MDNS_INCLUDED_H +#define SL_MDNS_INCLUDED_H + +#include +#include "sl_status.h" +#include "sl_wifi_host_interface.h" +#include "sl_ip_types.h" +#include "sl_net_constants.h" + +/** + * @addtogroup SERVICE_MDNS + * @{ + */ + +/// mDNS events +typedef enum { + SL_MDNS_SERVICE_DISCOVERED_EVENT = 0 ///< Event for mDNS service discovery. (Currently not supported.) +} sl_mdns_event_t; + +/// mDNS protocol +typedef enum { + SL_MDNS_PROTO_PHY = 0, ///< Run mDNS using ethernet frames. (Not supported in offload mode.) + SL_MDNS_PROTO_UDP ///< Run mDNS using UDP. +} sl_mdns_protocol_t; + +/****************************************************** + * Type Definitions + ******************************************************/ +/// mDNS service query +typedef struct { + const char *instance_name; ///< mDNS instance service name + const char *service_type; ///< mDNS service type + const char *service_sub_type; ///< mDNS service sub type + sl_net_interface_t interface; ///< Target interface + uint16_t timeout; ///< Time out for query +} sl_mdns_service_query_t; + +/// mDNS service +typedef struct { + const char *instance_name; ///< mDNS instance service name + const char *service_type; ///< mDNS service type + const char *service_message; ///< mDNS service message + uint16_t port; ///< Service port number + uint16_t ttl; ///< Service TTL + + /* NOTE: The following parameters are not used in offload mode. */ + sl_ipv4_address_t + ipv4; ///< IPv4 address of service. (In case of offload mode, it is provided by embedded network stack.) + sl_ipv6_address_t + ipv6; ///< IPv6 address of service. (In case of offload mode, it is provided by embedded network stack.) +} sl_mdns_service_t; + +/// mDNS service discovery request +typedef struct { + char *service_name; ///< Target service name for discovery + sl_net_interface_t + interface; ///< Interface on which to discover the service from [sl_net_interface_t](../wiseconnect-api-reference-guide-nwk-mgmt/sl-net-constants#sl-net-interface-t). + uint16_t timeout; ///< timeout for the discovery +} sl_mdns_discover_request_t; + +/// MDNS instance configuration +typedef struct { + sl_mdns_protocol_t protocol; ///< Protocol to use for mDNS from @ref sl_mdns_protocol_t. + sl_ip_version_t + type; ///< IP version to use for mDNS from [sl_ip_version_t](../wiseconnect-api-reference-guide-nwk-mgmt/sl-ip-address-t). + char host_name + [32]; ///< Host Name to use for the mDNS Instance. The host name Should contain dot(.) at the end (for example, "wiseconnect.local."). The string length should not exceed 32 including NULL terminator. +} sl_mdns_configuration_t; + +/// mDNS interface +typedef struct { + sl_net_interface_t + interface; ///< Network interface of type [sl_net_interface_t](../wiseconnect-api-reference-guide-nwk-mgmt/sl-net-constants#sl-net-interface-t). + sl_wifi_buffer_t * + service_list; ///< Pointer to the service list of type [sl_wifi_buffer_t](../wiseconnect-api-reference-guide-wi-fi/sl-wifi-buffer-t). +} sl_mdns_interface_t; + +// Forward declaration of the structure to allow usage in the sl_mdns_event_handler_t definition. +/// mDNS instance handle +typedef struct sl_mdns_s sl_mdns_t; + +/** + * @typedef sl_mdns_event_handler_t + * @brief mDNS event handler + * @param[in] mdns mDNS instance handle of type @ref sl_mdns_t. + * @param[in] event mDNS event of type @ref sl_mdns_event_t. + * @param[in] data Data pointer containing data structure of corresponding to corresponding event of type @ref sl_mdns_event_t. + */ +typedef void (*sl_mdns_event_handler_t)(sl_mdns_t *mdns, sl_mdns_event_t event, void *data); + +/// mDNS instance handle +struct sl_mdns_s { + sl_mdns_configuration_t configuration; ///< mDNS configuration of type @ref sl_mdns_configuration_t + sl_mdns_event_handler_t event_handler; ///< mDNS event handler of type @ref sl_mdns_event_handler_t + sl_wifi_buffer_t * + interface_list; ///< Pointer to interface list of type [sl_wifi_buffer_t](../wiseconnect-api-reference-guide-wi-fi/sl-wifi-buffer-t). + uint8_t service_count; ///< Count of the total number of services being registered on all interfaces. +}; + +/** + * @brief + * Initialize mDNS instance. + * @param[in] mdns mDNS instance handle of type @ref sl_mdns_t. This cannot be modified by the application after this API call. + * @param[in] config Valid pointer to mDNS configuration structure of type @ref sl_mdns_configuration_t. This value cannot be null. + * @param[in] event_handler Event handler of type @ref sl_mdns_event_handler_t for receiving asynchronous events. + * @return + * sl_status_t. See https://docs.silabs.com/gecko-platform/latest/platform-common/status for details. + * @note + * This API needs to be called before calling any other mDNS API + * @note + * @ref sl_mdns_event_handler_t currently not supported. User should pass NULL for event_handler. + */ +sl_status_t sl_mdns_init(sl_mdns_t *mdns, const sl_mdns_configuration_t *config, sl_mdns_event_handler_t event_handler); + +/** + * @brief + * De-initialize mDNS instance. + * @param[in] mdns mDNS instance handle of type @ref sl_mdns_t. + * @return + * sl_status_t. See https://docs.silabs.com/gecko-platform/latest/platform-common/status for details. + */ +sl_status_t sl_mdns_deinit(sl_mdns_t *mdns); + +/** + * @brief + * Add network interface to mDNS instance. + * @param[in] mdns mDNS instance handle of type @ref sl_mdns_t. + * @param[in] interface Network interface of type [sl_net_interface_t](../wiseconnect-api-reference-guide-nwk-mgmt/sl-net-constants#sl-net-interface-t) that needs to be added to mDNS instance. + * @return + * sl_status_t. See https://docs.silabs.com/gecko-platform/latest/platform-common/status for details. + */ +sl_status_t sl_mdns_add_interface(sl_mdns_t *mdns, sl_net_interface_t interface); + +/** + * @brief + * Remove network interface to mDNS instance. (Currently not supported.) + * @param[in] mdns mDNS instance handle of type @ref sl_mdns_t. + * @param[in] interface Network interface of type [sl_net_interface_t](../wiseconnect-api-reference-guide-nwk-mgmt/sl-net-constants#sl-net-interface-t) that needs to be removed from mDNS instance. + * @return + * sl_status_t. See https://docs.silabs.com/gecko-platform/latest/platform-common/status for details. + */ +sl_status_t sl_mdns_remove_interface(const sl_mdns_t *mdns, sl_net_interface_t interface); + +/** + * @brief + * Register a service in mDNS instance. + * @param[in] mdns mDNS instance handle of type @ref sl_mdns_t. + * @param[in] interface Network interface of type [sl_net_interface_t](../wiseconnect-api-reference-guide-nwk-mgmt/sl-net-constants#sl-net-interface-t) to which service needed to be added to mDNS instance. + * @param[in] service Valid pointer to mDNS service configuration structure of type @ref sl_mdns_service_t . This value cannot be null. + * @return + * sl_status_t. See https://docs.silabs.com/gecko-platform/latest/platform-common/status for details. + */ +sl_status_t sl_mdns_register_service(sl_mdns_t *mdns, sl_net_interface_t interface, const sl_mdns_service_t *service); + +/** + * @brief + * Unregister a service from mDNS instance. (Currently not supported.) + * @param[in] mdns mDNS instance handle of type @ref sl_mdns_t + * @param[in] service_query Valid pointer to mDNS service query structure of type @ref sl_mdns_service_query_t . This value cannot be null. + * @return + * sl_status_t. See https://docs.silabs.com/gecko-platform/latest/platform-common/status for details. + */ +sl_status_t sl_mdns_unregister_service(const sl_mdns_t *mdns, const sl_mdns_service_query_t *service_query); + +/** + * @brief + * Update service message of a service in mDNS instance. (Currently not supported.) + * @param[in] mdns mDNS instance handle of type @ref sl_mdns_t. + * @param[in] service_query Valid pointer to mDNS service query structure of type @ref sl_mdns_service_query_t. This value cannot be null. + * @param[in] message Valid pointer to a buffer containing service message string . This value cannot be null. + * @param[in] message_length Length of the message buffer. + * @return + * sl_status_t. See https://docs.silabs.com/gecko-platform/latest/platform-common/status for details. + */ +sl_status_t sl_mdns_update_service_message(const sl_mdns_t *mdns, + const sl_mdns_service_query_t *service_query, + const char *message, + uint32_t message_length); + +/** + * @brief + * Discover services using mDNS instance. (Currently not supported.) + * @param[in] mdns mDNS instance handle of type @ref sl_mdns_t + * @param[in] service_query Valid pointer to mDNS service query structure of type @ref sl_mdns_service_query_t . This value cannot be null. + * @return + * sl_status_t. See https://docs.silabs.com/gecko-platform/latest/platform-common/status for details. + */ +sl_status_t sl_mdns_discover_service(const sl_mdns_t *mdns, const sl_mdns_service_query_t *service_query); + +/** @} */ + +#endif //SL_MDNS_INCLUDED_H diff --git a/wiseconnect/components/service/mdns/si91x/sl_mdns.c b/wiseconnect/components/service/mdns/si91x/sl_mdns.c new file mode 100644 index 000000000..4a22e7b43 --- /dev/null +++ b/wiseconnect/components/service/mdns/si91x/sl_mdns.c @@ -0,0 +1,338 @@ +/***************************************************************************/ /** + * @file sl_mdns.c + ******************************************************************************* + * # License + * Copyright 2025 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ + +#include "sl_mdns.h" +#include "sl_slist.h" +#include "sl_si91x_driver.h" +#include "sl_string.h" + +#define MAX_MDNS_SERVICE_COUNT 1 +#define MDNSD_BUFFER_SIZE 1000 + +#define SI91X_MDNSD_INIT 1 //! MDNSD Initialize +#define SI91X_MDNSD_REGISTER_SERVICE 3 //! MDNSD register service +#define SI91X_MDNSD_DEINIT 6 //! MDNSD deinitialize + +// mdnsd init structure +typedef struct { + uint8_t ip_version; // ip version + uint8_t ttl[2]; // time to live +} sl_si91x_mdns_init_t; + +// mdnsd register-service structure +typedef struct { + uint8_t port[2]; // port number + uint8_t ttl[2]; // time to live + uint8_t more; // reset if it is last service in the list +} sl_si91x_mdns_reg_srv_t; + +// mdnsd structure +typedef struct { + uint8_t command_type; // command type 1-MDNSD init, 3- Register service, 6- Deinit + union { + sl_si91x_mdns_init_t init; // mdns init + sl_si91x_mdns_reg_srv_t reg_srv; // mdns register + } req_conf; + uint8_t buffer[MDNSD_BUFFER_SIZE]; //Data buffer +} sl_si91x_mdns_req_t; + +/********************************************************************************************************************** + * Static Functions * +**********************************************************************************************************************/ +static void sli_si91x_clean_service_handle(sl_wifi_buffer_t *service_handle) +{ + const sl_mdns_service_t *service = NULL; + uint16_t buffer_length = 0; + + service = (sl_mdns_service_t *)sl_si91x_host_get_buffer_data(service_handle, 0, &buffer_length); + free((char *)service->instance_name); + free((char *)service->service_type); + free((char *)service->service_message); + sli_si91x_host_free_buffer(service_handle); + + return; +} + +static void sli_si91x_clean_service_list(sl_mdns_interface_t *interface) +{ + sl_wifi_buffer_t *service = NULL; + sl_wifi_buffer_t *block = NULL; + + service = interface->service_list; + + while (service != NULL) { + block = service; + service = (sl_wifi_buffer_t *)service->node.node; + sli_si91x_clean_service_handle(block); + } + + return; +} + +static void sli_si91x_clean_mdns_handle(sl_mdns_t *mdns) +{ + sl_wifi_buffer_t *interface = NULL; + sl_wifi_buffer_t *block = NULL; + sl_mdns_interface_t *in = NULL; + uint16_t buffer_length; + + interface = mdns->interface_list; + + while (interface != NULL) { + block = interface; + interface = (sl_wifi_buffer_t *)interface->node.node; + in = (sl_mdns_interface_t *)sl_si91x_host_get_buffer_data(block, 0, &buffer_length); + sli_si91x_clean_service_list(in); + sli_si91x_host_free_buffer(block); + } + + return; +} +/********************************************************************************************************************** + * Public Functions * +**********************************************************************************************************************/ +sl_status_t sl_mdns_init(sl_mdns_t *mdns, const sl_mdns_configuration_t *config, sl_mdns_event_handler_t event_handler) +{ + sl_si91x_mdns_req_t req = { 0 }; + sl_status_t status = SL_STATUS_FAIL; + uint32_t length = 0; + uint16_t ttl = 0; + uint32_t length_host_name = 0; + + memset(mdns, 0, sizeof(sl_mdns_t)); + memcpy(&(mdns->configuration), config, sizeof(sl_mdns_configuration_t)); + mdns->event_handler = event_handler; + + req.command_type = SI91X_MDNSD_INIT; + req.req_conf.init.ttl[0] = ttl & 0x00ff; + req.req_conf.init.ttl[1] = (ttl >> 8) & 0x00ff; + + if (SL_IPV4_VERSION == config->type) { + req.req_conf.init.ip_version = 4; + } else if (SL_IPV6_VERSION == config->type) { + req.req_conf.init.ip_version = 6; + } else { + return SL_STATUS_INVALID_TYPE; + } + + length_host_name = strnlen((char *)config->host_name, 32) + 1; // +1 for null terminator + memcpy(req.buffer, config->host_name, length_host_name); + length = sizeof(sl_si91x_mdns_req_t) - MDNSD_BUFFER_SIZE + length_host_name; + + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_MDNSD, + SLI_SI91X_NETWORK_CMD, + &req, + length, + SLI_SI91X_WAIT_FOR_COMMAND_SUCCESS, + NULL, + NULL); + + return status; +} + +sl_status_t sl_mdns_deinit(sl_mdns_t *mdns) +{ + sl_si91x_mdns_req_t req = { 0 }; + sl_status_t status = SL_STATUS_FAIL; + uint32_t length = 0; + + req.command_type = SI91X_MDNSD_DEINIT; + length = sizeof(sl_si91x_mdns_req_t) - MDNSD_BUFFER_SIZE; + + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_MDNSD, + SLI_SI91X_NETWORK_CMD, + &req, + length, + SLI_SI91X_WAIT_FOR_COMMAND_SUCCESS, + NULL, + NULL); + + sli_si91x_clean_mdns_handle(mdns); + memset(mdns, 0, sizeof(sl_mdns_t)); + return status; +} + +sl_status_t sl_mdns_add_interface(sl_mdns_t *mdns, sl_net_interface_t interface) +{ + sl_status_t status = SL_STATUS_FAIL; + sl_wifi_buffer_t *new_interface = NULL; + sl_mdns_interface_t *in = NULL; + uint16_t buffer_length = 0; + + status = sli_si91x_host_allocate_buffer(&new_interface, SL_WIFI_CONTROL_BUFFER, sizeof(sl_mdns_interface_t), 1000); + VERIFY_STATUS_AND_RETURN(status); + in = (sl_mdns_interface_t *)sl_si91x_host_get_buffer_data(new_interface, 0, &buffer_length); + in->interface = interface; + in->service_list = NULL; + + new_interface->node.node = (sl_slist_node_t *)mdns->interface_list; + mdns->interface_list = new_interface; + + return SL_STATUS_OK; +} + +sl_status_t sl_mdns_remove_interface(const sl_mdns_t *mdns, sl_net_interface_t interface) +{ + UNUSED_PARAMETER(mdns); + UNUSED_PARAMETER(interface); + return SL_STATUS_NOT_SUPPORTED; +} + +sl_status_t sl_mdns_register_service(sl_mdns_t *mdns, sl_net_interface_t interface, const sl_mdns_service_t *service) +{ + sl_status_t status = SL_STATUS_FAIL; + sl_wifi_buffer_t *new_service = NULL; + sl_mdns_interface_t *in = NULL; + sl_mdns_service_t *srv = NULL; + sl_si91x_mdns_req_t req = { 0 }; + uint32_t length = 0; + uint16_t buffer_length = 0; + uint32_t length_service_type = 0; + uint32_t length_instance_name = 0; + uint32_t length_service_message = 0; + + if (NULL == mdns->interface_list) { + return SL_STATUS_INVALID_PARAMETER; + } + + if (mdns->service_count >= MAX_MDNS_SERVICE_COUNT) { + return SL_STATUS_INVALID_PARAMETER; + } + + if (NULL == service->service_type) { + return SL_STATUS_INVALID_PARAMETER; + } + + if (NULL == service->service_type) { + return SL_STATUS_INVALID_PARAMETER; + } + + if (NULL == service->service_message) { + return SL_STATUS_INVALID_PARAMETER; + } + + length_service_type = strnlen((char *)service->service_type, MDNSD_BUFFER_SIZE) + 1; + length_instance_name = strnlen((char *)service->instance_name, MDNSD_BUFFER_SIZE) + 1; + length_service_message = strnlen((char *)service->service_message, MDNSD_BUFFER_SIZE) + 1; + + if ((length_service_type + length_instance_name + length_service_message) > MDNSD_BUFFER_SIZE) { + return SL_STATUS_INVALID_PARAMETER; + } + + for (sl_wifi_buffer_t *target_in = mdns->interface_list; (NULL != target_in); + target_in = (sl_wifi_buffer_t *)target_in->node.node) { + in = (sl_mdns_interface_t *)sl_si91x_host_get_buffer_data(target_in, 0, &buffer_length); + + if (in->interface == interface) { + break; + } else { + in = NULL; + } + } + + if (NULL == in) { + return SL_STATUS_INVALID_PARAMETER; + } + + status = sli_si91x_host_allocate_buffer(&new_service, SL_WIFI_CONTROL_BUFFER, sizeof(sl_mdns_service_t), 1000); + VERIFY_STATUS_AND_RETURN(status); + srv = (sl_mdns_service_t *)sl_si91x_host_get_buffer_data(new_service, 0, &buffer_length); + srv->instance_name = malloc(length_instance_name); + memcpy((char *)srv->instance_name, service->instance_name, length_instance_name); + + srv->service_type = malloc(length_service_type); + memcpy((char *)srv->service_type, service->service_type, length_service_type); + + srv->service_message = malloc(length_service_message); + memcpy((char *)srv->service_message, service->service_message, length_service_message); + + srv->port = service->port; + srv->ttl = service->ttl; + + // Prepare Firmware request + req.command_type = SI91X_MDNSD_REGISTER_SERVICE; + req.req_conf.reg_srv.port[0] = service->port & 0x00ff; + req.req_conf.reg_srv.port[1] = (service->port >> 8) & 0x00ff; + req.req_conf.reg_srv.ttl[0] = service->ttl & 0x00ff; + req.req_conf.reg_srv.ttl[1] = (service->ttl >> 8) & 0x00ff; + req.req_conf.reg_srv.more = 0; + length = 0; + + memcpy((char *)req.buffer, service->service_type, length_service_type); + length += length_service_type; + + memcpy((char *)((req.buffer) + length), service->instance_name, length_instance_name); + length += length_instance_name; + + memcpy((char *)((req.buffer) + length), service->service_message, length_service_message); + length += length_service_message + (sizeof(sl_si91x_mdns_req_t) - MDNSD_BUFFER_SIZE); + + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_MDNSD, + SLI_SI91X_NETWORK_CMD, + &req, + length, + SLI_SI91X_WAIT_FOR_COMMAND_SUCCESS, + NULL, + NULL); + if (SL_STATUS_OK != status) { + sli_si91x_clean_service_handle(new_service); + VERIFY_STATUS_AND_RETURN(status); + } + + new_service->node.node = (sl_slist_node_t *)in->service_list; + in->service_list = new_service; + mdns->service_count++; + return SL_STATUS_OK; +} + +sl_status_t sl_mdns_unregister_service(const sl_mdns_t *mdns, const sl_mdns_service_query_t *service_query) +{ + UNUSED_PARAMETER(mdns); + UNUSED_PARAMETER(service_query); + return SL_STATUS_NOT_SUPPORTED; +} + +sl_status_t sl_mdns_update_service_message(const sl_mdns_t *mdns, + const sl_mdns_service_query_t *service_query, + const char *message, + uint32_t message_length) +{ + UNUSED_PARAMETER(mdns); + UNUSED_PARAMETER(service_query); + UNUSED_PARAMETER(message); + UNUSED_PARAMETER(message_length); + return SL_STATUS_NOT_SUPPORTED; +} + +sl_status_t sl_mdns_discover_service(const sl_mdns_t *mdns, const sl_mdns_service_query_t *service_query) +{ + UNUSED_PARAMETER(mdns); + UNUSED_PARAMETER(service_query); + return SL_STATUS_NOT_SUPPORTED; +} diff --git a/wiseconnect/components/service/mqtt/inc/sl_mqtt_client.h b/wiseconnect/components/service/mqtt/inc/sl_mqtt_client.h new file mode 100644 index 000000000..2ada2eea0 --- /dev/null +++ b/wiseconnect/components/service/mqtt/inc/sl_mqtt_client.h @@ -0,0 +1,294 @@ +/***************************************************************************/ /** + * @file sl_mqtt_client.h + ******************************************************************************* + * # License + * Copyright 2025 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ + +#pragma once +#include "sl_mqtt_client_types.h" +#include "sl_status.h" +#include + +/** + * @addtogroup SERVICE_MQTT_FUNCTIONS + * @{ + */ + +/***************************************************************************/ /** + * @brief + * Initialize the MQTT client and register the event handler. + * + * @details + * This function initializes the MQTT client by setting up the event handler, and initializing the subscription list. It also assigns the provided client structure to the global MQTT client pointer. + * + * @param[in] client + * Pointer to the MQTT client structure of type @ref sl_mqtt_client_t. This pointer must not be NULL. + * + * @param[in] event_handler + * Event handler of type @ref sl_mqtt_client_event_handler_t that would be called for various events on the client. This pointer must not be NULL. + * + * @return + * sl_status_t - Status of the operation. For more details, see https://docs.silabs.com/gecko-platform/latest/platform-common/status. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_WIFI_NULL_PTR_ARG: The event_handler pointer was NULL. + ******************************************************************************/ +sl_status_t sl_mqtt_client_init(sl_mqtt_client_t *client, sl_mqtt_client_event_handler_t event_handler); + +/***************************************************************************/ /** + * @brief + * Deinitialize the MQTT client. + * + * @details + * This function deinitializes the MQTT client by clearing its state and resetting the global MQTT client pointer. It ensures that the client is in a disconnected state before deinitializing. + * + * @pre + * The MQTT client must be in the disconnected state before calling this function. + * + * @param[in] client + * Pointer to the MQTT client structure of type @ref sl_mqtt_client_t. This pointer must not be NULL. + * + * @return + * sl_status_t - Status of the operation. For more details, see https://docs.silabs.com/gecko-platform/latest/platform-common/status. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_INVALID_STATE: The client is not in the disconnected state. + ******************************************************************************/ +sl_status_t sl_mqtt_client_deinit(sl_mqtt_client_t *client); + +/***************************************************************************/ +/** + * @brief + * Connect the client to an MQTT broker. + * + * @details + * This function connects the MQTT client to the specified broker using the provided configuration and last will message. It supports both synchronous, and asynchronous operations based on the timeout value. + * + * @pre + * @ref sl_mqtt_client_init should be called before this API. + * + * @param[in] client + * Pointer to the MQTT client structure of type @ref sl_mqtt_client_t that would be connected to the broker. + * + * @param[in] broker + * Pointer to the broker configuration of type @ref sl_mqtt_broker_t. This parameter could be NULL for subsequent calls to retain the previous broker configuration. + * + * @param[in] last_will_message + * Pointer to the last will message of the client of type @ref sl_mqtt_client_last_will_message_t. This parameter could be NULL for subsequent calls if no will message is to be sent. + * + * @param[in] configuration + * Pointer to the client configuration of type @ref sl_mqtt_client_configuration_t. This parameter could be NULL for subsequent calls to retain the previous configuration. + * + * @param[in] timeout + * Timeout for the API is in milliseconds. If the value is zero, the API operates in asynchronous mode. + * + * @return + * sl_status_t - Status of the operation. For more details, see https://docs.silabs.com/gecko-platform/latest/platform-common/status. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_IN_PROGRESS: Operation is in progress (for asynchronous calls). + * - SL_STATUS_INVALID_PARAMETER: One or more parameters are invalid. + * - SL_STATUS_FAIL: Operation failed. + * + * @note + * In subsequent calls to connect, it is optional to provide broker, last_will_message, and configuration parameters. + * If broker and configuration parameters are assigned as NULL, the values are retains from the first connect() call. + * For the last_will_message parameter, values assigned in each connect() call are considered. If the last_will_message parameter value is NULL, no will message are sent to the broker. + * is_clean_session, credential_id, client_id, and client_id_length of @ref sl_mqtt_client_configuration_t are consider. + * The topic length of the last_will_message must be less than SI91X_MQTT_CLIENT_WILL_TOPIC_MAXIMUM_LENGTH. + * The client ID length should be less than SI91X_MQTT_CLIENT_CLIENT_ID_MAXIMUM_LENGTH. + * The username and password length must be less than SI91X_MQTT_CLIENT_USERNAME_MAXIMUM_LENGTH and SI91X_MQTT_CLIENT_PASSWORD_MAXIMUM_LENGTH, respectively. + */ +sl_status_t sl_mqtt_client_connect(sl_mqtt_client_t *client, + const sl_mqtt_broker_t *broker, + const sl_mqtt_client_last_will_message_t *last_will_message, + const sl_mqtt_client_configuration_t *configuration, + uint32_t timeout); + +/***************************************************************************/ /** + * @brief + * Disconnect the client from the MQTT broker. + * + * @details + * This function disconnects the MQTT client from the broker and deinitializes the client context. It supports synchronous, and asynchronous operations based on the timeout value. The function ensures that the client is in a valid state before attempting to disconnect. + * - **Synchronous Mode:** If the timeout value is non-zero then, the function waits for the disconnect operation to complete within the specified timeout period. + * - **Asynchronous Mode:** If the timeout value is zero then, the function returns immediately, and the results are provided via the @ref sl_mqtt_client_event_handler_t callback. + + * @pre + * @ref sl_mqtt_client_connect should be called before this API. + * + * @param[in] client + * Pointer to the MQTT client structure of type @ref sl_mqtt_client_t disconnects from the broker. The pointer value must not be NULL. + * + * @param[in] timeout + * Timeout for the API in milliseconds. If the value is zero, the API is asynchronous. + * + * @return + * sl_status_t - Status of the operation. For more details, see https://docs.silabs.com/gecko-platform/latest/platform-common/status. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_IN_PROGRESS: Operation is in progress (for asynchronous calls). + * - SL_STATUS_INVALID_PARAMETER: One or more parameters are invalid. + * - SL_STATUS_INVALID_STATE: The client is not in a valid state to disconnect. + * - SL_STATUS_FAIL: Operation failed. + * + * @note + * The function deinitializes the MQTT client context after the disconnection. + * The client must be in one of the following states to call this function: SL_MQTT_CLIENT_CONNECTED, SL_MQTT_CLIENT_TA_DISCONNECTED, or SL_MQTT_CLIENT_CONNECTION_FAILED. + ******************************************************************************/ +sl_status_t sl_mqtt_client_disconnect(sl_mqtt_client_t *client, uint32_t timeout); + +/***************************************************************************/ /** + * @brief + * Publishes the message to the MQTT broker. + * + * @details + * This function publishes a message to the specified topic of the MQTT broker. It supports both synchronous, and asynchronous operations based on the timeout value. The function ensures that the client is in the connected state before attempting to publish the message. + * + * @pre + * @ref sl_mqtt_client_connect should be called before this API. + * + * @param[in] client + * Pointer to the MQTT client structure of type @ref sl_mqtt_client_t publishes the message. This pointer value must not be NULL, and the client must be in a connected state. + * + * @param[in] message + * Pointer publishes the message structure of @ref sl_mqtt_client_message_t type.The pointer must not be NULL, and the topic lenth value must be less than SI91X_MQTT_CLIENT_TOPIC_MAXIMUM_LENGTH. + * + * @param[in] timeout + * Timeout for the API is in milliseconds. If the value is zero, the API is asynchronous. + * + * @param[in] context + * Pointer to the context returns the event handler if the API is called asynchronously. The caller ensures that the lifecycle of the context is retained until the callback is invoked. The deallocation of the context is the responsibility of the caller. + * + * @return + * sl_status_t - Status of the operation. For more details, see https://docs.silabs.com/gecko-platform/latest/platform-common/status. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_IN_PROGRESS: Operation is in progress (for asynchronous calls). + * - SL_STATUS_INVALID_PARAMETER: One or more parameters are invalid. + * - SL_STATUS_INVALID_STATE: The client is not in a valid state to publish. + * - SL_STATUS_ALLOCATION_FAILED: Memory allocation failed. + * - SL_STATUS_FAIL: Operation failed. + * + * @note + * The maximum length of the topic must be less than SI91X_MQTT_CLIENT_TOPIC_MAXIMUM_LENGTH. + * The function allocates memory for the publish request, and free the space after the operation. + ******************************************************************************/ +sl_status_t sl_mqtt_client_publish(sl_mqtt_client_t *client, + const sl_mqtt_client_message_t *message, + uint32_t timeout, + void *context); + +/***************************************************************************/ /** + * @brief + * Subscribe a client to a specific topic on the MQTT broker. + * + * @details + * This function subscribes the specified client to a given topic on the MQTT broker. It supports both synchronous, and asynchronous operations based on the timeout value. The function ensures that the client is in a connected state before attempting to subscribe to the topic. + * + * @pre + * @ref sl_mqtt_client_connect should be called before this API. + * + * @param[in] client + * Pointer to the MQTT client structure of type @ref sl_mqtt_client_t that needs to subscribe to the topic. This pointer must not be NULL and the client must be in a connected state. + * + * @param[in] topic + * Pointer to the topic string that the client wants to subscribe to. This pointer must not be NULL. + * + * @param[in] topic_length + * Length of the topic string. The maximum length must be less than SI91X_MQTT_CLIENT_TOPIC_MAXIMUM_LENGTH. + * + * @param[in] qos_level + * Quality of Service level of type @ref sl_mqtt_qos_t is subscribed by the client to use the topic. + * + * @param[in] timeout + * Timeout for the API is in milliseconds. If the value is zero, the API operates in asynchronous mode. + * + * @param[in] message_handler + * Pointer to the message handler function of type @ref sl_mqtt_client_message_received_t that will be invoked for messages published on the subscribed topic. This pointer must not be NULL. + * + * @param[in] context + * Pointer to the context that will be sent back to the message handler. The caller must ensure that the lifecycle of the context is retained until the callback is invoked. The deallocation of the context is also the responsibility of the caller. + * + * @return + * sl_status_t - Status of the operation. For more details, see https://docs.silabs.com/gecko-platform/latest/platform-common/status. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_IN_PROGRESS: Operation is in progress (for asynchronous calls). + * - SL_STATUS_INVALID_PARAMETER: One or more parameters are invalid. + * - SL_STATUS_INVALID_STATE: The client is not in a valid state to subscribe. + * - SL_STATUS_ALLOCATION_FAILED: Memory allocation failed. + * - SL_STATUS_FAIL: Operation failed. + * + * @note + * The maximum length of the topic should be less than SI91X_MQTT_CLIENT_TOPIC_MAXIMUM_LENGTH. + ******************************************************************************/ +sl_status_t sl_mqtt_client_subscribe(sl_mqtt_client_t *client, + const uint8_t *topic, + uint16_t topic_length, + sl_mqtt_qos_t qos_level, + uint32_t timeout, + sl_mqtt_client_message_received_t message_handler, + void *context); + +/***************************************************************************/ /** + * @brief + * Unsubscribe a client from a specific topic on the MQTT broker. + * + * @details + * This function unsubscribes the specified client from a given topic on the MQTT broker. It supports both synchronous, and asynchronous operations based on the timeout value. The function ensures that the client is in a connected state before attempting to unsubscribe from the topic. + * + * @pre + * @ref sl_mqtt_client_subscribe should be called before this API. + * + * @param[in] client + * Pointer to the MQTT client structure of type @ref sl_mqtt_client_t that needs to unsubscribe from the topic. This pointer must not be NULL and the client must be in a connected state. + * + * @param[in] topic + * Pointer to the topic string that the client wants to unsubscribe from. This pointer must not be NULL. + * + * @param[in] topic_length + * Length of the topic string. The maximum length should be less than SI91X_MQTT_CLIENT_TOPIC_MAXIMUM_LENGTH. + * + * @param[in] timeout + * Timeout for the API in milliseconds. If the value is zero, the API is asynchronous. + * + * @param[in] context + * Pointer to the context that will be sent back to the event handler if the API is called asynchronously. The caller must ensure that the lifecycle of the context is retained until the callback is invoked. The deallocation of the context is also the responsibility of the caller. + * + * @return + * sl_status_t - Status of the operation. For more details, see https://docs.silabs.com/gecko-platform/latest/platform-common/status. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_IN_PROGRESS: Operation is in progress (for asynchronous calls). + * - SL_STATUS_INVALID_PARAMETER: One or more parameters are invalid. + * - SL_STATUS_INVALID_STATE: The client is not in a valid state to unsubscribe. + * - SL_STATUS_ALLOCATION_FAILED: Memory allocation failed. + * - SL_STATUS_FAIL: Operation failed. + * + * @note + * The maximum length of the topic must be less than SI91X_MQTT_CLIENT_TOPIC_MAXIMUM_LENGTH. + ******************************************************************************/ +sl_status_t sl_mqtt_client_unsubscribe(sl_mqtt_client_t *client, + const uint8_t *topic, + uint16_t topic_length, + uint32_t timeout, + void *context); + +/** @} */ diff --git a/wiseconnect/components/service/mqtt/inc/sl_mqtt_client_types.h b/wiseconnect/components/service/mqtt/inc/sl_mqtt_client_types.h new file mode 100644 index 000000000..274b5ffaf --- /dev/null +++ b/wiseconnect/components/service/mqtt/inc/sl_mqtt_client_types.h @@ -0,0 +1,357 @@ +/******************************************************************************* + * @file sl_mqtt_client_types.h + ******************************************************************************* + * # License + * Copyright 2025 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ + +#pragma once +#include +#include +#include "sl_slist.h" +#include "sl_net_constants.h" +#include "sl_ip_types.h" +#include "sl_wifi_device.h" + +typedef enum MQTTStatus { + MQTTSuccess = 0, /**< Function completed successfully. */ + MQTTBadParameter, /**< At least one parameter was invalid. */ + MQTTNoMemory, /**< A provided buffer was too small. */ + MQTTSendFailed, /**< The transport send function failed. */ + MQTTRecvFailed, /**< The transport receive function failed. */ + MQTTBadResponse, /**< An invalid packet was received from the server. */ + MQTTServerRefused, /**< The server refused a CONNECT or SUBSCRIBE. */ + MQTTNoDataAvailable, /**< No data available from the transport interface. */ + MQTTIllegalState, /**< An illegal state in the state record. */ + MQTTStateCollision, /**< A collision with an existing state record entry. */ + MQTTKeepAliveTimeout /**< Timeout while waiting for PINGRESP. */ +} MQTTStatus_t; + +/** + * @addtogroup SERVICE_MQTT_CONSTANTS + * @{ + */ + +/** + * @enum sl_mqtt_qos_t + * @brief MQTT Quality of Service (QoS) levels. + * + * @details + * This enumeration defines the Quality of Service (QoS) levels for MQTT messages. QoS levels determine the guarantee of delivery for a message. + * + * @note + * Quality of Service (QoS) level 2 is not currently supported. + */ +typedef enum { + SL_MQTT_QOS_LEVEL_0, ///< At most once delivery. The message is delivered according to the best efforts of the operating environment. Message loss can occur. + SL_MQTT_QOS_LEVEL_1, ///< At least once delivery. The message is assured to arrive but duplicates can occur. + SL_MQTT_QOS_LEVEL_2 ///< Exactly once delivery. The message is assured to arrive exactly once. (Not currently supported). +} sl_mqtt_qos_t; + +/** + * @enum sl_mqtt_client_connection_state_t + * @brief MQTT Client connection states. + * + * @details + * This enumeration defines the various states of the MQTT client during its lifecycle. These states indicate the current status of the client's connection to the MQTT broker. + */ +typedef enum { + SL_MQTT_CLIENT_DISCONNECTED, ///< Initial state. The client is not connected to the MQTT broker. + SL_MQTT_CLIENT_TA_INIT, ///< The client has successfully initialized the NWP(Network Wireless Processor) for MQTT. + SL_MQTT_CLIENT_CONNECTION_FAILED, ///< The client attempted to connect to the MQTT broker but failed. + SL_MQTT_CLIENT_CONNECTED, ///< The client is successfully connected to the MQTT broker. + SL_MQTT_CLIENT_TA_DISCONNECTED ///< The NWP(Network Wireless Processor) is disconnected from the broker, but NWP deinitialization has not yet been called. +} sl_mqtt_client_connection_state_t; + +/** + * @enum sl_mqtt_version_t + * @brief MQTT Protocol versions. + * + * @details + * This enumeration defines the supported versions of the MQTT protocol. The version determines the protocol features and behaviors used by the MQTT client. + */ +typedef enum { + SL_MQTT_VERSION_3, ///< MQTT Version 3.0 + SL_MQTT_VERSION_3_1 ///< MQTT Version 3.1 +} sl_mqtt_version_t; + +/** + * @enum sl_mqtt_client_event_t + * @brief MQTT Client Events. + * + * @details + * This enumeration defines the various events that can occur in the lifecycle of an MQTT client. These events notifies the application about changes in the client's state, or the receipt of messages. + */ +typedef enum { + SL_MQTT_CLIENT_CONNECTED_EVENT, ///< Event indicating that the MQTT client has successfully connected to the broker. + SL_MQTT_CLIENT_DISCONNECTED_EVENT, ///< Event indicating that the MQTT client has disconnected from the broker. + SL_MQTT_CLIENT_MESSAGE_PUBLISHED_EVENT, ///< Event indicating that a message has been successfully published by the MQTT client. + SL_MQTT_CLIENT_MESSAGED_RECEIVED_EVENT, ///< Event indicating that a message has been received by the MQTT client. + SL_MQTT_CLIENT_SUBSCRIBED_EVENT, ///< Event indicating that the MQTT client has successfully subscribed to a topic. + SL_MQTT_CLIENT_UNSUBSCRIBED_EVENT, ///< Event indicating that the MQTT client has successfully unsubscribed from a topic. + SL_MQTT_CLIENT_ERROR_EVENT ///< Event indicating that an error has occurred in the MQTT client. +} sl_mqtt_client_event_t; + +/** + * @enum sl_mqtt_client_error_status_t + * @brief MQTT Client error statuses. + * + * @details + * This enumeration defines the various error statuses that can occur in the MQTT client. These statuses indicate the type of error that has occurred during the client's operations. + */ +typedef enum { + SL_MQTT_CLIENT_CONNECT_FAILED, ///< Error status indicating that the MQTT client failed to connect to the broker. + SL_MQTT_CLIENT_PUBLISH_FAILED, ///< Error status indicating that the MQTT client failed to publish a message. + SL_MQTT_CLIENT_SUBSCRIBE_FAILED, ///< Error status indicating that the MQTT client failed to subscribe to a topic. + SL_MQTT_CLIENT_UNSUBSCRIBED_FAILED, ///< Error status indicating that the MQTT client failed to unsubscribe from a topic. + SL_MQTT_CLIENT_DISCONNECT_FAILED, ///< Error status indicating that the MQTT client failed to disconnect from the broker. + SL_MQTT_CLIENT_UNKNOWN_ERROR ///< Error status indicating that an unknown error occurred in the MQTT client. +} sl_mqtt_client_error_status_t; + +/** + * @enum sl_mqtt_client_disconnection_reason_t + * @brief MQTT Client disconnection reasons. + * + * @details + * This enumeration defines the various reasons for the disconnection of an MQTT client from the broker. These reasons help in identifying the cause of the disconnection. + */ +typedef enum { + SL_MQTT_CLIENT_REMOTE_TERMINATE_DISCONNECTION, ///< Disconnection due to remote termination by the broker. + SL_MQTT_CLIENT_WLAN_DISCONNECTION, ///< Disconnection due to WLAN (Wi-Fi) network issues. + SL_MQTT_CLIENT_USER_INITIATED_DISCONNECTION, ///< Disconnection initiated by the user. + SL_MQTT_CLIENT_KEEP_ALIVE_RESPONSE_TIMEOUT_DISCONNECTION ///< Disconnection due to keep-alive response timeout. +} sl_mqtt_client_disconnection_reason_t; + +/** + * @enum sl_mqtt_tls_flag_t + * @brief MQTT TLS and certificate options. + * + * @details + * This enumeration defines the various TLS (Transport Layer Security) and certificate options available for the MQTT client. These options include enabling TLS, selecting the TLS version, and choosing the certificate index. + * + * @note + * Only one TLS version (SL_MQTT_TLS_TLSV_X_X) can be set at a time. + * @note + * Only one certificate index (SL_MQTT_TLS_CERT_INDEX_X) can be set at a time. + * @note + * To select certificate index 0, no additional flags need to be configured explicitly. + */ +typedef enum { + SL_MQTT_TLS_ENABLE = BIT(0), ///< Enable TLS for MQTT. + SL_MQTT_TLS_TLSV_1_0 = BIT(1), ///< Enable TLS version 1.0 for MQTT. + SL_MQTT_TLS_TLSV_1_1 = BIT(2), ///< Enable TLS version 1.1 for MQTT. + SL_MQTT_TLS_TLSV_1_2 = BIT(3), ///< Enable TLS version 1.2 for MQTT. + SL_MQTT_TLS_TLSV_1_3 = BIT(4), ///< Enable TLS version 1.3 for MQTT. + SL_MQTT_TLS_CERT_INDEX_1 = BIT(5), ///< Use certificate index 1 for MQTT. + SL_MQTT_TLS_CERT_INDEX_2 = BIT(6) ///< Use certificate index 2 for MQTT. +} sl_mqtt_tls_flag_t; + +/** @} */ + +/** + * @addtogroup SERVICE_MQTT_TYPES + * @{ + */ + +/** + * @brief + * Structure representing the MQTT Client Last Will message. + * + * @details + * This structure holds the information for the Last Will message that will be sent by the MQTT broker if the client unexpectedly disconnects. The Last Will message is a retained message that can be used to notify other clients about the disconnection. + */ +typedef struct { + bool is_retained; ///< Flag indicating whether to retain the Last Will message. + sl_mqtt_qos_t will_qos_level; ///< Quality of Service level for the Last Will message. + uint8_t *will_topic; ///< Pointer to the topic name for the Last Will message. + uint16_t + will_topic_length; ///< Length of the topic name. It should not exceed 202 bytes that includes NULL termination character. + uint8_t *will_message; ///< Pointer to the Last Will message content. + uint32_t + will_message_length; ///< Length of the Last Will message content. Should not exceed 60 bytes including NULL termination character. +} sl_mqtt_client_last_will_message_t; + +/** + * @brief + * Structure representing an MQTT Client Message. + * + * @details + * This structure holds the information for a message received from or sent to the MQTT broker. It includes details such as the Quality of Service level, packet identifier, and message content. + */ +typedef struct { + sl_mqtt_qos_t qos_level; ///< Quality of Service level of the message. + uint16_t packet_identifier; ///< Packet identifier of the received message. + bool is_retained; ///< Flag indicating whether the message is retained by the broker. + bool is_duplicate_message; ///< Flag indicating whether this is a duplicate message. + uint8_t *topic; ///< Pointer to the topic name. Must not be NULL. + uint16_t + topic_length; ///< Length of the topic name. It should not exceed 202 bytes that includes NULL termination character. + uint8_t *content; ///< Pointer to the message content. Must not be NULL. + uint32_t content_length; ///< Length of the message content. It should not exceed 1024 bytes. +} sl_mqtt_client_message_t; + +/** + * @brief + * MQTT Client broker information structure. + * + * @details + * This structure holds the information required to connect to an MQTT broker, that includes the broker's IP address, port number, connection encryption status, connection timeout, keep-alive interval, and keep-alive retries. + */ +typedef struct { + sl_ip_address_t ip; ///< IP address of the broker. + uint16_t port; ///< Port number of the broker. + bool + is_connection_encrypted; ///< Indicates if the connection is encrypted. This field would be deprecated in future releases. You are recommended to use `tls_flags` in @ref sl_mqtt_client_configuration_t. + uint16_t connect_timeout; ///< MQTT connection timeout in milliseconds. + uint16_t keep_alive_interval; ///< Keep-alive interval of the MQTT connection in seconds. + uint16_t keep_alive_retries; ///< Number of MQTT ping retries. +} sl_mqtt_broker_t; + +/** + * @brief + * MQTT Client credentials structure. + * + * @details + * This structure holds the credentials required for the MQTT client to authenticate with the broker. It includes the lengths of the username and password, and a flexible array to store both the username and password. + */ +typedef struct { + uint16_t + username_length; ///< Length of the username. It should not exceed 120 bytes which includes NULL termination character. + uint16_t + password_length; ///< Length of the password. It should not exceed 60 bytes which includes NULL termination character. + uint8_t data[]; ///< Flexible array to store both the username and password. +} sl_mqtt_client_credentials_t; + +/** + * @brief + * MQTT Client Configuration structure. + * + * @details + * This structure holds the configuration parameters for the MQTT client, that includes connection settings, retry policies, session options, and security credentials. + */ +typedef struct { + bool auto_reconnect; ///< Whether to automatically reconnect to the broker in case of disconnection. + uint8_t retry_count; ///< Maximum number of retry attempts for auto reconnect. + uint16_t minimum_back_off_time; ///< Minimum back-off time (in seconds) between two successive reconnect attempts. + uint16_t maximum_back_off_time; ///< Maximum back-off time (in seconds) between two successive reconnect attempts. + bool is_clean_session; ///< Clean session flag to send to the broker in the connect request. + sl_mqtt_version_t mqt_version; ///< MQTT protocol version used by the client. + uint16_t client_port; ///< Port number used by the client for the connection. + sl_net_credential_id_t + credential_id; ///< Credential ID for the username and password used in the MQTT connect request. + uint8_t *client_id; ///< Pointer to the MQTT client ID string. + uint8_t + client_id_length; ///< Length of the client ID string. Should not exceed 60 bytes including NULL termination character. + sl_mqtt_tls_flag_t tls_flags; ///< TLS flags for various MQTT options. See @ref sl_mqtt_tls_flag_t for details. +} sl_mqtt_client_configuration_t; + +/** + * @typedef sl_mqtt_client_event_handler_t + * @brief + * Handler for MQTT client events. + * + * @details + * This function pointer type defines the handler for various events that occur on the MQTT client. The handler is invoked with details about the event, including the client on which the event occurred, the type of event, any associated event data, and a user-provided context. + * + * @param client + * Pointer to the MQTT client on which the event occurred. This pointer must not be NULL. + * + * @param event + * The type of event that occurred. This is of type @ref sl_mqtt_client_event_t. + * + * @param event_data + * Pointer to the event data. This parameter is non-null only for events of type MQTT_CLIENT_MESSAGE_RECEIVED and MQTT_CLIENT_ERROR. + * + * @param context + * Pointer to the user-provided context. This context is provided at the time of the API call (e.g., @ref sl_mqtt_client_init). The caller must ensure that the lifecycle of the context is retained until the callback is invoked. The deallocation of the context is also the responsibility of the caller. + */ +typedef void (*sl_mqtt_client_event_handler_t)(void *client, + sl_mqtt_client_event_t event, + void *event_data, + void *context); + +/** + * @typedef sl_mqtt_client_message_received_t + * @brief + * Handler for received MQTT client messages. + * + * @details + * This function pointer type defines the handler for messages received by the MQTT client. The handler is invoked with details about the client that received the message, the message itself, and a user-provided context. + * + * @param client + * Pointer to the MQTT client that received the message. This pointer must not be NULL. + * + * @param message_to_be_published + * Pointer to the received message of type @ref sl_mqtt_client_message_t. This pointer must not be NULL. + * + * @param context + * Pointer to the user-provided context. This context is provided at the time of the Subscribe API call. The caller (the function or module that invokes the Subscribe API) must ensure that the lifecycle of the context is retained until the callback is invoked. The deallocation of the context is also the responsibility of the caller. + * + * @note + * Due to constraints from the firmware, the fields `is_retained`, `qos_level`, `packet_identifier`, and `duplicate_message` of @ref sl_mqtt_client_message_t are not populated and should be ignored while processing the message. + */ +typedef void (*sl_mqtt_client_message_received_t)(void *client, + sl_mqtt_client_message_t *message_to_be_published, + void *context); + +/** + * @brief + * MQTT Client Topic Subscription Info structure. + * + * @details + * This structure holds the information related to a topic subscription for the MQTT client. It includes the message handler for the topic, the Quality of Service (QoS) level, and the topic itself. + */ +typedef struct { + sl_slist_node_t next_subscription; ///< Next node in the linked list of subscriptions. + sl_mqtt_client_message_received_t + topic_message_handler; ///< Function pointer to the message handler for the subscribed topic. + sl_mqtt_qos_t qos_of_subscription; ///< Quality of Service level for the subscription. + uint16_t + topic_length; ///< Length of the subscribed topic. It should not exceed 202 bytes that includes NULL termination character. + uint8_t topic[]; ///< Flexible array to store the topic name. +} sl_mqtt_client_topic_subscription_info_t; + +/** + * @brief + * MQTT Client Handle structure. + * + * @details + * This structure represents the handle for an MQTT client. It holds the current state of the client, broker configuration, last will message configuration, client configuration, subscription list, and event handler. + */ +typedef struct { + sl_mqtt_client_connection_state_t state; ///< Current state of the MQTT client. + const sl_mqtt_broker_t + *broker; ///< Pointer to the broker configuration, provided at the time of the connect() API call. + const sl_mqtt_client_last_will_message_t * + last_will_message; ///< Pointer to the last will message configuration, provided at the time of the connect() API call. + const sl_mqtt_client_configuration_t + *client_configuration; ///< Pointer to the client configuration, provided at the time of the connect() API call. + sl_mqtt_client_topic_subscription_info_t + *subscription_list_head; ///< Pointer to the head of the subscription linked list. + sl_mqtt_client_event_handler_t + client_event_handler; ///< Function pointer to the event handler, provided at the time of @ref sl_mqtt_client_init. +} sl_mqtt_client_t; + +/** @} */ diff --git a/wiseconnect/components/service/mqtt/si91x/sl_mqtt_client.c b/wiseconnect/components/service/mqtt/si91x/sl_mqtt_client.c new file mode 100644 index 000000000..efd1d765e --- /dev/null +++ b/wiseconnect/components/service/mqtt/si91x/sl_mqtt_client.c @@ -0,0 +1,1044 @@ +/***************************************************************************/ /** + * @file sl_mqtt_client.c + ******************************************************************************* + * # License + * Copyright 2025 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ +#include "sl_net.h" +#include "sl_slist.h" +#include "sl_si91x_driver.h" +#include "stdint.h" +#include +#include +#include +#include "sl_mqtt_client.h" +#include "sl_mqtt_client_types.h" +#include "si91x_mqtt_client_types.h" +#include "si91x_mqtt_client_utility.h" +#include "sl_status.h" + +/** + * MQTT CLIENT STATE MACHINE +========================================================================| + STATE EVENT NEXT_STATE | +========================================================================| + CONNECTED | CONNECT | UNKNOWN + CONNECTED | RECONNECT | UNKNOWN + CONNECTED | DISCONNECT | TA_DISCONNECTED -> DISCONNECTED + CONNECTED | PUBLISH | CONNECTED + CONNECTED | SUBSCRIBE | CONNECTED + CONNECTED | UNSUBSCRIBE | CONNECTED + CONNECTED | ON_DISCONNECT_RECV^ | DISCONNECTED + + DISCONNECTED | CONNECT | TRANSPORTATION_LAYER_CONNECTED -> CONNECTED + DISCONNECTED | RECONNECT | was_connect_called_previously ? TRANSPORTATION_LAYER_CONNECTED -> CONNECTED: UNKNOWN + DISCONNECTED | ANY_OTHER_EVENT | UNKNOWN + + TRANSPORTATION_LAYER_CONNECTED | ON_CONNECT_SUCCESS^ | CONNECTED + TRANSPORTATION_LAYER_CONNECTED | ON_CONNECT_FAILED^ | TRANSPORTATION_LAYER_CONNECTED + TRANSPORTATION_LAYER_CONNECTED | ANY_OTHER_EVENT | UNKNOWN + + TA_DISCONNECTED | ON_DISCONNECT_SUCCESS^| DISCONNECTED + TA_DISCONNECTED | ON_DISCONNECT_FAILED^ | TA_DISCONNECTED + TA_DISCONNECTED | ANY_OTHER_EVENT | UNKNOWN +============================================================ + + *UNKOWN -> Throw Error + ^ -> firmware events +**/ + +// Declaring strtok_r as extern to suppress implicit declaration warning. +extern char *strtok_r(char *, const char *, char **); + +extern sli_si91x_command_queue_t cmd_queues[SI91X_CMD_MAX]; + +#define SI91X_MQTT_CLIENT_INIT_TIMEOUT 5000 +#define SI91X_MQTT_CLIENT_DISCONNECT_TIMEOUT 5000 +#define SI91X_MQTT_CHECK_RETAIN_MESSAGE BIT(0) +#define SI91X_MQTT_CHECK_QOS_LEVEL (BIT(1) | BIT(2)) +#define SI91X_MQTT_CHECK_IS_DUPLICATE_MESSAGE BIT(3) + +#define VERIFY_AND_RETURN_ERROR_IF_FALSE(condition, status) \ + do { \ + if (!(condition)) { \ + PRINT_STATUS(INFO_TAG, status) \ + return status; \ + } \ + } while (0) + +static sl_mqtt_client_t *mqtt_client; +static sl_mqtt_client_error_status_t sli_si91x_get_event_error_status(sl_mqtt_client_event_t event); +static void sli_si91x_handle_connected_event(sl_status_t status, + sl_si91x_mqtt_client_context_t *sdk_context, + const sl_wifi_packet_t *rx_packet, + bool *is_error_event, + uint8_t **event_data, + sl_mqtt_client_disconnection_reason_t *reason); +static void sli_si91x_handle_subscribed_event(sl_status_t status, + sl_si91x_mqtt_client_context_t *sdk_context, + bool *is_error_event); +static void sli_si91x_handle_unsubscribed_event(sl_status_t status, + sl_si91x_mqtt_client_context_t *sdk_context, + bool *is_error_event); +static void sli_si91x_handle_message_received_event(sl_si91x_mqtt_client_context_t *sdk_context, + sl_wifi_packet_t *rx_packet); +static void sli_si91x_handle_disconnected_event(sl_status_t status, + sl_si91x_mqtt_client_context_t *sdk_context, + const sl_wifi_packet_t *rx_packet, + bool *is_error_event, + uint8_t **event_data, + sl_mqtt_client_disconnection_reason_t *reason); +/** + * A internal helper function to get node of list which matches the given topic. + * @param client Pointer to client object whose subscription list needs to be searched. + * @param topic Topic which needs to be searched in list. + * @param topic_length Length of the topic that needs to be searched. + * @param required_node A double pointer to node pointer. + */ +static void sli_si91x_get_subscription(const sl_mqtt_client_t *client, + const uint8_t *topic, + const uint16_t topic_length, + sl_mqtt_client_topic_subscription_info_t **required_subscription) +{ + sl_mqtt_client_topic_subscription_info_t *subscription = client->subscription_list_head; + + uint8_t subscribed_topic[SI91X_MQTT_CLIENT_TOPIC_MAXIMUM_LENGTH] = { 0 }; + uint8_t received_topic[SI91X_MQTT_CLIENT_TOPIC_MAXIMUM_LENGTH] = { 0 }; + *required_subscription = NULL; + + // If the topic length is greater than or equal to SI91X_MQTT_CLIENT_TOPIC_MAXIMUM_LENGTH, return. + if (topic_length >= SI91X_MQTT_CLIENT_TOPIC_MAXIMUM_LENGTH) { + return; + } + + while (subscription != NULL) { + memcpy(subscribed_topic, subscription->topic, subscription->topic_length); + memcpy(received_topic, topic, topic_length); + + // adding null character at the end of the string as topic might not end with null terminating character. + // the size of topic is guaranteed to be less than SI91X_MQTT_CLIENT_TOPIC_MAXIMUM_LENGTH. + subscribed_topic[subscription->topic_length] = '\0'; + + char *subscribed_topic_save_ptr = NULL; + char *received_topic_save_ptr = NULL; + + char *subscribed_topic_token = NULL; + const char *received_topic_token = NULL; + + subscribed_topic_token = + strtok_r((char *)subscribed_topic, SLI_SI91X_MQTT_CLIENT_TOPIC_DELIMITER, &subscribed_topic_save_ptr); + received_topic_token = + strtok_r((char *)received_topic, SLI_SI91X_MQTT_CLIENT_TOPIC_DELIMITER, &received_topic_save_ptr); + + while (subscribed_topic_token != NULL && received_topic_token != NULL) { + uint8_t subscribe_topic_length = + (uint8_t)(strnlen(subscribed_topic_token, SI91X_MQTT_CLIENT_TOPIC_MAXIMUM_LENGTH)); + + // This boolean stores whether the subscribed_topic_token is wildcard or not by checking the length of the token and character stored in it. + uint8_t is_wild_card = + (subscribe_topic_length == 1) + && ((strncmp(subscribed_topic_token, SLI_SI91X_MQTT_CLIENT_MULTI_LEVEL_WILD_CARD, 1) == 0) + || (strncmp(subscribed_topic_token, SLI_SI91X_MQTT_CLIENT_SINGLE_LEVEL_WILD_CARD, 1) == 0)); + + uint8_t is_multi_level_wild_card = + is_wild_card && (strncmp(subscribed_topic_token, SLI_SI91X_MQTT_CLIENT_MULTI_LEVEL_WILD_CARD, 1) == 0); + + // if subscribed_topic_token isn't wildcard and tokens does not match, break the loop and continue searching with other subscriptions. + if (!is_wild_card + && ((subscribe_topic_length + != strnlen((char *)received_topic_token, SI91X_MQTT_CLIENT_TOPIC_MAXIMUM_LENGTH)) + || memcmp(subscribed_topic_token, received_topic_token, subscribe_topic_length) != 0)) { + break; + } else if (is_multi_level_wild_card) { + // if the subscribed_topic_token is "#", the assign the current subscription as required_subscription and return. + *required_subscription = subscription; + return; + } else { + // The execution enters the else block either in case of single level wildcard or successful comparison of current token + subscribed_topic_token = strtok_r(NULL, SLI_SI91X_MQTT_CLIENT_TOPIC_DELIMITER, &subscribed_topic_save_ptr); + received_topic_token = strtok_r(NULL, SLI_SI91X_MQTT_CLIENT_TOPIC_DELIMITER, &received_topic_save_ptr); + } + + // If both strings reach end of token, assign this subscription as required_subscription + // Example: subscription Topic: Home/+/TemperatureSensor + // Received Topic: Home/Garden/TemperatureSensor + if (subscribed_topic_token == NULL && received_topic_token == NULL) { + *required_subscription = subscription; + return; + } + } + + subscription = (sl_mqtt_client_topic_subscription_info_t *)subscription->next_subscription.node; + } +} + +static void sli_si91x_remove_and_free_all_subscriptions(sl_mqtt_client_t *client) +{ + if (client == NULL) { + SL_DEBUG_LOG("MQTT client instance not initialized yet\n"); + return; + } + // Free subscription list. + sl_mqtt_client_topic_subscription_info_t *node_to_be_freed; + while ((node_to_be_freed = (sl_mqtt_client_topic_subscription_info_t *)(sl_slist_pop( + (sl_slist_node_t **)&client->subscription_list_head))) + != NULL) { + free(node_to_be_freed); + } +} +static inline bool is_connect_previously_called(const sl_mqtt_client_t *client) +{ + return (NULL != client->client_configuration); +} + +sl_status_t sli_si91x_build_mqtt_sdk_context_if_async(sl_mqtt_client_event_t event, + sl_mqtt_client_t *client, + void *user_context, + void *sdk_data, + uint32_t timeout, + sl_si91x_mqtt_client_context_t **context) +{ + *context = NULL; + if (timeout > 0) { + return SL_STATUS_OK; + } + + sl_si91x_mqtt_client_context_t *mqtt_client_sdk_context = calloc(sizeof(sl_si91x_mqtt_client_context_t), 1); + + if (mqtt_client_sdk_context == NULL) { + return SL_STATUS_ALLOCATION_FAILED; + } + + mqtt_client_sdk_context->client = client; + mqtt_client_sdk_context->event = event; + mqtt_client_sdk_context->sdk_data = sdk_data; + mqtt_client_sdk_context->user_context = user_context; + + *context = mqtt_client_sdk_context; + + return SL_STATUS_OK; +} + +void sli_si91x_get_mqtt_client(sl_mqtt_client_t **client) +{ + *client = mqtt_client; +} + +/** + * A internal function to map the event to its appropriate mqtt error state. + * @param event Event whose sl_mqtt_client_event_t value is required. + * @return sl_mqtt_client_error_status_t for the Event. + */ +static sl_mqtt_client_error_status_t sli_si91x_get_event_error_status(sl_mqtt_client_event_t event) +{ + switch (event) { + case SL_MQTT_CLIENT_CONNECTED_EVENT: { + return SL_MQTT_CLIENT_CONNECT_FAILED; + } + + case SL_MQTT_CLIENT_DISCONNECTED_EVENT: { + return SL_MQTT_CLIENT_DISCONNECT_FAILED; + } + + case SL_MQTT_CLIENT_MESSAGE_PUBLISHED_EVENT: { + return SL_MQTT_CLIENT_PUBLISH_FAILED; + } + + case SL_MQTT_CLIENT_SUBSCRIBED_EVENT: { + return SL_MQTT_CLIENT_SUBSCRIBE_FAILED; + } + + case SL_MQTT_CLIENT_UNSUBSCRIBED_EVENT: { + return SL_MQTT_CLIENT_UNSUBSCRIBED_FAILED; + } + + default: { + return SL_MQTT_CLIENT_UNKNOWN_ERROR; + } + } +} + +/** + * @brief Get MQTT client credentials from the credential manager. + * + * This function retrieves the MQTT client credentials associated with the given credential ID. + * + * @param[in] credential_id The ID of the MQTT client credentials. + * @param[out] credentials Double pointer to the MQTT client credentials. + * + * @return + * sl_status_t. See https://docs.silabs.com/gecko-platform/latest/platform-common/status for details. + * + * @note The function allocates memory for the credentials in case of successfully fetching the data, + * and the caller is responsible for freeing the memory. + */ +static sl_status_t sli_si91x_fetch_mqtt_client_credentials(sl_net_credential_id_t credential_id, + sl_mqtt_client_credentials_t **credentials) +{ + SL_VERIFY_POINTER_OR_RETURN(credentials, SL_STATUS_WIFI_NULL_PTR_ARG); + + *credentials = NULL; + + if (credential_id == (sl_net_credential_id_t)SL_NET_INVALID_CREDENTIAL_TYPE) { + return SL_STATUS_OK; + } + + uint32_t maximum_credential_size = sizeof(sl_mqtt_client_credentials_t) + SI91X_MQTT_CLIENT_USERNAME_MAXIMUM_LENGTH + + SI91X_MQTT_CLIENT_PASSWORD_MAXIMUM_LENGTH; + + sl_mqtt_client_credentials_t *mqtt_credentials = malloc(maximum_credential_size); + + if (mqtt_credentials == NULL) { + return SL_STATUS_ALLOCATION_FAILED; + } + + memset(mqtt_credentials, 0, maximum_credential_size); + sl_net_credential_type_t type = SL_NET_INVALID_CREDENTIAL_TYPE; + + sl_status_t status = sl_net_get_credential(credential_id, &type, mqtt_credentials, &maximum_credential_size); + + if (status != SL_STATUS_OK || type != SL_NET_MQTT_CLIENT_CREDENTIAL) { + free(mqtt_credentials); + return status != SL_STATUS_OK ? status : SL_STATUS_INVALID_CREDENTIALS; + } + + if (mqtt_credentials->username_length >= SI91X_MQTT_CLIENT_USERNAME_MAXIMUM_LENGTH + || mqtt_credentials->password_length >= SI91X_MQTT_CLIENT_PASSWORD_MAXIMUM_LENGTH) { + free(mqtt_credentials); + return SL_STATUS_INVALID_PARAMETER; + } + + *credentials = mqtt_credentials; + return SL_STATUS_OK; +} + +/** + * @brief Sends the MQTT init command to the firmware. + * + * @param client[in] Pointer to the MQTT client object. + * @param credentials[out] Pointer to the MQTT client credentials. + * + * @return + * sl_status_t. See https://docs.silabs.com/gecko-platform/latest/platform-common/status for details. + */ +static sl_status_t sli_si91x_send_firmware_mqtt_init(const sl_mqtt_client_t *client, + const sl_mqtt_client_credentials_t *credentials) +{ + + si91x_mqtt_client_init_request_t si91x_init_request = { 0 }; + + memcpy(&si91x_init_request.server_ip.server_ip_address, + &client->broker->ip.ip, + client->broker->ip.type == SL_IPV4 ? SL_IPV4_ADDRESS_LENGTH : SL_IPV6_ADDRESS_LENGTH); + si91x_init_request.server_ip.ip_version = client->broker->ip.type == SL_IPV6 ? SL_IPV6_VERSION : SL_IPV4_VERSION; + + si91x_init_request.command_type = SLI_SI91X_MQTT_CLIENT_INIT_COMMAND; + + si91x_init_request.server_port = client->broker->port; + si91x_init_request.clean = client->client_configuration->is_clean_session; + si91x_init_request.client_port = client->client_configuration->client_port; + + // Maintain backward compatibility by checking if tls_flags are non-zero + if (client->client_configuration->tls_flags != 0) { + si91x_init_request.encrypt = (uint8_t)client->client_configuration->tls_flags; + } else { + si91x_init_request.encrypt = client->broker->is_connection_encrypted; + } + + si91x_init_request.client_id_len = client->client_configuration->client_id_length; + memcpy(si91x_init_request.client_id, + client->client_configuration->client_id, + client->client_configuration->client_id_length); + + si91x_init_request.keep_alive_interval = client->broker->keep_alive_interval; + si91x_init_request.keep_alive_retries = client->broker->keep_alive_retries; + + if (credentials != NULL) { + + memcpy(si91x_init_request.user_name, &credentials->data[0], credentials->username_length); + + // credentials.username_length is being used as offset as we store both user_name, password in same array. + memcpy(si91x_init_request.password, &credentials->data[credentials->username_length], credentials->password_length); + + si91x_init_request.username_len = (uint8_t)(credentials->username_length); + si91x_init_request.password_len = (uint8_t)(credentials->password_length); + } + + return sli_si91x_driver_send_command(SLI_WLAN_REQ_EMB_MQTT_CLIENT, + SLI_SI91X_NETWORK_CMD, + &si91x_init_request, + sizeof(si91x_init_request), + SL_SI91X_WAIT_FOR(SI91X_MQTT_CLIENT_INIT_TIMEOUT), + NULL, + NULL); +} + +sl_status_t sl_mqtt_client_init(sl_mqtt_client_t *client, sl_mqtt_client_event_handler_t event_handler) +{ + SL_VERIFY_POINTER_OR_RETURN(event_handler, SL_STATUS_WIFI_NULL_PTR_ARG); + + client->client_event_handler = event_handler; + sl_slist_init((sl_slist_node_t **)&client->subscription_list_head); + + mqtt_client = client; + return SL_STATUS_OK; +} + +sl_status_t sl_mqtt_client_deinit(sl_mqtt_client_t *client) +{ + + VERIFY_AND_RETURN_ERROR_IF_FALSE(client->state == SL_MQTT_CLIENT_DISCONNECTED, SL_STATUS_INVALID_STATE); + memset(client, 0, sizeof(sl_mqtt_client_t)); + + mqtt_client = NULL; + return SL_STATUS_OK; +} + +sl_status_t sl_mqtt_client_connect(sl_mqtt_client_t *client, + const sl_mqtt_broker_t *broker, + const sl_mqtt_client_last_will_message_t *last_will, + const sl_mqtt_client_configuration_t *configuration, + uint32_t connect_timeout) +{ + SL_VERIFY_POINTER_OR_RETURN(client, SL_STATUS_WIFI_NULL_PTR_ARG); + + VERIFY_AND_RETURN_ERROR_IF_FALSE( + (client->state == SL_MQTT_CLIENT_DISCONNECTED || client->state == SL_MQTT_CLIENT_TA_INIT), + SL_STATUS_INVALID_STATE); + + // check if this the first time connect() call being made, + // In subsequent calls it not mandatory to pass parameters as we store them in client structure + if (!is_connect_previously_called(client)) { + SL_VERIFY_POINTER_OR_RETURN(broker, SL_STATUS_WIFI_NULL_PTR_ARG); + SL_VERIFY_POINTER_OR_RETURN(configuration, SL_STATUS_WIFI_NULL_PTR_ARG); + } + + if ((configuration != NULL && configuration->client_id_length >= SLI_SI91X_MQTT_CLIENT_ID_MAXIMUM_LENGTH) + || (last_will != NULL && last_will->will_topic_length >= SI91X_MQTT_CLIENT_WILL_TOPIC_MAXIMUM_LENGTH)) { + return SL_STATUS_INVALID_PARAMETER; + } + + sl_status_t status; + sli_si91x_mqtt_client_connect_request_t si91x_connect_request = { 0 }; + sl_si91x_mqtt_client_context_t *sdk_context = NULL; + sl_mqtt_client_credentials_t *credentials = NULL; + + // If user provides valid values in subsequent calls, we store the new values else we keep referring to structures provided in first connect call. + client->broker = broker == NULL ? client->broker : broker; + client->client_configuration = configuration == NULL ? client->client_configuration : configuration; + + // Special case for last_will, as NULL can be a legitimate value if client wouldn't want to provide a last will. + client->last_will_message = last_will; + + status = sli_si91x_fetch_mqtt_client_credentials(client->client_configuration->credential_id, &credentials); + + if (status != SL_STATUS_OK) { + SL_CLEANUP_MALLOC(credentials); + return status; + } + + // Host connect() call maps to two commands in firmware, init() and connect() + // since we can't send(At least with current design) two command in aysnc mode, + // We send init command in sync mode, whereas, connect will be sent as async + if (client->state == SL_MQTT_CLIENT_DISCONNECTED) { + status = sli_si91x_send_firmware_mqtt_init(client, credentials); + + if (status != SL_STATUS_OK) { + SL_CLEANUP_MALLOC(credentials); + + client->state = SL_MQTT_CLIENT_DISCONNECTED; + return status; + } + + client->state = SL_MQTT_CLIENT_TA_INIT; + } + + si91x_connect_request.command_type = SLI_SI91X_MQTT_CLIENT_CONNECT_COMMAND; + // NWP takes the username and password from init_request and validation bit from the connect request. + if (credentials != NULL) { + si91x_connect_request.is_password_present = 1; + si91x_connect_request.is_username_present = 1; + + SL_CLEANUP_MALLOC(credentials); + } + + if (client->last_will_message != NULL) { + si91x_connect_request.will_retain = client->last_will_message->is_retained; + si91x_connect_request.will_qos = (uint8_t)(client->last_will_message->will_qos_level); + si91x_connect_request.will_topic_len = + (uint8_t)(client->last_will_message->will_topic_length); // Narrowing of variable + si91x_connect_request.will_message_len = + (uint8_t)(client->last_will_message->will_message_length); // Narrowing of variable + + memcpy(si91x_connect_request.will_topic, + client->last_will_message->will_topic, + client->last_will_message->will_topic_length); + memcpy(si91x_connect_request.will_msg, + client->last_will_message->will_message, + client->last_will_message->will_message_length); + + si91x_connect_request.will_flag = 1; + } + + status = sli_si91x_build_mqtt_sdk_context_if_async(SL_MQTT_CLIENT_CONNECTED_EVENT, + client, + NULL, + NULL, + connect_timeout, + &sdk_context); + VERIFY_STATUS_AND_RETURN(status); + + status = sli_si91x_driver_send_command( + SLI_WLAN_REQ_EMB_MQTT_CLIENT, + SLI_SI91X_NETWORK_CMD, + &si91x_connect_request, + sizeof(si91x_connect_request), + connect_timeout == 0 ? SLI_SI91X_RETURN_IMMEDIATELY : SL_SI91X_WAIT_FOR(connect_timeout), + sdk_context, + NULL); + + if (status == SL_STATUS_IN_PROGRESS) { + return status; + } else if (status != SL_STATUS_OK) { + if (client->state == SL_MQTT_CLIENT_DISCONNECTED || status == SL_STATUS_SI91X_COMMAND_ISSUED_IN_REJOIN_STATE) { + SL_DEBUG_LOG("\r\nWLAN disconnected. No need to call the disconnect again.\r\n"); + return status; + } + client->state = SL_MQTT_CLIENT_CONNECTION_FAILED; + status = sl_mqtt_client_disconnect(client, SI91X_MQTT_CLIENT_DISCONNECT_TIMEOUT); + if (status != SL_STATUS_OK) { + SL_DEBUG_LOG( + "Failed to disconnect the client after failed connection attempt. User needs to call disconnect explicitly."); + } + SL_CLEANUP_MALLOC(sdk_context); + return status; + } + + client->state = SL_MQTT_CLIENT_CONNECTED; + return SL_STATUS_OK; +} + +sl_status_t sl_mqtt_client_disconnect(sl_mqtt_client_t *client, uint32_t timeout) +{ + + VERIFY_AND_RETURN_ERROR_IF_FALSE( + (client->state == SL_MQTT_CLIENT_CONNECTED || client->state == SL_MQTT_CLIENT_TA_DISCONNECTED + || client->state == SL_MQTT_CLIENT_CONNECTION_FAILED), + SL_STATUS_INVALID_STATE); + + SL_VERIFY_POINTER_OR_RETURN(client, SL_STATUS_WIFI_NULL_PTR_ARG); + + sl_status_t status = SL_STATUS_OK; + sl_si91x_mqtt_client_context_t *sdk_context = NULL; + + // As in connect, disconnect() call maps to disconnect and deinit in firmware + // We need to call disconnect even if the previous connect call was failed. + if (client->state == SL_MQTT_CLIENT_CONNECTED) { + sli_si91x_mqtt_client_command_request_t si91x_disconnect_request = { .command_type = + SLI_SI91X_MQTT_CLIENT_DISCONNECT_COMMAND }; + + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_EMB_MQTT_CLIENT, + SLI_SI91X_NETWORK_CMD, + &si91x_disconnect_request, + sizeof(si91x_disconnect_request), + SL_SI91X_WAIT_FOR(SI91X_MQTT_CLIENT_DISCONNECT_TIMEOUT), + sdk_context, + NULL); + + VERIFY_STATUS_AND_RETURN(status); + + client->state = SL_MQTT_CLIENT_TA_DISCONNECTED; + } + + sli_si91x_mqtt_client_command_request_t si91x_deinit_request = { .command_type = + SLI_SI91X_MQTT_CLIENT_DEINIT_COMMAND }; + + status = sli_si91x_build_mqtt_sdk_context_if_async(SL_MQTT_CLIENT_DISCONNECTED_EVENT, + client, + NULL, + NULL, + timeout, + &sdk_context); + VERIFY_STATUS_AND_RETURN(status); + + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_EMB_MQTT_CLIENT, + SLI_SI91X_NETWORK_CMD, + &si91x_deinit_request, + sizeof(si91x_deinit_request), + timeout <= 0 ? SLI_SI91X_RETURN_IMMEDIATELY : SL_SI91X_WAIT_FOR(timeout), + sdk_context, + NULL); + + if (status == SL_STATUS_IN_PROGRESS) { + return status; + } else if (status != SL_STATUS_OK) { + SL_CLEANUP_MALLOC(sdk_context); + return status; + } + + client->state = SL_MQTT_CLIENT_DISCONNECTED; + sli_si91x_remove_and_free_all_subscriptions(client); + + return SL_STATUS_OK; +} + +sl_status_t sl_mqtt_client_publish(sl_mqtt_client_t *client, + const sl_mqtt_client_message_t *message, + uint32_t timeout, + void *context) +{ + VERIFY_AND_RETURN_ERROR_IF_FALSE(client->state == SL_MQTT_CLIENT_CONNECTED, SL_STATUS_INVALID_STATE); + + SL_VERIFY_POINTER_OR_RETURN(client, SL_STATUS_WIFI_NULL_PTR_ARG); + + if (message->topic_length >= SI91X_MQTT_CLIENT_TOPIC_MAXIMUM_LENGTH) { + return SL_STATUS_INVALID_PARAMETER; + } + + sl_status_t status; + sl_si91x_mqtt_client_context_t *sdk_context = NULL; + uint32_t publish_request_size = sizeof(sli_si91x_mqtt_client_publish_request_t) + message->content_length; + + sli_si91x_mqtt_client_publish_request_t *si91x_publish_request = calloc(publish_request_size, 1); + if (si91x_publish_request == NULL) { + return SL_STATUS_ALLOCATION_FAILED; + } + status = sli_si91x_build_mqtt_sdk_context_if_async(SL_MQTT_CLIENT_MESSAGE_PUBLISHED_EVENT, + client, + context, + NULL, + timeout, + &sdk_context); + + if (status != SL_STATUS_OK) { + SL_CLEANUP_MALLOC(si91x_publish_request); + return SL_STATUS_ALLOCATION_FAILED; + } + + si91x_publish_request->command_type = SLI_SI91X_MQTT_CLIENT_PUBLISH_COMMAND; + + si91x_publish_request->dup = message->is_duplicate_message; + si91x_publish_request->qos = (uint8_t)(message->qos_level); + si91x_publish_request->retained = message->is_retained; + + si91x_publish_request->topic_len = (uint8_t)(message->topic_length); // Narrowing of variable + si91x_publish_request->msg_len = (uint16_t)(message->content_length); // Narrowing of variable + + si91x_publish_request->msg = (int8_t *)si91x_publish_request + sizeof(sli_si91x_mqtt_client_publish_request_t); + memcpy(si91x_publish_request->topic, message->topic, message->topic_length); + memcpy(si91x_publish_request->msg, message->content, message->content_length); + + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_EMB_MQTT_CLIENT, + SLI_SI91X_NETWORK_CMD, + si91x_publish_request, + publish_request_size, + timeout <= 0 ? SLI_SI91X_RETURN_IMMEDIATELY : SL_SI91X_WAIT_FOR(timeout), + sdk_context, + NULL); + free(si91x_publish_request); + + if (status == SL_STATUS_IN_PROGRESS) { + return status; + } + + SL_CLEANUP_MALLOC(sdk_context); + VERIFY_STATUS_AND_RETURN(status); + + return status; +} + +sl_status_t sl_mqtt_client_subscribe(sl_mqtt_client_t *client, + const uint8_t *topic, + uint16_t topic_length, + sl_mqtt_qos_t qos_level, + uint32_t timeout, + sl_mqtt_client_message_received_t message_handler, + void *context) +{ + VERIFY_AND_RETURN_ERROR_IF_FALSE(client->state == SL_MQTT_CLIENT_CONNECTED, SL_STATUS_INVALID_STATE); + + SL_VERIFY_POINTER_OR_RETURN(client, SL_STATUS_WIFI_NULL_PTR_ARG); + SL_VERIFY_POINTER_OR_RETURN(topic, SL_STATUS_WIFI_NULL_PTR_ARG); + SL_VERIFY_POINTER_OR_RETURN(message_handler, SL_STATUS_WIFI_NULL_PTR_ARG); + + if (topic_length >= SI91X_MQTT_CLIENT_TOPIC_MAXIMUM_LENGTH) { + return SL_STATUS_INVALID_PARAMETER; + } + + sl_status_t status; + sli_si91x_mqtt_client_subscribe_t si91x_subscribe_request = { 0 }; + sl_si91x_mqtt_client_context_t *sdk_context = NULL; + + sl_mqtt_client_topic_subscription_info_t *subscription = + calloc(sizeof(sl_mqtt_client_topic_subscription_info_t) + topic_length, 1); + + status = sli_si91x_build_mqtt_sdk_context_if_async(SL_MQTT_CLIENT_SUBSCRIBED_EVENT, + client, + context, + subscription, + timeout, + &sdk_context); + + if (subscription == NULL || status != SL_STATUS_OK) { + SL_CLEANUP_MALLOC(subscription); + SL_CLEANUP_MALLOC(sdk_context); + + return SL_STATUS_ALLOCATION_FAILED; + } + + si91x_subscribe_request.command_type = SLI_SI91X_MQTT_CLIENT_SUBSCRIBE_COMMAND; + + si91x_subscribe_request.topic_len = (uint8_t)topic_length; + si91x_subscribe_request.qos = (int8_t)qos_level; + + subscription->topic_length = topic_length; + subscription->topic_message_handler = message_handler; + + memcpy(si91x_subscribe_request.topic, topic, topic_length); + memcpy(subscription->topic, topic, topic_length); + + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_EMB_MQTT_CLIENT, + SLI_SI91X_NETWORK_CMD, + &si91x_subscribe_request, + sizeof(si91x_subscribe_request), + timeout <= 0 ? SLI_SI91X_RETURN_IMMEDIATELY : SL_SI91X_WAIT_FOR(timeout), + sdk_context, + NULL); + + if (status == SL_STATUS_IN_PROGRESS) { + return status; + } else if (status != SL_STATUS_OK) { + + free(subscription); + SL_CLEANUP_MALLOC(sdk_context); + return status; + } + + sl_slist_push((sl_slist_node_t **)&client->subscription_list_head, (sl_slist_node_t *)subscription); + return status; +} + +sl_status_t sl_mqtt_client_unsubscribe(sl_mqtt_client_t *client, + const uint8_t *topic, + uint16_t topic_length, + uint32_t timeout, + void *context) +{ + VERIFY_AND_RETURN_ERROR_IF_FALSE(client->state == SL_MQTT_CLIENT_CONNECTED, SL_STATUS_INVALID_STATE); + + SL_VERIFY_POINTER_OR_RETURN(client, SL_STATUS_WIFI_NULL_PTR_ARG); + SL_VERIFY_POINTER_OR_RETURN(topic, SL_STATUS_WIFI_NULL_PTR_ARG); + + if (topic_length >= SI91X_MQTT_CLIENT_TOPIC_MAXIMUM_LENGTH) { + return SL_STATUS_INVALID_PARAMETER; + } + + sl_status_t status; + sl_si91x_mqtt_client_context_t *sdk_context = NULL; + sli_si91x_mqtt_client_unsubscribe_request_t si91x_unsubscribe_request = { 0 }; + sl_mqtt_client_topic_subscription_info_t *subscription; + + sli_si91x_get_subscription(client, topic, topic_length, &subscription); + + status = sli_si91x_build_mqtt_sdk_context_if_async(SL_MQTT_CLIENT_UNSUBSCRIBED_EVENT, + client, + context, + subscription, + timeout, + &sdk_context); + + VERIFY_STATUS_AND_RETURN(status); + si91x_unsubscribe_request.command_type = SLI_SI91X_MQTT_CLIENT_UNSUBSCRIBE_COMMAND; + si91x_unsubscribe_request.topic_len = (uint8_t)topic_length; + memcpy(si91x_unsubscribe_request.topic, topic, topic_length); + + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_EMB_MQTT_CLIENT, + SLI_SI91X_NETWORK_CMD, + &si91x_unsubscribe_request, + sizeof(si91x_unsubscribe_request), + timeout <= 0 ? SLI_SI91X_RETURN_IMMEDIATELY : SL_SI91X_WAIT_FOR(timeout), + sdk_context, + NULL); + + if (status == SL_STATUS_IN_PROGRESS) { + return status; + } else if (status != SL_STATUS_OK) { + + SL_CLEANUP_MALLOC(sdk_context); + return status; + } + + if (subscription != NULL) { + sl_slist_remove((sl_slist_node_t **)&client->subscription_list_head, (sl_slist_node_t *)subscription); + free(subscription); + } + + return status; +} + +static uint8_t sli_si91x_mqtt_identification_function(sl_wifi_buffer_t *buffer, const void *user_data) +{ + UNUSED_PARAMETER(user_data); + if (buffer == NULL) + return false; + const sl_wifi_packet_t *packet = sl_si91x_host_get_buffer_data(buffer, 0, NULL); + return (SLI_WLAN_REQ_EMB_MQTT_CLIENT == packet->command); +} + +sl_status_t sli_si91x_mqtt_event_handler(sl_status_t status, + sl_si91x_mqtt_client_context_t *sdk_context, + sl_wifi_packet_t *rx_packet) +{ + sl_mqtt_client_error_status_t error_status = sli_si91x_get_event_error_status(sdk_context->event); + bool is_error_event = false; + uint8_t *event_data = NULL; + sl_mqtt_client_disconnection_reason_t reason = { 0 }; + + switch (sdk_context->event) { + case SL_MQTT_CLIENT_CONNECTED_EVENT: + sli_si91x_handle_connected_event(status, sdk_context, rx_packet, &is_error_event, &event_data, &reason); + break; + + case SL_MQTT_CLIENT_SUBSCRIBED_EVENT: + sli_si91x_handle_subscribed_event(status, sdk_context, &is_error_event); + break; + + case SL_MQTT_CLIENT_UNSUBSCRIBED_EVENT: + sli_si91x_handle_unsubscribed_event(status, sdk_context, &is_error_event); + break; + + case SL_MQTT_CLIENT_MESSAGED_RECEIVED_EVENT: + sli_si91x_handle_message_received_event(sdk_context, rx_packet); + return SL_STATUS_OK; + + case SL_MQTT_CLIENT_DISCONNECTED_EVENT: + sli_si91x_handle_disconnected_event(status, sdk_context, rx_packet, &is_error_event, &event_data, &reason); + break; + + case SL_MQTT_CLIENT_MESSAGE_PUBLISHED_EVENT: + if (status != SL_STATUS_OK) { + is_error_event = true; + } + break; + + default: + break; + } + + sdk_context->client->client_event_handler(sdk_context->client, + is_error_event ? SL_MQTT_CLIENT_ERROR_EVENT : sdk_context->event, + is_error_event ? &error_status : event_data, + sdk_context->user_context); + + // Free the sdk_context after event handler is triggered. + free(sdk_context); + return SL_STATUS_OK; +} + +static void sli_si91x_handle_connected_event(sl_status_t status, + sl_si91x_mqtt_client_context_t *sdk_context, + const sl_wifi_packet_t *rx_packet, + bool *is_error_event, + uint8_t **event_data, + sl_mqtt_client_disconnection_reason_t *reason) +{ + if (status == SL_STATUS_OK) { + sdk_context->client->state = SL_MQTT_CLIENT_CONNECTED; + return; + } + + if (rx_packet->command == SLI_WLAN_RSP_JOIN) { + *reason = SL_MQTT_CLIENT_WLAN_DISCONNECTION; + *event_data = (uint8_t *)reason; + sdk_context->client->state = SL_MQTT_CLIENT_DISCONNECTED; + // Free all subscriptions as we have disconnected from MQTT broker + sli_si91x_remove_and_free_all_subscriptions(sdk_context->client); + return; + } + + *is_error_event = true; + // This state updates is necessary as we need to send NWP disconnect even in case of connection failure. + sdk_context->client->state = SL_MQTT_CLIENT_CONNECTION_FAILED; + // NWP requires deinit call if connection fails for any reason. + sl_status_t disconnection_status = SL_STATUS_FAIL; + disconnection_status = sl_mqtt_client_disconnect(sdk_context->client, SI91X_MQTT_CLIENT_DISCONNECT_TIMEOUT); + + if (disconnection_status != SL_STATUS_OK) { + SL_DEBUG_LOG( + "Failed to disconnect the client after failed connection attempt. User needs to call disconnect explicitly."); + } else { + sdk_context->client->state = SL_MQTT_CLIENT_DISCONNECTED; + } + return; +} + +static void sli_si91x_handle_subscribed_event(sl_status_t status, + sl_si91x_mqtt_client_context_t *sdk_context, + bool *is_error_event) +{ + if (status != SL_STATUS_OK) { + // Free subscription passed in subscribe() call if subscription call failed. + free(sdk_context->sdk_data); + *is_error_event = true; + return; + } + + // As subscription is success, add the subscription to list. + sl_slist_push((sl_slist_node_t **)&sdk_context->client->subscription_list_head, + (sl_slist_node_t *)sdk_context->sdk_data); + return; +} + +static void sli_si91x_handle_unsubscribed_event(sl_status_t status, + sl_si91x_mqtt_client_context_t *sdk_context, + bool *is_error_event) +{ + if (status != SL_STATUS_OK) { + *is_error_event = true; + return; + } + + // Free subscription if the unsubscription API call is successful. + sl_slist_remove((sl_slist_node_t **)&sdk_context->client->subscription_list_head, + (sl_slist_node_t *)sdk_context->sdk_data); + free(sdk_context->sdk_data); + return; +} + +static void sli_si91x_handle_message_received_event(sl_si91x_mqtt_client_context_t *sdk_context, + sl_wifi_packet_t *rx_packet) +{ + // Extract the MQTT message from payload and create sl_mqtt_message + sl_mqtt_client_message_t received_message; + sl_mqtt_client_topic_subscription_info_t *subscription; + + sli_si91x_mqtt_client_received_message_t *si91x_message = (sli_si91x_mqtt_client_received_message_t *)rx_packet->data; + + received_message.topic = si91x_message->data; + received_message.topic_length = si91x_message->topic_length; + received_message.content_length = si91x_message->current_chunk_length; + received_message.content = &si91x_message->data[si91x_message->topic_length]; + + // Extract the MQTT flags from the received message + // The flags are stored in the first four bits of the mqtt_flags field + + // Use the SI91X_MQTT_CHECK_RETAIN_MESSAGE macro to extract the zeroth bit and determine if the message is retained + received_message.is_retained = si91x_message->mqtt_flags & SI91X_MQTT_CHECK_RETAIN_MESSAGE; + + // Use the SI91X_MQTT_CHECK_QOS_LEVEL macro to extract the first and second bits and determine the QoS level + received_message.qos_level = (si91x_message->mqtt_flags & SI91X_MQTT_CHECK_QOS_LEVEL) >> 1; + + // Use the SI91X_MQTT_CHECK_IS_DUPLICATE_MESSAGE macro to extract the third bit and determine if the message is a duplicate + received_message.is_duplicate_message = si91x_message->mqtt_flags & SI91X_MQTT_CHECK_IS_DUPLICATE_MESSAGE; + + sli_si91x_get_subscription(sdk_context->client, received_message.topic, received_message.topic_length, &subscription); + + if (subscription == NULL) { + SL_DEBUG_LOG("Unable to find subscription: Dropping MQTT message handling"); + } else { + subscription->topic_message_handler(sdk_context->client, &received_message, sdk_context->user_context); + } + + free(sdk_context); + return; +} + +static void sli_si91x_handle_disconnected_event(sl_status_t status, + sl_si91x_mqtt_client_context_t *sdk_context, + const sl_wifi_packet_t *rx_packet, + bool *is_error_event, + uint8_t **event_data, + sl_mqtt_client_disconnection_reason_t *reason) +{ + /* + If the received packet is a disconnect packet and the status is not successful, + it implies that the disconnect was initiated by the user and failed. + However, if the status is SL_STATUS_SI91X_MQTT_KEEP_ALIVE_TERMINATE_ERROR, + it indicates a keep-alive terminate error, not a user-initiated disconnect failure. + */ + if (rx_packet->command == SLI_WLAN_REQ_EMB_MQTT_CLIENT && status != SL_STATUS_OK + && status != SL_STATUS_SI91X_MQTT_KEEP_ALIVE_TERMINATE_ERROR) { + *is_error_event = true; + return; + } + + /* Flush the pending tx request packets from the network command queue */ + sl_status_t flush_status = + sli_si91x_flush_queue_based_on_type(&cmd_queues[SLI_SI91X_NETWORK_CMD], + SL_SI91X_RESPONSE_FLAG(SLI_SI91X_NETWORK_CMD), + (uint16_t)status, + (sli_si91x_compare_function_t)sli_si91x_mqtt_identification_function, + NULL); + if (flush_status != SL_STATUS_OK) { + SL_DEBUG_LOG("\r\nFlush nodes failed Status:%ld\r\n", status); + } + + bool is_keep_alive_response_timeout_termination = + (rx_packet->command == SLI_WLAN_REQ_EMB_MQTT_CLIENT && status == SL_STATUS_SI91X_MQTT_KEEP_ALIVE_TERMINATE_ERROR); + sl_status_t disconnection_status = SL_STATUS_FAIL; + + if (rx_packet->command == SLI_WLAN_RSP_MQTT_REMOTE_TERMINATE || is_keep_alive_response_timeout_termination) { + sdk_context->client->state = SL_MQTT_CLIENT_TA_DISCONNECTED; + disconnection_status = sl_mqtt_client_disconnect(sdk_context->client, SI91X_MQTT_CLIENT_DISCONNECT_TIMEOUT); + + // NWP requires deinit call if remote termination is received. + // If the disconnect call fails, we can't set the state to disconnected. + if (disconnection_status != SL_STATUS_OK) { + SL_DEBUG_LOG( + "Failed to disconnect the client after remote termination. User needs to call disconnect explicitly"); + return; + } + + *reason = is_keep_alive_response_timeout_termination ? SL_MQTT_CLIENT_KEEP_ALIVE_RESPONSE_TIMEOUT_DISCONNECTION + : SL_MQTT_CLIENT_REMOTE_TERMINATE_DISCONNECTION; + *event_data = (uint8_t *)reason; + } else { + // As keep alive response timeout is already handled, + // we can safely assume that the disconnection is user initiated if the frame type is SLI_WLAN_REQ_EMB_MQTT_CLIENT. + *reason = (rx_packet->command == SLI_WLAN_RSP_JOIN) ? SL_MQTT_CLIENT_WLAN_DISCONNECTION + : SL_MQTT_CLIENT_USER_INITIATED_DISCONNECTION; + *event_data = (uint8_t *)reason; + } + + if (rx_packet->command == SLI_WLAN_RSP_JOIN || rx_packet->command == SLI_WLAN_REQ_EMB_MQTT_CLIENT + || ((rx_packet->command == SLI_WLAN_RSP_MQTT_REMOTE_TERMINATE || is_keep_alive_response_timeout_termination) + && disconnection_status == SL_STATUS_OK)) { + sdk_context->client->state = SL_MQTT_CLIENT_DISCONNECTED; + // Free all subscriptions as we have disconnected from mqtt broker + sli_si91x_remove_and_free_all_subscriptions(sdk_context->client); + } + return; +} + +void sli_mqtt_client_cleanup() +{ + if (mqtt_client == NULL) { + SL_DEBUG_LOG("MQTT client instance not initialized yet\n"); + return; + } + sli_si91x_remove_and_free_all_subscriptions(mqtt_client); + memset(mqtt_client, 0, sizeof(sl_mqtt_client_t)); + mqtt_client = NULL; +} diff --git a/wiseconnect/components/service/sl_websocket_client/inc/sl_websocket_client.h b/wiseconnect/components/service/sl_websocket_client/inc/sl_websocket_client.h new file mode 100644 index 000000000..e431ad78e --- /dev/null +++ b/wiseconnect/components/service/sl_websocket_client/inc/sl_websocket_client.h @@ -0,0 +1,169 @@ +/***************************************************************************/ /** + * @file sl_websocket_client.h + ******************************************************************************* + * # License + * Copyright 2025 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ +#ifndef SL_WEBSOCKET_CLIENT_H +#define SL_WEBSOCKET_CLIENT_H + +#include +#include +#include "cmsis_os2.h" +#include "sl_status.h" +#include "sl_wifi_types.h" +#include "sl_ip_types.h" +#include "sl_net_constants.h" +#include "sl_si91x_socket_types.h" +#include "sl_websocket_client_types.h" + +/****************************************************** + * Function Declarations + ******************************************************/ + +/** + * @addtogroup SERVICE_WEBSOCKET_CLIENT_FUNCTIONS + * @{ + */ + +/***************************************************************************/ /** + * @brief Initialize the WebSocket client. + * + * @details + * This function initializes the WebSocket client with the provided configuration. + * It allocates necessary resources and sets up the client for connection. + * + * @param[out] handle + * Pointer to the WebSocket client structure. Must not be NULL. + * + * @param[in] config + * Pointer to the WebSocket client configuration constant structure. Must not be NULL. + * + * @return + * sl_websocket_error_t - Error code indicating the result of the operation. + */ +sl_websocket_error_t sl_websocket_init(sl_websocket_client_t *handle, const sl_websocket_config_t *config); + +/***************************************************************************/ /** + * @brief Connect to a WebSocket server. + * + * @details + * This function creates a socket, binds it, and connects to the specified WebSocket server. + * + * @pre + * The WebSocket handle should be initialized using @ref sl_websocket_init before calling this function. + * + * @param[in] handle + * Pointer to the WebSocket client structure. Must not be NULL. + * + * @return + * sl_websocket_error_t - Error code indicating the result of the operation. + */ +sl_websocket_error_t sl_websocket_connect(sl_websocket_client_t *handle); + +/***************************************************************************/ /** + * @brief Send a WebSocket frame. + * + * @details + * This function sends a WebSocket frame to the server. Masking is taken care of by the firmware. + * + * @pre + * The WebSocket handle should be initialized using @ref sl_websocket_init and connected using @ref sl_websocket_connect before calling this function. + * + * @param[in] handle + * Pointer to the WebSocket client structure. Must not be NULL. + * + * @param[in] send_request + * Pointer to the send request constant structure containing the frame details. Must not be NULL. + * + * @return + * sl_websocket_error_t - Error code indicating the result of the operation. + * + * @note Masking refers to applying a random 32-bit mask to the data sent to the server to ensure data integrity and security. + * + * @note The following table lists the maximum individual chunk of data that can be sent over each supported protocol. + * The length of the payload is specified in the `length` field of the `sl_websocket_send_request_t` structure. + * + * Protocol | Maximum data chunk (bytes) + * --------------------|--------------------------- + * WebSocket | 1450 bytes + * WebSocket over SSL | 1362 bytes + */ +sl_websocket_error_t sl_websocket_send_frame(sl_websocket_client_t *handle, + const sl_websocket_send_request_t *send_request); + +/***************************************************************************/ /** + * @brief Close the WebSocket connection. + * + * @details + * This function closes the WebSocket connection and cleans up resources. + * + * @pre + * The WebSocket handle should be initialized using @ref sl_websocket_init and connected using @ref sl_websocket_connect before calling this function. + * + * @param[in] handle + * Pointer to the WebSocket client structure. Must not be NULL. + * + * @return + * sl_websocket_error_t - Error code indicating the result of the operation. + */ +sl_websocket_error_t sl_websocket_close(sl_websocket_client_t *handle); + +/***************************************************************************/ /** + * @brief Deinitialize the WebSocket client. + * + * @details + * This function deinitializes the WebSocket client by freeing allocated resources and resetting the state. + * It should be called only after the WebSocket connection has been closed because it also attempts to close the socket as part of its cleanup process if the socket remains open after the @ref sl_websocket_close is invoked. + * Therefore, calling this function is mandatory whenever a WebSocket connection is terminated, ensuring proper cleanup. + * + * @pre + * The WebSocket handle should be initialized using @ref sl_websocket_init before calling this function. + * + * @param[in] handle + * Pointer to the WebSocket client structure. Must not be NULL. + * + * @return + * sl_websocket_error_t - Error code indicating the result of the operation. + */ +sl_websocket_error_t sl_websocket_deinit(sl_websocket_client_t *handle); + +/***************************************************************************/ /** + * @brief Extracts the WebSocket opcode from a given socket ID. + * + * @details + * This function determines the opcode of the WebSocket from the socket ID. + * The socket_id is obtained from the recv_data_callback from the sl_si91x_socket_metadata_t parameter. + * + * @param[in] socket_id + * The 16-bit socket ID from which to extract the opcode. + * + * @return + * sl_websocket_opcode_t - The extracted WebSocket opcode. + */ +sl_websocket_opcode_t sl_si91x_get_opcode_from_socket_id(uint16_t socket_id); +/** @} */ + +#endif //SL_WEBSOCKET_CLIENT_H \ No newline at end of file diff --git a/wiseconnect/components/service/sl_websocket_client/inc/sl_websocket_client_types.h b/wiseconnect/components/service/sl_websocket_client/inc/sl_websocket_client_types.h new file mode 100644 index 000000000..3e99c428d --- /dev/null +++ b/wiseconnect/components/service/sl_websocket_client/inc/sl_websocket_client_types.h @@ -0,0 +1,195 @@ +/***************************************************************************/ /** + * @file sl_websocket_client_types.h + ******************************************************************************* + * # License + * Copyright 2025 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ +#ifndef SL_WEBSOCKET_CLIENT_TYPES_H +#define SL_WEBSOCKET_CLIENT_TYPES_H + +#include "sl_status.h" +#include "sl_wifi_types.h" +#include "sl_ip_types.h" +#include "sl_net_constants.h" +#include "cmsis_os2.h" +#include "sl_si91x_socket_types.h" +#include "sl_si91x_protocol_types.h" +#include +#include + +/****************************************************** + * Constants + ******************************************************/ +/** + * @addtogroup SERVICE_WEBSOCKET_CLIENT_CONSTANTS + * @{ + */ + +#define SL_WEBSOCKET_FIN_BIT 0x80 /**< Final frame bit. Not to be configured by the user. */ + +#define SL_SI91X_WEBSOCKET_MAX_HOST_LENGTH 51 /**< Websocket max host length. Not to be configured by the user. */ + +#define SL_SI91X_WEBSOCKET_MAX_RESOURCE_LENGTH \ + 51 /**< Websocket max resource length. Not to be configured by the user. */ + +/****************************************************** + * Enumerations + ******************************************************/ + +/** + * @brief WebSocket opcodes for different frame types. + * + * @details This enumeration defines the opcodes used in WebSocket frames to indicate the type of frame being sent or received. + */ +typedef enum { + SL_WEBSOCKET_OPCODE_CONTINUE = 0x0, /**< Continuation frame */ + SL_WEBSOCKET_OPCODE_TEXT = 0x1, /**< Text frame */ + SL_WEBSOCKET_OPCODE_BINARY = 0x2, /**< Binary frame */ + SL_WEBSOCKET_OPCODE_CLOSE = + 0x8, /**< Connection close frame. The close frame payload contains a close code (first 2 bytes) and optional reason text, with a maximum length of 125 bytes.*/ + SL_WEBSOCKET_OPCODE_PING = 0x9, /**< Ping frame */ + SL_WEBSOCKET_OPCODE_PONG = 0xA, /**< Pong frame */ +} sl_websocket_opcode_t; + +/** + * @brief Error codes for WebSocket operations. + * + * @details This enumeration defines the error codes that can be returned by WebSocket operations to indicate the result of the operation. + */ +typedef enum { + SL_WEBSOCKET_SUCCESS = 0, /**< Operation successful */ + SL_WEBSOCKET_ERR_SOCKET_CREATION = -1, /**< Error creating socket */ + SL_WEBSOCKET_ERR_SOCKET_BIND = -2, /**< Error binding socket */ + SL_WEBSOCKET_ERR_SOCKET_CONNECT = -3, /**< Error connecting socket */ + SL_WEBSOCKET_ERR_SEND_FRAME = -4, /**< Error sending frame */ + SL_WEBSOCKET_ERR_RECEIVE_FRAME = -5, /**< Error receiving frame */ + SL_WEBSOCKET_ERR_CLOSE_FRAME = -6, /**< Error closing frame */ + SL_WEBSOCKET_ERR_SSL_SETSOCKOPT = -7, /**< Error setting socket options for SSL */ + SL_WEBSOCKET_ERR_INVALID_PARAMETER = -8 /**< Invalid input parameter */ +} sl_websocket_error_t; + +/** + * @brief WebSocket connection states. + * + * @details This enumeration defines the possible states of a WebSocket connection. + */ +typedef enum { + SL_WEBSOCKET_STATE_DISCONNECTED, /**< The WebSocket client is not connected to the server. This is the initial state before any connection attempt is made or after a connection has been closed or failed. */ + SL_WEBSOCKET_STATE_CONNECTING, /**< The WebSocket client is in the process of establishing a connection to the server. This state is set when the connection process begins. */ + SL_WEBSOCKET_STATE_CONNECTED, /**< The WebSocket client is successfully connected to the server. This state indicates that the client can now send and receive WebSocket frames. */ + SL_WEBSOCKET_STATE_CLOSING, /**< The WebSocket client is in the process of closing the connection. This state is set when the client initiates a close operation. */ + SL_WEBSOCKET_STATE_CLOSED /**< The WebSocket connection has been closed. This state indicates that the client is no longer connected to the server. */ +} sl_websocket_state_t; + +/** + * @brief WebSocket predefined status codes for Close frames. + * + * @details This enumeration defines the pre-defined status codes that endpoints may use when sending a Close frame. + */ +typedef enum { + SL_WEBSOCKET_CLOSE_NORMAL = 1000, /**< Normal closure */ + SL_WEBSOCKET_CLOSE_GOING_AWAY = 1001, /**< Endpoint is going away */ + SL_WEBSOCKET_CLOSE_PROTOCOL_ERROR = 1002, /**< Protocol error */ + SL_WEBSOCKET_CLOSE_UNSUPPORTED_DATA = 1003, /**< Received data type not supported */ + SL_WEBSOCKET_CLOSE_RESERVED = 1004, /**< Reserved */ + SL_WEBSOCKET_CLOSE_NO_STATUS = 1005, /**< No status code present */ + SL_WEBSOCKET_CLOSE_ABNORMAL = 1006, /**< Abnormal closure */ + SL_WEBSOCKET_CLOSE_INVALID_PAYLOAD = 1007, /**< Invalid payload data */ + SL_WEBSOCKET_CLOSE_POLICY_VIOLATION = 1008, /**< Policy violation */ + SL_WEBSOCKET_CLOSE_MESSAGE_TOO_BIG = 1009, /**< Message too big */ + SL_WEBSOCKET_CLOSE_MISSING_EXTENSION = 1010, /**< Missing required extension */ + SL_WEBSOCKET_CLOSE_INTERNAL_ERROR = 1011, /**< Internal server error */ + SL_WEBSOCKET_CLOSE_TLS_HANDSHAKE_FAILURE = 1015 /**< TLS handshake failure */ +} sl_websocket_close_status_code_t; + +/** @} */ +/****************************************************** + * Type Definitions + ******************************************************/ +// Forward declaration of the type +typedef struct sl_websocket_client_s sl_websocket_client_t; + +/****************************************************** + * Structures + ******************************************************/ + +/** + * @addtogroup SERVICE_WEBSOCKET_CLIENT_TYPES + * @{ + */ + +/** + * @brief WebSocket client configuration structure. + * + * @details This structure holds the configuration parameters for initializing a WebSocket client. + */ +typedef struct { + char *host; /**< WebSocket server host (for example, "example.com"). */ + char *resource; /**< WebSocket resource path (for example, "/chat"). */ + uint16_t server_port; /**< WebSocket server port number. */ + uint16_t client_port; /**< Local client port number. */ + char *ip_address; /**< WebSocket server IP address. */ + sl_si91x_socket_receive_data_callback_t data_cb; /**< Data receive callback function. */ + sl_si91x_socket_remote_termination_callback_t + remote_terminate_cb; /**< Callback function for remote termination event. */ + bool enable_ssl; /**< Enable SSL for WebSocket connection. */ +} sl_websocket_config_t; + +/** + * @brief WebSocket client structure. + * + * @details This structure holds the state and configuration of a WebSocket client, including socket descriptors, connection state, and callback functions. This is not a user configurable structure. + */ +typedef struct sl_websocket_client_s { + int socket_fd; /**< BSD socket file descriptor. */ + char host[SL_SI91X_WEBSOCKET_MAX_HOST_LENGTH]; /**< WebSocket server host (for example, "example.com"). */ + char resource[SL_SI91X_WEBSOCKET_MAX_RESOURCE_LENGTH]; /**< WebSocket resource path (for example, "/chat"). */ + char subprotocol[SLI_WEBS_MAX_SUBPROTOCOL_LENGTH]; /**< WebSocket subprotocol (for example, "mqtt"). */ + uint16_t server_port; /**< WebSocket server port number. */ + uint16_t client_port; /**< Local client port number. */ + sl_ip_address_t ip_address; /**< WebSocket server IP address. */ + uint8_t mask_key[4]; /**< Masking key for client-to-server frames (used in hosted mode). */ + sl_websocket_state_t state; /**< WebSocket connection state. */ + sl_si91x_socket_receive_data_callback_t data_cb; /**< Data receive callback function. */ + sl_si91x_socket_remote_termination_callback_t + remote_terminate_cb; /**< Callback function for remote termination event. */ + bool enable_ssl; /**< Enable SSL for WebSocket connection. */ + void *user_context; /**< User-defined context (for future reference). */ +} sl_websocket_client_t; + +/** + * @brief WebSocket send request structure. + * + * @details This structure holds the parameters for sending a WebSocket frame, including the opcode, payload buffer, and payload length. + */ +typedef struct { + sl_websocket_opcode_t opcode; /**< Opcode (TEXT, BINARY, and so on.). */ + const uint8_t *buffer; /**< Pointer to the payload. */ + size_t length; /**< Length of the payload. */ +} sl_websocket_send_request_t; + +/** @} */ + +#endif //SL_WEBSOCKET_CLIENT_TYPES_H diff --git a/wiseconnect/components/service/sl_websocket_client/inc/sli_websocket_client_sync.h b/wiseconnect/components/service/sl_websocket_client/inc/sli_websocket_client_sync.h new file mode 100644 index 000000000..15eef1015 --- /dev/null +++ b/wiseconnect/components/service/sl_websocket_client/inc/sli_websocket_client_sync.h @@ -0,0 +1,85 @@ +/***************************************************************************/ /** +* @file +* @brief WebSocket Client - Synchronous Socket +******************************************************************************* +* # License +* Copyright 2025 Silicon Laboratories Inc. www.silabs.com +******************************************************************************* +* +* SPDX-License-Identifier: Zlib +* +* The licensor of this software is Silicon Laboratories Inc. +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +* +******************************************************************************/ +#ifndef SLI_WEBSOCKET_CLIENT_SYNC_H +#define SLI_WEBSOCKET_CLIENT_SYNC_H + +#include +#include +#include "cmsis_os2.h" +#include "sl_status.h" +#include "sl_wifi_types.h" +#include "sl_ip_types.h" +#include "sl_net_constants.h" +#include "sl_si91x_socket_types.h" +#include "sl_websocket_client_types.h" +#include "sl_websocket_client.h" +/****************************************************** + * Function Declarations + ******************************************************/ + +/***************************************************************************/ /** + * @brief Connect to a WebSocket server synchronously. + * + * @details + * This function creates a socket, binds it, and connects to the specified WebSocket server. + * Ensures that the data_cb and remote_terminate_cb fields are explicitly set to NULL during initialization. + * + * @pre + * The WebSocket handle should be initialized using @ref sl_websocket_init before calling this function. + * + * @param[in] client + * Pointer to the WebSocket client structure. Must not be NULL. + * + * @return + * sl_websocket_error_t - Error code indicating the result of the operation. + */ +sl_websocket_error_t sli_websocket_connect_sync(sl_websocket_client_t *client); + +/***************************************************************************/ /** + * @brief Sets the subprotocol for the WebSocket client. + * + * This function allows the user to specify a subprotocol for the WebSocket + * client. Subprotocols are used to define a specific protocol to be used + * over the WebSocket connection, enabling the server and client to agree + * on a common protocol for communication. + * + * @param[in] client Pointer to the WebSocket client instance. + * @param[in] subprotocol Pointer to a null-terminated string specifying + * the desired subprotocol. The string must remain + * valid for the duration of the WebSocket connection. + * + * @return SL_STATUS_OK if the subprotocol was successfully set. + * An appropriate error code otherwise. + */ +sl_status_t sli_websocket_set_subprotocol(sl_websocket_client_t *client, const char *subprotocol); + +/***************************************************************************/ + +#endif // SLI_WEBSOCKET_CLIENT_SYNC_H \ No newline at end of file diff --git a/wiseconnect/components/service/sl_websocket_client/src/sl_websocket_client.c b/wiseconnect/components/service/sl_websocket_client/src/sl_websocket_client.c new file mode 100644 index 000000000..923da2bf5 --- /dev/null +++ b/wiseconnect/components/service/sl_websocket_client/src/sl_websocket_client.c @@ -0,0 +1,355 @@ +/***************************************************************************/ /** + * @file sl_websocket_client.c + ******************************************************************************* + * # License + * Copyright 2025 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ +#include "sl_websocket_client.h" +#include "sl_websocket_client_types.h" +#include "sl_constants.h" +#include "sl_slist.h" +#include "sl_net.h" +#include "sl_net_constants.h" +#include "sl_wifi_constants.h" +#include "sl_si91x_constants.h" +#include "sl_si91x_driver.h" +#include "sl_si91x_protocol_types.h" +#include "sl_si91x_socket_utility.h" +#include "sl_si91x_socket_constants.h" +#include "sl_si91x_socket.h" +#ifndef __ZEPHYR__ +#include "socket.h" +#else +#include +#endif +#include "sli_net_utility.h" +#include +#include +#include + +/****************************************************** + * API Definitions + ******************************************************/ +sl_websocket_error_t sl_websocket_init(sl_websocket_client_t *handle, const sl_websocket_config_t *config) +{ + if (!handle || !config) { + return SL_WEBSOCKET_ERR_INVALID_PARAMETER; + } + + // Clear the handle structure to ensure all fields are initialized to zero + memset(handle, 0, sizeof(sl_websocket_client_t)); + + // Check if the WebSocket client is in a valid state to initialize + if (handle->state != SL_WEBSOCKET_STATE_DISCONNECTED) { + SL_DEBUG_LOG("\r\nInvalid state for initializing a WebSocket client\r\n"); + return SL_WEBSOCKET_ERR_INVALID_PARAMETER; + } + + // Copy host + if (config->host != NULL) { + if (strlen(config->host) >= SL_SI91X_WEBSOCKET_MAX_HOST_LENGTH) { + SL_DEBUG_LOG("\r\nHost name length exceeds maximum allowed length\r\n"); + return SL_WEBSOCKET_ERR_INVALID_PARAMETER; + } + snprintf(handle->host, sizeof(handle->host), "%s", config->host); + } + + // Copy resource + if (config->resource != NULL) { + if (strlen(config->resource) >= SL_SI91X_WEBSOCKET_MAX_RESOURCE_LENGTH) { + SL_DEBUG_LOG("\r\nResource name length exceeds maximum allowed length\r\n"); + return SL_WEBSOCKET_ERR_INVALID_PARAMETER; + } + snprintf(handle->resource, sizeof(handle->resource), "%s", config->resource); + } + +#ifdef SLI_SI91X_ENABLE_IPV6 + unsigned char hex_addr[SL_IPV6_ADDRESS_LENGTH] = { 0 }; + sl_status_t status = sl_inet_pton6(config->ip_address, + config->ip_address + strlen(config->ip_address), + hex_addr, + (unsigned int *)handle->ip_address.ip.v6.value); + if (status != 0x1) { + printf("\r\nIPv6 conversion failed.\r\n"); + return SL_WEBSOCKET_ERR_INVALID_PARAMETER; + } + handle->ip_address.type = SL_IPV6; +#else + sl_status_t status = sl_net_inet_addr(config->ip_address, &(handle->ip_address.ip.v4.value)); + if (status != SL_STATUS_OK) { + printf("Failed to convert IP address \r\n"); + return SL_WEBSOCKET_ERR_INVALID_PARAMETER; + } + handle->ip_address.type = SL_IPV4; +#endif + + handle->server_port = config->server_port; + handle->client_port = config->client_port; + handle->data_cb = config->data_cb; + handle->remote_terminate_cb = config->remote_terminate_cb; + handle->state = SL_WEBSOCKET_STATE_DISCONNECTED; + handle->enable_ssl = config->enable_ssl; + handle->user_context = NULL; + return SL_WEBSOCKET_SUCCESS; +} + +sl_websocket_error_t sl_websocket_connect(sl_websocket_client_t *handle) +{ + // Check for null handle + if (!handle) { + return SL_WEBSOCKET_ERR_INVALID_PARAMETER; + } + + // Check if the WebSocket client is in a valid state to connect + if (handle->state != SL_WEBSOCKET_STATE_DISCONNECTED) { + SL_DEBUG_LOG("\r\nInvalid state for connecting a WebSocket client\r\n"); + return SL_WEBSOCKET_ERR_INVALID_PARAMETER; + } + + SL_DEBUG_LOG("\r\nIn websocket connect"); + + int client_socket = -1; + int socket_return_value = 0; + + handle->state = SL_WEBSOCKET_STATE_CONNECTING; + + sl_si91x_set_remote_termination_callback(handle->remote_terminate_cb); + +#ifdef SLI_SI91X_ENABLE_IPV6 + struct sockaddr_in6 server_address = { 0 }; + struct sockaddr_in6 client_address = { 0 }; + socklen_t socket_length = sizeof(struct sockaddr_in6); + client_address.sin6_family = AF_INET6; + server_address.sin6_family = AF_INET6; + server_address.sin6_port = handle->server_port; + client_address.sin6_port = handle->client_port; +#ifndef __ZEPHYR__ + memcpy(&server_address.sin6_addr.__u6_addr.__u6_addr32, + &(handle->ip_address.ip.v6.value), + sizeof(server_address.sin6_addr.__u6_addr.__u6_addr32)); +#else + memcpy(&server_address.sin6_addr.s6_addr32, + &(handle->ip_address.ip.v6.value), + sizeof(server_address.sin6_addr.s6_addr32)); +#endif + client_socket = sl_si91x_socket_async(AF_INET6, SOCK_STREAM, IPPROTO_TCP, handle->data_cb); +#else + struct sockaddr_in server_address = { 0 }; + struct sockaddr_in client_address = { 0 }; + socklen_t socket_length = sizeof(struct sockaddr_in); + client_address.sin_family = AF_INET; + server_address.sin_family = AF_INET; + server_address.sin_port = handle->server_port; + client_address.sin_port = handle->client_port; + memcpy(&server_address.sin_addr.s_addr, &(handle->ip_address.ip.v4.value), sizeof(server_address.sin_addr.s_addr)); + client_socket = sl_si91x_socket_async(AF_INET, SOCK_STREAM, IPPROTO_TCP, handle->data_cb); +#endif + + if (client_socket < 0) { + SL_DEBUG_LOG("\r\nSocket creation failed with bsd error: %d\r\n", errno); + handle->state = SL_WEBSOCKET_STATE_DISCONNECTED; + return SL_WEBSOCKET_ERR_SOCKET_CREATION; + } + + SL_DEBUG_LOG("\r\nClient Socket ID : %d\r\n", client_socket); + handle->socket_fd = client_socket; + + if (handle->enable_ssl) { + socket_return_value = sl_si91x_setsockopt(client_socket, SOL_TCP, TCP_ULP, TLS, sizeof(TLS)); + if (socket_return_value < 0) { + SL_DEBUG_LOG("\r\nSet socket failed with bsd error: %d\r\n", errno); + sli_si91x_shutdown(client_socket, SHUTDOWN_BY_ID); + return SL_WEBSOCKET_ERR_SSL_SETSOCKOPT; + } + } + + socket_return_value = sl_si91x_bind(client_socket, (struct sockaddr *)&client_address, socket_length); + if (socket_return_value < 0) { + SL_DEBUG_LOG("\r\nSocket bind failed with bsd error: %d\r\n", errno); + sli_si91x_shutdown(client_socket, SHUTDOWN_BY_ID); + handle->state = SL_WEBSOCKET_STATE_DISCONNECTED; + return SL_WEBSOCKET_ERR_SOCKET_BIND; + } + + // Retrieve the socket using the socket index + sli_si91x_socket_t *si91x_socket = sli_get_si91x_socket(client_socket); + if (!si91x_socket) { + SL_DEBUG_LOG("\r\nFailed to retrieve si91x socket\r\n"); + sli_si91x_shutdown(client_socket, SHUTDOWN_BY_ID); + handle->state = SL_WEBSOCKET_STATE_DISCONNECTED; + return SL_WEBSOCKET_ERR_SOCKET_CREATION; + } + si91x_socket->ssl_bitmap |= SLI_SI91X_WEBSOCKET_FEAT; + + // Copy the host name and resource name from handle to si91x_socket->websocket_info + size_t host_length = strlen(handle->host); + size_t resource_length = strlen(handle->resource); + + // Allocate memory for websocket_info + si91x_socket->websocket_info = + (sli_si91x_websocket_info_t *)malloc(sizeof(sli_si91x_websocket_info_t) + host_length + resource_length); + + // Check if memory allocation was successful + if (si91x_socket->websocket_info == NULL) { + SL_DEBUG_LOG("\r\nMemory allocation for websocket_info failed\r\n"); + sli_si91x_shutdown(client_socket, SHUTDOWN_BY_ID); + handle->state = SL_WEBSOCKET_STATE_DISCONNECTED; + return SL_WEBSOCKET_ERR_SOCKET_CREATION; + } + + // Set the lengths + si91x_socket->websocket_info->host_length = host_length; + si91x_socket->websocket_info->resource_length = resource_length; + + // Copy the host and resource names to websocket_info + memcpy(si91x_socket->websocket_info->websocket_data, handle->host, host_length); + memcpy(si91x_socket->websocket_info->websocket_data + host_length, handle->resource, resource_length); + + socket_return_value = sli_si91x_connect(client_socket, (struct sockaddr *)&server_address, socket_length); + if (socket_return_value < 0) { + SL_DEBUG_LOG("\r\nSocket Connect failed with bsd error: %d\r\n", errno); + sli_si91x_shutdown(client_socket, SHUTDOWN_BY_ID); + handle->state = SL_WEBSOCKET_STATE_DISCONNECTED; + return SL_WEBSOCKET_ERR_SOCKET_CONNECT; + } + SL_DEBUG_LOG("\r\nSocket connected to TCP server\r\n"); + + handle->state = SL_WEBSOCKET_STATE_CONNECTED; + return SL_WEBSOCKET_SUCCESS; +} + +sl_websocket_error_t sl_websocket_send_frame(sl_websocket_client_t *handle, + const sl_websocket_send_request_t *send_request) +{ + // Check for null handle + if (!handle) { + return SL_WEBSOCKET_ERR_INVALID_PARAMETER; + } + + // Check for null send_request + if (!send_request) { + return SL_WEBSOCKET_ERR_INVALID_PARAMETER; + } + + // Check if the WebSocket client is in a valid state to send a frame + if (handle->state != SL_WEBSOCKET_STATE_CONNECTED) { + SL_DEBUG_LOG("\r\nInvalid state for sending a WebSocket frame\r\n"); + return SL_WEBSOCKET_ERR_INVALID_PARAMETER; + } + + // Retrieve the socket using the socket index + sli_si91x_socket_t *si91x_socket = sli_get_si91x_socket(handle->socket_fd); + if (!si91x_socket) { + SL_DEBUG_LOG("\r\nFailed to retrieve socket\r\n"); + return SL_WEBSOCKET_ERR_SOCKET_CREATION; + } + + // Set the opcode for the WebSocket frame + si91x_socket->opcode = send_request->opcode; + + // Send the WebSocket frame + int sent_bytes = sl_si91x_send(handle->socket_fd, send_request->buffer, send_request->length, 0); + if (sent_bytes < 0) { + if (errno == ENOBUFS) { + return SL_WEBSOCKET_SUCCESS; + } + SL_DEBUG_LOG("\r\nFailed to send WebSocket frame with error: %d\r\n", errno); + return SL_WEBSOCKET_ERR_SEND_FRAME; + } + + SL_DEBUG_LOG("\r\nSent bytes: %d\r\n", sent_bytes); + return SL_WEBSOCKET_SUCCESS; +} + +sl_websocket_error_t sl_websocket_close(sl_websocket_client_t *handle) +{ + if (!handle) { + return SL_WEBSOCKET_ERR_INVALID_PARAMETER; + } + + // Check if the WebSocket client is in a valid state to be closed + if (handle->state != SL_WEBSOCKET_STATE_CONNECTED && handle->state != SL_WEBSOCKET_STATE_CLOSING) { + SL_DEBUG_LOG("\r\nInvalid state for closing the WebSocket connection\r\n"); + return SL_WEBSOCKET_ERR_INVALID_PARAMETER; + } + + // Update state to closing + handle->state = SL_WEBSOCKET_STATE_CLOSING; + SL_DEBUG_LOG("\r\nAttempting to close socket with fd: %d\r\n", handle->socket_fd); + int status = sli_si91x_shutdown(handle->socket_fd, SHUTDOWN_BY_ID); + if (status == 0) { + SL_DEBUG_LOG("\r\nSocket closed in FW"); + handle->socket_fd = -1; // Invalidate the socket file descriptor + handle->state = SL_WEBSOCKET_STATE_CLOSED; // Update state to closed + return SL_WEBSOCKET_SUCCESS; + } else { + SL_DEBUG_LOG("\r\nSocket closed in FW failed with error: %d\r\n", errno); + return SL_WEBSOCKET_ERR_CLOSE_FRAME; + } +} + +sl_websocket_error_t sl_websocket_deinit(sl_websocket_client_t *handle) +{ + if (!handle) { + return SL_WEBSOCKET_ERR_INVALID_PARAMETER; + } + + // Check if the socket is closed + if (handle->state != SL_WEBSOCKET_STATE_CLOSED) { + SL_DEBUG_LOG("\r\nSocket is not closed. Deinit can only be called if the socket is closed.\r\n"); + return SL_WEBSOCKET_ERR_INVALID_PARAMETER; + } + + // Close the WebSocket connection if it's still open + if (handle->socket_fd >= 0) { + SL_DEBUG_LOG("\r\nDeinit: Closing socket with fd: %d\r\n", handle->socket_fd); + sli_si91x_shutdown(handle->socket_fd, SHUTDOWN_BY_ID); + handle->socket_fd = -1; + } + + // Free the allocated memory for websocket_info + sli_si91x_socket_t *si91x_socket = sli_get_si91x_socket(handle->socket_fd); + if (si91x_socket && si91x_socket->websocket_info) { + free(si91x_socket->websocket_info); + si91x_socket->websocket_info = NULL; + } + + // Set the handle's state to disconnected + handle->state = SL_WEBSOCKET_STATE_DISCONNECTED; + + SL_DEBUG_LOG("\r\nWebSocket deinit success\r\n"); + return SL_WEBSOCKET_SUCCESS; +} + +sl_websocket_opcode_t sl_si91x_get_opcode_from_socket_id(uint16_t socket_id) +{ + // Extract the second byte of the socket_id + uint8_t websocket_info = (socket_id >> 8) & 0xFF; + + // Extract the first 4 bits to get the opcode + sl_websocket_opcode_t opcode = (sl_websocket_opcode_t)(websocket_info & 0x0F); + + return opcode; +} diff --git a/wiseconnect/components/service/sl_websocket_client/src/sli_websocket_client_sync.c b/wiseconnect/components/service/sl_websocket_client/src/sli_websocket_client_sync.c new file mode 100644 index 000000000..a0265dd3a --- /dev/null +++ b/wiseconnect/components/service/sl_websocket_client/src/sli_websocket_client_sync.c @@ -0,0 +1,194 @@ +/***************************************************************************/ /** +* @file +* @brief WebSocket Client - Synchronous Socket +******************************************************************************* +* # License +* Copyright 2025 Silicon Laboratories Inc. www.silabs.com +******************************************************************************* +* +* SPDX-License-Identifier: Zlib +* +* The licensor of this software is Silicon Laboratories Inc. +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +* +******************************************************************************/ +#include "sl_websocket_client.h" +#include "sl_websocket_client_types.h" +#include "sli_websocket_client_sync.h" +#include "sl_constants.h" +#include "sl_slist.h" +#include "sl_net.h" +#include "sl_net_constants.h" +#include "sl_wifi_constants.h" +#include "sl_si91x_constants.h" +#include "sl_si91x_driver.h" +#include "sl_si91x_protocol_types.h" +#include "sl_si91x_socket_utility.h" +#include "sl_si91x_socket_constants.h" +#include "sl_si91x_socket.h" +#ifndef __ZEPHYR__ +#include "socket.h" +#else +#include +#endif +#include "sli_net_utility.h" +#include +#include +#include + +sl_websocket_error_t sli_websocket_connect_sync(sl_websocket_client_t *client) +{ + // Validate the WebSocket client + if (!client) { + return SL_WEBSOCKET_ERR_INVALID_PARAMETER; + } + + // Ensure the WebSocket client is in the correct state + if (client->state != SL_WEBSOCKET_STATE_DISCONNECTED) { + SL_DEBUG_LOG("\r\nWebSocket client is not in a disconnected state\r\n"); + return SL_WEBSOCKET_ERR_INVALID_PARAMETER; + } + + SL_DEBUG_LOG("\r\nStarting synchronous WebSocket connection"); + + int sock_fd = -1; + int sock_result = 0; + + // Set callbacks to NULL for synchronous operation + client->data_cb = NULL; + client->remote_terminate_cb = NULL; + + client->state = SL_WEBSOCKET_STATE_CONNECTING; + +#ifdef SLI_SI91X_ENABLE_IPV6 + struct sockaddr_in6 server_address = { 0 }; + struct sockaddr_in6 client_address = { 0 }; + socklen_t socket_length = sizeof(struct sockaddr_in6); + client_address.sin6_family = AF_INET6; + server_address.sin6_family = AF_INET6; + server_address.sin6_port = client->server_port; + client_address.sin6_port = client->client_port; +#ifndef __ZEPHYR__ + memcpy(&server_address.sin6_addr.__u6_addr.__u6_addr32, + &(client->ip_address.ip.v6.value), + sizeof(server_address.sin6_addr.__u6_addr.__u6_addr32)); +#else + memcpy(&server_address.sin6_addr.s6_addr32, + &(client->ip_address.ip.v6.value), + sizeof(server_address.sin6_addr.s6_addr32)); +#endif + sock_fd = sl_si91x_socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); +#else + struct sockaddr_in server_address = { 0 }; + struct sockaddr_in client_address = { 0 }; + socklen_t socket_length = sizeof(struct sockaddr_in); + client_address.sin_family = AF_INET; + server_address.sin_family = AF_INET; + server_address.sin_port = client->server_port; + client_address.sin_port = client->client_port; + memcpy(&server_address.sin_addr.s_addr, &(client->ip_address.ip.v4.value), sizeof(server_address.sin_addr.s_addr)); + sock_fd = sl_si91x_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); +#endif + + if (sock_fd < 0) { + SL_DEBUG_LOG("\r\nFailed to create socket, error: %d\r\n", errno); + client->state = SL_WEBSOCKET_STATE_DISCONNECTED; + return SL_WEBSOCKET_ERR_SOCKET_CREATION; + } + + SL_DEBUG_LOG("\r\nClient Socket created successfully, ID %d\r\n", sock_fd); + client->socket_fd = sock_fd; + + if (client->enable_ssl) { + sock_result = setsockopt(sock_fd, SOL_TCP, TCP_ULP, TLS, sizeof(TLS)); + if (sock_result < 0) { + SL_DEBUG_LOG("\r\nFailed to set SSL options, error: %d\r\n", errno); + close(sock_fd); + return SL_WEBSOCKET_ERR_SSL_SETSOCKOPT; + } + } + + sock_result = sl_si91x_bind(sock_fd, (struct sockaddr *)&client_address, socket_length); + if (sock_result < 0) { + SL_DEBUG_LOG("\r\nSocket bind failed, error: %d\r\n", errno); + close(sock_fd); + client->state = SL_WEBSOCKET_STATE_DISCONNECTED; + return SL_WEBSOCKET_ERR_SOCKET_BIND; + } + + // Retrieve the socket using the socket index + sli_si91x_socket_t *si91x_socket = sli_get_si91x_socket(sock_fd); + if (!si91x_socket) { + SL_DEBUG_LOG("\r\nUnable to retrieve socket information\r\n"); + close(sock_fd); + client->state = SL_WEBSOCKET_STATE_DISCONNECTED; + return SL_WEBSOCKET_ERR_SOCKET_CREATION; + } + si91x_socket->ssl_bitmap |= SLI_SI91X_WEBSOCKET_FEAT; + + // Copy the host name,resource name and subprotocol name from client to si91x_socket->websocket_info + size_t host_length = strlen(client->host); + size_t resource_length = strlen(client->resource); + size_t subprotocol_length = strlen(client->subprotocol); + + // Allocate memory for websocket_info + si91x_socket->websocket_info = (sli_si91x_websocket_info_t *)malloc(sizeof(sli_si91x_websocket_info_t) + host_length + + resource_length + subprotocol_length); + + if (si91x_socket->websocket_info == NULL) { + SL_DEBUG_LOG("\r\nMemory allocation for WebSocket info failed\r\n"); + close(sock_fd); + client->state = SL_WEBSOCKET_STATE_DISCONNECTED; + return SL_WEBSOCKET_ERR_SOCKET_CREATION; + } + + // Set lengths and copy data + si91x_socket->websocket_info->host_length = host_length; + si91x_socket->websocket_info->resource_length = resource_length; + si91x_socket->websocket_info->subprotocol_length = subprotocol_length; + + // Copy the host, resource name and subprotocol name to websocket_info + memcpy(si91x_socket->websocket_info->websocket_data, client->host, host_length); + memcpy(si91x_socket->websocket_info->websocket_data + host_length, client->resource, resource_length); + memcpy(si91x_socket->websocket_info->websocket_data + host_length + resource_length, + client->subprotocol, + subprotocol_length); + + sock_result = connect(sock_fd, (struct sockaddr *)&server_address, socket_length); + if (sock_result < 0) { + SL_DEBUG_LOG("\r\nFailed to connect socket, error: %d\r\n", errno); + close(sock_fd); + client->state = SL_WEBSOCKET_STATE_DISCONNECTED; + return SL_WEBSOCKET_ERR_SOCKET_CONNECT; + } + SL_DEBUG_LOG("\r\nSocket successfully connected to the server\r\n"); + + client->state = SL_WEBSOCKET_STATE_CONNECTED; + return SL_WEBSOCKET_SUCCESS; +} + +// Sets the subprotocol for the WebSocket client. +sl_status_t sli_websocket_set_subprotocol(sl_websocket_client_t *client, const char *subprotocol) +{ + if (client == NULL || strlen(subprotocol) >= SLI_WEBS_MAX_SUBPROTOCOL_LENGTH) { + return SL_STATUS_FAIL; + } + strncpy(client->subprotocol, subprotocol, SLI_WEBS_MAX_SUBPROTOCOL_LENGTH - 1); + client->subprotocol[SLI_WEBS_MAX_SUBPROTOCOL_LENGTH - 1] = '\0'; // Ensure null termination + return SL_STATUS_OK; +} diff --git a/wiseconnect/components/service/sntp/inc/sl_sntp.h b/wiseconnect/components/service/sntp/inc/sl_sntp.h new file mode 100644 index 000000000..9479dff53 --- /dev/null +++ b/wiseconnect/components/service/sntp/inc/sl_sntp.h @@ -0,0 +1,365 @@ +/***************************************************************************/ /** + * @file sl_sntp.h + ******************************************************************************* + * # License + * Copyright 2025 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ +#pragma once + +#include "sl_status.h" +#include "sl_common.h" +#include +#include + +/****************************************************** + * Macros + ******************************************************/ + +/****************************************************** + * Constants + ******************************************************/ + +/** + * @addtogroup SERVICE_SNTP_CONSTANTS + * @{ + */ + +/** + * @addtogroup SERVICE_SNTP_METHODS SNTP Methods + * @{ + */ +/** + * @def SL_SNTP_BROADCAST_MODE + * @brief + * Enable SNTP for broadcast mode. + * + * @details + * In the broadcast mode, the server periodically sends broadcast messages to the client, and the client receives the messages from the server. + * + * @note + * To use broadcast mode, the client, and the SNTP server must be in the same subnet. + */ +#define SL_SNTP_BROADCAST_MODE 1 + +/** + * @def SL_SNTP_UNICAST_MODE + * @brief + * Enable SNTP for unicast mode. + * + * @details + * In the unicast mode, the client initiates a request to the server. After receiving the request, the server constructs a response message based on the local time and sends the response message back to the client. + */ +#define SL_SNTP_UNICAST_MODE 2 +/** @} */ + +/** + * @addtogroup SERVICE_SNTP_FLAGS SNTP Flags + * @{ + */ +/** + * @def SL_SNTP_ENABLE_IPV6 + * @brief + * Enables IPv6 for SNTP client. + * + * @details + * This macro enables IPv6 support for the SNTP client. By default, IPv4 is enabled. + * + * @note + * - 1: Enable IPv6 support. + * - 0: Disable IPv6 support. + */ +#define SL_SNTP_ENABLE_IPV6 1 +/** @} */ + +/****************************************************** + * Enumerations + ******************************************************/ + +/** + * @enum sl_sntp_client_events_t + * @brief + * Enumeration of SNTP client events. + * + * @details + * The enumeration defines the various events that the SNTP client can trigger. These events are used to notify the application about the status, and results of SNTP operations. + */ +typedef enum { + SL_SNTP_CLIENT_START = 1, ///< Event indicates SNTP client start. + SL_SNTP_CLIENT_GET_TIME, ///< Event indicates the SNTP client retrieves the current time. + SL_SNTP_CLIENT_GET_TIME_DATE, ///< Event indicates the SNTP client retrieves the current date and time. + SL_SNTP_CLIENT_GET_SERVER_INFO, ///< Event indicates the SNTP client retrieves server information. + SL_SNTP_CLIENT_STOP ///< Event indicates SNTP client stop. +} sl_sntp_client_events_t; + +/** @} */ + +/****************************************************** + * Type Definitions + ******************************************************/ + +/** + * @addtogroup SERVICE_SNTP_TYPES + * @{ + */ + +/** + * @brief + * Structure to hold SNTP Server Information response. + * + * @details + * This structure contains the information about the SNTP server, including the command type, IP version, server IP address, and SNTP method. + */ +typedef struct { + uint8_t command_type; ///< Command type indicating the type of SNTP command. + uint8_t ip_version; ///< IP version of the SNTP server. 4 for IPv4 and 6 for IPv6. + union { + uint8_t ipv4_address[4]; ///< IPv4 address of the SNTP server. + unsigned long ipv6_address[4]; ///< IPv6 address of the SNTP server. + } SL_ATTRIBUTE_PACKED server_ip_address; ///< Server IP address, either IPv4 or IPv6. + uint8_t sntp_method; ///< SNTP server method used for synchronization. Supported methods: + ///< - 1: Broadcast mode + ///< - 2: Unicast mode +} SL_ATTRIBUTE_PACKED sl_sntp_server_info_t; + +/** + * @brief + * Structure to hold SNTP client response. + * + * @details + * The structure contains the response information from the SNTP client, including the event type, status, response data, and the length of the response data. + */ +typedef struct { + uint8_t event_type; ///< Type of event being received, indicated by @ref sl_sntp_client_events_t. + sl_status_t + status; ///< Status of the call which triggered this response. See https://docs.silabs.com/gecko-platform/latest/platform-common/status for details. + uint8_t * + data; ///< Pointer to response data. For SL_SNTP_CLIENT_GET_TIME and SL_SNTP_CLIENT_GET_TIME_DATE events, it is a (uint8_t *) buffer. For SL_SNTP_CLIENT_GET_SERVER_INFO event, it is a pointer to an sl_sntp_server_info_t structure. + uint16_t data_length; ///< Length of the response data. +} sl_sntp_client_response_t; + +/** + * @typedef sl_sntp_client_event_handler_t + * @brief + * Callback type for handling SNTP client responses. + * + * @details + * This callback is invoked to handle responses from the SNTP client. It provides the response data, user data, and the length of the user data. + * + * @param[in] response + * Pointer to the SNTP client response of type @ref sl_sntp_client_response_t. + * + * @param[in] user_data + * Pointer to user data passed in the APIs @ref sl_sntp_client_get_time(), @ref sl_sntp_client_get_time_date(), and @ref sl_sntp_client_get_server_info(). + * + * @param[in] user_data_length + * Length of the user data passed in the APIs @ref sl_sntp_client_get_time(), @ref sl_sntp_client_get_time_date(), and @ref sl_sntp_client_get_server_info(). + */ +typedef void (*sl_sntp_client_event_handler_t)(sl_sntp_client_response_t *response, + uint8_t *user_data, + uint16_t user_data_length); + +/** + * @typedef sl_sntp_set_time_sync_notification_handler_t + * @brief + * Callback type for handling SNTP time synchronization notifications. + * + * @details + * This callback is invoked to handle notifications related to SNTP time synchronization. It provides the command type, status of the operation, and a data buffer. + * + * @param[in] cmd_type + * Command type indicating the type of SNTP operation. + * + * @param[in] status + * Status of the SNTP operation. See https://docs.silabs.com/gecko-platform/latest/platform-common/status for details. + * + * @param[in] buffer + * Pointer to the data buffer containing additional information related to the SNTP operation. + * + * @note + * This feature is currently not supported in SiWx91x chipsets. + */ +typedef void (*sl_sntp_set_time_sync_notification_handler_t)(const uint8_t cmd_type, + sl_status_t status, + const uint8_t *buffer); + +/** + * @brief + * SNTP client configuration structure. + * + * @details + * This structure holds the configuration parameters for the SNTP client, including server details, synchronization methods, timeouts, and various flags. + */ +typedef struct { + uint8_t *server_host_name; ///< SNTP server host name or address. + uint8_t sntp_method; ///< SNTP method. One of the defines from @ref SERVICE_SNTP_METHODS. + uint16_t sntp_timeout; ///< SNTP operation timeout in milliseconds (ms). + uint8_t flags; ///< SNTP flags. Combination of values from @ref SERVICE_SNTP_FLAGS. + ///< @note Bits 1-7 are reserved. User must set them to 0. + sl_sntp_client_event_handler_t event_handler; ///< SNTP response handler of type @ref sl_sntp_client_event_handler_t. + sl_sntp_set_time_sync_notification_handler_t + time_sync_notifiication_handler; ///< Time synchronization notification handler of type @ref sl_sntp_set_time_sync_notification_handler_t. + ///< @note This feature is currently not supported in SiWx91x chipsets. + bool smooth_sync; ///< Set to true if smooth synchronization is required. + ///< @note This feature is currently not supported in SiWx91x chipsets. + bool server_from_dhcp; ///< Set to true to request NTP server configuration from DHCP. + ///< @note This feature is currently not supported in SiWx91x chipsets. + bool renew_servers_after_new_ip; ///< Set to true to refresh the server list if NTP is provided by DHCP. + ///< @note This feature is currently not supported in SiWx91x chipsets. +} sl_sntp_client_config_t; +/** @} */ + +/** + * @addtogroup SERVICE_SNTP_FUNCTIONS + * @{ + */ + +/** + * @brief + * Start the SNTP client. + * + * @details + * This function initializes and starts the SNTP client with the provided configuration. It verifies the input parameters, sets up the client request, and sends the command to start the SNTP client. The function can operate in both blocking and non-blocking modes based on the timeout value. + * + * @param[in] config + * A valid pointer to the client configuration structure of type @ref sl_sntp_client_config_t. This parameter must not be null. + * + * @param[in] timeout + * The timeout for starting the SNTP client. If the timeout is greater than 0, this function operates in blocking mode. Otherwise, results are returned via the @ref sl_sntp_client_event_handler_t callback. + * + * @return + * sl_status_t - Status of the operation. For more details, see https://docs.silabs.com/gecko-platform/latest/platform-common/status. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_FAIL: Operation failed. + * - SL_STATUS_INVALID_PARAMETER: Invalid input parameter. + * @note + * This API needs to be called before calling any other SNTP API. + */ +sl_status_t sl_sntp_client_start(sl_sntp_client_config_t *config, uint32_t timeout); + +/** + * @brief + * Get the current NTP epoch time in seconds. + * + * @details + * The function retrieves the current NTP epoch time. It operates in synchronous and asynchronous mode based on the timeout value. + * - **Synchronous Mode:** If the timeout value is non-zero then, function waits for the response until the timeout occurs. It returns the value in the provided data buffer. + * - **Asynchronous Mode:** If the timeout value is zero then, function returns immediately, It returns the value via the @ref sl_sntp_client_event_handler_t callback. + * + * @param[in] data + * A valid pointer to a data buffer which should be greater than or equal to 50 bytes. In synchronous mode, the time is returned in this buffer in string format. In asynchronous mode, this parameter is given to the user in the user_data parameter of the event handler. + * + * @param[in] data_length + * The length of the data buffer. In asynchronous mode, this parameter is given to the user in the user_data_length parameter of the event handler. + * + * @param[in] timeout + * The timeout for getting the time. If the timeout is greater than 0, this function operates in blocking mode. Otherwise, results are returned via the @ref sl_sntp_client_event_handler_t callback. + * + * @return + * sl_status_t - Status of the operation. For more details, see https://docs.silabs.com/gecko-platform/latest/platform-common/status. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_FAIL: Operation failed. + * - SL_STATUS_INVALID_PARAMETER: Invalid input parameter. + * + * @note + * This API must be called after the SNTP client has started using @ref sl_sntp_client_start. + */ +sl_status_t sl_sntp_client_get_time(uint8_t *data, uint16_t data_length, uint32_t timeout); + +/** + * @brief + * Get time and date information from NTP. + * + * @details + * The function retrieves the current NTP epoch time and date. It operates in synchronous, and asynchronous mode based on the timeout value. + * - **Synchronous Mode:** If the timeout value is non-zero then, function waits for the response until the timeout occurs. It returns the value in the provided data buffer. + * - **Asynchronous Mode:** If the timeout value is zero then, function returns immediately. It returns the value via the @ref sl_sntp_client_event_handler_t callback. + * + * @param[in] data + * A valid pointer to a data buffer must have value greater than or equal to 50 bytes. In synchronous mode, the time and date parameters are return in string format in the buffer. In asynchronous mode, the parameter is specified by the user via a user_data parameter of the event handler. + * + * @param[in] data_length + * The parameter captures the length of the data buffer in synchronous mode. In asynchronous mode, the parameter is stated to the user via the user_data_length parameter of the event handler. + * + * @param[in] timeout + * The timeout captures time and date.If the timeout value is greater than 0, the function operates in blocking mode else, it returns the result via the @ref sl_sntp_client_event_handler_t callback. + * + * @return + * sl_status_t - Status of the operation. For more details, see https://docs.silabs.com/gecko-platform/latest/platform-common/status. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_FAIL: Operation failed. + * - SL_STATUS_INVALID_PARAMETER: Invalid input parameter. + * + * @note + * This API must be called after the SNTP client has started using @ref sl_sntp_client_start. + */ +sl_status_t sl_sntp_client_get_time_date(uint8_t *data, uint16_t data_length, uint32_t timeout); + +/** + * @brief + * Retrieve NTP server information. + * + * @details + * The function retrieves information about the NTP server. It operates in both synchronous and asynchronous modes based on the timeout value. + * In synchronous mode, the server returns the information in the specific data buffer. + * In asynchronous mode, the result is return via the @ref sl_sntp_client_event_handler_t callback. + * + * @param[out] data + * A pointer is a sl_sntp_server_info_t type buffer where the server information is stored. In asynchronous mode, the parameter is passed to the user in the user_data parameter of the event handler. + * In synchronous mode the server information is returned in this data buffer. + * @param[in] timeout + * The timeout receives server information. If the timeout is greater than 0, the function operates in blocking mode else, it would return the result via the @ref sl_sntp_client_event_handler_t callback. + * + * @return + * sl_status_t - Status of the operation. For more details, see https://docs.silabs.com/gecko-platform/latest/platform-common/status. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_FAIL: Operation failed. + * - SL_STATUS_INVALID_PARAMETER: Invalid input parameter. + * + * @note + * This API must be called after the SNTP client has been started using @ref sl_sntp_client_start. + */ +sl_status_t sl_sntp_client_get_server_info(sl_sntp_server_info_t *data, uint32_t timeout); + +/** + * @brief + * Stop the SNTP client. + * + * @details + * The function stops the SNTP client. It operates in both synchronous and asynchronous modes based on the timeout value. In synchronous mode, the function blocks until the client stops or the timeout occurs. In asynchronous mode, the results are returned via the @ref sl_sntp_client_event_handler_t callback. + * + * @param[in] timeout + * The timeout to stop the SNTP client. The function operates in blocking mode if the timeout value is greater than 0 else, results are returned via the @ref sl_sntp_client_event_handler_t callback. + * + * @return + * sl_status_t - Status of the operation. For more details, see https://docs.silabs.com/gecko-platform/latest/platform-common/status. + * - SL_STATUS_OK: Operation successful. + * - SL_STATUS_FAIL: Operation failed. + * - SL_STATUS_INVALID_PARAMETER: Invalid input parameter. + * + * @note + * This API must be called after the SNTP client has started using @ref sl_sntp_client_start. + */ +sl_status_t sl_sntp_client_stop(uint32_t timeout); +/** @} */ diff --git a/wiseconnect/components/service/sntp/si91x/sl_sntp.c b/wiseconnect/components/service/sntp/si91x/sl_sntp.c new file mode 100644 index 000000000..90e487d31 --- /dev/null +++ b/wiseconnect/components/service/sntp/si91x/sl_sntp.c @@ -0,0 +1,305 @@ +/***************************************************************************/ /** + * @file sl_sntp.c + ******************************************************************************* + * # License + * Copyright 2025 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Zlib + * + * The licensor of this software is Silicon Laboratories Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + ******************************************************************************/ + +#include "sl_slist.h" +#include "sl_si91x_driver.h" +#include "sl_status.h" +#include "sl_constants.h" +#include "sl_si91x_core_utilities.h" +#include "cmsis_os2.h" + +#include "sl_sntp.h" +#include "si91x_sntp_client_types.h" +#include + +typedef struct { + uint8_t callback_event_type; + uint8_t *data; + uint16_t data_length; +} sli_sntp_client_context_t; + +static osMutexId_t sntp_mutex; +static sl_sntp_client_event_handler_t sntp_event_handler; + +static sl_status_t sli_sntp_client_get_time_date(uint8_t *data, + uint16_t data_length, + uint32_t timeout, + uint16_t cmd_type) +{ + sl_status_t status = SL_STATUS_FAIL; + sli_si91x_sntp_client_t client_req = { 0 }; + sli_si91x_wait_period_t wait_time = 0; + sl_wifi_buffer_t *buffer = NULL; + const sl_wifi_packet_t *packet = NULL; + sl_wifi_buffer_t *sdk_context = NULL; + sli_sntp_client_context_t *node = NULL; + uint16_t length = 0; + uint16_t buffer_length = 0; + + if ((timeout > 0) && ((data == NULL) || (data_length == 0))) { + return SL_STATUS_INVALID_PARAMETER; + } + + client_req.command_type = (uint8_t)cmd_type; + + if (timeout > 0) { + wait_time = SL_SI91X_WAIT_FOR_RESPONSE(timeout); + } else { + status = + sli_si91x_host_allocate_buffer(&sdk_context, SL_WIFI_CONTROL_BUFFER, sizeof(sli_sntp_client_context_t), 1000); + VERIFY_STATUS_AND_RETURN(status); + node = sl_si91x_host_get_buffer_data(sdk_context, 0, &buffer_length); + if (cmd_type == SLI_SI91X_SNTP_CLIENT_GETTIME) { + node->callback_event_type = SL_SNTP_CLIENT_GET_TIME; + } else { + node->callback_event_type = SL_SNTP_CLIENT_GET_TIME_DATE; + } + node->data = data; + node->data_length = data_length; + wait_time = SLI_SI91X_RETURN_IMMEDIATELY; + } + + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_SNTP_CLIENT, + SLI_SI91X_NETWORK_CMD, + &client_req, + sizeof(client_req), + wait_time, + (void *)sdk_context, + &buffer); + if ((status != SL_STATUS_OK) && (buffer != NULL)) { + sli_si91x_host_free_buffer(buffer); + } + VERIFY_STATUS_AND_RETURN(status); + + packet = sl_si91x_host_get_buffer_data(buffer, 0, NULL); + if (packet->length > data_length) { + length = data_length; + } else { + length = packet->length; + } + memcpy(data, packet->data, length); + status = SL_STATUS_OK; + + sli_si91x_host_free_buffer(buffer); + return status; +} + +sl_status_t sli_si91x_sntp_event_handler(sli_si91x_queue_packet_t *data) +{ + uint16_t status = 0; + sl_sntp_client_response_t response = { 0 }; + sl_wifi_packet_t *raw_rx_packet = sl_si91x_host_get_buffer_data(data->host_packet, 0, NULL); + sl_wifi_buffer_t *sdk_context = (sl_wifi_buffer_t *)data->sdk_context; + sli_sntp_client_context_t *node = NULL; + uint16_t buffer_length = 0; + + node = (sli_sntp_client_context_t *)sl_si91x_host_get_buffer_data(sdk_context, 0, &buffer_length); + + osMutexAcquire(sntp_mutex, 0xFFFFFFFFUL); + if (NULL != sntp_event_handler) { + status = sli_get_si91x_frame_status(raw_rx_packet); + response.event_type = node->callback_event_type; + response.status = sli_convert_and_save_firmware_status(status); + response.data = raw_rx_packet->data; + response.data_length = raw_rx_packet->length; + + sntp_event_handler(&response, node->data, node->data_length); + } + osMutexRelease(sntp_mutex); + + sli_si91x_host_free_buffer(sdk_context); + return SL_STATUS_OK; +} + +sl_status_t sl_sntp_client_start(sl_sntp_client_config_t *config, uint32_t timeout) +{ + sl_status_t status = SL_STATUS_FAIL; + sli_si91x_sntp_client_t client_req = { 0 }; + sli_si91x_wait_period_t wait_time = 0; + sl_wifi_buffer_t *sdk_context = NULL; + sli_sntp_client_context_t *node = NULL; + uint16_t buffer_length = 0; + + if (NULL == config) { + return SL_STATUS_INVALID_PARAMETER; + } + + if (NULL == config->server_host_name) { + return SL_STATUS_INVALID_PARAMETER; + } + + if ((SL_SNTP_BROADCAST_MODE != config->sntp_method) && (SL_SNTP_UNICAST_MODE != config->sntp_method)) { + return SL_STATUS_INVALID_PARAMETER; + } + + if (NULL == sntp_mutex) { + sntp_mutex = osMutexNew(NULL); + } + + osMutexAcquire(sntp_mutex, 0xFFFFFFFFUL); + sntp_event_handler = config->event_handler; + osMutexRelease(sntp_mutex); + + client_req.command_type = SLI_SI91X_SNTP_CLIENT_CREATE; + client_req.sntp_method = config->sntp_method; + *((uint16_t *)client_req.sntp_timeout) = config->sntp_timeout; + + // Check for IP version + if (config->flags & 0x01) { + client_req.ip_version = 6; + memcpy(client_req.server_ip_address.ipv6_address, config->server_host_name, 16); + } else { + client_req.ip_version = 4; + memcpy(client_req.server_ip_address.ipv4_address, config->server_host_name, 4); + } + + if (timeout > 0) { + wait_time = SL_SI91X_WAIT_FOR_RESPONSE(timeout); + } else { + status = + sli_si91x_host_allocate_buffer(&sdk_context, SL_WIFI_CONTROL_BUFFER, sizeof(sli_sntp_client_context_t), 1000); + VERIFY_STATUS_AND_RETURN(status); + node = sl_si91x_host_get_buffer_data(sdk_context, 0, &buffer_length); + node->callback_event_type = SL_SNTP_CLIENT_START; + node->data = NULL; + node->data_length = 0; + wait_time = SLI_SI91X_RETURN_IMMEDIATELY; + } + + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_SNTP_CLIENT, + SLI_SI91X_NETWORK_CMD, + &client_req, + sizeof(client_req), + wait_time, + (void *)sdk_context, + NULL); + VERIFY_STATUS_AND_RETURN(status); + return status; +} + +sl_status_t sl_sntp_client_get_time(uint8_t *data, uint16_t data_length, uint32_t timeout) +{ + return sli_sntp_client_get_time_date(data, data_length, timeout, SLI_SI91X_SNTP_CLIENT_GETTIME); +} + +sl_status_t sl_sntp_client_get_time_date(uint8_t *data, uint16_t data_length, uint32_t timeout) +{ + return sli_sntp_client_get_time_date(data, data_length, timeout, SLI_SI91X_SNTP_CLIENT_GETTIME_DATE); +} + +sl_status_t sl_sntp_client_get_server_info(sl_sntp_server_info_t *data, uint32_t timeout) +{ + sl_status_t status = SL_STATUS_FAIL; + sli_si91x_sntp_client_t client_req = { 0 }; + sli_si91x_wait_period_t wait_time = 0; + sl_wifi_buffer_t *buffer = NULL; + const sl_wifi_packet_t *packet = NULL; + uint16_t length = 0; + sl_wifi_buffer_t *sdk_context = NULL; + sli_sntp_client_context_t *node = NULL; + uint16_t buffer_length = 0; + + if ((timeout > 0) && (data == NULL)) { + return SL_STATUS_INVALID_PARAMETER; + } + + client_req.command_type = SLI_SI91X_SNTP_CLIENT_GET_SERVER_INFO; + + if (timeout > 0) { + wait_time = SL_SI91X_WAIT_FOR_RESPONSE(timeout); + } else { + status = + sli_si91x_host_allocate_buffer(&sdk_context, SL_WIFI_CONTROL_BUFFER, sizeof(sli_sntp_client_context_t), 1000); + VERIFY_STATUS_AND_RETURN(status); + node = sl_si91x_host_get_buffer_data(sdk_context, 0, &buffer_length); + node->callback_event_type = SL_SNTP_CLIENT_GET_SERVER_INFO; + node->data = (uint8_t *)data; + node->data_length = sizeof(sl_sntp_server_info_t); + wait_time = SLI_SI91X_RETURN_IMMEDIATELY; + } + + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_SNTP_CLIENT, + SLI_SI91X_NETWORK_CMD, + &client_req, + sizeof(client_req), + wait_time, + (void *)sdk_context, + &buffer); + if ((status != SL_STATUS_OK) && (buffer != NULL)) { + sli_si91x_host_free_buffer(buffer); + } + + VERIFY_STATUS_AND_RETURN(status); + packet = sl_si91x_host_get_buffer_data(buffer, 0, NULL); + if (packet->length > sizeof(sl_sntp_server_info_t)) { + length = sizeof(sl_sntp_server_info_t); + } else { + length = packet->length; + } + memcpy(data, packet->data, length); + status = SL_STATUS_OK; + + sli_si91x_host_free_buffer(buffer); + return status; +} + +sl_status_t sl_sntp_client_stop(uint32_t timeout) +{ + sl_status_t status = SL_STATUS_FAIL; + sli_si91x_wait_period_t wait_time = 0; + sli_si91x_sntp_client_t client_req = { 0 }; + sl_wifi_buffer_t *sdk_context = NULL; + sli_sntp_client_context_t *node = NULL; + uint16_t buffer_length = 0; + + client_req.command_type = SLI_SI91X_SNTP_CLIENT_DELETE; + + if (timeout > 0) { + wait_time = SL_SI91X_WAIT_FOR_RESPONSE(timeout); + } else { + status = + sli_si91x_host_allocate_buffer(&sdk_context, SL_WIFI_CONTROL_BUFFER, sizeof(sli_sntp_client_context_t), 1000); + VERIFY_STATUS_AND_RETURN(status); + node = sl_si91x_host_get_buffer_data(sdk_context, 0, &buffer_length); + node->callback_event_type = SL_SNTP_CLIENT_STOP; + node->data = NULL; + node->data_length = 0; + wait_time = SLI_SI91X_RETURN_IMMEDIATELY; + } + + status = sli_si91x_driver_send_command(SLI_WLAN_REQ_SNTP_CLIENT, + SLI_SI91X_NETWORK_CMD, + &client_req, + sizeof(client_req), + wait_time, + (void *)sdk_context, + NULL); + VERIFY_STATUS_AND_RETURN(status); + return status; +}