From cfb5aa8c94491f15481767ae569f39dceb9723fe Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 28 Oct 2023 15:08:41 +0200 Subject: [PATCH] drivers/winc1500: Update host driver/firmware to version 19.7.7. --- src/drivers/winc1500/firmware/winc_19_7_7.bin | Bin 0 -> 524288 bytes .../winc1500/include/bsp/include/nm_bsp.h | 226 +- .../include/bsp/include/nm_bsp_internal.h | 4 + .../bus_wrapper/include/nm_bus_wrapper.h | 26 +- .../include/common/include/nm_common.h | 226 +- .../include/common/include/nm_debug.h | 6 +- .../include/driver/include/m2m_ate_mode.h | 2 + .../include/driver/include/m2m_crypto.h | 1 - .../winc1500/include/driver/include/m2m_ota.h | 516 ++- .../include/driver/include/m2m_periph.h | 364 +- .../winc1500/include/driver/include/m2m_ssl.h | 181 +- .../include/driver/include/m2m_types.h | 3974 ++++++++--------- .../include/driver/include/m2m_wifi.h | 1081 +++-- .../socket/include/m2m_socket_host_if.h | 73 +- .../winc1500/include/socket/include/socket.h | 2856 ++++++------ src/drivers/winc1500/src/m2m_ota.c | 592 ++- src/drivers/winc1500/src/m2m_periph.c | 83 +- src/drivers/winc1500/src/m2m_ssl.c | 771 ++-- src/drivers/winc1500/src/m2m_wifi.c | 2836 ++++++------ src/drivers/winc1500/src/nm_bus_wrapper.c | 17 +- src/drivers/winc1500/src/nm_common.c | 153 +- src/drivers/winc1500/src/nmasic.c | 3 + src/drivers/winc1500/src/nmdrv.c | 12 +- src/drivers/winc1500/src/nmspi.c | 665 +-- src/drivers/winc1500/src/nmuart.c | 34 +- src/drivers/winc1500/src/socket.c | 1880 ++++---- src/drivers/winc1500/src/spi_flash.c | 17 +- 27 files changed, 8588 insertions(+), 8011 deletions(-) create mode 100755 src/drivers/winc1500/firmware/winc_19_7_7.bin diff --git a/src/drivers/winc1500/firmware/winc_19_7_7.bin b/src/drivers/winc1500/firmware/winc_19_7_7.bin new file mode 100755 index 0000000000000000000000000000000000000000..4405866c01504a4f5fe898b35d9c7bf5c65f277c GIT binary patch literal 524288 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 z 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 z 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 z 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

;oFcru>901u$Ipl7;BmahyTW@Fk^ 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 Support Package) module. @@ -51,25 +51,24 @@ #define _NM_BSP_H_ #define NMI_API -/*!< -* Attribute used to define the memory section to map Functions in host memory. -*/ +/*!< + * Attribute used to define the memory section to map Functions in host memory. + */ #define CONST const -/*!< -* Used for code portability. -*/ +/*!< + * Used for code portability. + */ #ifndef NULL #define NULL ((void*)0) #endif -/*!< +/*!< * Void Pointer to '0' in case NULL is not defined. */ - #define BSP_MIN(x,y) ((x)>(y)?(y):(x)) -/*!< +/*!< * Computes the minimum value between \b x and \b y. */ /**@}*/ //BSPDefine @@ -77,62 +76,61 @@ /**@addtogroup DataT * @{ */ - /*! - * @typedef void (*tpfNmBspIsr) (void); + * @typedef void (*tpfNmBspIsr) (void); * @brief Pointer to function.\n Used as a data type of ISR function registered by * @ref nm_bsp_register_isr. - * @return None + * @return None */ typedef void (*tpfNmBspIsr)(void); - /*! - * @ingroup DataTypes - * @typedef unsigned char uint8; - * @brief Range of values between 0 to 255 - */ -typedef unsigned char uint8; +/*! +* @ingroup DataTypes +* @typedef unsigned char uint8; +* @brief Range of values between 0 to 255 +*/ +typedef unsigned char uint8; - /*! - * @ingroup DataTypes - * @typedef unsigned short uint16; - * @brief Range of values between 0 to 65535 - */ -typedef unsigned short uint16; +/*! +* @ingroup DataTypes +* @typedef unsigned short uint16; +* @brief Range of values between 0 to 65535 +*/ +typedef unsigned short uint16; - /*! - * @ingroup Data Types - * @typedef unsigned long uint32; - * @brief Range of values between 0 to 4294967295 - */ -typedef unsigned long uint32; +/*! +* @ingroup Data Types +* @typedef unsigned long uint32; +* @brief Range of values between 0 to 4294967295 +*/ +typedef unsigned long uint32; - /*! - * @ingroup Data Types - * @typedef signed char sint8; - * @brief Range of values between -128 to 127 - */ -typedef signed char sint8; +/*! +* @ingroup Data Types +* @typedef signed char sint8; +* @brief Range of values between -128 to 127 +*/ +typedef signed char sint8; - /*! - * @ingroup DataTypes - * @typedef signed short sint16; - * @brief Range of values between -32768 to 32767 - */ -typedef signed short sint16; +/*! +* @ingroup DataTypes +* @typedef signed short sint16; +* @brief Range of values between -32768 to 32767 +*/ +typedef signed short sint16; - /*! - * @ingroup DataTypes - * @typedef signed long sint32; - * @brief Range of values between -2147483648 to 2147483647 - */ -typedef signed long sint32; +/*! +* @ingroup DataTypes +* @typedef signed long sint32; +* @brief Range of values between -2147483648 to 2147483647 +*/ +typedef signed long sint32; /**@}*/ //DataTypes #ifndef CORTUS_APP #ifdef __cplusplus -extern "C"{ +extern "C" { #endif /** @defgroup NmBspInitFn nm_bsp_init @@ -141,10 +139,10 @@ extern "C"{ * such as Reset and Chip Enable Pins for WINC, delays, register ISR, enable/disable IRQ for WINC, etc. * You must use this function at the head of your application to enable WINC and Host Driver to * communicate with each other. + * @{ */ - /**@{*/ /*! - * @fn sint8 nm_bsp_init(void); + * @fn sint8 nm_bsp_init(void); * @brief This function is used to initialize the Board Support * Package (BSP) in order to prepare the WINC before any * WINC API calls. @@ -156,50 +154,49 @@ extern "C"{ * initializes the WINC, a hard reset must be applied to start the WINC board by calling * @ref nm_bsp_reset. * - * @note Implementation of this function is host dependent. - * @warning Inappropriate use of this function will lead to unavailability of host-chip communication. - * - * @see nm_bsp_deinit, nm_bsp_reset + * @note Implementation of this function is host dependent. + * @warning Omitting this function will lead to unavailability of host-chip communication. + * + * @see nm_bsp_deinit, nm_bsp_reset * @return The function returns @ref M2M_SUCCESS for successful operations and a negative value otherwise. */ sint8 nm_bsp_init(void); /**@}*/ //NmBspInitFn - - /** @defgroup NmBspDeinitFn nm_bsp_deinit - * @ingroup BSPAPI + +/** @defgroup NmBspDeinitFn nm_bsp_deinit + * @ingroup BSPAPI * De-initialization of the BSP (Board Support Package).\n * This function should be called only after a successful call to @ref nm_bsp_init. + * @{ */ - /**@{*/ /*! - * @fn sint8 nm_bsp_deinit(void); - * @brief This function is used to de-initialize the BSP and turn off the WINC board. + * @fn sint8 nm_bsp_deinit(void); + * @brief This function is used to de-initialize the BSP and turn off the WINC board. * @details The nm_bsp_deinit is the last function that should be called after the application has * finished and before the WINC is switched off. A call to this function will turn off the WINC * board by setting CHIP_EN and RESET_N signals low. Every function call of @ref nm_bsp_init * should be matched with a call to nm_bsp_deinit. Failure to do so may result in the WINC * consuming higher power than expected, since it won't be properly de-initialized. * @pre The BSP should be initialized through @ref nm_bsp_init first. - * @note Implementation of this function is host dependent. - * @warning Misuse may lead to unknown behavior in case of soft reset. - * @see nm_bsp_init + * @note Implementation of this function is host dependent. + * @warning Omitting this function may lead to unknown behavior in case of soft reset. + * @see nm_bsp_init * @return The function returns @ref M2M_SUCCESS for successful operations and a negative value otherwise. */ sint8 nm_bsp_deinit(void); /**@}*/ //NmBspDeinitFn - /** @defgroup NmBspResetFn nm_bsp_reset -* @ingroup BSPAPI + * @ingroup BSPAPI * Resets the WINC SoC by setting CHIP_EN and RESET_N signals low, then after an appropriate delay, * this function will put CHIP_EN high then RESET_N high, for more details on the timing between signals * please check the WINC data-sheet. -*/ -/**@{*/ - /*! - * @fn void nm_bsp_reset(void); + * @{ + */ +/*! + * @fn void nm_bsp_reset(void); * @brief Performs a hardware reset to the WINC board. * @details The nm_bsp_reset is used to perform a hard reset on the WINC board by setting CHIP_EN and * RESET_N signals low, then after an appropriate delay this function puts CHIP_EN high then @@ -212,81 +209,77 @@ sint8 nm_bsp_deinit(void); * de-initialized before calling nm_bsp_reset and initialized again, which can be achieved by * use of @ref m2m_wifi_init and @ref m2m_wifi_deinit. * @pre Initialize the BSP first by calling @ref nm_bsp_init. - * @note Implementation of this function is host dependent and it is called by HIF layer. - * @warning Calling this function will drop any connection and lose the internal state saved on the - * WINC firmware. - * @see nm_bsp_init, m2m_wifi_init, m2m_wifi_deinit - * @return None - */ + * @note Implementation of this function is host dependent and called by HIF layer. + * @warning Calling this function will drop any connection and lose the internal state saved on the WINC firmware. + * @see nm_bsp_init, m2m_wifi_init, m2m_wifi_deinit + * @return None +*/ void nm_bsp_reset(void); /**@}*/ //NmBspResetFn - /** @defgroup NmBspSleepFn nm_bsp_sleep -* @ingroup BSPAPI -* Sleep in units of milliseconds.\n -* This function used by the HIF Layer on several different scenarios. -*/ -/**@{*/ + * @ingroup BSPAPI + * Sleep in units of milliseconds.\n + * This function used by the HIF Layer on several different scenarios. + * @{ + */ /*! - * @fn void nm_bsp_sleep(uint32); + * @fn void nm_bsp_sleep(uint32 u32TimeMsec); * @brief Used to put the host to sleep for the specified duration (in milliseconds). * Forcing the host to sleep for extended period may lead to host not being able to respond * to WINC board events. It is important to be considerate while choosing the sleep period. - * @param [in] u32TimeMsec + * @param [in] u32TimeMsec * Time unit in milliseconds. * @pre Initialize @ref nm_bsp_init first. - * @note Implementation of this function is host dependent. + * @note Implementation of this function is host dependent. * @warning Maximum value must nor exceed 4294967295 milliseconds which is equal to 4294967.295 seconds. - * @see nm_bsp_init - * @return None + * @see nm_bsp_init + * @return None */ void nm_bsp_sleep(uint32 u32TimeMsec); /**@}*/ //NmBspSleepFn - /** @defgroup NmBspRegisterFn nm_bsp_register_isr -* @ingroup BSPAPI -* Register ISR (Interrupt Service Routine) in the initialization of the HIF (Host Interface) Layer. -* When the interrupt is triggered, the BSP layer should call the pfisr function from the interrupt handler. -*/ -/**@{*/ + * @ingroup BSPAPI + * Register ISR (Interrupt Service Routine) in the initialization of the HIF (Host Interface) Layer. + * When the interrupt is triggered, the BSP layer should call the pfisr function from the interrupt handler. + * @{ + */ /*! - * @fn void nm_bsp_register_isr(tpfNmBspIsr pfIsr); - * @brief Register the host interface interrupt service routine. + * @fn void nm_bsp_register_isr(tpfNmBspIsr pfIsr); + * @brief Register the host interface interrupt service routine. * @details The WINC board uses the SPI interface to communicate with the host. This function registers * the SPI interrupt to notify the host whenever there is an outstanding message from the WINC * board. This function should be called during the initialization of the host interface. It is * an internal driver function and shouldn't be called by the Application. - * @param [in] pfIsr tpfNmBspIsr - * Pointer to ISR handler in the HIF Layer. - * @note Implementation of this function is host dependent and called by HIF layer. + * @param [in] pfIsr + * Pointer to ISR handler in the HIF layer. + * @note Implementation of this function is host dependent and called by HIF layer. * @warning Make sure that ISR for IRQ pin for WINC is disabled by default in your implementation. - * @see tpfNmBspIsr - * @return None + * @see tpfNmBspIsr + * @return None */ void nm_bsp_register_isr(tpfNmBspIsr pfIsr); /**@}*/ //NmBspRegisterFn - /** @defgroup NmBspInterruptCtrl nm_bsp_interrupt_ctrl -* @ingroup BSPAPI -* Synchronous enable/disable of WINC to host interrupts. -*/ -/**@{*/ + * @ingroup BSPAPI + * Synchronous enable/disable of WINC to host interrupts. + * @{ + */ /*! - * @fn void nm_bsp_interrupt_ctrl(uint8); + * @fn void nm_bsp_interrupt_ctrl(uint8 u8Enable); * @brief Enable/Disable interrupts from the WINC. * @details This function can be used to enable/disable the WINC to host interrupts, depending on how * the driver is implemented. It is an internal driver function and shouldn't be called by * the application. - * @param [in] u8Enable + * @param [in] u8Enable * - '0' disable interrupts. * - '1' enable interrupts. * @pre The interrupt must be registered using @ref nm_bsp_register_isr first. - * @note Implementation of this function is host dependent and called by HIF layer. + * @note Implementation of this function is host dependent and called by HIF layer. * @see tpfNmBspIsr, nm_bsp_register_isr - * @return None + * @return None */ void nm_bsp_interrupt_ctrl(uint8 u8Enable); /**@}*/ //NmBspInterruptCtrl @@ -297,19 +290,28 @@ void nm_bsp_interrupt_ctrl(uint8 u8Enable); #endif +/** + * @addtogroup BSPDefine + * @{ + */ #ifdef _NM_BSP_BIG_END +/*! Switch endianness of 32bit word (In the case that Host is BE) */ #define NM_BSP_B_L_32(x) \ ((((x) & 0x000000FF) << 24) + \ (((x) & 0x0000FF00) << 8) + \ (((x) & 0x00FF0000) >> 8) + \ (((x) & 0xFF000000) >> 24)) +/*! Switch endianness of 16bit word (In the case that Host is BE) */ #define NM_BSP_B_L_16(x) \ ((((x) & 0x00FF) << 8) + \ (((x) & 0xFF00) >> 8)) #else +/*! Retain endianness of 32bit word (In the case that Host is LE) */ #define NM_BSP_B_L_32(x) (x) +/*! Retain endianness of 16bit word (In the case that Host is LE) */ #define NM_BSP_B_L_16(x) (x) #endif +/**@}*/ //BSPDefine -#endif /*_NM_BSP_H_*/ +#endif /*_NM_BSP_H_*/ diff --git a/src/drivers/winc1500/include/bsp/include/nm_bsp_internal.h b/src/drivers/winc1500/include/bsp/include/nm_bsp_internal.h index 1395acd6f..1f8c03521 100644 --- a/src/drivers/winc1500/include/bsp/include/nm_bsp_internal.h +++ b/src/drivers/winc1500/include/bsp/include/nm_bsp_internal.h @@ -96,6 +96,10 @@ #include "bsp/include/nm_bsp_same70.h" #endif +#if (defined __SAMR30G18A__) || (defined __SAMR30E18A__) +#include "bsp/include/nm_bsp_samr30.h" +#endif + #ifdef CORTUS_APP #include "crt_iface.h" #endif diff --git a/src/drivers/winc1500/include/bus_wrapper/include/nm_bus_wrapper.h b/src/drivers/winc1500/include/bus_wrapper/include/nm_bus_wrapper.h index 1394b0aba..20604f48b 100644 --- a/src/drivers/winc1500/include/bus_wrapper/include/nm_bus_wrapper.h +++ b/src/drivers/winc1500/include/bus_wrapper/include/nm_bus_wrapper.h @@ -4,7 +4,7 @@ * * \brief This module contains NMC1000 bus wrapper APIs declarations. * - * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries. + * Copyright (c) 2016-2022 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * @@ -164,6 +164,30 @@ sint8 nm_bus_reinit(void *); uint8 nm_bus_get_chip_type(void); sint8 nm_bus_break(void); #endif + +/** + * @fn nm_bus_speed + * @brief Either set the bus speed to default (HIGH) or a reduced speed (LOW) + to increase stability during WINC wakeup + * @param [in] uint8 level + * HIGH(1) or LOW(0) + * @return M2M_SUCCESS in case of success and M2M_ERR_INVALID_ARG in case of an + incorrect parameter + */ +sint8 nm_bus_speed(uint8 level); + +/** + * @fn spi_rw + * @brief Process SPI Read/Write operation + * @param pu8Mosi TX Data buffer + * @param pu8Miso RX Data buffer + * @param u16Sz Transfer length + * @return ZERO in case of success and M2M_ERR_BUS_FAIL in case of failure + */ +#ifdef CONF_WINC_USE_SPI +sint8 nm_spi_rw(uint8* pu8Mosi, uint8* pu8Miso, uint16 u16Sz); +#endif + #ifdef __cplusplus } #endif diff --git a/src/drivers/winc1500/include/common/include/nm_common.h b/src/drivers/winc1500/include/common/include/nm_common.h index 4510d51ae..e52680802 100644 --- a/src/drivers/winc1500/include/common/include/nm_common.h +++ b/src/drivers/winc1500/include/common/include/nm_common.h @@ -4,7 +4,7 @@ * * \brief WINC Driver Common API Declarations. * - * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries. + * Copyright (c) 2016-2021 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * @@ -33,10 +33,10 @@ */ /** @defgroup COMMON Common -@{ - @defgroup COMMONDEF Defines - @defgroup COMMONAPI Functions -@} + @{ + @defgroup COMMONDEF Defines + @defgroup COMMONAPI Functions + @} */ #ifndef _NM_COMMON_H_ @@ -45,104 +45,120 @@ #include "bsp/include/nm_bsp.h" #include "common/include/nm_debug.h" #define WINC1500_EXPORT(name) winc1500_ ## name + /**@addtogroup COMMONDEF + * @{ */ -/**@{*/ -#define M2M_TIME_OUT_DELAY 10000 +#define M2M_TIME_OUT_DELAY 10000 /*states*/ -#define M2M_SUCCESS ((sint8)0) -#define M2M_ERR_SEND ((sint8)-1) -#define M2M_ERR_RCV ((sint8)-2) -#define M2M_ERR_MEM_ALLOC ((sint8)-3) -#define M2M_ERR_TIME_OUT ((sint8)-4) -#define M2M_ERR_INIT ((sint8)-5) -#define M2M_ERR_BUS_FAIL ((sint8)-6) -#define M2M_NOT_YET ((sint8)-7) -#define M2M_ERR_FIRMWARE ((sint8)-8) -#define M2M_SPI_FAIL ((sint8)-9) -#define M2M_ERR_FIRMWARE_bURN ((sint8)-10) -#define M2M_ACK ((sint8)-11) -#define M2M_ERR_FAIL ((sint8)-12) -#define M2M_ERR_FW_VER_MISMATCH ((sint8)-13) -#define M2M_ERR_SCAN_IN_PROGRESS ((sint8)-14) -#define M2M_ERR_INVALID_ARG ((sint8)-15) -#define M2M_ERR_INVALID ((sint8)-16) +#define M2M_SUCCESS ((sint8)0) +#define M2M_ERR_SEND ((sint8)-1) +#define M2M_ERR_RCV ((sint8)-2) +#define M2M_ERR_MEM_ALLOC ((sint8)-3) +#define M2M_ERR_TIME_OUT ((sint8)-4) +#define M2M_ERR_INIT ((sint8)-5) +#define M2M_ERR_BUS_FAIL ((sint8)-6) +#define M2M_NOT_YET ((sint8)-7) +#define M2M_ERR_FIRMWARE ((sint8)-8) +#define M2M_SPI_FAIL ((sint8)-9) +#define M2M_ERR_FIRMWARE_bURN ((sint8)-10) +#define M2M_ACK ((sint8)-11) +#define M2M_ERR_FAIL ((sint8)-12) +#define M2M_ERR_FW_VER_MISMATCH ((sint8)-13) +#define M2M_ERR_SCAN_IN_PROGRESS ((sint8)-14) -/*i2c MAASTER ERR*/ -#define I2C_ERR_LARGE_ADDRESS 0xE1UL -/*!< The address exceeds the max addressing mode in I2C flash. - */ -#define I2C_ERR_TX_ABRT 0xE2UL -/*!< NO ACK from slave. - */ +/* Invalid argument */ +#define M2M_ERR_INVALID_ARG ((sint8)-15) +#define M2M_ERR_INVALID ((sint8)-16) + +/* I2C MASTER ERR */ +#define I2C_ERR_LARGE_ADDRESS 0xE1UL /*!< The address exceeds the max addressing mode in I2C flash. */ +#define I2C_ERR_TX_ABRT 0xE2UL /*!< NO ACK from slave. */ #define I2C_ERR_OVER_SIZE 0xE3UL -#define ERR_PREFIX_NMIS 0xE4UL -/*!< Wrong first four bytes in flash NMIS. - */ -#define ERR_FIRMEWARE_EXCEED_SIZE 0xE5UL -/*!< Total size of firmware exceeds the max size 256k. - */ -/**/ -#define PROGRAM_START 0x26961735UL -#define BOOT_SUCCESS 0x10add09eUL -#define BOOT_START 0x12345678UL +#define ERR_PREFIX_NMIS 0xE4UL /*!< Wrong first four bytes in flash NMIS. */ +#define ERR_FIRMWARE_EXCEED_SIZE 0xE5UL /*!< Total size of firmware exceeds the max size 256k. */ -#define NBIT31 (0x80000000) -#define NBIT30 (0x40000000) -#define NBIT29 (0x20000000) -#define NBIT28 (0x10000000) -#define NBIT27 (0x08000000) -#define NBIT26 (0x04000000) -#define NBIT25 (0x02000000) -#define NBIT24 (0x01000000) -#define NBIT23 (0x00800000) -#define NBIT22 (0x00400000) -#define NBIT21 (0x00200000) -#define NBIT20 (0x00100000) -#define NBIT19 (0x00080000) -#define NBIT18 (0x00040000) -#define NBIT17 (0x00020000) -#define NBIT16 (0x00010000) -#define NBIT15 (0x00008000) -#define NBIT14 (0x00004000) -#define NBIT13 (0x00002000) -#define NBIT12 (0x00001000) -#define NBIT11 (0x00000800) -#define NBIT10 (0x00000400) -#define NBIT9 (0x00000200) -#define NBIT8 (0x00000100) -#define NBIT7 (0x00000080) -#define NBIT6 (0x00000040) -#define NBIT5 (0x00000020) -#define NBIT4 (0x00000010) -#define NBIT3 (0x00000008) -#define NBIT2 (0x00000004) -#define NBIT1 (0x00000002) -#define NBIT0 (0x00000001) +#define PROGRAM_START 0x26961735UL +#define BOOT_SUCCESS 0x10add09eUL +#define BOOT_START 0x12345678UL -#define M2M_MAX(A,B) ((A) > (B) ? (A) : (B)) -#define M2M_SEL(x,m1,m2,m3) ((x>1)?((x>2)?(m3):(m2)):(m1)) -#define WORD_ALIGN(val) (((val) & 0x03) ? ((val) + 4 - ((val) & 0x03)) : (val)) +#define NBIT31 (0x80000000) +#define NBIT30 (0x40000000) +#define NBIT29 (0x20000000) +#define NBIT28 (0x10000000) +#define NBIT27 (0x08000000) +#define NBIT26 (0x04000000) +#define NBIT25 (0x02000000) +#define NBIT24 (0x01000000) +#define NBIT23 (0x00800000) +#define NBIT22 (0x00400000) +#define NBIT21 (0x00200000) +#define NBIT20 (0x00100000) +#define NBIT19 (0x00080000) +#define NBIT18 (0x00040000) +#define NBIT17 (0x00020000) +#define NBIT16 (0x00010000) +#define NBIT15 (0x00008000) +#define NBIT14 (0x00004000) +#define NBIT13 (0x00002000) +#define NBIT12 (0x00001000) +#define NBIT11 (0x00000800) +#define NBIT10 (0x00000400) +#define NBIT9 (0x00000200) +#define NBIT8 (0x00000100) +#define NBIT7 (0x00000080) +#define NBIT6 (0x00000040) +#define NBIT5 (0x00000020) +#define NBIT4 (0x00000010) +#define NBIT3 (0x00000008) +#define NBIT2 (0x00000004) +#define NBIT1 (0x00000002) +#define NBIT0 (0x00000001) -#define DATA_PKT_OFFSET 4 +/*! Maximum of two values */ +#define M2M_MAX(A,B) ((A) > (B) ? (A) : (B)) +/*! Choose one of three values */ +#define M2M_SEL(x,m1,m2,m3) ((x>1)?((x>2)?(m3):(m2)):(m1)) +/*! Align to next multiple of 4 */ +#define WORD_ALIGN(val) (((val) & 0x03) ? ((val) + 4 - ((val) & 0x03)) : (val)) + +#define DATA_PKT_OFFSET 4 #if _BYTE_ORDER == _LITTLE_ENDIAN -#define BYTE_0(word) ((uint8)(((word) >> 0 ) & 0x000000FFUL)) -#define BYTE_1(word) ((uint8)(((word) >> 8 ) & 0x000000FFUL)) -#define BYTE_2(word) ((uint8)(((word) >> 16) & 0x000000FFUL)) -#define BYTE_3(word) ((uint8)(((word) >> 24) & 0x000000FFUL)) +/*! Most significant byte of 32bit word (LE) */ +#define BYTE_0(word) ((uint8)(((word) >> 0) & 0x000000FFUL)) +/*! Second most significant byte of 32bit word (LE) */ +#define BYTE_1(word) ((uint8)(((word) >> 8) & 0x000000FFUL)) +/*! Third most significant byte of 32bit word (LE) */ +#define BYTE_2(word) ((uint8)(((word) >> 16) & 0x000000FFUL)) +/*! Least significant byte of 32bit word (LE) */ +#define BYTE_3(word) ((uint8)(((word) >> 24) & 0x000000FFUL)) #else -#define BYTE_0(word) ((uint8)(((word) >> 24) & 0x000000FFUL)) -#define BYTE_1(word) ((uint8)(((word) >> 16) & 0x000000FFUL)) -#define BYTE_2(word) ((uint8)(((word) >> 8 ) & 0x000000FFUL)) -#define BYTE_3(word) ((uint8)(((word) >> 0 ) & 0x000000FFUL)) +/*! Most significant byte of 32bit word (BE) */ +#define BYTE_0(word) ((uint8)(((word) >> 24) & 0x000000FFUL)) +/*! Second most significant byte of 32bit word (BE) */ +#define BYTE_1(word) ((uint8)(((word) >> 16) & 0x000000FFUL)) +/*! Third most significant byte of 32bit word (BE) */ +#define BYTE_2(word) ((uint8)(((word) >> 8) & 0x000000FFUL)) +/*! Least significant byte of 32bit word (BE) */ +#define BYTE_3(word) ((uint8)(((word) >> 0) & 0x000000FFUL)) #endif +/**@}*/ + +#define PASS 0 +#define FAIL 1 +#define LOW 0 +#define HIGH 1 + +#define Min(a, b) (((a) < (b)) ? (a) : (b)) +#define Max(a, b) (((a) > (b)) ? (a) : (b)) +#define min(a, b) Min(a, b) +#define max(a, b) Max(a, b) -/**@}*/ //COMMONDEF #ifdef __cplusplus - extern "C" { - #endif +extern "C" { +#endif /*! * @ingroup COMMONAPI @@ -156,7 +172,7 @@ * Destination buffer. * @return None */ -NMI_API void m2m_memcpy(uint8* pDst,uint8* pSrc,uint32 sz); +NMI_API void m2m_memcpy(uint8 *pDst, const uint8 *pSrc, uint32 sz); /*! * @ingroup COMMONAPI @@ -170,7 +186,7 @@ NMI_API void m2m_memcpy(uint8* pDst,uint8* pSrc,uint32 sz); * The specified data buffer (whose data bytes will be set to the specified value). * @return None */ -NMI_API void m2m_memset(uint8* pBuf,uint8 val,uint32 sz); +NMI_API void m2m_memset(uint8 *pBuf, uint8 val, uint32 sz); /*! * @ingroup COMMONAPI @@ -180,7 +196,7 @@ NMI_API void m2m_memset(uint8* pBuf,uint8 val,uint32 sz); * Null terminated string buffer. * @return Length of the string in the specified string buffer. */ -NMI_API uint16 m2m_strlen(uint8 * pcStr); +NMI_API uint16 m2m_strlen(uint8 *pcStr); /*! * @ingroup COMMONAPI @@ -194,7 +210,7 @@ NMI_API uint16 m2m_strlen(uint8 * pcStr); * One of two data buffers for the comparison. * @return Zero if matched, one if not matched. */ -NMI_API sint8 m2m_memcmp(uint8 *pu8Buff1,uint8 *pu8Buff2 ,uint32 u32Size); +NMI_API sint8 m2m_memcmp(uint8 *pu8Buff1, uint8 *pu8Buff2, uint32 u32Size); /*! * @ingroup COMMONAPI @@ -218,9 +234,9 @@ NMI_API uint8 m2m_strncmp(uint8 *pcS1, uint8 *pcS2, uint16 u16Len); * One of two string buffers. * @param [in] pcIn * One of two string buffers. - * @return If pcStr string is part of pcIn string return a valid pointer to the start of pcStr within pcIn. If not, a NULL Pointer is returned + * @return If pcStr string is part of pcIn string return a valid pointer to the start of pcStr within pcIn. If not, a NULL Pointer is returned. */ -NMI_API uint8 * m2m_strstr(uint8 *pcIn, uint8 *pcStr); +NMI_API uint8 *m2m_strstr(uint8 *pcIn, uint8 *pcStr); /*! * @ingroup COMMONAPI @@ -232,15 +248,29 @@ NMI_API uint8 * m2m_strstr(uint8 *pcIn, uint8 *pcStr); * The specified data buffer (whose data bytes will be used to calculate the checksum). * @return The calculated checksum. */ -NMI_API uint8 m2m_checksum(uint8* buf, int sz); +NMI_API uint8 m2m_checksum(uint8 *buf, int sz); /*! * @ingroup COMMONAPI - * @fn void (*at_sb_printf)(const char *_format, ...); + * @fn sint8 hexstr_2_bytes(uint8 *pu8Out, uint8 *pu8In, uint8 u8SizeOut); + * @brief Converts a string of hex characters to bytes. + * @param[out] pu8Out + * Output buffer (eg {0x11, 0x12, 0x13,...}) + * @param[in] pu8In + * Input buffer (eg {0x31, 0x31, 0x31, 0x32, 0x31, 0x33, ...}) + * @param[in] u8SizeOut + * Length of output buffer (should be half of the length of the input buffer). + * @return @ref M2M_SUCCESS if successful, M2M_ERR_INVALID_ARG otherwise (eg unrecognised hexchar in input). + */ +sint8 hexstr_2_bytes(uint8 *pu8Out, uint8 *pu8In, uint8 u8SizeOut); + +/*! + * @ingroup COMMONAPI + * @fn void (*at_sb_printf)(const char *_format, ...); * @brief Chooses which function to use in order to output debug. */ extern NMI_API void (*at_sb_printf)(const char *_format, ...); #ifdef __cplusplus } - #endif -#endif /*_NM_COMMON_H_*/ +#endif +#endif /*_NM_COMMON_H_*/ diff --git a/src/drivers/winc1500/include/common/include/nm_debug.h b/src/drivers/winc1500/include/common/include/nm_debug.h index ea8fe5514..c65729efd 100644 --- a/src/drivers/winc1500/include/common/include/nm_debug.h +++ b/src/drivers/winc1500/include/common/include/nm_debug.h @@ -1,4 +1,4 @@ -/** +/** * * \file * @@ -64,10 +64,8 @@ #define M2M_PRINT(...) #if (CONF_WINC_DEBUG == 1) -#undef M2M_LOG_LEVEL -#define M2M_LOG_LEVEL M2M_LOG_DBG #undef M2M_PRINT -#define M2M_PRINT(...) do{CONF_WINC_PRINTF(__VA_ARGS__);CONF_WINC_PRINTF("\r");}while(0) +#define M2M_PRINT(...) do{CONF_WINC_PRINTF(__VA_ARGS__);}while(0) #if (M2M_LOG_LEVEL >= M2M_LOG_ERROR) #undef M2M_ERR #define M2M_ERR(...) do{CONF_WINC_PRINTF("(APP)(ERR)[%s][%d]",__FUNCTION__,__LINE__); CONF_WINC_PRINTF(__VA_ARGS__);CONF_WINC_PRINTF("\r");}while(0) diff --git a/src/drivers/winc1500/include/driver/include/m2m_ate_mode.h b/src/drivers/winc1500/include/driver/include/m2m_ate_mode.h index 36db15b02..5fee3591e 100644 --- a/src/drivers/winc1500/include/driver/include/m2m_ate_mode.h +++ b/src/drivers/winc1500/include/driver/include/m2m_ate_mode.h @@ -34,6 +34,8 @@ #ifdef _M2M_ATE_FW_ + + #ifndef _M2M_ATE_MODE_H_ #define _M2M_ATE_MODE_H_ diff --git a/src/drivers/winc1500/include/driver/include/m2m_crypto.h b/src/drivers/winc1500/include/driver/include/m2m_crypto.h index 90d7db303..2dad9ed13 100644 --- a/src/drivers/winc1500/include/driver/include/m2m_crypto.h +++ b/src/drivers/winc1500/include/driver/include/m2m_crypto.h @@ -100,7 +100,6 @@ typedef enum{ tenuM2mCryptoCmd */ typedef void (*tpfAppCryproCb) (uint8 u8MsgType,void * pvResp, void * pvMsg); - /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* FUNCTION PROTOTYPES *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ diff --git a/src/drivers/winc1500/include/driver/include/m2m_ota.h b/src/drivers/winc1500/include/driver/include/m2m_ota.h index 0a125042b..55825a6ce 100644 --- a/src/drivers/winc1500/include/driver/include/m2m_ota.h +++ b/src/drivers/winc1500/include/driver/include/m2m_ota.h @@ -4,7 +4,7 @@ * * \brief WINC OTA Upgrade API Interface. * - * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries. + * Copyright (c) 2016-2021 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * @@ -35,10 +35,11 @@ /**@defgroup OTAAPI OTA @brief The WINC supports OTA (Over-The-Air) updates. Using the APIs described in this module, - it is possible to request an ATWINC15x0 to update its firmware, or safely rollback to - the previous firmware version.\n There are also APIs to download files and store them in - the WINC's Flash (supported by ATWINC1510 only), which can be used for Host MCU OTA - updates or accessing information stored remotely. + it is possible to request an ATWINC15x0 to update its firmware image, or safely rollback to + the previous firmware image.\n Note that it is NOT possible to update other areas of the WINC + flash (e.g. the HTTP file area) using the OTA mechanism.\n\n There are also APIs to download + files and store them in the WINC's Flash (supported by ATWINC1510 only), which can be used + for Host MCU OTA updates or accessing information stored remotely. @{ @defgroup OTACALLBACKS Callbacks @brief @@ -58,7 +59,12 @@ @defgroup OTAFUNCTIONS Functions @brief Lists the full set of available APIs to manage OTA updates and Host File Downloads. -@} + @{ + @defgroup OTACOMMON Common + @defgroup WINCOTA WINC + @defgroup HFD HFD + @} + @} */ #ifndef __M2M_OTA_H__ @@ -72,39 +78,66 @@ INCLUDES #include "driver/include/m2m_types.h" #include "driver/include/nmdrv.h" +/**@addtogroup OTATYPEDEF + * @{ + */ +/*! +@enum \ + tenuOTASSLOption +@brief + Enumeration for OTA SSL options + The following SSL options can be set for OTA +*/ +typedef enum { + WIFI_OTA_SSL_OPT_BYPASS_SERVER_AUTH = 0x1, + /*!\n",s8Ret); - while(1); - } - //Initialize the OTA module - m2m_ota_init(OtaUpdateCb,NULL); + while(1); + } + // Initialize the OTA module + m2m_ota_init(OtaUpdateCb,NULL); - //connect to AP that provide connection to the OTA server - m2m_wifi_default_connect(); + // Connect to AP that provides connection to the OTA server + m2m_wifi_default_connect(); - while(1) - { - //Handle the app state machine plus the WINC event handler - while(m2m_wifi_handle_events(NULL) != M2M_SUCCESS) { - - } - } + while(1) + { + // Handle the app state machine plus the WINC event handler + while(m2m_wifi_handle_events(NULL) != M2M_SUCCESS) { + } + } } -@endcode +@endcode */ -NMI_API sint8 m2m_ota_start_update(unsigned char * pcDownloadUrl); +NMI_API sint8 m2m_ota_start_update(unsigned char *pcDownloadUrl); /*! -@fn \ - NMI_API sint8 m2m_ota_rollback(void); +@ingroup WINCOTA +@fn \ + sint8 m2m_ota_rollback(void); @brief - Request OTA Roll-back to the old (inactive) WINC image, the WINC firmware will check the validity of the Roll-back image - and activate it if valid. On completion, a callback of type tpfOtaUpdateCb is called (application must previously have - provided the callback via m2m_ota_init). If the callback indicates successful activation, the newly-activated image + Request OTA Roll-back to the old (inactive) WINC image, the WINC firmware will check the validity of the inactive image + and activate it if valid. On completion, a callback of type @ref tpfOtaUpdateCb is called (application must previously have + provided the callback via @ref m2m_ota_init). If the callback indicates successful activation, the newly-activated image will start running after next system reset. @warning If rollback requires a host driver update in order to maintain HIF compatibility (HIF - major value change), then it is recommended to update the host driver prior to calling this - API.\n + major value change), then it is recommended to update the host driver prior to calling this API.\n In the event of system reset with incompatible driver/firmware, compatibility can be - recovered by calling @ref m2m_ota_rollback or @ref m2m_ota_switch_firmware. + recovered by calling @ref m2m_ota_rollback or @ref m2m_ota_switch_firmware. See @ref OTAExample. -@sa - m2m_ota_init - m2m_ota_start_update +@sa + m2m_ota_init + m2m_ota_start_update -@return - The function returns @ref M2M_SUCCESS for successful operations and a negative value otherwise. +@return + The function returns @ref M2M_SUCCESS for successful operations and a negative value otherwise. */ NMI_API sint8 m2m_ota_rollback(void); /*! -@fn \ - NMI_API sint8 m2m_ota_abort(void); +@ingroup OTACOMMON +@fn \ + sint8 m2m_ota_abort(void); @brief Request the WINC to abort an OTA or Host File download in progress.\n - If no download is in progress, the API will respond with failure. + If no download is in progress, the API will respond with failure. + +@sa + m2m_ota_init + m2m_ota_start_update @return - The function returns @ref M2M_SUCCESS for successful operation and a negative value otherwise. + The function returns @ref M2M_SUCCESS for a successful operation and a negative value otherwise. */ NMI_API sint8 m2m_ota_abort(void); /*! -@fn \ - NMI_API sint8 m2m_ota_switch_firmware(void); +@ingroup WINCOTA +@fn \ + sint8 m2m_ota_switch_firmware(void); @brief Request switch to the updated WINC image. The WINC firmware will check the validity of the - inactive image and activate it if it is valid. On completion, a callback of type @ref tpfOtaUpdateCb + inactive image and activate it if valid. On completion, a callback of type @ref tpfOtaUpdateCb is called (application must previously have provided the callback via @ref m2m_ota_init). If the callback indicates successful activation, the newly-activated image will start running after next system reset. @warning - If switch will necessitate a host driver update in order to maintain HIF compatibility (HIF - major value change), then it is recommended to update the host driver prior to calling this - API.\n + If switch requires a host driver update in order to maintain HIF compatibility (HIF + major value change), then it is recommended to update the host driver prior to calling this API.\n In the event of system reset with incompatible driver/firmware, compatibility can be - recovered by calling @ref m2m_ota_rollback or @ref m2m_ota_switch_firmware. + recovered by calling @ref m2m_ota_rollback or @ref m2m_ota_switch_firmware. See @ref OTAExample. -@sa - m2m_ota_init - m2m_ota_start_update +@sa + m2m_ota_init + m2m_ota_start_update -@return - The function returns @ref M2M_SUCCESS for successful operations and a negative value otherwise. +@return + The function returns @ref M2M_SUCCESS for successful operations and a negative value otherwise. */ NMI_API sint8 m2m_ota_switch_firmware(void); /*! -@fn \ - NMI_API sint8 m2m_ota_host_file_get(unsigned char *pcDownloadUrl, tpfFileGetCb pfHFDGetCb); +@ingroup HFD +@fn \ + sint8 m2m_ota_host_file_get(unsigned char *pcDownloadUrl, tpfFileGetCb pfHFDGetCb); @brief Download a file from a remote location and store it in WINC's Flash. @param[in] pcDownloadUrl - Url pointing to the remote file. HTTP/HTTPS only. + Url pointing to the remote file. HTTP/HTTPS only. @param[in] pfHFDGetCb Pointer to a callback (see @ref tpfFileGetCb) to be executed when the download finishes. @@ -481,8 +550,9 @@ NMI_API sint8 m2m_ota_switch_firmware(void); NMI_API sint8 m2m_ota_host_file_get(unsigned char *pcDownloadUrl, tpfFileGetCb pfHFDGetCb); /*! -@fn \ - NMI_API sint8 m2m_ota_host_file_read_hif(uint8 u8Handler, uint32 u32Offset, uint32 u32Size, tpfFileReadCb pfHFDReadCb); +@ingroup HFD +@fn \ + sint8 m2m_ota_host_file_read_hif(uint8 u8Handler, uint32 u32Offset, uint32 u32Size, tpfFileReadCb pfHFDReadCb); @brief Read a certain amount of bytes from a file previously stored in WINC's Flash using HIF transfer. @@ -491,10 +561,10 @@ NMI_API sint8 m2m_ota_host_file_get(unsigned char *pcDownloadUrl, tpfFileGetCb p Handler of the file we are trying to read from. Must be valid. @param[in] u32Offset - Offset from start of the file to read from (in bytes). + Offset from start of the file to read from (in bytes). @param[in] u32Size - The amount of data to read (in bytes). + The amount of data to read (in bytes). @param[in] pfHFDReadCb Callback (see @ref tpfFileReadCb) to be executed when the read operation completes. @@ -506,13 +576,13 @@ NMI_API sint8 m2m_ota_host_file_get(unsigned char *pcDownloadUrl, tpfFileGetCb p Requires @ref m2m_ota_init to be called before a read via HIF can be requested. @warning - There is a limitation on how much data can be transferred at a time using hif read, which is 128 bytes. + There is a limitation on how much data can be transferred at a time using HIF read, which is 128 bytes. The limitation described above can potentially reduce the speed of the read due to extra overhead, but using the HIF is non-blocking and therefore the Application can continue execution as normal, being interrupted only when data is available. Another advantage is that it does not require the WINC to be reset or put in download mode, as it is the case for reading the file via SPI (see @ref m2m_ota_host_file_read_spi).\n A valid file handler must be provided, this means that it needs to match the handler internally stored - by the WINC and must not be @ref HFD_INVALID_HANDLER.\n + by the WINC and must not be @ref HFD_INVALID_HANDLER \n Providing a callback is mandatory. @note @@ -528,8 +598,9 @@ NMI_API sint8 m2m_ota_host_file_get(unsigned char *pcDownloadUrl, tpfFileGetCb p NMI_API sint8 m2m_ota_host_file_read_hif(uint8 u8Handler, uint32 u32Offset, uint32 u32Size, tpfFileReadCb pfHFDReadCb); /*! -@fn \ - NMI_API sint8 m2m_ota_host_file_read_spi(uint8 u8Handler, uint8 *pu8Buff, uint32 u32Offset, uint32 u32Size); +@ingroup HFD +@fn \ + sint8 m2m_ota_host_file_read_spi(uint8 u8Handler, uint8 *pu8Buff, uint32 u32Offset, uint32 u32Size); @brief Read a certain amount of bytes from a file in WINC's Flash using SPI transfer. @@ -541,10 +612,10 @@ NMI_API sint8 m2m_ota_host_file_read_hif(uint8 u8Handler, uint32 u32Offset, uint Pointer to a buffer to store the data being read. Must not be NULL. @param[in] u32Offset - Offset from start of the file to read from (in bytes). + Offset from start of the file to read from (in bytes). @param[in] u32Size - The amount of data to read (in Bytes). + The amount of data to read (in Bytes). @return The function SHALL return @ref M2M_SUCCESS for success and a negative value otherwise. @@ -631,7 +702,7 @@ static void FileGetCallback(uint8 u8Status, uint8 u8Handler, uint32 u32Size) static void ReadFileSPI(void) { sint8 s8Ret = M2M_ERR_FAIL; - + if(WIFI_STATE_DEINIT != m2m_wifi_get_state()) m2m_wifi_deinit(NULL); @@ -686,11 +757,12 @@ void main(void) } @endcode */ -NMI_API sint8 m2m_ota_host_file_read_spi(uint8 u8Handler, uint8* pu8Buff, uint32 u32Offset, uint32 u32Size); +NMI_API sint8 m2m_ota_host_file_read_spi(uint8 u8Handler, uint8 *pu8Buff, uint32 u32Offset, uint32 u32Size); /*! -@fn \ - NMI_API sint8 m2m_ota_host_file_erase(uint8 u8Handler, tpfFileEraseCb pfHFDEraseCb); +@ingroup HFD +@fn \ + sint8 m2m_ota_host_file_erase(uint8 u8Handler, tpfFileEraseCb pfHFDEraseCb); @brief Erase any traces of file stored in WINC's Flash. @@ -719,7 +791,7 @@ NMI_API sint8 m2m_ota_host_file_read_spi(uint8 u8Handler, uint8* pu8Buff, uint32 @warning A valid file handler must be provided, this means that it needs to match the handler internally stored - by the WINC and must not be @ref HFD_INVALID_HANDLER.\n + by the WINC and must not be @ref HFD_INVALID_HANDLER \n The handlers will be destroyed regardless of the call returning success or not. @sa @@ -731,18 +803,82 @@ NMI_API sint8 m2m_ota_host_file_erase(uint8 u8Handler, tpfFileEraseCb pfHFDErase #if 0 NMI_API sint8 m2m_ota_test(void); #endif -/**@}*/ //OTAFUNCTIONS /*! @ingroup VERSIONAPI -@fn NMI_API sint8 m2m_ota_get_firmware_version(tstrM2mRev* pstrRev); +@fn sint8 m2m_ota_get_firmware_version(tstrM2mRev* pstrRev); @brief Get the OTA Firmware version. @details Get OTA Firmware version info from the inactive partition, as defined in the structure tstrM2mRev. -@param [out] pstrRev +@param[out] pstrRev Pointer to the structure tstrM2mRev that contains the firmware version parameters. @return The function returns @ref M2M_SUCCESS for successful operations and a negative value otherwise. */ -NMI_API sint8 m2m_ota_get_firmware_version(tstrM2mRev* pstrRev); +NMI_API sint8 m2m_ota_get_firmware_version(tstrM2mRev *pstrRev); + +/*! +@ingroup WINCOTA +@fn \ + sint8 m2m_ota_set_ssl_option(tenuOTASSLOption enuOptionName, const void *pOptionValue, size_t OptionLen) + +@brief + Set specific SSL options to be used when the WINC performs an OTA from an https server. + +@details + The following options can be set:\n + + @ref WIFI_OTA_SSL_OPT_BYPASS_SERVER_AUTH \n + @ref WIFI_OTA_SSL_OPT_SNI_VALIDATION \n + @ref WIFI_OTA_SSL_OPT_SNI_SERVERNAME \n + + The setting applies to all subsequent OTA attempts via @ref m2m_ota_start_update \n + +@param[in] enuOptionName + The option to set. + +@param[in] pOptionValue + Pointer to a buffer containing the value to set. The buffer must be at least as long as OptionLen. + If OptionLen is 0, then pOptionValue may be NULL. + +@param[in] OptionLen + The length of the option value being set (including the null terminator for strings). + +@return + The function returns @ref M2M_SUCCESS if the parameters are valid and @ref M2M_ERR_INVALID_ARG otherwise. +*/ +NMI_API sint8 m2m_ota_set_ssl_option(tenuOTASSLOption enuOptionName, const void *pOptionValue, size_t OptionLen); + + +/*! +@ingroup WINCOTA +@fn \ + sint8 m2m_ota_get_ssl_option(tenuOTASSLOption enuOptionName, void *pOptionValue, size_t *pOptionLen) + +@brief + Get (read) SSL options relating to OTA + +@details + The following options can be read:\n + + @ref WIFI_OTA_SSL_OPT_BYPASS_SERVER_AUTH \n + @ref WIFI_OTA_SSL_OPT_SNI_VALIDATION \n + @ref WIFI_OTA_SSL_OPT_SNI_SERVERNAME \n + +@param[in] enuOptionName + The option to get. + +@param[out] pOptionValue + Pointer to a buffer to contain the value to get. The buffer must be at least as long as the value pointed to by pOptionLen. + +@param[inout] pOptionLen + Pointer to a length. + When calling the function, this length must be the length of the buffer available for reading the option value, + and must be large enough to hold the returned option value otherwise an @ref M2M_ERR_INVALID_ARG error will be returned. + When the function returns, this length is the length of the data that has been populated by the function. + +@return + The function returns @ref M2M_SUCCESS if the parameters are valid and @ref M2M_ERR_INVALID_ARG otherwise. +*/ +NMI_API sint8 m2m_ota_get_ssl_option(tenuOTASSLOption enuOptionName, void *pOptionValue, size_t *pOptionLen); #ifdef __cplusplus } diff --git a/src/drivers/winc1500/include/driver/include/m2m_periph.h b/src/drivers/winc1500/include/driver/include/m2m_periph.h index 10cecf126..8745c10c8 100644 --- a/src/drivers/winc1500/include/driver/include/m2m_periph.h +++ b/src/drivers/winc1500/include/driver/include/m2m_periph.h @@ -4,7 +4,7 @@ * * \brief WINC Peripherals Application Interface. * - * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries. + * Copyright (c) 2016-2021 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * @@ -35,12 +35,10 @@ #ifndef _M2M_PERIPH_H_ #define _M2M_PERIPH_H_ - /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* INCLUDES *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ - #include "common/include/nm_common.h" #include "driver/include/m2m_types.h" @@ -53,144 +51,63 @@ DATA TYPES *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ /*! -@struct \ - tstrPerphInitParam +@enum \ + tenuGpioNum @brief - Peripheral module initialization parameters. -*/ -typedef struct { - void * arg; -} tstrPerphInitParam; - - -/*! -@enum \ - tenuGpioNum - -@brief - A list of GPIO numbers configurable through the m2m_periph module. + A list of GPIO numbers configurable through the m2m_periph module. */ typedef enum { - M2M_PERIPH_GPIO3, /*!< GPIO15 pad */ - M2M_PERIPH_GPIO4, /*!< GPIO16 pad */ - M2M_PERIPH_GPIO5, /*!< GPIO18 pad */ - M2M_PERIPH_GPIO6, /*!< GPIO18 pad */ - M2M_PERIPH_GPIO15, /*!< GPIO15 pad */ - M2M_PERIPH_GPIO16, /*!< GPIO16 pad */ - M2M_PERIPH_GPIO18, /*!< GPIO18 pad */ - M2M_PERIPH_GPIO_MAX + M2M_PERIPH_GPIO0 = 0, /*!< GPIO0 pad */ + M2M_PERIPH_GPIO1 = 1, /*!< GPIO1 pad */ + M2M_PERIPH_GPIO2 = 2, /*!< GPIO2 pad */ + M2M_PERIPH_GPIO3 = 3, /*!< GPIO3 pad */ + M2M_PERIPH_GPIO4 = 4, /*!< GPIO4 pad */ + M2M_PERIPH_GPIO5 = 5, /*!< GPIO5 pad */ + M2M_PERIPH_GPIO6 = 6, /*!< GPIO6 pad */ + M2M_PERIPH_GPIO_MAX } tenuGpioNum; - /*! -@enum \ - tenuI2cMasterSclMuxOpt +@enum \ + tenuPullupMask @brief - Allowed pin multiplexing options for I2C master SCL signal. + Bitwise-ORed flags for use in @ref m2m_periph_pullup_ctrl. + +@sa + m2m_periph_pullup_ctrl */ typedef enum { - M2M_PERIPH_I2C_MASTER_SCL_MUX_OPT_HOST_WAKEUP, /*!< I2C master SCL is avaiable on HOST_WAKEUP. */ - M2M_PERIPH_I2C_MASTER_SCL_MUX_OPT_SD_DAT3, /*!< I2C master SCL is avaiable on SD_DAT3 (GPIO 7). */ - M2M_PERIPH_I2C_MASTER_SCL_MUX_OPT_GPIO13, /*!< I2C master SCL is avaiable on GPIO 13. */ - M2M_PERIPH_I2C_MASTER_SCL_MUX_OPT_GPIO4, /*!< I2C master SCL is avaiable on GPIO 4.*/ - M2M_PERIPH_I2C_MASTER_SCL_MUX_OPT_I2C_SCL, /*!< I2C master SCL is avaiable on I2C slave SCL. */ - M2M_PERIPH_I2C_MASTER_SCL_MUX_OPT_NUM -} tenuI2cMasterSclMuxOpt; - -/*! -@enum \ - tenuI2cMasterSdaMuxOpt - -@brief - Allowed pin multiplexing options for I2C master SDA signal. -*/ -typedef enum { - M2M_PERIPH_I2C_MASTER_SDA_MUX_OPT_RTC_CLK , /*!< I2C master SDA is avaiable on RTC_CLK. */ - M2M_PERIPH_I2C_MASTER_SDA_MUX_OPT_SD_CLK, /*!< I2C master SDA is avaiable on SD_CLK (GPIO 8). */ - M2M_PERIPH_I2C_MASTER_SDA_MUX_OPT_GPIO14, /*!< I2C master SDA is avaiable on GPIO 14. */ - M2M_PERIPH_I2C_MASTER_SDA_MUX_OPT_GPIO6, /*!< I2C master SDA is avaiable on GPIO 6.*/ - M2M_PERIPH_I2C_MASTER_SDA_MUX_OPT_I2C_SDA, /*!< I2C master SDA is avaiable on I2C slave SDA. */ - M2M_PERIPH_I2C_MASTER_SDA_MUX_OPT_NUM -} tenuI2cMasterSdaMuxOpt; - - -/*! -@struct \ - tstrI2cMasterInitParam - -@brief - I2C master configuration parameters. -@sa - tenuI2cMasterSclMuxOpt - tenuI2cMasterSdaMuxOpt -*/ -typedef struct { - uint8 enuSclMuxOpt; /*!< SCL multiplexing option. Allowed value are defined in tenuI2cMasterSclMuxOpt */ - uint8 enuSdaMuxOpt; /*!< SDA multiplexing option. Allowed value are defined in tenuI2cMasterSdaMuxOpt */ - uint8 u8ClkSpeedKHz; /*!< I2C master clock speed in KHz. */ -} tstrI2cMasterInitParam; - -/*! -@enum \ - tenuI2cMasterFlags - -@brief - Bitwise-ORed flags for use in m2m_periph_i2c_master_write and m2m_periph_i2c_master_read -@sa - m2m_periph_i2c_master_write - m2m_periph_i2c_master_read -*/ -typedef enum { - I2C_MASTER_NO_FLAGS = 0x00, - /*!< No flags. */ - I2C_MASTER_NO_STOP = 0x01, - /*!< No stop bit after this transaction. Useful for scattered buffer read/write operations. */ - I2C_MASTER_NO_START = 0x02, - /*!< No start bit at the beginning of this transaction. Useful for scattered buffer read/write operations.*/ -} tenuI2cMasterFlags; - -/*! -@enum \ - tenuPullupMask - -@brief - Bitwise-ORed flags for use in m2m_perph_pullup_ctrl. -@sa - m2m_periph_pullup_ctrl - -*/ -typedef enum { - M2M_PERIPH_PULLUP_DIS_HOST_WAKEUP = (1ul << 0), - M2M_PERIPH_PULLUP_DIS_RTC_CLK = (1ul << 1), - M2M_PERIPH_PULLUP_DIS_IRQN = (1ul << 2), - M2M_PERIPH_PULLUP_DIS_GPIO_3 = (1ul << 3), - M2M_PERIPH_PULLUP_DIS_GPIO_4 = (1ul << 4), - M2M_PERIPH_PULLUP_DIS_GPIO_5 = (1ul << 5), - M2M_PERIPH_PULLUP_DIS_SD_DAT3 = (1ul << 6), - M2M_PERIPH_PULLUP_DIS_SD_DAT2_SPI_RXD = (1ul << 7), - M2M_PERIPH_PULLUP_DIS_SD_DAT1_SPI_SSN = (1ul << 9), - M2M_PERIPH_PULLUP_DIS_SD_CMD_SPI_SCK = (1ul << 10), - M2M_PERIPH_PULLUP_DIS_SD_DAT0_SPI_TXD = (1ul << 11), - M2M_PERIPH_PULLUP_DIS_GPIO_6 = (1ul << 12), - M2M_PERIPH_PULLUP_DIS_SD_CLK = (1ul << 13), - M2M_PERIPH_PULLUP_DIS_I2C_SCL = (1ul << 14), - M2M_PERIPH_PULLUP_DIS_I2C_SDA = (1ul << 15), - M2M_PERIPH_PULLUP_DIS_GPIO_11 = (1ul << 16), - M2M_PERIPH_PULLUP_DIS_GPIO_12 = (1ul << 17), - M2M_PERIPH_PULLUP_DIS_GPIO_13 = (1ul << 18), - M2M_PERIPH_PULLUP_DIS_GPIO_14 = (1ul << 19), - M2M_PERIPH_PULLUP_DIS_GPIO_15 = (1ul << 20), - M2M_PERIPH_PULLUP_DIS_GPIO_16 = (1ul << 21), - M2M_PERIPH_PULLUP_DIS_GPIO_17 = (1ul << 22), - M2M_PERIPH_PULLUP_DIS_GPIO_18 = (1ul << 23), - M2M_PERIPH_PULLUP_DIS_GPIO_19 = (1ul << 24), - M2M_PERIPH_PULLUP_DIS_GPIO_20 = (1ul << 25), - M2M_PERIPH_PULLUP_DIS_GPIO_21 = (1ul << 26), - M2M_PERIPH_PULLUP_DIS_GPIO_22 = (1ul << 27), - M2M_PERIPH_PULLUP_DIS_GPIO_23 = (1ul << 28), - M2M_PERIPH_PULLUP_DIS_GPIO_24 = (1ul << 29), + M2M_PERIPH_PULLUP_DIS_HOST_WAKEUP = (1ul << 0), + M2M_PERIPH_PULLUP_DIS_RTC_CLK = (1ul << 1), + M2M_PERIPH_PULLUP_DIS_IRQN = (1ul << 2), + M2M_PERIPH_PULLUP_DIS_GPIO_3 = (1ul << 3), + M2M_PERIPH_PULLUP_DIS_GPIO_4 = (1ul << 4), + M2M_PERIPH_PULLUP_DIS_GPIO_5 = (1ul << 5), + M2M_PERIPH_PULLUP_DIS_SD_DAT3 = (1ul << 6), + M2M_PERIPH_PULLUP_DIS_SD_DAT2_SPI_RXD = (1ul << 7), + M2M_PERIPH_PULLUP_DIS_SD_DAT1_SPI_SSN = (1ul << 9), + M2M_PERIPH_PULLUP_DIS_SD_CMD_SPI_SCK = (1ul << 10), + M2M_PERIPH_PULLUP_DIS_SD_DAT0_SPI_TXD = (1ul << 11), + M2M_PERIPH_PULLUP_DIS_GPIO_6 = (1ul << 12), + M2M_PERIPH_PULLUP_DIS_SD_CLK = (1ul << 13), + M2M_PERIPH_PULLUP_DIS_I2C_SCL = (1ul << 14), + M2M_PERIPH_PULLUP_DIS_I2C_SDA = (1ul << 15), + M2M_PERIPH_PULLUP_DIS_GPIO_11 = (1ul << 16), + M2M_PERIPH_PULLUP_DIS_GPIO_12 = (1ul << 17), + M2M_PERIPH_PULLUP_DIS_GPIO_13 = (1ul << 18), + M2M_PERIPH_PULLUP_DIS_GPIO_14 = (1ul << 19), + M2M_PERIPH_PULLUP_DIS_GPIO_15 = (1ul << 20), + M2M_PERIPH_PULLUP_DIS_GPIO_16 = (1ul << 21), + M2M_PERIPH_PULLUP_DIS_GPIO_17 = (1ul << 22), + M2M_PERIPH_PULLUP_DIS_GPIO_18 = (1ul << 23), + M2M_PERIPH_PULLUP_DIS_GPIO_19 = (1ul << 24), + M2M_PERIPH_PULLUP_DIS_GPIO_20 = (1ul << 25), + M2M_PERIPH_PULLUP_DIS_GPIO_21 = (1ul << 26), + M2M_PERIPH_PULLUP_DIS_GPIO_22 = (1ul << 27), + M2M_PERIPH_PULLUP_DIS_GPIO_23 = (1ul << 28), + M2M_PERIPH_PULLUP_DIS_GPIO_24 = (1ul << 29), } tenuPullupMask; /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* @@ -199,200 +116,90 @@ FUNCTION PROTOTYPES #ifdef __cplusplus - extern "C" { +extern "C" { #endif /*! -@fn \ - NMI_API sint8 m2m_periph_init(tstrPerphInitParam * param); +@fn \ + NMI_API sint8 m2m_periph_gpio_set_dir(uint8 u8GpioNum, uint8 u8GpioDir); @brief - Initialize the NMC1500 peripheral driver module. + Configure a specific WINC15x0 pad as a GPIO and sets its direction (input or output). -@param [in] param - Peripheral module initialization structure. See members of tstrPerphInitParam. +@param[in] u8GpioNum + GPIO number. Allowed values are defined in @ref tenuGpioNum. + +@param[in] u8GpioDir + GPIO direction: Zero = input. Non-zero = output. @return - The function SHALL return 0 for success and a negative value otherwise. + The function returns @ref M2M_SUCCESS for success and a negative value otherwise. @sa - tstrPerphInitParam -*/ -NMI_API sint8 m2m_periph_init(tstrPerphInitParam * param); - -/*! -@fn \ - NMI_API sint8 m2m_periph_gpio_set_dir(uint8 u8GpioNum, uint8 u8GpioDir); - -@brief - Configure a specific NMC1500 pad as a GPIO and sets its direction (input or output). - -@param [in] u8GpioNum - GPIO number. Allowed values are defined in tenuGpioNum. - -@param [in] u8GpioDir - GPIO direction: Zero = input. Non-zero = output. - -@return - The function SHALL return 0 for success and a negative value otherwise. - -@sa - tenuGpioNum + tenuGpioNum */ NMI_API sint8 m2m_periph_gpio_set_dir(uint8 u8GpioNum, uint8 u8GpioDir); /*! -@fn \ - NMI_API sint8 m2m_periph_gpio_set_val(uint8 u8GpioNum, uint8 u8GpioVal); +@fn \ + NMI_API sint8 m2m_periph_gpio_set_val(uint8 u8GpioNum, uint8 u8GpioVal); @brief - Set an NMC1500 GPIO output level high or low. + Set an WINC15x0 GPIO output level high or low. -@param [in] u8GpioNum - GPIO number. Allowed values are defined in tenuGpioNum. +@param[in] u8GpioNum + GPIO number. Allowed values are defined in @ref tenuGpioNum. -@param [in] u8GpioVal - GPIO output value. Zero = low, non-zero = high. +@param[in] u8GpioVal + GPIO output value. Zero = low, non-zero = high. @return - The function SHALL return 0 for success and a negative value otherwise. + The function SHALL return 0 for success and a negative value otherwise. @sa - tenuGpioNum + tenuGpioNum */ NMI_API sint8 m2m_periph_gpio_set_val(uint8 u8GpioNum, uint8 u8GpioVal); /*! -@fn \ - NMI_API sint8 m2m_periph_gpio_get_val(uint8 u8GpioNum, uint8 * pu8GpioVal); +@fn \ + NMI_API sint8 m2m_periph_gpio_get_val(uint8 u8GpioNum, uint8 * pu8GpioVal); @brief - Read an NMC1500 GPIO input level. + Read an WINC15x0 GPIO input level. -@param [in] u8GpioNum - GPIO number. Allowed values are defined in tenuGpioNum. +@param[in] u8GpioNum + GPIO number. Allowed values are defined in @ref tenuGpioNum. @param [out] pu8GpioVal - GPIO input value. Zero = low, non-zero = high. + GPIO input value. Zero = low, non-zero = high. @return - The function SHALL return 0 for success and a negative value otherwise. + The function returns @ref M2M_SUCCESS for success and a negative value otherwise. @sa - tenuGpioNum + tenuGpioNum */ -NMI_API sint8 m2m_periph_gpio_get_val(uint8 u8GpioNum, uint8 * pu8GpioVal); +NMI_API sint8 m2m_periph_gpio_get_val(uint8 u8GpioNum, uint8 *pu8GpioVal); /*! -@fn \ - NMI_API sint8 m2m_periph_gpio_pullup_ctrl(uint8 u8GpioNum, uint8 u8PullupEn); +@fn \ + NMI_API sint8 m2m_periph_pullup_ctrl(uint32 pinmask, uint8 enable); @brief - Set an NMC1500 GPIO pullup resistor enable or disable. + Control the programmable pull-up resistor on the chip pads . -@param [in] u8GpioNum - GPIO number. Allowed values are defined in tenuGpioNum. +@param[in] pinmask + Write operation bitwise-ORed mask for which pads to control. Allowed values are defined in @ref tenuPullupMask. -@param [in] u8PullupEn - Zero: pullup disabled. Non-zero: pullup enabled. +@param[in] enable + Set to 0 to disable pull-up resistor. Non-zero will enable the pull-up. @return - The function SHALL return 0 for success and a negative value otherwise. + The function returns @ref M2M_SUCCESS for success and a negative value otherwise. @sa - tenuGpioNum -*/ -NMI_API sint8 m2m_periph_gpio_pullup_ctrl(uint8 u8GpioNum, uint8 u8PullupEn); - -/*! -@fn \ - NMI_API sint8 m2m_periph_i2c_master_init(tstrI2cMasterInitParam * param); - -@brief - Initialize and configure the NMC1500 I2C master peripheral. - -@param [in] param - I2C master initialization structure. See members of tstrI2cMasterInitParam. - -@return - The function SHALL return 0 for success and a negative value otherwise. - -@sa - tstrI2cMasterInitParam -*/ -NMI_API sint8 m2m_periph_i2c_master_init(tstrI2cMasterInitParam * param); - -/*! -@fn \ - NMI_API sint8 m2m_periph_i2c_master_write(uint8 u8SlaveAddr, uint8 * pu8Buf, uint16 u16BufLen, uint8 flags); - -@brief - Write a stream of bytes to the I2C slave device. - -@param [in] u8SlaveAddr - 7-bit I2C slave address. -@param [in] pu8Buf - A pointer to an input buffer which contains a stream of bytes. -@param [in] u16BufLen - Input buffer length in bytes. -@param [in] flags - Write operation bitwise-ORed flags. See tenuI2cMasterFlags. - -@return - The function SHALL return 0 for success and a negative value otherwise. - -@sa - tenuI2cMasterFlags -*/ -NMI_API sint8 m2m_periph_i2c_master_write(uint8 u8SlaveAddr, uint8 * pu8Buf, uint16 u16BufLen, uint8 flags); - - -/*! -@fn \ - NMI_API sint8 m2m_periph_i2c_master_read(uint8 u8SlaveAddr, uint8 * pu8Buf, uint16 u16BufLen, uint16 * pu16ReadLen, uint8 flags); - -@brief - Write a stream of bytes to the I2C slave device. - -@param [in] u8SlaveAddr - 7-bit I2C slave address. -@param [out] pu8Buf - A pointer to an output buffer in which a stream of bytes are received. -@param [in] u16BufLen - Max output buffer length in bytes. -@param [out] pu16ReadLen - Actual number of bytes received. -@param [in] flags - Write operation bitwise-ORed flags. See tenuI2cMasterFlags. - -@return - The function SHALL return 0 for success and a negative value otherwise. - -@sa - tenuI2cMasterFlags -*/ -NMI_API sint8 m2m_periph_i2c_master_read(uint8 u8SlaveAddr, uint8 * pu8Buf, uint16 u16BufLen, uint16 * pu16ReadLen, uint8 flags); - - -/*! -@fn \ - NMI_API sint8 m2m_periph_pullup_ctrl(uint32 pinmask, uint8 enable); - -@brief - Control the programmable pull-up resistor on the chip pads . - - -@param [in] pinmask - Write operation bitwise-ORed mask for which pads to control. Allowed values are defined in tenuPullupMask. - -@param [in] enable - Set to 0 to disable pull-up resistor. Non-zero will enable the pull-up. - -@return - The function SHALL return 0 for success and a negative value otherwise. - -@sa - tenuPullupMask + tenuPullupMask */ NMI_API sint8 m2m_periph_pullup_ctrl(uint32 pinmask, uint8 enable); @@ -400,5 +207,4 @@ NMI_API sint8 m2m_periph_pullup_ctrl(uint32 pinmask, uint8 enable); } #endif - #endif /* _M2M_PERIPH_H_ */ \ No newline at end of file diff --git a/src/drivers/winc1500/include/driver/include/m2m_ssl.h b/src/drivers/winc1500/include/driver/include/m2m_ssl.h index 33cb2307f..328d1fdb2 100644 --- a/src/drivers/winc1500/include/driver/include/m2m_ssl.h +++ b/src/drivers/winc1500/include/driver/include/m2m_ssl.h @@ -4,7 +4,7 @@ * * \brief WINC Application Interface Internal Types. * - * Copyright (c) 2017-2018 Microchip Technology Inc. and its subsidiaries. + * Copyright (c) 2017-2021 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * @@ -32,7 +32,7 @@ * */ -/**@defgroup SSLAPI SSL +/** @defgroup SSLAPI SSL @brief Provides a description of the SSL Layer. @{ @@ -60,19 +60,19 @@ INCLUDES #include "common/include/nm_common.h" #include "driver/include/m2m_types.h" #include "driver/include/nmdrv.h" -#include "driver/include/ecc_types.h" +#include "ecc_types.h" #include "socket/include/socket.h" /*! @ingroup SSLCallbacks @typedef void (*tpfAppSSLCb)(uint8 u8MsgType, void* pvMsg); -@brief A callback to get SSL notifications. -@param[in] u8MsgType +@brief A callback to get SSL notifications. +@param[in] u8MsgType The type of the message received. @param[in] pvMsg A structure to provide notification payload. */ -typedef void (*tpfAppSSLCb) (uint8 u8MsgType, void * pvMsg); +typedef void (*tpfAppSSLCb)(uint8 u8MsgType, void *pvMsg); /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* FUNCTION PROTOTYPES @@ -81,9 +81,9 @@ FUNCTION PROTOTYPES /*! @ingroup SSLFUNCTIONS @fn NMI_API sint8 m2m_ssl_init(tpfAppSSLCb pfAppSSLCb); - @brief Initializes the SSL layer. - @param [in] pfAppSslCb - Application SSL callback function. +@brief Initializes the SSL layer. +@param[in] pfAppSSLCb + Application SSL callback function. @return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. */ NMI_API sint8 m2m_ssl_init(tpfAppSSLCb pfAppSSLCb); @@ -91,72 +91,139 @@ NMI_API sint8 m2m_ssl_init(tpfAppSSLCb pfAppSSLCb); /*! @ingroup SSLFUNCTIONS @fn NMI_API sint8 m2m_ssl_handshake_rsp(tstrEccReqInfo* strECCResp, uint8* pu8RspDataBuff, uint16 u16RspDataSz); - @brief Sends ECC responses to the WINC - @param [in] strECCResp - ECC Response struct. - @param [in] pu8RspDataBuff - Pointer of the response data to be sent. - @param [in] u16RspDataSz - Response data size. +@brief Sends ECC responses to the WINC. +@param[in] strECCResp + ECC Response struct. +@param[in] pu8RspDataBuff + Pointer of the response data to be sent. +@param[in] u16RspDataSz + Response data size. @return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. */ -NMI_API sint8 m2m_ssl_handshake_rsp(tstrEccReqInfo* strECCResp, uint8* pu8RspDataBuff, uint16 u16RspDataSz); +NMI_API sint8 m2m_ssl_handshake_rsp(tstrEccReqInfo *strECCResp, uint8 *pu8RspDataBuff, uint16 u16RspDataSz); /*! @ingroup SSLFUNCTIONS @fn NMI_API sint8 m2m_ssl_send_certs_to_winc(uint8* pu8Buffer, uint32 u32BufferSz); - @brief Sends certificates to the WINC - @param [in] pu8Buffer - Pointer to the certificates. - @param [in] u32BufferSz - Size of the certificates. +@brief Sends certificates to the WINC. +@param[in] pu8Buffer + Pointer to the certificates. The buffer format must match the format of @ref tstrTlsSrvSecHdr. +@param[in] u32BufferSz + Size of the certificates. @return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. */ -NMI_API sint8 m2m_ssl_send_certs_to_winc(uint8* pu8Buffer, uint32 u32BufferSz); +NMI_API sint8 m2m_ssl_send_certs_to_winc(uint8 *pu8Buffer, uint32 u32BufferSz); /*! @ingroup SSLFUNCTIONS -@fn NMI_API sint8 m2m_ssl_retrieve_cert(uint16* pu16CurveType, uint8* pu8Hash, uint8* pu8Sig, tstrECPoint* pu8Key); - @brief Retrieve the certificate to be verified from the WINC - @param [in] pu16CurveType - Pointer to the certificate curve type. - @param [in] pu8Hash - Pointer to the certificate hash. - @param [in] pu8Sig - Pointer to the certificate signature. - @param [in] pu8Key - Pointer to the certificate Key. +@fn NMI_API sint8 m2m_ssl_retrieve_next_for_verifying(tenuEcNamedCurve *penuCurve, uint8 *pu8Value, uint16 *pu16ValueSz, uint8 *pu8Sig, uint16 *pu16SigSz, tstrECPoint *pstrKey); +@brief Retrieve the next set of information from the WINC for ECDSA verification. +@param[out] penuCurve + The named curve. +@param[out] pu8Value + Value retrieved for verification. This is the digest of the message, truncated/prepended to the appropriate size. +@param[inout] pu16ValueSz + in: Size of value buffer provided by caller. + out: Size of value retrieved (provided for convenience; the value size is in fact determined by the curve). +@param[out] pu8Sig + Signature retrieved for verification. +@param[inout] pu16SigSz + in: Size of signature buffer provided by caller. + out: Size of signature retrieved (provided for convenience; the signature size is in fact determined by the curve). +@param[out] pstrKey + Public key retrieved for verification. @return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. + +@pre This function should only be called after the application has been notified that + verification information is ready via @ref ECC_REQ_SIGN_VERIFY. + +@warning If this function returns @ref M2M_ERR_FAIL, then any remaining verification info from + the WINC is lost. */ -NMI_API sint8 m2m_ssl_retrieve_cert(uint16* pu16CurveType, uint8* pu8Hash, uint8* pu8Sig, tstrECPoint* pu8Key); +NMI_API sint8 m2m_ssl_retrieve_next_for_verifying(tenuEcNamedCurve *penuCurve, uint8 *pu8Value, uint16 *pu16ValueSz, uint8 *pu8Sig, uint16 *pu16SigSz, tstrECPoint *pstrKey); /*! @ingroup SSLFUNCTIONS -@fn NMI_API sint8 m2m_ssl_retrieve_hash(uint8* pu8Hash, uint16 u16HashSz); -@brief Retrieve the certificate hash. - @param [in] pu8Hash - Pointer to the certificate hash. - @param [in] u16HashSz - Hash size. +@fn NMI_API sint8 m2m_ssl_retrieve_cert(uint16* pu16Curve, uint8* pu8Value, uint8* pu8Sig, tstrECPoint* pstrKey); +@brief Retrieve the next set of information from the WINC for ECDSA verification. +@param[out] pu16Curve + The named curve, to be cast to type @ref tenuEcNamedCurve. +@param[out] pu8Value + Value retrieved for verification. This is the digest of the message, truncated/prepended to the appropriate size. + The size of the value is equal to the field size of the curve, hence is determined by pu16Curve. +@param[out] pu8Sig + Signature retrieved for verification. + The size of the signature is equal to twice the field size of the curve, hence is determined by pu16Curve. +@param[out] pstrKey + Public key retrieved for verification. @return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. + +@pre This function should only be called after the application has been notified that + verification information is ready via @ref ECC_REQ_SIGN_VERIFY. + +@warning If this function returns @ref M2M_ERR_FAIL, then any remaining verification info from + the WINC is lost. + +@warning This API has been deprecated and is kept for legacy purposes only. It is recommended + that @ref m2m_ssl_retrieve_next_for_verifying is used instead. */ -NMI_API sint8 m2m_ssl_retrieve_hash(uint8* pu8Hash, uint16 u16HashSz); +NMI_API sint8 m2m_ssl_retrieve_cert(uint16 *pu16Curve, uint8 *pu8Value, uint8 *pu8Sig, tstrECPoint *pstrKey); + +/*! +@ingroup SSLFUNCTIONS +@fn NMI_API sint8 m2m_ssl_retrieve_hash(uint8* pu8Value, uint16 u16ValueSz) +@brief Retrieve the value from the WINC for ECDSA signing. +@param[out] pu8Value + Value retrieved for signing. This is the digest of the message, truncated/prepended to the appropriate size. +@param[in] u16ValueSz + Size of value to be retrieved. (The application should obtain this information, + along with the curve, from the associated @ref ECC_REQ_SIGN_GEN notification.) +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. + +@pre This function should only be called after the application has been notified that + signing information is ready via @ref ECC_REQ_SIGN_GEN. + +@warning If this function returns @ref M2M_ERR_FAIL, then the value for signing is lost. +*/ +NMI_API sint8 m2m_ssl_retrieve_hash(uint8 *pu8Value, uint16 u16ValueSz); + +/*! +@ingroup SSLFUNCTIONS +@fn NMI_API void m2m_ssl_stop_retrieving(void); +@brief Allow SSL driver to tidy up when the application chooses not to retrieve all available + information. + +@warning The application must call this function if it has been notified (via + @ref ECC_REQ_SIGN_GEN or @ref ECC_REQ_SIGN_VERIFY) that information is available for + retrieving from the WINC, but chooses not to retrieve it all. + The application must not call this function if it has retrieved all the available + information, or if a retrieve function returned @ref M2M_ERR_FAIL indicating that any + remaining information has been lost. + +@see m2m_ssl_retrieve_next_for_verifying\n + m2m_ssl_retrieve_cert\n + m2m_ssl_retrieve_hash +*/ +NMI_API void m2m_ssl_stop_retrieving(void); /*! @ingroup SSLFUNCTIONS @fn NMI_API void m2m_ssl_stop_processing_certs(void); - @brief Allow ssl driver to tidy up in case application does not read all available certificates. - @warning This API must only be called if some certificates are left unread. - @return None. +@brief Allow SSL driver to tidy up in case application does not read all available certificates. + +@warning This API has been deprecated and is kept for legacy purposes only. It is recommended + that @ref m2m_ssl_stop_retrieving is used instead. */ NMI_API void m2m_ssl_stop_processing_certs(void); /*! @ingroup SSLFUNCTIONS @fn NMI_API void m2m_ssl_ecc_process_done(void); - @brief Allow ssl driver to tidy up after application has finished processing ecc message. -@warning This API must be called after receiving a SSL callback with message type @ref M2M_SSL_REQ_ECC. - @return None. +@brief Allow SSL driver to tidy up after application has finished processing ECC message. + +@warning The application should call this function after receiving an SSL callback with message + type @ref M2M_SSL_REQ_ECC, after retrieving any related information, and before + calling @ref m2m_ssl_handshake_rsp. */ NMI_API void m2m_ssl_ecc_process_done(void); @@ -166,19 +233,15 @@ NMI_API void m2m_ssl_ecc_process_done(void); @brief Sets the active ciphersuites. @details Override the default Active SSL ciphers in the SSL module with a certain combination selected by the caller in the form of a bitmap containing the required ciphers to be on.\n - There is no need to call this function if the application will not change the default ciphersuites. -@param [in] u32SslCsBMP - Bitmap containing the desired ciphers to be enabled for the SSL module. The ciphersuites are defined in - @ref SSLCipherSuiteID. - The default ciphersuites are all ciphersuites supported by the firmware with the exception of ECC ciphersuites. - The caller can override the default with any desired combination, except for combinations involving both RSA - and ECC; if any RSA ciphersuite is enabled, then firmware will disable all ECC ciphersuites. - If u32SslCsBMP does not contain any ciphersuites supported by firmware, then the current active list will not + There is no need to call this function if the application will not change the default ciphersuites. +@param[in] u32SslCsBMP + Bitmap containing the desired ciphers to be enabled for the SSL module. The ciphersuites are defined in + @ref SSLCipherSuiteID. + The default ciphersuites are all ciphersuites supported by the firmware with the exception of ECC ciphersuites. + The caller can override the default with any desired combination. + If u32SslCsBMP does not contain any ciphersuites supported by firmware, then the current active list will not change. - -@return - - @ref SOCK_ERR_NO_ERROR - - @ref SOCK_ERR_INVALID_ARG +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. */ NMI_API sint8 m2m_ssl_set_active_ciphersuites(uint32 u32SslCsBMP); diff --git a/src/drivers/winc1500/include/driver/include/m2m_types.h b/src/drivers/winc1500/include/driver/include/m2m_types.h index e5b8d263a..45f611ede 100644 --- a/src/drivers/winc1500/include/driver/include/m2m_types.h +++ b/src/drivers/winc1500/include/driver/include/m2m_types.h @@ -4,7 +4,7 @@ * * \brief WINC Application Interface Internal Types. * - * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries. + * Copyright (c) 2016-2021 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * @@ -49,6 +49,8 @@ INCLUDES #include "../../../firmware/wifi_v111/src/m2m/include/m2m_common.h" #endif #endif + +#ifndef _lint #endif @@ -81,59 +83,63 @@ MACROS #define M2M_GET_FW_PATCH(ver_info_word) M2M_GET_PATCH(M2M_GET_FW_VER(ver_info_word)) #define M2M_MAKE_VERSION(major, minor, patch) ( \ - ((uint16)((major) & 0xff) << M2M_MAJOR_SHIFT) | \ - ((uint16)((minor) & 0x0f) << M2M_MINOR_SHIFT) | \ - ((uint16)((patch) & 0x0f) << M2M_PATCH_SHIFT)) + ((uint16)((major) & 0xff) << M2M_MAJOR_SHIFT) | \ + ((uint16)((minor) & 0x0f) << M2M_MINOR_SHIFT) | \ + ((uint16)((patch) & 0x0f) << M2M_PATCH_SHIFT)) #define M2M_MAKE_VERSION_INFO(fw_major, fw_minor, fw_patch, drv_major, drv_minor, drv_patch) \ - ( \ - ( ((uint32)M2M_MAKE_VERSION((fw_major), (fw_minor), (fw_patch))) << M2M_FW_VERSION_SHIFT) | \ - ( ((uint32)M2M_MAKE_VERSION((drv_major), (drv_minor), (drv_patch))) << M2M_DRV_VERSION_SHIFT)) + ( \ + ( ((uint32)M2M_MAKE_VERSION((fw_major), (fw_minor), (fw_patch))) << M2M_FW_VERSION_SHIFT) | \ + ( ((uint32)M2M_MAKE_VERSION((drv_major), (drv_minor), (drv_patch))) << M2M_DRV_VERSION_SHIFT)) -#define REL_19_7_0_VER M2M_MAKE_VERSION_INFO(19,7,0,19,3,0) -#define REL_19_6_0_VER M2M_MAKE_VERSION_INFO(19,6,0,19,3,0) -#define REL_19_5_3_VER M2M_MAKE_VERSION_INFO(19,5,3,19,3,0) -#define REL_19_5_2_VER M2M_MAKE_VERSION_INFO(19,5,2,19,3,0) -#define REL_19_5_1_VER M2M_MAKE_VERSION_INFO(19,5,1,19,3,0) -#define REL_19_5_0_VER M2M_MAKE_VERSION_INFO(19,5,0,19,3,0) -#define REL_19_4_6_VER M2M_MAKE_VERSION_INFO(19,4,6,19,3,0) -#define REL_19_4_5_VER M2M_MAKE_VERSION_INFO(19,4,5,19,3,0) -#define REL_19_4_4_VER M2M_MAKE_VERSION_INFO(19,4,4,19,3,0) -#define REL_19_4_3_VER M2M_MAKE_VERSION_INFO(19,4,3,19,3,0) -#define REL_19_4_2_VER M2M_MAKE_VERSION_INFO(19,4,2,19,3,0) -#define REL_19_4_1_VER M2M_MAKE_VERSION_INFO(19,4,1,19,3,0) -#define REL_19_4_0_VER M2M_MAKE_VERSION_INFO(19,4,0,19,3,0) -#define REL_19_3_1_VER M2M_MAKE_VERSION_INFO(19,3,1,19,3,0) -#define REL_19_3_0_VER M2M_MAKE_VERSION_INFO(19,3,0,19,3,0) -#define REL_19_2_2_VER M2M_MAKE_VERSION_INFO(19,2,2,19,2,0) -#define REL_19_2_1_VER M2M_MAKE_VERSION_INFO(19,2,1,19,2,0) -#define REL_19_2_0_VER M2M_MAKE_VERSION_INFO(19,2,0,19,2,0) -#define REL_19_1_0_VER M2M_MAKE_VERSION_INFO(19,1,0,18,2,0) -#define REL_19_0_0_VER M2M_MAKE_VERSION_INFO(19,0,0,18,1,1) +#define REL_19_7_7_VER M2M_MAKE_VERSION_INFO(19,7,7,19,3,0) +#define REL_19_7_6_VER M2M_MAKE_VERSION_INFO(19,7,6,19,3,0) +#define REL_19_7_5_VER M2M_MAKE_VERSION_INFO(19,7,5,19,3,0) +#define REL_19_7_0_VER M2M_MAKE_VERSION_INFO(19,7,0,19,3,0) +#define REL_19_6_4_VER M2M_MAKE_VERSION_INFO(19,6,4,19,3,0) +#define REL_19_6_0_VER M2M_MAKE_VERSION_INFO(19,6,0,19,3,0) +#define REL_19_5_3_VER M2M_MAKE_VERSION_INFO(19,5,3,19,3,0) +#define REL_19_5_2_VER M2M_MAKE_VERSION_INFO(19,5,2,19,3,0) +#define REL_19_5_1_VER M2M_MAKE_VERSION_INFO(19,5,1,19,3,0) +#define REL_19_5_0_VER M2M_MAKE_VERSION_INFO(19,5,0,19,3,0) +#define REL_19_4_6_VER M2M_MAKE_VERSION_INFO(19,4,6,19,3,0) +#define REL_19_4_5_VER M2M_MAKE_VERSION_INFO(19,4,5,19,3,0) +#define REL_19_4_4_VER M2M_MAKE_VERSION_INFO(19,4,4,19,3,0) +#define REL_19_4_3_VER M2M_MAKE_VERSION_INFO(19,4,3,19,3,0) +#define REL_19_4_2_VER M2M_MAKE_VERSION_INFO(19,4,2,19,3,0) +#define REL_19_4_1_VER M2M_MAKE_VERSION_INFO(19,4,1,19,3,0) +#define REL_19_4_0_VER M2M_MAKE_VERSION_INFO(19,4,0,19,3,0) +#define REL_19_3_1_VER M2M_MAKE_VERSION_INFO(19,3,1,19,3,0) +#define REL_19_3_0_VER M2M_MAKE_VERSION_INFO(19,3,0,19,3,0) +#define REL_19_2_2_VER M2M_MAKE_VERSION_INFO(19,2,2,19,2,0) +#define REL_19_2_1_VER M2M_MAKE_VERSION_INFO(19,2,1,19,2,0) +#define REL_19_2_0_VER M2M_MAKE_VERSION_INFO(19,2,0,19,2,0) +#define REL_19_1_0_VER M2M_MAKE_VERSION_INFO(19,1,0,18,2,0) +#define REL_19_0_0_VER M2M_MAKE_VERSION_INFO(19,0,0,18,1,1) /*======*======*======*======* - FIRMWARE VERSION NO INFO + FIRMWARE VERSION NO INFO *======*======*======*======*/ -#define M2M_RELEASE_VERSION_MAJOR_NO (19) +#define M2M_RELEASE_VERSION_MAJOR_NO (19) /*!< Firmware Major release version number. */ #ifndef BLDTESTVERSION1 // the real version number must appear first as the release script greps it out to create folder. -#define M2M_RELEASE_VERSION_MINOR_NO (6) +#define M2M_RELEASE_VERSION_MINOR_NO (7) #else -#define M2M_RELEASE_VERSION_MINOR_NO (8) +#define M2M_RELEASE_VERSION_MINOR_NO (8) #endif /*!< Firmware Minor release version number. */ -#define M2M_RELEASE_VERSION_PATCH_NO (1) +#define M2M_RELEASE_VERSION_PATCH_NO (7) /*!< Firmware patch release version number. */ -#define M2M_RELEASE_VERSION_SVN_VERSION (SVN_REVISION) +#define M2M_RELEASE_VERSION_SVN_VERSION (SVN_REVISION) /*!< Firmware SVN release version number. */ @@ -141,20 +147,20 @@ MACROS SUPPORTED DRIVER VERSION NO INFO *======*======*======*======*/ -#define M2M_MIN_REQ_DRV_VERSION_MAJOR_NO (19) +#define M2M_MIN_REQ_DRV_VERSION_MAJOR_NO (19) /*!< Driver Major release version number. */ -#define M2M_MIN_REQ_DRV_VERSION_MINOR_NO (3) +#define M2M_MIN_REQ_DRV_VERSION_MINOR_NO (3) /*!< Driver Minor release version number. */ -#define M2M_MIN_REQ_DRV_VERSION_PATCH_NO (0) +#define M2M_MIN_REQ_DRV_VERSION_PATCH_NO (0) /*!< Driver patch release version number. */ -#define M2M_MIN_REQ_DRV_SVN_VERSION (0) +#define M2M_MIN_REQ_DRV_SVN_VERSION (0) /*!< Driver svn version. */ @@ -168,121 +174,120 @@ MACROS * @{ */ -#define M2M_BUFFER_MAX_SIZE (1600UL - 4) +#define M2M_BUFFER_MAX_SIZE (1600UL - 4) /*!< Maximum size for the shared packet buffer. */ - #define M2M_MAC_ADDRES_LEN 6 -/*!< The size fo 802 MAC address. +/*!< The size of the 802 MAC address. */ -#define M2M_ETHERNET_HDR_OFFSET 34 +#define M2M_ETHERNET_HDR_OFFSET 34 /*!< The offset of the Ethernet header within the WLAN Tx Buffer. */ -#define M2M_ETHERNET_HDR_LEN 14 +#define M2M_ETHERNET_HDR_LEN 14 /*!< Length of the Ethernet header in bytes. */ -#define M2M_MAX_SSID_LEN 33 +#define M2M_MAX_SSID_LEN 33 /*!< 1 more than the max SSID length. - This matches the size of SSID buffers (max SSID length + 1-byte length field). + This matches the size of SSID buffers (max SSID length + 1-byte length field). */ -#define M2M_MAX_PSK_LEN 65 +#define M2M_MAX_PSK_LEN 65 /*!< 1 more than the WPA PSK length (in ASCII format). - This matches the size of the WPA PSK/Passphrase buffer (max ASCII contents + 1-byte length field). - Alternatively it matches the WPA PSK length (in ASCII format) + 1 byte NULL termination. + This matches the size of the WPA PSK/Passphrase buffer (max ASCII contents + 1-byte length field). + Alternatively it matches the WPA PSK length (in ASCII format) + 1 byte NULL termination. */ -#define M2M_MIN_PSK_LEN 9 +#define M2M_MIN_PSK_LEN 9 /*!< 1 more than the minimum WPA PSK Passphrase length. - It matches the minimum WPA PSK Passphrase length + 1 byte NULL termination. + It matches the minimum WPA PSK Passphrase length + 1 byte NULL termination. */ -#define M2M_DEVICE_NAME_MAX 48 +#define M2M_DEVICE_NAME_MAX 48 /*!< Maximum Size for the device name including the NULL termination. */ -#define M2M_NTP_MAX_SERVER_NAME_LENGTH 32 +#define M2M_NTP_MAX_SERVER_NAME_LENGTH 32 /*!< Maximum NTP server name length */ -#define M2M_LISTEN_INTERVAL 1 +#define M2M_LISTEN_INTERVAL 1 /*!< The STA uses the Listen Interval parameter to indicate to the AP how - many beacon intervals it shall sleep before it retrieves the queued frames - from the AP. + many beacon intervals it shall sleep before it retrieves the queued frames + from the AP. */ -#define MAX_HIDDEN_SITES 4 +#define MAX_HIDDEN_SITES 4 /*!< - max number of hidden SSID supported by scan request + max number of hidden SSID supported by scan request */ -#define M2M_CUST_IE_LEN_MAX 252 +#define M2M_CUST_IE_LEN_MAX 252 /*!< The maximum size of IE (Information Element). */ -#define M2M_CRED_STORE_FLAG 0x01 +#define M2M_CRED_STORE_FLAG 0x01 /*!< Flag used in @ref tstrM2mConnCredHdr to indicate that Wi-Fi connection - credentials should be stored in WINC flash. + credentials should be stored in WINC flash. */ -#define M2M_CRED_ENCRYPT_FLAG 0x02 +#define M2M_CRED_ENCRYPT_FLAG 0x02 /*!< Flag used in @ref tstrM2mConnCredHdr to indicate that Wi-Fi connection - credentials should be encrypted when stored in WINC flash. + credentials should be encrypted when stored in WINC flash. */ -#define M2M_CRED_IS_STORED_FLAG 0x10 +#define M2M_CRED_IS_STORED_FLAG 0x10 /*!< Flag used in @ref tstrM2mConnCredHdr to indicate that Wi-Fi connection - credentials are stored in WINC flash. May only be set by WINC firmware. + credentials are stored in WINC flash. May only be set by WINC firmware. */ -#define M2M_CRED_IS_ENCRYPTED_FLAG 0x20 +#define M2M_CRED_IS_ENCRYPTED_FLAG 0x20 /*!< Flag used in @ref tstrM2mConnCredHdr to indicate that Wi-Fi connection - credentials are encrypted in WINC flash. May only be set by WINC firmware. + credentials are encrypted in WINC flash. May only be set by WINC firmware. */ -#define M2M_WIFI_CONN_BSSID_FLAG 0x01 +#define M2M_WIFI_CONN_BSSID_FLAG 0x01 /*!< Flag used in @ref tstrM2mConnCredCmn to indicate that Wi-Fi connection - must be restricted to an AP with a certain BSSID. + must be restricted to an AP with a certain BSSID. */ -#define M2M_AUTH_1X_USER_LEN_MAX 132 +#define M2M_AUTH_1X_USER_LEN_MAX 100 /*!< The maximum length (in ASCII characters) of domain name + username (including '@' or '\') - for authentication with Enterprise methods. + for authentication with Enterprise methods. */ -#define M2M_AUTH_1X_PASSWORD_LEN_MAX 256 +#define M2M_AUTH_1X_PASSWORD_LEN_MAX 256 /*!< The maximum length (in ASCII characters) of password for authentication with Enterprise MSCHAPv2 methods. */ -#define M2M_AUTH_1X_PRIVATEKEY_LEN_MAX 256 +#define M2M_AUTH_1X_PRIVATEKEY_LEN_MAX 256 /*!< The maximum length (in bytes) of private key modulus for authentication with Enterprise TLS methods. - Private key exponent must be the same length as modulus, pre-padded with 0s if necessary. + Private key exponent must be the same length as modulus, pre-padded with 0s if necessary. */ -#define M2M_AUTH_1X_CERT_LEN_MAX 1584 +#define M2M_AUTH_1X_CERT_LEN_MAX 1584 /*!< The maximum length (in bytes) of certificate for authentication with Enterprise TLS methods. */ -#define M2M_802_1X_UNENCRYPTED_USERNAME_FLAG 0x80 +#define M2M_802_1X_UNENCRYPTED_USERNAME_FLAG 0x80 /*!< Flag to indicate that the 802.1x user-name should be sent (unencrypted) in the initial EAP - identity response. Intended for use with EAP-TLS only. + identity response. Intended for use with EAP-TLS only. */ -#define M2M_802_1X_PREPEND_DOMAIN_FLAG 0x40 +#define M2M_802_1X_PREPEND_DOMAIN_FLAG 0x40 /*!< Flag to indicate that the 802.1x domain name should be prepended to the user-name: - "Domain\Username". If the flag is not set then domain name is appended to the user-name: - "Username@Domain". (Note that the '@' or '\' must be included in the domain name.) + "Domain\Username". If the flag is not set then domain name is appended to the user-name: + "Username@Domain". (Note that the '@' or '\' must be included in the domain name.) */ -#define M2M_802_1X_MSCHAP2_FLAG 0x01 +#define M2M_802_1X_MSCHAP2_FLAG 0x01 /*!< Flag to indicate 802.1x MsChapV2 credentials: domain/user-name/password. */ -#define M2M_802_1X_TLS_FLAG 0x02 +#define M2M_802_1X_TLS_FLAG 0x02 /*!< Flag to indicate 802.1x TLS credentials: domain/user-name/private-key/certificate. */ -#define M2M_802_1X_TLS_CLIENT_CERTIFICATE 1 +#define M2M_802_1X_TLS_CLIENT_CERTIFICATE 1 /*!< Info type used in @ref tstrM2mWifiAuthInfoHdr to indicate Enterprise TLS client certificate. */ -#define PSK_CALC_LEN 40 +#define PSK_CALC_LEN 40 /*!< PSK is 32 bytes generated either: - from 64 ASCII characters - by SHA1 operations on up to 63 ASCII characters @@ -295,34 +300,34 @@ MACROS * WIFI GROUP requests */ -#define M2M_CONFIG_CMD_BASE 1 +#define M2M_CONFIG_CMD_BASE 1 /*!< The base value of all the host configuration commands opcodes. */ -#define M2M_STA_CMD_BASE 40 +#define M2M_STA_CMD_BASE 40 /*!< The base value of all the station mode host commands opcodes. */ -#define M2M_AP_CMD_BASE 70 +#define M2M_AP_CMD_BASE 70 /*!< The base value of all the Access Point mode host commands opcodes. */ -/**@}*/ //WlanDefines -#define M2M_P2P_CMD_BASE 90 +/**@cond P2P_DOC + */ +#define M2M_P2P_CMD_BASE 90 /*!< The base value of all the P2P mode host commands opcodes. */ -/**@addtogroup WlanDefines - * @{ - */ -#define M2M_SERVER_CMD_BASE 100 +/**@endcond*/ //P2P_DOC + +#define M2M_SERVER_CMD_BASE 100 /*!< The base value of all the power save mode host commands codes. */ -#define M2M_GEN_CMD_BASE 105 +#define M2M_GEN_CMD_BASE 105 /*!< The base value of additional host wifi command opcodes. * Usage restrictions (eg STA mode only) should always be made clear at the API layer in any case. */ /********************** * OTA GROUP requests */ -#define M2M_OTA_CMD_BASE 100 +#define M2M_OTA_CMD_BASE 100 /*!< The base value of all the OTA mode host commands opcodes. * The OTA Have special group so can extended from 1-M2M_MAX_GRP_NUM_REQ */ @@ -330,49 +335,49 @@ MACROS * * CRYPTO group requests */ -#define M2M_CRYPTO_CMD_BASE 1 +#define M2M_CRYPTO_CMD_BASE 1 /*!< The base value of all the crypto mode host commands opcodes. * The crypto Have special group so can extended from 1-M2M_MAX_GRP_NUM_REQ */ -#define M2M_MAX_GRP_NUM_REQ (127) +#define M2M_MAX_GRP_NUM_REQ (127) /*!< max number of request in one group equal to 127 as the last bit reserved for config or data pkt */ -#define WEP_40_KEY_SIZE ((uint8)5) +#define WEP_40_KEY_SIZE ((uint8)5) /*!< The size in bytes of a 40-bit wep key. */ -#define WEP_104_KEY_SIZE ((uint8)13) +#define WEP_104_KEY_SIZE ((uint8)13) /*!< The size in bytes of a 104-bit wep key. */ -#define WEP_40_KEY_STRING_SIZE ((uint8)10) +#define WEP_40_KEY_STRING_SIZE ((uint8)10) /*!< The string length of a 40-bit wep key. */ -#define WEP_104_KEY_STRING_SIZE ((uint8)26) +#define WEP_104_KEY_STRING_SIZE ((uint8)26) /*!< The string length of a 104-bit wep key. */ -#define WEP_KEY_MAX_INDEX ((uint8)4) +#define WEP_KEY_MAX_INDEX ((uint8)4) /*!< WEP key index is in the range 1 to 4 inclusive. (This is decremented to * result in an index in the range 0 to 3 on air.) */ -#define M2M_SHA256_CONTEXT_BUFF_LEN (128) +#define M2M_SHA256_CONTEXT_BUFF_LEN (128) /*!< sha256 context size */ -#define M2M_SCAN_DEFAULT_NUM_SLOTS (2) +#define M2M_SCAN_DEFAULT_NUM_SLOTS (2) /*!< The default number of scan slots used by the WINC board. */ -#define M2M_SCAN_DEFAULT_SLOT_TIME (30) -/*!< The default duration in miliseconds of an active scan slot used by the WINC board. +#define M2M_SCAN_DEFAULT_SLOT_TIME (30) +/*!< The default duration in milliseconds of an active scan slot used by the WINC board. */ -#define M2M_SCAN_DEFAULT_PASSIVE_SLOT_TIME (300) +#define M2M_SCAN_DEFAULT_PASSIVE_SLOT_TIME (300) /*!< The passive scan slot default duration in ms. */ -#define M2M_SCAN_DEFAULT_NUM_PROBE (2) +#define M2M_SCAN_DEFAULT_NUM_PROBE (2) /*!< The default number of probes per scan slot. */ -#define M2M_FASTCONNECT_DEFAULT_RSSI_THRESH (-45) +#define M2M_FASTCONNECT_DEFAULT_RSSI_THRESH (-45) /*!< The default threshold RSSI for fast reconnection to an AP. */ @@ -396,20 +401,20 @@ MACROS #define TLS_CRL_DATA_MAX_LEN 64 /* Every bit have 3dB gain control each. - for example: - 1 ->3db - 3 ->6db - 7 ->9db - */ - uint16 u8PPAGFor11GN; - /*!< PPA gain for 11GN (as the RF document represented) - PPA_AGC<0:2> Every bit have 3dB gain control each. - for example: - 1 ->3db - 3 ->6db - 7 ->9db - */ -}tstrM2mWifiGainsParams; +typedef enum { + SNTP_DISABLE_DHCP = 0, + /*!< Don't use the NTP server provided by the DHCP server when falling back. + */ + SNTP_ENABLE_DHCP = 1 + /*!< Use the NTP server provided by the DHCP server when falling back. + */ +} tenuSNTPUseDHCP; /*! -@struct \ - tstrM2mConnCredHdr +@struct \ + tstrM2mWifiGainsParams @brief - Wi-Fi Connect Credentials Header + Gain Values */ -typedef struct{ - uint16 u16CredSize; - /*!< Total size of connect credentials, not including tstrM2mConnCredHdr: - tstrM2mConnCredCmn - Auth details (variable size) - */ - uint8 u8CredStoreFlags; - /*!< Credential storage options represented with flags: - @ref M2M_CRED_STORE_FLAG - @ref M2M_CRED_ENCRYPT_FLAG - @ref M2M_CRED_IS_STORED_FLAG - @ref M2M_CRED_IS_ENCRYPTED_FLAG - */ - uint8 u8Channel; - /*!< Wi-Fi channel(s) on which to attempt connection. */ -}tstrM2mConnCredHdr; +typedef struct { + uint16 u8PPAGFor11B; + /*!< PPA gain for 11B (as the RF document representation) + PPA_AGC<0:2> Every bit have 3dB gain control each. + for example: + 1 ->3db + 3 ->6db + 7 ->9db + */ + uint16 u8PPAGFor11GN; + /*!< PPA gain for 11GN (as the RF document represented) + PPA_AGC<0:2> Every bit have 3dB gain control each. + for example: + 1 ->3db + 3 ->6db + 7 ->9db + */ +} tstrM2mWifiGainsParams; + +/*! +@struct \ + tstrM2mConnCredHdr + +@brief + Wi-Fi Connect Credentials Header +*/ +typedef struct { + uint16 u16CredSize; + /*!< Total size of connect credentials, not including tstrM2mConnCredHdr: + tstrM2mConnCredCmn + Auth details (variable size) + */ + uint8 u8CredStoreFlags; + /*!< Credential storage options represented with flags: + @ref M2M_CRED_STORE_FLAG + @ref M2M_CRED_ENCRYPT_FLAG + @ref M2M_CRED_IS_STORED_FLAG + @ref M2M_CRED_IS_ENCRYPTED_FLAG + */ + uint8 u8Channel; + /*!< Wi-Fi channel(s) on which to attempt connection. */ +} tstrM2mConnCredHdr; #ifndef _lint #ifdef __GNUC__ _Static_assert((sizeof(tstrM2mConnCredHdr)%4)==0, "Structure alignment error"); @@ -1497,28 +1450,28 @@ static_assert((sizeof(tstrM2mConnCredHdr)%4)==0, "Structure alignment error"); #endif /*! -@struct \ - tstrM2mConnCredCmn +@struct \ + tstrM2mConnCredCmn @brief - Wi-Fi Connect Credentials Common section -*/ -typedef struct{ - uint8 u8SsidLen; - /*!< SSID length. */ - uint8 au8Ssid[M2M_MAX_SSID_LEN-1]; - /*!< SSID. */ - uint8 u8Options; - /*!< Common flags: - @ref M2M_WIFI_CONN_BSSID_FLAG - */ - uint8 au8Bssid[M2M_MAC_ADDRES_LEN]; - /*!< BSSID to restrict on, or all 0 if M2M_WIFI_CONN_BSSID_FLAG is not set in u8Options. */ - uint8 u8AuthType; - /*!< Connection auth type. See @ref tenuM2mSecType. */ - uint8 au8Rsv[3]; - /*!< Reserved for future use. Set to 0. */ -}tstrM2mConnCredCmn; + Wi-Fi Connect Credentials Common section + */ +typedef struct { + uint8 u8SsidLen; + /*!< SSID length. */ + uint8 au8Ssid[M2M_MAX_SSID_LEN-1]; + /*!< SSID. */ + uint8 u8Options; + /*!< Common flags: + @ref M2M_WIFI_CONN_BSSID_FLAG + */ + uint8 au8Bssid[M2M_MAC_ADDRES_LEN]; + /*!< BSSID to restrict on, or all 0 if M2M_WIFI_CONN_BSSID_FLAG is not set in u8Options. */ + uint8 u8AuthType; + /*!< Connection auth type. See @ref tenuM2mSecType. */ + uint8 au8Rsv[3]; + /*!< Reserved for future use. Set to 0. */ +} tstrM2mConnCredCmn; #ifndef _lint #ifdef __GNUC__ _Static_assert((sizeof(tstrM2mConnCredCmn)%4)==0, "Structure alignment error"); @@ -1528,22 +1481,22 @@ static_assert((sizeof(tstrM2mConnCredCmn)%4)==0, "Structure alignment error"); #endif /*! -@struct \ - tstrM2mWifiWep +@struct \ + tstrM2mWifiWep @brief - WEP security key header. - */ -typedef struct{ - uint8 u8KeyIndex; - /*!< WEP Key Index. */ - uint8 u8KeyLen; - /*!< WEP Key Size. */ - uint8 au8WepKey[WEP_104_KEY_SIZE]; - /*!< WEP Key represented in bytes (padded with 0's if WEP-40). */ - uint8 u8Rsv; - /*!< Reserved for future use. Set to 0. */ -}tstrM2mWifiWep; + WEP security key header. +*/ +typedef struct { + uint8 u8KeyIndex; + /*!< WEP Key Index. */ + uint8 u8KeyLen; + /*!< WEP Key Size. */ + uint8 au8WepKey[WEP_104_KEY_SIZE]; + /*!< WEP Key represented in bytes (padded with 0's if WEP-40). */ + uint8 u8Rsv; + /*!< Reserved for future use. Set to 0. */ +} tstrM2mWifiWep; #ifndef _lint #ifdef __GNUC__ _Static_assert((sizeof(tstrM2mWifiWep)%4)==0, "Structure alignment error"); @@ -1553,24 +1506,24 @@ static_assert((sizeof(tstrM2mWifiWep)%4)==0, "Structure alignment error"); #endif /*! -@struct \ +@struct \ tstrM2mWifiPsk @brief - Passphrase and PSK for WPA(2) PSK. + Passphrase and PSK for WPA(2) PSK. */ -typedef struct{ - uint8 u8PassphraseLen; - /*!< Length of passphrase (8 to 63) or 64 if au8Passphrase contains ASCII representation of PSK. */ - uint8 au8Passphrase[M2M_MAX_PSK_LEN-1]; - /*!< Passphrase, or ASCII representation of PSK if u8PassphraseLen is 64. */ - uint8 au8Psk[PSK_CALC_LEN]; - /*!< PSK calculated by firmware. Driver sets this to 0. */ - uint8 u8PskCalculated; - /*!< Flag used by firmware to avoid unnecessary recalculation of PSK. Driver sets this to 0. */ - uint8 au8Rsv[2]; - /*!< Reserved for future use. Set to 0. */ -}tstrM2mWifiPsk; +typedef struct { + uint8 u8PassphraseLen; + /*!< Length of passphrase (8 to 63) or 64 if au8Passphrase contains ASCII representation of PSK. */ + uint8 au8Passphrase[M2M_MAX_PSK_LEN-1]; + /*!< Passphrase, or ASCII representation of PSK if u8PassphraseLen is 64. */ + uint8 au8Psk[PSK_CALC_LEN]; + /*!< PSK calculated by firmware. Driver sets this to 0. */ + uint8 u8PskCalculated; + /*!< Flag used by firmware to avoid unnecessary recalculation of PSK. Driver sets this to 0. */ + uint8 au8Rsv[2]; + /*!< Reserved for future use. Set to 0. */ +} tstrM2mWifiPsk; #ifndef _lint #ifdef __GNUC__ _Static_assert((sizeof(tstrM2mWifiPsk)%4)==0, "Structure alignment error"); @@ -1580,39 +1533,49 @@ static_assert((sizeof(tstrM2mWifiPsk)%4)==0, "Structure alignment error"); #endif /*! -@struct \ - tstrM2mWifi1xHdr +@struct \ + tstrM2mWifi1xHdr @brief - Wi-Fi Authentication 802.1x header for parameters. - The parameters (Domain, UserName, PrivateKey/Password) are appended to this structure. + Wi-Fi Authentication 802.1x header for parameters. + The parameters (Domain, UserName, PrivateKey/Password) are appended to this structure. */ -typedef struct{ - uint8 u8Flags; - /*!< 802.1x-specific flags: - @ref M2M_802_1X_MSCHAP2_FLAG - @ref M2M_802_1X_TLS_FLAG - @ref M2M_802_1X_UNENCRYPTED_USERNAME_FLAG - @ref M2M_802_1X_PREPEND_DOMAIN_FLAG - */ - uint8 u8DomainLength; - /*!< Length of Domain. (Offset of Domain understood to be 0.) */ - uint16 u16UserNameLength; - /*!< Length of UserName. (Offset of UserName understood to be u8DomainLength.) */ - uint16 u16PrivateKeyOffset; - /*!< Offset within au81xAuthDetails of PrivateKey/Password. */ - uint16 u16PrivateKeyLength; - /*!< Length of PrivateKey/Password. In the case of PrivateKey, this is the length of the modulus. */ - uint16 u16CertificateOffset; - /*!< Offset within au81xAuthDetails of Certificate. */ - uint16 u16CertificateLength; - /*!< Length of Certificate. */ - uint8 au81xAuthDetails[]; - /*!< Placeholder for concatenation of Domain, UserName, PrivateKey/Password, Certificate. - Padding (0s) is placed between UserName and PrivateKey/Password so that - PrivateKey/Password begins on a 4-byte boundary. - Certificate (for 1x Tls only) is sent over HIF separately from the other parameters. */ -}tstrM2mWifi1xHdr; +typedef struct { + uint8 u8Flags; + /*!< 802.1x-specific flags: + @ref M2M_802_1X_MSCHAP2_FLAG + @ref M2M_802_1X_TLS_FLAG + @ref M2M_802_1X_UNENCRYPTED_USERNAME_FLAG + @ref M2M_802_1X_PREPEND_DOMAIN_FLAG + */ + uint8 u8DomainLength; + /*!< Length of Domain. (Offset of Domain, within au81xAuthDetails, understood to be 0.) */ + uint8 u8UserNameLength; + /*!< Length of UserName. (Offset of UserName, within au81xAuthDetails, understood to be u8DomainLength.) */ + uint8 u8HdrLength; + /*!< Length of header (offset of au81xAuthDetails within tstrM2mWifi1xHdr). + Legacy implementations may have 0 here, in which case header is 12 bytes. + The unusual placing of this field is in order to hit a zero in legacy implementations. */ + uint16 u16PrivateKeyOffset; + /*!< Offset within au81xAuthDetails of PrivateKey/Password. */ + uint16 u16PrivateKeyLength; + /*!< Length of PrivateKey/Password. In the case of PrivateKey, this is the length of the modulus. */ + uint16 u16CertificateOffset; + /*!< Offset within au81xAuthDetails of Certificate. */ + uint16 u16CertificateLength; + /*!< Length of Certificate. */ + uint8 au8TlsSpecificRootNameSha1[20]; + /*!< SHA1 digest of subject name to identify specific root certificate for phase 1 server verification. */ + uint32 u32Rsv1; + /*!< Reserved, set to 0. */ + uint32 u32TlsHsFlags; + /*!< TLS handshake flags for phase 1. */ + uint32 u32Rsv2; + /*!< Reserved, set to 0. */ + uint8 au81xAuthDetails[]; + /*!< Placeholder for concatenation of Domain, UserName, PrivateKey/Password, Certificate. + Certificate (for 1x Tls only) is sent over HIF separately from the other parameters. */ +} tstrM2mWifi1xHdr; #ifndef _lint #ifdef __GNUC__ _Static_assert((sizeof(tstrM2mWifi1xHdr)%4)==0, "Structure alignment error"); @@ -1622,27 +1585,27 @@ static_assert((sizeof(tstrM2mWifi1xHdr)%4)==0, "Structure alignment error"); #endif /*! -@struct \ - tstrM2mWifiAuthInfoHdr +@struct \ + tstrM2mWifiAuthInfoHdr @brief - Generic Wi-Fi authentication information to be sent in a separate HIF message of type - @ref M2M_WIFI_IND_CONN_PARAM (preceding @ref M2M_WIFI_REQ_CONN). - */ -typedef struct{ - uint8 u8Type; - /*!< Type of info: - @ref M2M_802_1X_TLS_CLIENT_CERTIFICATE - */ - uint8 au8Rsv[3]; - /*!< Reserved for future use. Set to 0. */ - uint16 u16InfoPos; - /*!< Information about positioning of the Info. The interpretation depends on u8Type. */ - uint16 u16InfoLen; - /*!< Info length (not including this header). */ - uint8 au8Info[]; - /*!< Placeholder for info. */ -}tstrM2mWifiAuthInfoHdr; + Generic Wi-Fi authentication information to be sent in a separate HIF message of type + @ref M2M_WIFI_IND_CONN_PARAM (preceding @ref M2M_WIFI_REQ_CONN). +*/ +typedef struct { + uint8 u8Type; + /*!< Type of info: + @ref M2M_802_1X_TLS_CLIENT_CERTIFICATE + */ + uint8 au8Rsv[3]; + /*!< Reserved for future use. Set to 0. */ + uint16 u16InfoPos; + /*!< Information about positioning of the Info. The interpretation depends on u8Type. */ + uint16 u16InfoLen; + /*!< Info length (not including this header). */ + uint8 au8Info[]; + /*!< Placeholder for info. */ +} tstrM2mWifiAuthInfoHdr; #ifndef _lint #ifdef __GNUC__ _Static_assert((sizeof(tstrM2mWifiAuthInfoHdr)%4)==0, "Structure alignment error"); @@ -1652,25 +1615,25 @@ static_assert((sizeof(tstrM2mWifiAuthInfoHdr)%4)==0, "Structure alignment error" #endif /*! -@struct \ - tstrM2mWifiConnHdr +@struct \ + tstrM2mWifiConnHdr @brief - Wi-Fi Connect Request (new format) for use with @ref M2M_WIFI_REQ_CONN. - This structure is sent across the HIF along with the relevant auth details. One of: - @ref tstrM2mWifiPsk - @ref tstrM2mWifiWep - @ref tstrM2mWifi1xHdr - If further authentication details need to be sent (such as client certificate for 1x TLS), they - are sent with header @ref tstrM2mWifiAuthInfoHdr in a preceding HIF message of type - @ref M2M_WIFI_IND_CONN_PARAM + Wi-Fi Connect Request (new format) for use with @ref M2M_WIFI_REQ_CONN. + This structure is sent across the HIF along with the relevant auth details. One of: + @ref tstrM2mWifiPsk + @ref tstrM2mWifiWep + @ref tstrM2mWifi1xHdr + If further authentication details need to be sent (such as client certificate for 1x TLS), they + are sent with header @ref tstrM2mWifiAuthInfoHdr in a preceding HIF message of type + @ref M2M_WIFI_IND_CONN_PARAM */ -typedef struct{ - tstrM2mConnCredHdr strConnCredHdr; - /*!< Credentials header. */ - tstrM2mConnCredCmn strConnCredCmn; - /*!< Credentials common section, including auth type and SSID. */ -}tstrM2mWifiConnHdr; +typedef struct { + tstrM2mConnCredHdr strConnCredHdr; + /*!< Credentials header. */ + tstrM2mConnCredCmn strConnCredCmn; + /*!< Credentials common section, including auth type and SSID. */ +} tstrM2mWifiConnHdr; #ifndef _lint #ifdef __GNUC__ _Static_assert((sizeof(tstrM2mWifiConnHdr)%4)==0, "Structure alignment error"); @@ -1680,22 +1643,22 @@ static_assert((sizeof(tstrM2mWifiConnHdr)%4)==0, "Structure alignment error"); #endif /*! -@struct \ - tstrM2mWifiApId +@struct \ + tstrM2mWifiApId @brief - Specify an access point (by SSID) + Specify an access point (by SSID) */ -typedef struct{ - uint8 au8SSID[M2M_MAX_SSID_LEN]; - /*!< - SSID of the desired AP, prefixed by length byte. - First byte 0xFF used to mean all access points. - */ - uint8 __PAD__[3]; - /*!< Padding bytes for forcing 4-byte alignment - */ -}tstrM2mWifiApId; +typedef struct { + uint8 au8SSID[M2M_MAX_SSID_LEN]; + /*!< + SSID of the desired AP, prefixed by length byte. + First byte 0xFF used to mean all access points. + */ + uint8 __PAD__[3]; + /*!< Padding bytes for forcing 4-byte alignment + */ +} tstrM2mWifiApId; #ifndef _lint #ifdef __GNUC__ _Static_assert((sizeof(tstrM2mWifiApId)%4)==0, "Structure alignment error"); @@ -1705,21 +1668,21 @@ static_assert((sizeof(tstrM2mWifiApId)%4)==0, "Structure alignment error"); #endif /*! -@struct \ - tstrM2MGenericResp +@struct \ + tstrM2MGenericResp @brief - Generic success/error response + Generic success/error response */ -typedef struct{ - sint8 s8ErrorCode; - /*!< - Generic success/error code. Possible values are: - - @ref M2M_SUCCESS - - @ref M2M_ERR_FAIL - */ - uint8 __PAD24__[3]; -}tstrM2MGenericResp; +typedef struct { + sint8 s8ErrorCode; + /*!< + Generic success/error code. Possible values are: + - @ref M2M_SUCCESS + - @ref M2M_ERR_FAIL + */ + uint8 __PAD24__[3]; +} tstrM2MGenericResp; #ifndef _lint #ifdef __GNUC__ _Static_assert((sizeof(tstrM2MGenericResp)%4)==0, "Structure alignment error"); @@ -1729,26 +1692,26 @@ static_assert((sizeof(tstrM2MGenericResp)%4)==0, "Structure alignment error"); #endif /*! -@struct \ - tstrM2MWPSConnect +@struct \ + tstrM2MWPSConnect @brief - WPS Configuration parameters + WPS Configuration parameters @sa - tenuWPSTrigger + tenuWPSTrigger */ typedef struct { - uint8 u8TriggerType; - /*!< WPS triggering method (Push button or PIN) - */ - char acPinNumber[8]; - /*!< WPS PIN No (for PIN method) - */ - uint8 __PAD24__[3]; - /*!< Padding bytes for forcing 4-byte alignment - */ -}tstrM2MWPSConnect; + uint8 u8TriggerType; + /*!< WPS triggering method (Push button or PIN) + */ + char acPinNumber[8]; + /*!< WPS PIN No (for PIN method) + */ + uint8 __PAD24__[3]; + /*!< Padding bytes for forcing 4-byte alignment + */ +} tstrM2MWPSConnect; #ifndef _lint #ifdef __GNUC__ _Static_assert((sizeof(tstrM2MWPSConnect)%4)==0, "Structure alignment error"); @@ -1758,54 +1721,54 @@ static_assert((sizeof(tstrM2MWPSConnect)%4)==0, "Structure alignment error"); #endif /*! -@struct \ - tstrM2MWPSInfo +@struct \ + tstrM2MWPSInfo -@brief WPS Result +@brief WPS Result - This structure is passed to the application in response to a WPS request. If the WPS session is completed successfully, the - structure will have Non-ZERO authentication type. If the WPS Session fails (due to error or timeout) the authentication type - is set to ZERO. + This structure is passed to the application in response to a WPS request. If the WPS session is completed successfully, the + structure will have Non-ZERO authentication type. If the WPS Session fails (due to error or timeout) the authentication type + is set to ZERO. @sa - tenuM2mSecType + tenuM2mSecType */ -typedef struct{ - uint8 u8AuthType; - /*!< Network authentication type. - */ - uint8 u8Ch; - /*!< RF Channel for the AP. - */ - uint8 au8SSID[M2M_MAX_SSID_LEN]; - /*!< SSID obtained from WPS. - */ - uint8 au8PSK[M2M_MAX_PSK_LEN]; - /*!< PSK for the network obtained from WPS. - */ -}tstrM2MWPSInfo; +typedef struct { + uint8 u8AuthType; + /*!< Network authentication type. + */ + uint8 u8Ch; + /*!< RF Channel for the AP. + */ + uint8 au8SSID[M2M_MAX_SSID_LEN]; + /*!< SSID obtained from WPS. + */ + uint8 au8PSK[M2M_MAX_PSK_LEN]; + /*!< PSK for the network obtained from WPS. + */ +} tstrM2MWPSInfo; /*! -@struct \ - tstrM2MDefaultConnResp +@struct \ + tstrM2MDefaultConnResp @brief - Response error of the m2m_default_connect + Response error of the m2m_default_connect @sa - M2M_DEFAULT_CONN_SCAN_MISMATCH - M2M_DEFAULT_CONN_EMPTY_LIST + M2M_DEFAULT_CONN_SCAN_MISMATCH + M2M_DEFAULT_CONN_EMPTY_LIST */ -typedef struct{ - sint8 s8ErrorCode; - /*!< - Default connect error code. possible values are: - - M2M_DEFAULT_CONN_EMPTY_LIST - - M2M_DEFAULT_CONN_SCAN_MISMATCH - */ - uint8 __PAD24__[3]; -}tstrM2MDefaultConnResp; +typedef struct { + sint8 s8ErrorCode; + /*!< + Default connect error code. possible values are: + - M2M_DEFAULT_CONN_EMPTY_LIST + - M2M_DEFAULT_CONN_SCAN_MISMATCH + */ + uint8 __PAD24__[3]; +} tstrM2MDefaultConnResp; #ifndef _lint #ifdef __GNUC__ _Static_assert((sizeof(tstrM2MDefaultConnResp)%4)==0, "Structure alignment error"); @@ -1815,61 +1778,102 @@ static_assert((sizeof(tstrM2MDefaultConnResp)%4)==0, "Structure alignment error" #endif /*! -@struct \ - tstrM2MScanOption +@struct \ + tstrM2MScanOption @brief This struct contains the configuration options for Wi-Fi scan. @sa - tenuM2mScanCh - tstrM2MScan + tenuM2mScanCh + tstrM2MScan */ typedef struct { - uint8 u8NumOfSlot; + uint8 u8NumOfSlot; /*!< The number of scan slots per channel. Refers to both active and passive scan. Valid settings are in the range 0u8Status == M2M_SUCCESS) - { - tstrNetworkId strNetworkId = {NULL, pstrProvInfo->au8SSID, (uint8)strlen((char*)(pstrProvInfo->au8SSID)), M2M_WIFI_CH_ALL}; - tstrAuthPsk strAuthPsk = {NULL, pstrProvInfo->au8Password, (uint8)strlen((char*)(pstrProvInfo->au8Password))}; - m2m_wifi_connect_psk(WIFI_CRED_SAVE_ENCRYPTED, &strNetworkId, &strAuthPsk); - - printf("PROV SSID : %s\n",pstrProvInfo->au8SSID); - printf("PROV PSK : %s\n",pstrProvInfo->au8Password); - } - else - { - printf("(ERR) Provisioning Failed\n"); - } - } - break; - - default: - break; - } - } - - int main() - { - tstrWifiInitParam param; - - param.pfAppWifiCb = wifi_event_cb; - if(!m2m_wifi_init(¶m)) - { - tstrM2MAPConfig apConfig; - uint8 bEnableRedirect = 1; - - strcpy(apConfig.au8SSID, "WINC_SSID"); - apConfig.u8ListenChannel = 1; - apConfig.u8SecType = M2M_WIFI_SEC_OPEN; - apConfig.u8SsidHide = 0; - - // IP Address - apConfig.au8DHCPServerIP[0] = 192; - apConfig.au8DHCPServerIP[1] = 168; - apConfig.au8DHCPServerIP[2] = 1; - apConfig.au8DHCPServerIP[3] = 1; - - m2m_wifi_start_provision_mode(&apConfig, "atmelwincconf.com", bEnableRedirect); - - while(1) - { - m2m_wifi_handle_events(NULL); - } - } - } -@endcode -*/ -NMI_API sint8 m2m_wifi_start_provision_mode(tstrM2MAPConfig *pstrAPConfig, char *pcHttpServerDomainName, uint8 bEnableHttpRedirect); - -/*! -@fn \ - NMI_API sint8 m2m_wifi_start_provision_mode_ext(tstrM2MAPModeConfig *pstrAPModeConfig, char *pcHttpServerDomainName, uint8 bEnableHttpRedirect); - -@brief - Asynchronous API for control of Wi-Fi provisioning functionality with extended options. - -@details - Asynchronous Wi-Fi provisioning function, which starts the WINC HTTP PROVISIONING mode. - The function triggers the WINC to activate the Wi-Fi AP (HOTSPOT) mode with the passed - configuration parameters and then starts the HTTP Provision WEB Server. - - Provisioning status is returned in an event @ref M2M_WIFI_RESP_PROVISION_INFO. - -@param [in] pstrAPModeConfig - AP configuration parameters as defined in @ref tstrM2MAPModeConfig configuration structure. - A NULL value passed in, will result in a negative error @ref M2M_ERR_FAIL. - -@param [in] pcHttpServerDomainName +@param[in] pcHttpServerDomainName Domain name of the HTTP Provision WEB server which others will use to load the provisioning Home page. The domain name can have one of the following 3 forms: - 1. "wincprov.com" - 2. "http://wincprov.com" - 3. "https://wincprov.com" - The forms 1 and 2 are equivalent, they both will start a plain http server, while form 3 + Forms 1 and 2 are equivalent, they will both start a plain http server, while form 3 will start a secure HTTP provisioning Session (HTTP over SSL connection). @param [in] bEnableHttpRedirect @@ -1377,22 +1429,21 @@ NMI_API sint8 m2m_wifi_start_provision_mode(tstrM2MAPConfig *pstrAPConfig, char - The event @ref M2M_WIFI_RESP_CONN_INFO must be handled in the callback to receive the requested connection info. @warning - DO Not use ".local" in the pcHttpServerDomainName. - + DO NOT use ".local" in the pcHttpServerDomainName. + +@return + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. + @see tpfAppWifiCb m2m_wifi_init M2M_WIFI_RESP_PROVISION_INFO m2m_wifi_stop_provision_mode - tstrM2MAPModeConfig + tstrM2MAPConfig -@return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. - -\section WIFIExample1b Example - The example demonstrates a code snippet for how provisioning is triggered and the response event - received accordingly. +\section WIFIExample1 Example + The example demonstrates a code snippet for how provisioning is triggered and the response event + received accordingly. @code #include "m2m_wifi.h" @@ -1404,7 +1455,7 @@ NMI_API sint8 m2m_wifi_start_provision_mode(tstrM2MAPConfig *pstrAPConfig, char { case M2M_WIFI_RESP_PROVISION_INFO: { - tstrM2MProvisionInfo *pstrProvInfo = (tstrM2MProvisionInfo*)pvMsg; + tstrM2MProvisionInfo *pstrProvInfo = (tstrM2MProvisionInfo*)pvMsg; if(pstrProvInfo->u8Status == M2M_SUCCESS) { tstrNetworkId strNetworkId = {NULL, pstrProvInfo->au8SSID, (uint8)strlen((char*)(pstrProvInfo->au8SSID)), M2M_WIFI_CH_ALL}; @@ -1428,9 +1479,134 @@ NMI_API sint8 m2m_wifi_start_provision_mode(tstrM2MAPConfig *pstrAPConfig, char int main() { - tstrWifiInitParam param; - - param.pfAppWifiCb = wifi_event_cb; + tstrWifiInitParam param; + + param.pfAppWifiCb = wifi_event_cb; + if(!m2m_wifi_init(¶m)) + { + tstrM2MAPConfig apConfig; + uint8 bEnableRedirect = 1; + + strcpy(apConfig.au8SSID, "WINC_SSID"); + apConfig.u8ListenChannel = 1; + apConfig.u8SecType = M2M_WIFI_SEC_OPEN; + apConfig.u8SsidHide = 0; + + // IP Address + apConfig.au8DHCPServerIP[0] = 192; + apConfig.au8DHCPServerIP[1] = 168; + apConfig.au8DHCPServerIP[2] = 1; + apConfig.au8DHCPServerIP[3] = 1; + + m2m_wifi_start_provision_mode(&apConfig, "atmelwincconf.com", bEnableRedirect); + + while(1) + { + m2m_wifi_handle_events(NULL); + } + } + } +@endcode +*/ +NMI_API sint8 m2m_wifi_start_provision_mode(tstrM2MAPConfig *pstrAPConfig, char *pcHttpServerDomainName, uint8 bEnableHttpRedirect); + +/*! +@ingroup WLANPROVISION +@fn \ + NMI_API sint8 m2m_wifi_start_provision_mode_ext(tstrM2MAPModeConfig *pstrAPModeConfig, char *pcHttpServerDomainName, uint8 bEnableHttpRedirect); + +@brief + Asynchronous API for control of Wi-Fi provisioning functionality with extended options. + +@details + Asynchronous Wi-Fi provisioning function, which starts the WINC HTTP PROVISIONING mode. + The function triggers the WINC to activate the Wi-Fi AP (HOTSPOT) mode with the passed + configuration parameters and then starts the HTTP Provision WEB Server. + + Provisioning status is returned in an event @ref M2M_WIFI_RESP_PROVISION_INFO. + +@param[in] pstrAPModeConfig + AP configuration parameters as defined in @ref tstrM2MAPModeConfig configuration structure. + A NULL value passed in, will result in a negative error @ref M2M_ERR_FAIL. + +@param[in] pcHttpServerDomainName + Domain name of the HTTP Provision WEB server which others will use to load the provisioning Home page. + The domain name can have one of the following 3 forms: + - 1. "wincprov.com" + - 2. "http://wincprov.com" + - 3. "https://wincprov.com" + + The forms 1 and 2 are equivalent, they both will start a plain http server, while form 3 + will start a secure HTTP provisioning Session (HTTP over SSL connection). + +@param[in] bEnableHttpRedirect + A flag to enable/disable the HTTP redirect feature. If Secure provisioning is enabled (i.e. the server + domain name uses "https" prefix) this flag is ignored (no meaning for redirect in HTTPS). + Possible values are: + - ZERO value, which means DO NOT use HTTP Redirect. In this case, the associated device could open the provisioning + page ONLY when the HTTP Provision URL of the WINC HTTP Server is correctly written on the browser. + - Non-Zero value, means use HTTP Redirect. In this case, all http traffic (http://URL) from the associated + device (Phone, PC, etc) will be redirected to the WINC HTTP Provisioning Home page. + +@pre + - A Wi-Fi notification callback of type @ref tpfAppWifiCb MUST be implemented and registered at startup. Registering the callback + is done through passing it to the initialization @ref m2m_wifi_init function. + - The event @ref M2M_WIFI_RESP_CONN_INFO must be handled in the callback to receive the requested connection info. + +@warning + DO Not use ".local" in the pcHttpServerDomainName. + +@see + tpfAppWifiCb + m2m_wifi_init + M2M_WIFI_RESP_PROVISION_INFO + m2m_wifi_stop_provision_mode + tstrM2MAPModeConfig + +@return + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. + +\section WIFIExample1b Example + The example demonstrates a code snippet for how provisioning is triggered and the response event + received accordingly. + +@code + #include "m2m_wifi.h" + #include "m2m_types.h" + + void wifi_event_cb(uint8 u8WiFiEvent, void * pvMsg) + { + switch(u8WiFiEvent) + { + case M2M_WIFI_RESP_PROVISION_INFO: + { + tstrM2MProvisionInfo *pstrProvInfo = (tstrM2MProvisionInfo*)pvMsg; + if(pstrProvInfo->u8Status == M2M_SUCCESS) + { + tstrNetworkId strNetworkId = {NULL, pstrProvInfo->au8SSID, (uint8)strlen((char*)(pstrProvInfo->au8SSID)), M2M_WIFI_CH_ALL}; + tstrAuthPsk strAuthPsk = {NULL, pstrProvInfo->au8Password, (uint8)strlen((char*)(pstrProvInfo->au8Password))}; + m2m_wifi_connect_psk(WIFI_CRED_SAVE_ENCRYPTED, &strNetworkId, &strAuthPsk); + + printf("PROV SSID : %s\n",pstrProvInfo->au8SSID); + printf("PROV PSK : %s\n",pstrProvInfo->au8Password); + } + else + { + printf("(ERR) Provisioning Failed\n"); + } + } + break; + + default: + break; + } + } + + int main() + { + tstrWifiInitParam param; + + param.pfAppWifiCb = wifi_event_cb; if(!m2m_wifi_init(¶m)) { tstrM2MAPModeConfig apModeConfig; @@ -1472,8 +1648,9 @@ NMI_API sint8 m2m_wifi_start_provision_mode(tstrM2MAPConfig *pstrAPConfig, char NMI_API sint8 m2m_wifi_start_provision_mode_ext(tstrM2MAPModeConfig *pstrAPModeConfig, char *pcHttpServerDomainName, uint8 bEnableHttpRedirect); /*! -@fn \ - sint8 m2m_wifi_stop_provision_mode(void); +@ingroup WLANPROVISION +@fn \ + sint8 m2m_wifi_stop_provision_mode(void); @brief Synchronous API for terminating provisioning mode on the WINC IC. @@ -1486,7 +1663,7 @@ NMI_API sint8 m2m_wifi_start_provision_mode_ext(tstrM2MAPModeConfig *pstrAPModeC An active provisioning session must be active before it is terminated through this function. @return - The function returns ZERO for success and a negative value otherwise. + The function returns @ref M2M_SUCCESS for success and a negative value otherwise. @see m2m_wifi_start_provision_mode @@ -1494,8 +1671,9 @@ NMI_API sint8 m2m_wifi_start_provision_mode_ext(tstrM2MAPModeConfig *pstrAPModeC NMI_API sint8 m2m_wifi_stop_provision_mode(void); /*! -@fn \ - sint8 m2m_wifi_get_connection_info(void); +@ingroup WLANCONNECT +@fn \ + sint8 m2m_wifi_get_connection_info(void); @brief Asynchronous API for retrieving the WINC IC's connection status. @@ -1526,9 +1704,8 @@ NMI_API sint8 m2m_wifi_stop_provision_mode(void); - In case the WINC is operating in AP mode, the SSID field will be returned as a NULL string. @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. - + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. + @see M2M_WIFI_RESP_CONN_INFO, tstrM2MConnInfo @@ -1587,8 +1764,9 @@ NMI_API sint8 m2m_wifi_stop_provision_mode(void); NMI_API sint8 m2m_wifi_get_connection_info(void); /*! -@fn \ - NMI_API sint8 m2m_wifi_set_mac_address(uint8 au8MacAddress[6]); +@ingroup WLANCONF +@fn \ + NMI_API sint8 m2m_wifi_set_mac_address(uint8 au8MacAddress[6]); @brief Synchronous API for assigning a MAC address to the WINC IC. @@ -1600,15 +1778,15 @@ NMI_API sint8 m2m_wifi_get_connection_info(void); @param [in] au8MacAddress MAC Address to be provisioned to the WINC. -@return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. +@return + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. */ NMI_API sint8 m2m_wifi_set_mac_address(uint8 au8MacAddress[6]); /*! -@fn \ - NMI_API sint8 m2m_wifi_wps(uint8 u8TriggerType,const char * pcPinNumber); +@ingroup WLANWPS +@fn \ + NMI_API sint8 m2m_wifi_wps(uint8 u8TriggerType,const char * pcPinNumber); @brief Asynchronous API to engage the WINC IC's Wi-Fi Protected Setup (enrollee) function. @@ -1619,12 +1797,12 @@ NMI_API sint8 m2m_wifi_set_mac_address(uint8 au8MacAddress[6]); @param [in] u8TriggerType WPS Trigger method. This may be: - - [WPS_PIN_TRIGGER](@ref WPS_PIN_TRIGGER) Push button method - - [WPS_PBC_TRIGGER](@ref WPS_PBC_TRIGGER) Pin method - -@param [in] pcPinNumber - PIN number for WPS PIN method. It is not used if the trigger type is WPS_PBC_TRIGGER. It must follow the rules - stated by the WPS standard. + - @ref WPS_PIN_TRIGGER Push button method + - @ref WPS_PBC_TRIGGER Pin method + +@param[in] pcPinNumber + PIN number for WPS PIN method. It is not used if the trigger type is WPS_PBC_TRIGGER. It must follow the rules + stated by the WPS standard. @pre - A Wi-Fi notification callback of type (@ref tpfAppWifiCb MUST be implemented and registered at @@ -1640,8 +1818,7 @@ NMI_API sint8 m2m_wifi_set_mac_address(uint8 au8MacAddress[6]); This function is not allowed in AP mode. @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. @see tpfAppWifiCb @@ -1715,8 +1892,9 @@ NMI_API sint8 m2m_wifi_set_mac_address(uint8 au8MacAddress[6]); NMI_API sint8 m2m_wifi_wps(uint8 u8TriggerType,const char *pcPinNumber); /*! -@fn \ - NMI_API sint8 m2m_wifi_wps_disable(void); +@ingroup WLANWPS +@fn \ + NMI_API sint8 m2m_wifi_wps_disable(void); @brief Stops the ongoing WPS session. @@ -1725,22 +1903,19 @@ NMI_API sint8 m2m_wifi_wps(uint8 u8TriggerType,const char *pcPinNumber); WINC should be already in WPS mode using @ref m2m_wifi_wps. @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. @see m2m_wifi_wps */ NMI_API sint8 m2m_wifi_wps_disable(void); -/**@}*/ - + /**@cond P2P_DOC - * @addtogroup WLANAPI */ -/**@{*/ /*! -@fn \ - NMI_API sint8 m2m_wifi_p2p(uint8 u8Channel); +@ingroup WLANP2P +@fn \ + NMI_API sint8 m2m_wifi_p2p(uint8 u8Channel); @warning P2P has been deprecated from version 19.5.3! @@ -1748,22 +1923,20 @@ NMI_API sint8 m2m_wifi_wps_disable(void); NMI_API sint8 m2m_wifi_p2p(uint8 u8Channel); /*! -@fn \ - NMI_API sint8 m2m_wifi_p2p_disconnect(void); +@ingroup WLANP2P +@fn \ + NMI_API sint8 m2m_wifi_p2p_disconnect(void); @warning P2P has been deprecated from version 19.5.3! */ NMI_API sint8 m2m_wifi_p2p_disconnect(void); -/**@}*/ -/**@endcond*/ +/**@endcond*/ //P2P_DOC -/**@addtogroup WLANAPI - */ -/**@{*/ /*! -@fn \ - NMI_API sint8 m2m_wifi_enable_ap(CONST tstrM2MAPConfig* pstrM2MAPConfig); +@ingroup WLANAP +@fn \ + NMI_API sint8 m2m_wifi_enable_ap(CONST tstrM2MAPConfig* pstrM2MAPConfig); @brief Asynchronous API to enable access point (AKA "hot-spot") mode on the WINC IC. @@ -1771,14 +1944,13 @@ NMI_API sint8 m2m_wifi_p2p_disconnect(void); @details The WINC IC supports the ability to operate as an access point with the following limitations: - Only 1 station may be associated at any given time. - - open system and WEP are the only security suites supported. + - Open system and WEP are the only security suites supported. -@param [in] pstrM2MAPConfig - A structure holding the AP configurations. +@param[in] pstrM2MAPConfig + A structure holding the AP configurations. @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. @pre - A Wi-Fi notification callback of type @ref tpfAppWifiCb MUST be implemented and registered at initialization. Registering the callback @@ -1856,7 +2028,8 @@ NMI_API sint8 m2m_wifi_p2p_disconnect(void); NMI_API sint8 m2m_wifi_enable_ap(CONST tstrM2MAPConfig* pstrM2MAPConfig); /*! -@fn \ +@ingroup WLANAP +@fn \ NMI_API sint8 m2m_wifi_enable_ap_ext(CONST tstrM2MAPModeConfig* pstrM2MAPModeConfig); @brief @@ -1865,14 +2038,13 @@ NMI_API sint8 m2m_wifi_enable_ap(CONST tstrM2MAPConfig* pstrM2MAPConfig); @details The WINC IC supports the ability to operate as an access point with the following limitations: - Only 1 station may be associated at any given time. - - open system and WEP are the only security suites supported. + - Open system and WEP are the only security suites supported. @param [in] pstrM2MAPModeConfig A structure holding the AP configurations. @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. @pre - A Wi-Fi notification callback of type @ref tpfAppWifiCb MUST be implemented and registered at initialization. Registering the callback @@ -1962,8 +2134,9 @@ NMI_API sint8 m2m_wifi_enable_ap(CONST tstrM2MAPConfig* pstrM2MAPConfig); NMI_API sint8 m2m_wifi_enable_ap_ext(CONST tstrM2MAPModeConfig* pstrM2MAPModeConfig); /*! -@fn \ - NMI_API sint8 m2m_wifi_disable_ap(void); +@ingroup WLANAP +@fn \ + NMI_API sint8 m2m_wifi_disable_ap(void); @brief Synchronous API to disable access point mode on the WINC IC. @@ -1973,8 +2146,7 @@ NMI_API sint8 m2m_wifi_enable_ap_ext(CONST tstrM2MAPModeConfig* pstrM2MAPModeCon function. Otherwise the call to this function will not be useful. @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. @see m2m_wifi_enable_ap @@ -1982,8 +2154,9 @@ NMI_API sint8 m2m_wifi_enable_ap_ext(CONST tstrM2MAPModeConfig* pstrM2MAPModeCon NMI_API sint8 m2m_wifi_disable_ap(void); /*! -@fn \ - NMI_API sint8 m2m_wifi_set_static_ip(tstrM2MIPConfig * pstrStaticIPConf); +@ingroup WLANCONNECT +@fn \ + NMI_API sint8 m2m_wifi_set_static_ip(tstrM2MIPConfig * pstrStaticIPConf); @brief Synchronous API to manually assign a (static) IP address to the WINC IC. @@ -2011,18 +2184,18 @@ NMI_API sint8 m2m_wifi_disable_ap(void); DNS address). @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. - + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. + @see tstrM2MIPConfig */ NMI_API sint8 m2m_wifi_set_static_ip(tstrM2MIPConfig * pstrStaticIPConf); /*! -@fn \ - NMI_API sint8 m2m_wifi_request_dhcp_client(void); - +@ingroup WLANCONNECT +@fn \ + NMI_API sint8 m2m_wifi_request_dhcp_client(void); + @brief Legacy (deprecated) Asynchronous API for starting a DHCP client on the WINC IC. @@ -2039,8 +2212,9 @@ NMI_API sint8 m2m_wifi_set_static_ip(tstrM2MIPConfig * pstrStaticIPConf); NMI_API sint8 m2m_wifi_request_dhcp_client(void); /*! -@fn \ - NMI_API sint8 m2m_wifi_request_dhcp_server(uint8* addr); +@ingroup WLANCONNECT +@fn \ + NMI_API sint8 m2m_wifi_request_dhcp_server(uint8* addr); @brief Dhcp requested by the firmware automatically in STA/AP mode). @@ -2055,9 +2229,10 @@ NMI_API sint8 m2m_wifi_request_dhcp_client(void); NMI_API sint8 m2m_wifi_request_dhcp_server(uint8* addr); /*! -@fn \ - NMI_API sint8 m2m_wifi_enable_dhcp(uint8 u8DhcpEn ); - +@ingroup WLANCONNECT +@fn \ + NMI_API sint8 m2m_wifi_enable_dhcp(uint8 u8DhcpEn); + @brief Enable/Disable the DHCP client after connection. @@ -2082,21 +2257,21 @@ NMI_API sint8 m2m_wifi_request_dhcp_server(uint8* addr); NMI_API sint8 m2m_wifi_enable_dhcp(uint8 u8DhcpEn ); /*! -@fn \ +@ingroup WLANSCAN +@fn \ sint8 m2m_wifi_set_scan_options(tstrM2MScanOption* ptstrM2MScanOption); @brief Synchronous API for configuring the behaviour of the WINC IC's network scanning functions. @details - This function sets the time configuration parameters for the scan operation. + This function sets the configuration parameters for the scan operation. @param [in] ptstrM2MScanOption; Pointer to the structure holding the Scan Parameters. @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. @see tenuM2mScanCh @@ -2106,7 +2281,37 @@ NMI_API sint8 m2m_wifi_enable_dhcp(uint8 u8DhcpEn ); NMI_API sint8 m2m_wifi_set_scan_options(tstrM2MScanOption* ptstrM2MScanOption); /*! -@fn \ +@ingroup WLANSCAN +@fn \ + sint8 m2m_wifi_set_stop_scan_on_first(uint8 u8StopScanOption); + +@brief + Synchronous API for enabling/disabling the stop scan on first result of the WINC IC's network scanning functions. + +@details + Allows for enabling/disabling of stop scan on first result. When enabled, the WINC will stop the scan as soon as + it detects a network and return the results to the host. Setting is persistent and will need to be explicitly + reverted back by the application if it no longer wishes for it to be enabled. + +@param[in] u8StopScanOption; + Setting for enabling or disabling Stopping Scan on first result. + 1 = Enabled, 0 = Disabled (Default) + +@return + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. + +@see + tenuM2mScanCh + tstrM2MScanOption + tstrM2MStopScanOption + m2m_wifi_request_scan + m2m_wifi_set_scan_options +*/ +NMI_API sint8 m2m_wifi_set_stop_scan_on_first(uint8 u8StopScanOption); + +/*! +@ingroup WLANSCAN +@fn \ sint8 m2m_wifi_set_scan_region(uint16 ScanRegion); @brief @@ -2125,8 +2330,7 @@ NMI_API sint8 m2m_wifi_set_scan_options(tstrM2MScanOption* ptstrM2MScanOption); NORTH_AMERICA @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. @see tenuM2mScanCh @@ -2135,8 +2339,9 @@ NMI_API sint8 m2m_wifi_set_scan_options(tstrM2MScanOption* ptstrM2MScanOption); NMI_API sint8 m2m_wifi_set_scan_region(uint16 ScanRegion); /*! -@fn \ - NMI_API sint8 m2m_wifi_request_scan(uint8 ch); +@ingroup WLANSCAN +@fn \ + NMI_API sint8 m2m_wifi_request_scan(uint8 ch); @brief Asynchronous API to request the WINC IC to scan for networks. @@ -2154,8 +2359,7 @@ NMI_API sint8 m2m_wifi_set_scan_region(uint16 ScanRegion); value of @ref M2M_WIFI_CH_ALL to scan all channels. @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. @pre - A Wi-Fi notification callback of type @ref tpfAppWifiCb MUST be implemented and registered at @@ -2265,8 +2469,9 @@ NMI_API sint8 m2m_wifi_set_scan_region(uint16 ScanRegion); NMI_API sint8 m2m_wifi_request_scan(uint8 ch); /*! -@fn \ - NMI_API sint8 m2m_wifi_request_scan_passive(uint8 ch, uint16 scan_time); +@ingroup WLANSCAN +@fn \ + NMI_API sint8 m2m_wifi_request_scan_passive(uint8 ch, uint16 scan_time); @brief Similar to @ref m2m_wifi_request_scan but performs passive scanning instead of active scanning. @@ -2305,6 +2510,7 @@ NMI_API sint8 m2m_wifi_request_scan(uint8 ch); NMI_API sint8 m2m_wifi_request_scan_passive(uint8 ch, uint16 scan_time); /*! +@ingroup WLANSCAN @fn \ NMI_API sint8 m2m_wifi_request_scan_ssid_list(uint8 ch,uint8 * u8SsidList); @@ -2464,6 +2670,7 @@ NMI_API sint8 m2m_wifi_request_scan_passive(uint8 ch, uint16 scan_time); NMI_API sint8 m2m_wifi_request_scan_ssid_list(uint8 ch,uint8 * u8Ssidlist); /*! +@ingroup WLANSCAN @fn \ NMI_API uint8 m2m_wifi_get_num_ap_found(void); @@ -2580,6 +2787,7 @@ NMI_API sint8 m2m_wifi_request_scan_ssid_list(uint8 ch,uint8 * u8Ssidlist); NMI_API uint8 m2m_wifi_get_num_ap_found(void); /*! +@ingroup WLANSCAN @fn \ NMI_API sint8 m2m_wifi_req_scan_result(uint8 index); @@ -2597,8 +2805,7 @@ NMI_API uint8 m2m_wifi_get_num_ap_found(void); Index for the requested result, the index range start from 0 till number of AP's found. @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. @see tstrM2mWifiscanResult @@ -2707,6 +2914,7 @@ NMI_API uint8 m2m_wifi_get_num_ap_found(void); NMI_API sint8 m2m_wifi_req_scan_result(uint8 index); /*! +@ingroup WLANCONNECT @fn \ NMI_API sint8 m2m_wifi_req_curr_rssi(void); @@ -2714,17 +2922,17 @@ NMI_API sint8 m2m_wifi_req_scan_result(uint8 index); Asynchronous API to request the current Receive Signal Strength (RSSI) of the current connection. @details - The response received in through the @ref M2M_WIFI_RESP_CURRENT_RSSI event. + This function will result in the application receiving the RSSI via a + @ref M2M_WIFI_RESP_CURRENT_RSSI event. @pre - - A Wi-Fi notification callback of type @ref tpfAppWifiCb MUST be implemented and registered - before initialization. Registering the callback is done through passing it to the - @ref m2m_wifi_init through the @ref tstrWifiInitParam initialization structure. - - The event @ref M2M_WIFI_RESP_CURRENT_RSSI must be handled in the callback to receive the requested Rssi information. + - A Wi-Fi notification callback of type @ref tpfAppWifiCb MUST be implemented and registered + during initialization. Registration of the callback is done through passing it to @ref m2m_wifi_init + via the @ref tstrWifiInitParam initialization structure. + - The event @ref M2M_WIFI_RESP_CURRENT_RSSI must be handled in the callback to receive the requested Rssi information. @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. \section WIFIExample9 Example The code snippet demonstrates how the RSSI request is called in the application's main function and the handling of the event received in the callback. @@ -2770,6 +2978,7 @@ NMI_API sint8 m2m_wifi_req_scan_result(uint8 index); NMI_API sint8 m2m_wifi_req_curr_rssi(void); /*! +@ingroup WLANCONF @fn \ NMI_API sint8 m2m_wifi_get_otp_mac_address(uint8 *pu8MacAddr, uint8 * pu8IsValid); @@ -2791,8 +3000,8 @@ NMI_API sint8 m2m_wifi_req_curr_rssi(void); Output MAC address buffer 6 bytes in size. Valid only if *pu8Valid=1. @param [out] pu8IsValid - Output boolean value to indicate the validity of pu8MacAddr in OTP. - Output zero if the OTP memory is not programmed, non-zero otherwise. + A boolean value set by the callee to indicate the validity of pu8MacAddr in OTP. If no MAC has + been programmed in the OTP the value of this parameter will be zero; otherwise it will be non-zero. @return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. @@ -2803,6 +3012,7 @@ NMI_API sint8 m2m_wifi_req_curr_rssi(void); NMI_API sint8 m2m_wifi_get_otp_mac_address(uint8 *pu8MacAddr, uint8 * pu8IsValid); /*! +@ingroup WLANCONF @fn \ NMI_API sint8 m2m_wifi_get_mac_address(uint8 *pu8MacAddr) @@ -2829,6 +3039,7 @@ NMI_API sint8 m2m_wifi_get_otp_mac_address(uint8 *pu8MacAddr, uint8 * pu8IsValid NMI_API sint8 m2m_wifi_get_mac_address(uint8 *pu8MacAddr); /*! +@ingroup WLANPS @fn \ NMI_API sint8 m2m_wifi_set_sleep_mode(uint8 PsTyp, uint8 BcastEn); @@ -2840,7 +3051,7 @@ NMI_API sint8 m2m_wifi_get_mac_address(uint8 *pu8MacAddr); to tweak the system power consumption. Such tweaking can be done through one of two ways: - 1) Changing the power save mode, to one of the allowed power save modes (see @ref tenuPowerSaveModes). This is done by setting the first parameter. - 2) Configuring DTIM monitoring: Configuring beacon monitoring parameters by enabling or disabling the reception of broadcast/multicast data. - this is done by setting the second parameter. + This is done by setting the second parameter. @param [in] PsTyp Desired power saving mode. Supported types are enumerated in @ref tenuPowerSaveModes. @@ -2865,6 +3076,7 @@ NMI_API sint8 m2m_wifi_get_mac_address(uint8 *pu8MacAddr); NMI_API sint8 m2m_wifi_set_sleep_mode(uint8 PsTyp, uint8 BcastEn); /*! +@ingroup WLANPS @fn \ NMI_API sint8 m2m_wifi_request_sleep(uint32 u32SlpReqTime); @@ -2900,6 +3112,7 @@ NMI_API sint8 m2m_wifi_set_sleep_mode(uint8 PsTyp, uint8 BcastEn); NMI_API sint8 m2m_wifi_request_sleep(uint32 u32SlpReqTime); /*! +@ingroup WLANPS @fn \ NMI_API uint8 m2m_wifi_get_sleep_mode(void); @@ -2971,6 +3184,7 @@ NMI_API sint8 m2m_wifi_req_client_ctrl(uint8 cmd); NMI_API sint8 m2m_wifi_req_server_init(uint8 ch); /*! +@ingroup WLANCONF @fn \ NMI_API sint8 m2m_wifi_set_device_name(uint8 *pu8DeviceName, uint8 u8DeviceNameLength); @@ -2998,12 +3212,12 @@ NMI_API sint8 m2m_wifi_req_server_init(uint8 ch); length @ref M2M_DEVICE_NAME_MAX (including null character). @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. */ NMI_API sint8 m2m_wifi_set_device_name(uint8 *pu8DeviceName, uint8 u8DeviceNameLength); /*! +@ingroup WLANTIME @fn \ NMI_API sint8 m2m_wifi_configure_sntp(uint8 *pu8NTPServerName, uint8 u8NTPServerNameLength, tenuSNTPUseDHCP useDHCP); @@ -3011,10 +3225,9 @@ NMI_API sint8 m2m_wifi_set_device_name(uint8 *pu8DeviceName, uint8 u8DeviceNameL Configures what NTP server the SNTP client should use. @details - Configures what NTP server the SNTP client should use. Only 1 server name can be provided, if the configured server name begins with an asterisk then it will be treated as a server pool. - The SNTP client can also use the NTP server provided by the DHCP server through option 42. - By default the NTP server provided by DHCP will be tried first, then the built-in default NTP server (time.nist.gov) will be used. - Configuring a server name will overwrite the built-in default server until next reboot. + Configures what NTP server the SNTP client should use. Only 1 server name can be provided, if the configured server name begins with an asterisk then it will be treated as a server pool. + The SNTP client can also use the NTP server provided by the DHCP server through option 42. + By default the NTP server provided by DHCP will be tried first, then the built-in default NTP server (time.nist.gov) will be used. @param [in] pu8NTPServerName Buffer holding the NTP server name. If the first character is an asterisk (*) then it will be treated as a server pool, where the asterisk will @@ -3023,8 +3236,16 @@ NMI_API sint8 m2m_wifi_set_device_name(uint8 *pu8DeviceName, uint8 u8DeviceNameL @param [in] u8NTPServerNameLength Length of the NTP server name. Should not exceed the maximum NTP server name length of @ref M2M_NTP_MAX_SERVER_NAME_LENGTH. -@param [in] useDHCP - Should the NTP server provided by the DHCP server be used. +@param[in] useDHCP + Explicity tell the WINC if it should use the NTP server provided by the DHCP server or not. + +@warning + SNTP should be configured before the connection takes place. If SNTP is configured after the device connects to a + network, the new configuration can take a minimum of 24h to be applied. However, it can take even longer since it is + triggered by DHCP renewal. + Currently there is also a known issue in which if the WINC obtains the NTP server from DHCP and then connects to a + different network, it will still use the NTP from the previous network. + Configuring a server name will overwrite the built-in default server until next reboot. @return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. @@ -3032,6 +3253,7 @@ NMI_API sint8 m2m_wifi_set_device_name(uint8 *pu8DeviceName, uint8 u8DeviceNameL NMI_API sint8 m2m_wifi_configure_sntp(uint8 *pu8NTPServerName, uint8 u8NTPServerNameLength, tenuSNTPUseDHCP useDHCP); /*! +@ingroup WLANPS @fn \ NMI_API sint8 m2m_wifi_set_lsn_int(tstrM2mLsnInt * pstrM2mLsnInt); @@ -3062,7 +3284,10 @@ NMI_API sint8 m2m_wifi_configure_sntp(uint8 *pu8NTPServerName, uint8 u8NTPServer */ NMI_API sint8 m2m_wifi_set_lsn_int(tstrM2mLsnInt *pstrM2mLsnInt); +/**@cond MON_DOC + */ /*! +@ingroup WLANMON @fn \ NMI_API sint8 m2m_wifi_enable_monitoring_mode(tstrM2MWifiMonitorModeCtrl *, uint8 *, uint16 , uint16); @@ -3108,8 +3333,7 @@ NMI_API sint8 m2m_wifi_set_lsn_int(tstrM2mLsnInt *pstrM2mLsnInt); When this mode is enabled, you cannot be connected in any mode (Station or Access Point). @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. @see tstrM2MWifiMonitorModeCtrl @@ -3179,6 +3403,7 @@ NMI_API sint8 m2m_wifi_enable_monitoring_mode(tstrM2MWifiMonitorModeCtrl *pstrMt uint16 u16BufferSize, uint16 u16DataOffset); /*! +@ingroup WLANMON @fn \ NMI_API sint8 m2m_wifi_disable_monitoring_mode(void); @@ -3190,8 +3415,7 @@ NMI_API sint8 m2m_wifi_enable_monitoring_mode(tstrM2MWifiMonitorModeCtrl *pstrMt no negative impact will reside. @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. @see m2m_wifi_enable_monitoring_mode @@ -3199,6 +3423,7 @@ NMI_API sint8 m2m_wifi_enable_monitoring_mode(tstrM2MWifiMonitorModeCtrl *pstrMt NMI_API sint8 m2m_wifi_disable_monitoring_mode(void); /*! +@ingroup WLANMON @fn \ NMI_API sint8 m2m_wifi_send_wlan_pkt(uint8 *, uint16, uint16); @@ -3224,16 +3449,17 @@ NMI_API sint8 m2m_wifi_disable_monitoring_mode(void); The size of the whole packet in bytes. @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. @see m2m_wifi_enable_monitoring_mode m2m_wifi_disable_monitoring_mode */ NMI_API sint8 m2m_wifi_send_wlan_pkt(uint8 *pu8WlanPacket, uint16 u16WlanHeaderLength, uint16 u16WlanPktSize); +/**@endcond*/ //MON_DOC /*! +@ingroup WLANETH @fn \ NMI_API sint8 m2m_wifi_send_ethernet_pkt(uint8* pu8Packet,uint16 u16PacketSize); @@ -3258,8 +3484,7 @@ NMI_API sint8 m2m_wifi_send_wlan_pkt(uint8 *pu8WlanPacket, uint16 u16WlanHeaderL The size of the whole packet in bytes. @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. @see m2m_wifi_enable_mac_mcast @@ -3268,6 +3493,7 @@ NMI_API sint8 m2m_wifi_send_wlan_pkt(uint8 *pu8WlanPacket, uint16 u16WlanHeaderL NMI_API sint8 m2m_wifi_send_ethernet_pkt(uint8* pu8Packet,uint16 u16PacketSize); /*! +@ingroup WLANTIME @fn \ NMI_API sint8 m2m_wifi_enable_sntp(uint8); @@ -3291,9 +3517,8 @@ NMI_API sint8 m2m_wifi_send_ethernet_pkt(uint8* pu8Packet,uint16 u16PacketSize); '0' :disable SNTP '1' :enable SNTP -@return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. +@return + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. @see m2m_wifi_set_system_time @@ -3301,6 +3526,7 @@ NMI_API sint8 m2m_wifi_send_ethernet_pkt(uint8* pu8Packet,uint16 u16PacketSize); NMI_API sint8 m2m_wifi_enable_sntp(uint8 bEnable); /*! +@ingroup WLANTIME @fn \ NMI_API sint8 m2m_wifi_set_system_time(uint32); @@ -3316,8 +3542,7 @@ NMI_API sint8 m2m_wifi_enable_sntp(uint8 bEnable); Seconds elapsed since January 1, 1900 (NTP Timestamp). @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. @see m2m_wifi_enable_sntp @@ -3330,6 +3555,7 @@ NMI_API sint8 m2m_wifi_enable_sntp(uint8 bEnable); NMI_API sint8 m2m_wifi_set_system_time(uint32 u32UTCSeconds); /*! +@ingroup WLANTIME @fn \ NMI_API sint8 m2m_wifi_get_system_time(void); @@ -3346,8 +3572,7 @@ NMI_API sint8 m2m_wifi_set_system_time(uint32 u32UTCSeconds); Get the system time from the SNTP client using the API @ref m2m_wifi_get_system_time. @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. @see m2m_wifi_enable_sntp @@ -3356,6 +3581,7 @@ NMI_API sint8 m2m_wifi_set_system_time(uint32 u32UTCSeconds); NMI_API sint8 m2m_wifi_get_system_time(void); /*! +@ingroup WLANCONF @fn \ NMI_API sint8 m2m_wifi_set_cust_InfoElement(uint8*); @@ -3388,8 +3614,7 @@ NMI_API sint8 m2m_wifi_get_system_time(void); @endverbatim @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. @see m2m_wifi_enable_sntp @@ -3430,15 +3655,15 @@ NMI_API sint8 m2m_wifi_get_system_time(void); NMI_API sint8 m2m_wifi_set_cust_InfoElement(uint8* pau8M2mCustInfoElement); /*! +@ingroup WLANPS @fn \ NMI_API sint8 m2m_wifi_set_power_profile(uint8 u8PwrMode); @brief Change the power profile mode -@param [in] u8PwrMode - Change the WINC15x0 power profile to different mode based on the enumeration - @ref tenuM2mPwrMode. +@param [in] u8PwrMode + Change the WINC power profile to different mode based on the enumeration @ref tenuM2mPwrMode. @warning May only be called after initialization, before any connection request, and may not be used to change @@ -3454,6 +3679,7 @@ NMI_API sint8 m2m_wifi_set_cust_InfoElement(uint8* pau8M2mCustInfoElement); sint8 m2m_wifi_set_power_profile(uint8 u8PwrMode); /*! +@ingroup WLANCONF @fn \ NMI_API sint8 m2m_wifi_set_tx_power(uint8 u8TxPwrLevel); @@ -3476,6 +3702,7 @@ sint8 m2m_wifi_set_power_profile(uint8 u8PwrMode); sint8 m2m_wifi_set_tx_power(uint8 u8TxPwrLevel); /*! +@ingroup WLANCONF @fn \ NMI_API sint8 m2m_wifi_set_gain_table_idx(uint8 u8GainTableIdx); @@ -3498,6 +3725,7 @@ sint8 m2m_wifi_set_tx_power(uint8 u8TxPwrLevel); sint8 m2m_wifi_set_gain_table_idx(uint8 u8GainTableIdx); /*! +@ingroup WLANLOG @fn \ NMI_API sint8 m2m_wifi_enable_firmware_logs(uint8 u8Enable); @@ -3505,8 +3733,7 @@ sint8 m2m_wifi_set_gain_table_idx(uint8 u8GainTableIdx); Enable or Disable logs in run time. @details - Enable or Disable logs in run time (Disable Firmware logs will enhance the firmware start-up time - and performance). + Enable or Disable logs in run time (Disable Firmware logs will enhance the firmware start-up time and performance). @param [in] u8Enable Set 1 to enable the logs, 0 for disable. @@ -3524,6 +3751,7 @@ sint8 m2m_wifi_set_gain_table_idx(uint8 u8GainTableIdx); sint8 m2m_wifi_enable_firmware_logs(uint8 u8Enable); /*! +@ingroup WLANCONF @fn \ NMI_API sint8 m2m_wifi_set_battery_voltage(uint8 u8BattVolt); @@ -3545,6 +3773,7 @@ sint8 m2m_wifi_enable_firmware_logs(uint8 u8Enable); sint8 m2m_wifi_set_battery_voltage(uint16 u16BattVoltx100); /*! +@ingroup WLANCONF @fn \ sint8 m2m_wifi_set_gains(tstrM2mWifiGainsParams* pstrM2mGain); @@ -3558,8 +3787,7 @@ sint8 m2m_wifi_set_battery_voltage(uint16 u16BattVoltx100); Must be called after initialization through the following function @ref m2m_wifi_init. @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. @see m2m_wifi_init @@ -3568,6 +3796,7 @@ sint8 m2m_wifi_set_gains(tstrM2mWifiGainsParams* pstrM2mGain); #ifdef ETH_MODE /*! +@ingroup WLANETH @fn \ NMI_API sint8 m2m_wifi_enable_mac_mcast(uint8 *, uint8); @@ -3594,8 +3823,7 @@ sint8 m2m_wifi_set_gains(tstrM2mWifiGainsParams* pstrM2mGain); Make sure that the application defines ETH_MODE. @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. @see m2m_wifi_set_receive_buffer @@ -3604,6 +3832,7 @@ sint8 m2m_wifi_set_gains(tstrM2mWifiGainsParams* pstrM2mGain); NMI_API sint8 m2m_wifi_enable_mac_mcast(uint8* pu8MulticastMacAddress, uint8 u8AddRemove); /*! +@ingroup WLANETH @fn \ NMI_API sint8 m2m_wifi_set_receive_buffer(void *, uint16); @@ -3629,8 +3858,7 @@ NMI_API sint8 m2m_wifi_enable_mac_mcast(uint8* pu8MulticastMacAddress, uint8 u8A This function is available in the Ethernet/bypass mode ONLY. Make sure that the application defines ETH_MODE.\n @return - The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC, - and a negative value otherwise. + The function returns @ref M2M_SUCCESS if the command has been successfully queued to the WINC and a negative value otherwise. @see m2m_wifi_enable_mac_mcast @@ -3641,22 +3869,25 @@ NMI_API sint8 m2m_wifi_set_receive_buffer(void* pvBuffer,uint16 u16BufferLen); #endif /* ETH_MODE */ /*! +@ingroup WLANCRYPTO @fn \ sint8 m2m_wifi_prng_get_random_bytes(uint8* pu8PrngBuff,uint16 u16PrngSize); @brief - Asynchronous function for retrieving from the firmware a pseudo-random set of bytes as specified in the size passed in as a parameter. - The registered wifi-cb function retrieves the random bytes through the response @ref M2M_WIFI_RESP_GET_PRNG + Asynchronous function for retrieving from the firmware a pseudo-random set of bytes. -@param [in] pu8PrngBuff - Pointer to a buffer to receive data. +@details + Asynchronous function for retrieving from the firmware a pseudo-random set of bytes as specified in the size passed in as a parameter. + The registered wifi-cb function retrieves the random bytes through the response @ref M2M_WIFI_RESP_GET_PRNG -@param [in] u16PrngSize - Request size in bytes +@param [in] pu8PrngBuff + Pointer to a buffer to receive data. + +@param [in] u16PrngSize + Requested size in bytes. Maximum 1580. @warning - Size greater than the maximum specified (@ref M2M_BUFFER_MAX_SIZE - sizeof(tstrPrng)) - causes a negative error @ref M2M_ERR_FAIL. + Size greater than the maximum specified (1580) causes a negative error @ref M2M_ERR_FAIL. @return The function returns @ref M2M_SUCCESS for successful operations and a negative value otherwise. @@ -3664,7 +3895,8 @@ NMI_API sint8 m2m_wifi_set_receive_buffer(void* pvBuffer,uint16 u16BufferLen); sint8 m2m_wifi_prng_get_random_bytes(uint8 * pu8PrngBuff,uint16 u16PrngSize); /*! -@fn \ +@ingroup WLANCONF +@fn \ NMI_API sint8 m2m_wifi_conf_auto_rate(tstrConfAutoRate * pstrConfAutoRate); @brief @@ -3687,6 +3919,7 @@ sint8 m2m_wifi_prng_get_random_bytes(uint8 * pu8PrngBuff,uint16 u16PrngSize); NMI_API sint8 m2m_wifi_conf_auto_rate(tstrConfAutoRate * pstrConfAutoRate); /*! +@ingroup WLANROAMING @fn \ sint8 m2m_wifi_enable_roaming(uint8 bEnableDhcp); @@ -3717,6 +3950,7 @@ NMI_API sint8 m2m_wifi_conf_auto_rate(tstrConfAutoRate * pstrConfAutoRate); */ sint8 m2m_wifi_enable_roaming(uint8 bEnableDhcp); /*! +@ingroup WLANROAMING @fn \ sint8 m2m_wifi_disable_roaming(void); @@ -3735,6 +3969,7 @@ sint8 m2m_wifi_enable_roaming(uint8 bEnableDhcp); sint8 m2m_wifi_disable_roaming(void); /*! +@ingroup WLANINIT @fn \ NMI_API uint8 m2m_wifi_get_state(void); @@ -3755,11 +3990,8 @@ sint8 m2m_wifi_disable_roaming(void); */ NMI_API uint8 m2m_wifi_get_state(void); -/**@}*/ //WLANAPI - - -/** @addtogroup VERSION Version -@brief +/** @defgroup VERSION Version + @brief Describes the APIs for reading the version information of the WINC firmware. @{ @defgroup VERSIONDEF Defines @@ -3785,6 +4017,27 @@ NMI_API uint8 m2m_wifi_get_state(void); */ NMI_API sint8 m2m_wifi_get_firmware_version(tstrM2mRev* pstrRev); +/*! +@ingroup WLANPS +@fn \ + sint8 m2m_wifi_enable_XO_during_sleep(uint8 bXOSleepEnable); + +@brief + Specifies whether the crystal oscillator is left on or off during deep sleep (defaults to off). + +@details + m2m_wifi_enable_XO_during_sleep controls the state of the crystal oscillator during deep sleep. + +@param[in] bXOSleepEnable + 0 : The XO will be switched off during deep sleep + 1 : The XO will be kept on during deep sleep + +@return + The function returns @ref M2M_SUCCESS for successful operations and a negative value otherwise. + +*/ +sint8 m2m_wifi_enable_XO_during_sleep(uint8 bXOSleepEnable); + #ifdef __cplusplus } #endif diff --git a/src/drivers/winc1500/include/socket/include/m2m_socket_host_if.h b/src/drivers/winc1500/include/socket/include/m2m_socket_host_if.h index 470d2321f..a6ab37ca4 100644 --- a/src/drivers/winc1500/include/socket/include/m2m_socket_host_if.h +++ b/src/drivers/winc1500/include/socket/include/m2m_socket_host_if.h @@ -4,7 +4,7 @@ * * \brief BSD compatible socket interface internal types. * - * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries. + * Copyright (c) 2016-2021 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * @@ -65,7 +65,19 @@ MACROS #define SSL_MAX_OPT_LEN HOSTNAME_MAX_SIZE - +#define ALPN_LIST_MIN_SIZE 4 +#define ALPN_LIST_MAX_SIZE 32 +/*!< + Maximum length of ALPN list that can be specified by the application. + The list is in the following format: + @verbatim + 0 1 2 3 ... (bytes) + +-------+-------+-------+ ... +-------+ ... +-------+ ... + | Length L (BE) | len1 | name1... | len2 | name2... | len3 | name3... + +-------+-------+-------+ ... +-------+ ... +-------+ ... + Length fields do not include themselves. + @endverbatim +*/ #define SOCKET_CMD_INVALID 0x00 /*!< @@ -139,7 +151,7 @@ MACROS */ -#define SOCKET_CMD_SSL_SEND 0x4C +#define SOCKET_CMD_SSL_SEND 0x4C /*!< SSL-Socket Send command value. */ @@ -195,6 +207,16 @@ MACROS */ +#define SOCKET_CMD_SECURE 0x56 +/*!< + Make secure a previously opened socket. +*/ + +#define SOCKET_CMD_SSL_CONNECT_ALPN 0x57 +/*!< + SSL-Socket Connect with ALPN command value. +*/ + #define PING_ERR_SUCCESS 0 #define PING_ERR_DEST_UNREACH 1 @@ -318,14 +340,49 @@ typedef struct{ typedef struct{ SOCKET sock; sint8 s8Error; - uint16 u16AppDataOffset; /*!< - In further packet send requests the host interface should put the user application - data at this offset in the allocated shared data packet. + 0 for successful connection, in which case u16AppDataOffset is valid. + Negative for failed connection, in which case u8ErrorType and u8ErrorDetail may give more info. */ + union { + uint16 u16AppDataOffset; + /*!< + In further packet send requests the host interface should put the user application + data at this offset in the allocated shared data packet. + */ + struct { + uint8 u8ErrSource; + /*!< + 0: No detail + 1: TLS Alert received from peer + 2: TLS Alert generated locally + */ + uint8 u8ErrCode; + /*!< + For TLS Alerts, this is the Alert ID. + */ + }; + }; }tstrConnectReply; +/*! +@struct \ + tstrConnectAlpnReply + +@brief + Connect Reply, contains sock number, error value and index of negotiated application protocol. +*/ +typedef struct{ + tstrConnectReply strConnReply; + uint8 u8AppProtocolIdx; + /*!< + 1-based index of application-layer protocol negotiated during TLS handshake. + */ + uint8 __PAD24__[3]; +}tstrConnectAlpnReply; + + /*! @brief */ @@ -363,11 +420,13 @@ typedef struct{ SOCKET sock; uint8 u8Void; uint16 u16SessionID; + uint16 u16BufLen; }tstrRecvCmd; /*! -@struct +@struct \ + tstrRecvReply @brief */ typedef struct{ diff --git a/src/drivers/winc1500/include/socket/include/socket.h b/src/drivers/winc1500/include/socket/include/socket.h index 95a83ad86..db2281a27 100644 --- a/src/drivers/winc1500/include/socket/include/socket.h +++ b/src/drivers/winc1500/include/socket/include/socket.h @@ -4,7 +4,7 @@ * * \brief WINC BSD compatible Socket Interface. * - * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries. + * Copyright (c) 2016-2021 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * @@ -43,9 +43,9 @@ extern "C" { * BSD compatible socket interface between the host layer and the network * protocol stacks in the firmware. * These functions are used by the host application to send or receive - * packets and to do other socket operations. + * packets and to do other socket operations. */ - + /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* INCLUDES *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ @@ -56,222 +56,323 @@ INCLUDES /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* MACROS *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ -/** - * @defgroup SocketDefines Defines +/**@defgroup SocketDefines Defines * @ingroup SocketHeader */ -/** @defgroup IPDefines TCP/IP Defines +/**@defgroup IPDefines TCP/IP Defines * @ingroup SocketDefines * The following list of macros are used to define constants used throughout the socket layer. * @{ */ /* - * HOSTNAME_MAX_SIZE is defined here and also in host_drv/socket/include/m2m_socket_host_if.h - * The two definitions must match. + * HOSTNAME_MAX_SIZE is defined here and also in host_drv/socket/include/m2m_socket_host_if.h + * The two definitions must match. */ -#define HOSTNAME_MAX_SIZE 64 -/*!< - Maximum allowed size for a host domain name passed to the function gethostbyname @ref gethostbyname. - command value. Used with the setsockopt function. -*/ - -#define SOCKET_BUFFER_MAX_LENGTH 1400 -/*!< - Maximum allowed size for a socket data buffer. Used with @ref send socket - function to ensure that the buffer sent is within the allowed range. -*/ - -#define AF_INET 2 -/*!< - The AF_INET is the address family used for IPv4. An IPv4 transport address is specified with the @ref sockaddr_in structure. - (It is the only supported type for the current implementation.) -*/ - - -#define SOCK_STREAM 1 -/*!< - One of the IPv4 supported socket types for reliable connection-oriented stream connection. - Passed to the @ref socket function for the socket creation operation. -*/ - -#define SOCK_DGRAM 2 +#define HOSTNAME_MAX_SIZE 64 /*!< - One of the IPv4 supported socket types for unreliable connectionless datagram connection. - Passed to the @ref socket function for the socket creation operation. + Maximum allowed size for a host domain name passed to the function gethostbyname @ref gethostbyname. + command value. Used with the @ref setsockopt function. */ -#define SOCKET_FLAGS_SSL 0x01 -/*!< - This flag shall be passed to the socket API for SSL session. -*/ - -#define TCP_SOCK_MAX (7) +#define SOCKET_BUFFER_MAX_LENGTH 1400 /*!< - Maximum number of simultaneous TCP sockets. + Maximum allowed size for a socket data buffer. Used with @ref send socket + function to ensure that the buffer sent is within the allowed range. */ -#define UDP_SOCK_MAX 4 +#define AF_INET 2 /*!< - Maximum number of simultaneous UDP sockets. + The AF_INET is the address family used for IPv4. An IPv4 transport address is specified with the @ref sockaddr_in structure. + (It is the only supported type for the current implementation.) */ -#define MAX_SOCKET (TCP_SOCK_MAX + UDP_SOCK_MAX) +#define SOCK_STREAM 1 /*!< - Maximum number of simultaneous sockets. + One of the IPv4 supported socket types for reliable connection-oriented stream connection. + Passed to the @ref socket function for the socket creation operation. */ -#define SOL_SOCKET 1 -/*!< - Socket option. - Used with the @ref setsockopt function -*/ - -#define SOL_SSL_SOCKET 2 -/*!< - SSL Socket option level. - Used with the @ref setsockopt function -*/ - -#define SO_SET_UDP_SEND_CALLBACK 0x00 +#define SOCK_DGRAM 2 /*!< - Socket option used by the application to enable/disable - the use of UDP send callbacks. - Used with the @ref setsockopt function. + One of the IPv4 supported socket types for unreliable connectionless datagram connection. + Passed to the @ref socket function for the socket creation operation. */ -#define IP_ADD_MEMBERSHIP 0x01 +#define SOCKET_FLAGS_SSL 0x01 /*!< - Set Socket Option Add Membership command value (to join a multicast group). - Used with the @ref setsockopt function. + This flag may be set in the u8Config parameter of @ref socket, to create a + TLS socket.\n + Note that the number of TLS sockets is limited to 4.\n + This flag is kept for legacy purposes. It is recommended that applications + use @ref SOCKET_CONFIG_SSL_ON instead. */ -#define IP_DROP_MEMBERSHIP 0x02 +#define SOCKET_CONFIG_SSL_OFF 0 /*!< - Set Socket Option Drop Membership command value (to leave a multicast group). - Used with the @ref setsockopt function. + This value may be passed in the u8Config parameter of @ref socket, to + create a socket not capable of TLS. +*/ +#define SOCKET_CONFIG_SSL_ON 1 +/*!< + This value may be passed in the u8Config parameter of @ref socket, to + create a TLS socket.\n + Note that the number of TLS sockets is limited to 4. +*/ +#define SOCKET_CONFIG_SSL_DELAY 2 +/*!< + This value may be passed in the u8Config parameter of @ref socket, to + create a TCP socket which has the potential to upgrade to a TLS socket + later (by calling @ref secure).\n + Note that the total number of TLS sockets and potential TLS sockets is + limited to 4. */ -#define SO_KEEPALIVE 0x04 +#define TCP_SOCK_MAX (7) /*!< - Enable or disable TCP keep-alive. - Used with the @ref setsockopt function. + Maximum number of simultaneous TCP sockets. */ -#define TCP_KEEPIDLE 0x05 +#define UDP_SOCK_MAX 4 /*!< - Duration between two keepalive transmissions in idle condition (in 500ms increments, so 4 would be 2 seconds). Max 2^32. - Used with the @ref setsockopt function. + Maximum number of simultaneous UDP sockets. */ -#define TCP_KEEPINTVL 0x06 +#define MAX_SOCKET (TCP_SOCK_MAX + UDP_SOCK_MAX) /*!< - Duration between two successive keepalive retransmissions, if acknowledgment to the previous keepalive - transmission is not received (in 500ms increments, so 4 would be 2 seconds). Max 255 (127.5 seconds). - Used with the @ref setsockopt function. + Maximum number of simultaneous sockets. */ -#define TCP_KEEPCNT 0x07 +#define SOL_SOCKET 1 /*!< - Number of retransmissions to be carried out before declaring that the remote end is not available. Max 255. - Used with the @ref setsockopt function. + Socket option. + Used with the @ref setsockopt function +*/ + +#define SOL_SSL_SOCKET 2 +/*!< + SSL Socket option level. + Used with the @ref setsockopt function +*/ + +#define SO_SET_UDP_SEND_CALLBACK 0x00 +/*!< + Socket option used by the application to enable/disable + the use of UDP send callbacks.\n + Used with the @ref setsockopt function.\n + The option value should be cast to int type.\n + 0: disable UDP send callbacks.\n + 1: enable UDP send callbacks.\n + Default setting is enable. + + @warning @ref connect and @ref bind cause this setting to + be lost, so the application should only set this option + after calling @ref connect or @ref bind. +*/ + +#define IP_ADD_MEMBERSHIP 0x01 +/*!< + Set Socket Option Add Membership command value (to join a multicast group). + Used with the @ref setsockopt function. +*/ + +#define IP_DROP_MEMBERSHIP 0x02 +/*!< + Set Socket Option Drop Membership command value (to leave a multicast group). + Used with the @ref setsockopt function. +*/ + +#define SO_TCP_KEEPALIVE 0x04 +/*!< + Socket option to enable or disable TCP keep-alive.\n + Used with the @ref setsockopt function.\n + The option value should be cast to int type.\n + 0: disable TCP keep-alive.\n + 1: enable TCP keep-alive.\n + Default setting is disable. + + @warning @ref connect and @ref bind cause this setting to + be lost, so the application should only set this option + after calling @ref connect or @ref bind. +*/ + +#define SO_TCP_KEEPIDLE 0x05 +/*!< + Socket option to set the time period after which the socket will trigger keep-alive transmissions.\n + Used with the @ref setsockopt function.\n + The option value should be cast to int type.\n + Option value is the time period in units of 500ms. Maximum 2^32 - 1. + Default setting is 120 (60 seconds). + + @warning @ref connect and @ref bind cause this setting to + be lost, so the application should only set this option + after calling @ref connect or @ref bind. +*/ + +#define SO_TCP_KEEPINTVL 0x06 +/*!< + Socket option to set the time period between keep-alive retransmissions.\n + Used with the @ref setsockopt function.\n + The option value should be cast to int type.\n + Option value is the time period in units of 500ms. Maximum 255. + Default setting is 1 (0.5 seconds). + + @warning @ref connect and @ref bind cause this setting to + be lost, so the application should only set this option + after calling @ref connect or @ref bind. +*/ + +#define SO_TCP_KEEPCNT 0x07 +/*!< + Socket option to set the number of keep-alive retransmissions to be carried out before declaring that the remote end is not available.\n + Used with the @ref setsockopt function.\n + The option value should be cast to int type.\n + Maximum 255. + Default setting is 20. + + @warning @ref connect and @ref bind cause this setting to + be lost, so the application should only set this option + after calling @ref connect or @ref bind. */ /**@}*/ //IPDefines + +/**@addtogroup TLSDefines + * @{ + */ +#define ALPN_LIST_MAX_APP_LENGTH 30 +/*!< + Maximum length of ALPN list that can be specified by the application. + This length includes separators (spaces) and terminator (NUL). +*/ +/**@}*/ // TLSDefines + /** - * @defgroup TLSDefines TLS Defines - * @ingroup SocketDefines + * @defgroup TLSDefines TLS Defines + * @ingroup SOCKETDEF * @ingroup SSLAPI */ -/** @defgroup SSLSocketOptions TLS Socket Options +/**@defgroup SSLSocketOptions TLS Socket Options * @ingroup TLSDefines * The following list of macros are used to define SSL Socket options. * @{ * @sa setsockopt */ -#define SO_SSL_BYPASS_X509_VERIF 0x01 +#define SO_SSL_BYPASS_X509_VERIF 0x01 /*!< - Allow an opened SSL socket to bypass the X509 certificate - verification process. - It is highly required NOT to use this socket option in production - software applications. It is supported for debugging and testing - purposes. - The option value should be casted to int type and it is handled - as a boolean flag. + Allow an opened SSL socket to bypass the X509 certificate verification + process. + It is recommended NOT to use this socket option in production software + applications. It is supported for debugging and testing purposes.\n + The option value should be casted to int type.\n + 0: do not bypass the X509 certificate verification process (default, + recommended).\n + 1: bypass the X509 certificate verification process.\n + + This option only takes effect if it is set after calling @ref socket and + before calling @ref connect or @ref secure. */ -#define SO_SSL_SNI 0x02 +#define SO_SSL_SNI 0x02 /*!< - Set the Server Name Indicator (SNI) for an SSL socket. The - SNI is a NULL terminated string containing the server name - associated with the connection. It must not exceed the size - of HOSTNAME_MAX_SIZE. + Set the Server Name Indicator (SNI) for an SSL socket. The SNI is a NULL- + terminated string containing the server name associated with the + connection. Its size must not exceed @ref HOSTNAME_MAX_SIZE. If the SNI is + not a null string, then TLS Client Hello messages will include the SNI + extension.\n + + This option only takes effect if it is set after calling @ref socket and + before calling @ref connect or @ref secure. */ -#define SO_SSL_ENABLE_SESSION_CACHING 0x03 +#define SO_SSL_ENABLE_SESSION_CACHING 0x03 /*!< - This option allow the TLS to cache the session information for fast - TLS session establishment in future connections using the - TLS Protocol session resume features. + This option allow the TLS to cache the session information for fast TLS + session establishment in future connections using the TLS Protocol session + resume features.\n + The option value should be casted to int type.\n + 0: disable TLS session caching (default).\n + 1: enable TLS session caching.\n + Note that TLS session caching is always enabled in TLS Server Mode and this + option is ignored.\n + + This option only takes effect if it is set after calling @ref socket and + before calling @ref connect or @ref secure. */ -#define SO_SSL_ENABLE_SNI_VALIDATION 0x04 +#define SO_SSL_ENABLE_SNI_VALIDATION 0x04 /*!< - Enable SNI validation against the server's certificate subject - common name. If there is no SNI provided (via the SO_SSL_SNI - option), setting this option does nothing. + Enable internal validation of server name against the server's + certificate subject common name. If there is no server name + provided (via the @ref SO_SSL_SNI option), setting this option + does nothing.\n + The option value should be casted to int type.\n + 0: disable server certificate name validation (default).\n + 1: enable server certificate name validation (recommended).\n + + This option only takes effect if it is set after calling @ref socket and + before calling @ref connect or @ref secure. +*/ + +#define SO_SSL_ALPN 0x05 +/*!< + Set the list to use for Application-Layer Protocol Negotiation + for an SSL socket. \n + This option is intended for internal use and should not be + used by the application. Applications should use the API @ref + set_alpn_list. */ /**@}*/ //SSLSocketOptions -/** @defgroup LegacySSLCipherSuite Legacy names for TLS Cipher Suite IDs +/**@defgroup LegacySSLCipherSuite Legacy names for TLS Cipher Suite IDs * @ingroup TLSDefines * The following list of macros MUST NOT be used. Instead use the new names under SSLCipherSuiteID * @sa m2m_ssl_set_active_ciphersuites * @{ */ -#define SSL_ENABLE_RSA_SHA_SUITES 0x01 +#define SSL_ENABLE_RSA_SHA_SUITES 0x01 /*!< - Enable RSA Hmac_SHA based Cipher suites. For example, - TLS_RSA_WITH_AES_128_CBC_SHA + Enable RSA Hmac_SHA based Cipher suites. For example, + TLS_RSA_WITH_AES_128_CBC_SHA */ -#define SSL_ENABLE_RSA_SHA256_SUITES 0x02 +#define SSL_ENABLE_RSA_SHA256_SUITES 0x02 /*!< - Enable RSA Hmac_SHA256 based Cipher suites. For example, - TLS_RSA_WITH_AES_128_CBC_SHA256 + Enable RSA Hmac_SHA256 based Cipher suites. For example, + TLS_RSA_WITH_AES_128_CBC_SHA256 */ -#define SSL_ENABLE_DHE_SHA_SUITES 0x04 +#define SSL_ENABLE_DHE_SHA_SUITES 0x04 /*!< - Enable DHE Hmac_SHA based Cipher suites. For example, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA + Enable DHE Hmac_SHA based Cipher suites. For example, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA */ -#define SSL_ENABLE_DHE_SHA256_SUITES 0x08 +#define SSL_ENABLE_DHE_SHA256_SUITES 0x08 /*!< - Enable DHE Hmac_SHA256 based Cipher suites. For example, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 + Enable DHE Hmac_SHA256 based Cipher suites. For example, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 */ -#define SSL_ENABLE_RSA_GCM_SUITES 0x10 +#define SSL_ENABLE_RSA_GCM_SUITES 0x10 /*!< - Enable RSA AEAD based Cipher suites. For example, - TLS_RSA_WITH_AES_128_GCM_SHA256 + Enable RSA AEAD based Cipher suites. For example, + TLS_RSA_WITH_AES_128_GCM_SHA256 */ -#define SSL_ENABLE_DHE_GCM_SUITES 0x20 +#define SSL_ENABLE_DHE_GCM_SUITES 0x20 /*!< - Enable DHE AEAD based Cipher suites. For example, - TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 + Enable DHE AEAD based Cipher suites. For example, + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 */ #define SSL_ENABLE_ALL_SUITES 0x0000003F /*!< - Enable all possible supported cipher suites. + Enable all possible supported cipher suites. */ /**@}*/ //LegacySSLCipherSuite @@ -283,103 +384,103 @@ MACROS * @{ */ -#define SSL_CIPHER_RSA_WITH_AES_128_CBC_SHA NBIT0 -#define SSL_CIPHER_RSA_WITH_AES_128_CBC_SHA256 NBIT1 -#define SSL_CIPHER_DHE_RSA_WITH_AES_128_CBC_SHA NBIT2 -#define SSL_CIPHER_DHE_RSA_WITH_AES_128_CBC_SHA256 NBIT3 -#define SSL_CIPHER_RSA_WITH_AES_128_GCM_SHA256 NBIT4 -#define SSL_CIPHER_DHE_RSA_WITH_AES_128_GCM_SHA256 NBIT5 -#define SSL_CIPHER_RSA_WITH_AES_256_CBC_SHA NBIT6 -#define SSL_CIPHER_RSA_WITH_AES_256_CBC_SHA256 NBIT7 -#define SSL_CIPHER_DHE_RSA_WITH_AES_256_CBC_SHA NBIT8 -#define SSL_CIPHER_DHE_RSA_WITH_AES_256_CBC_SHA256 NBIT9 -#define SSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA NBIT10 -#define SSL_CIPHER_ECDHE_RSA_WITH_AES_256_CBC_SHA NBIT11 -#define SSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA256 NBIT12 -#define SSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 NBIT13 -#define SSL_CIPHER_ECDHE_RSA_WITH_AES_128_GCM_SHA256 NBIT14 -#define SSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 NBIT15 +#define SSL_CIPHER_RSA_WITH_AES_128_CBC_SHA NBIT0 +#define SSL_CIPHER_RSA_WITH_AES_128_CBC_SHA256 NBIT1 +#define SSL_CIPHER_DHE_RSA_WITH_AES_128_CBC_SHA NBIT2 +#define SSL_CIPHER_DHE_RSA_WITH_AES_128_CBC_SHA256 NBIT3 +#define SSL_CIPHER_RSA_WITH_AES_128_GCM_SHA256 NBIT4 +#define SSL_CIPHER_DHE_RSA_WITH_AES_128_GCM_SHA256 NBIT5 +#define SSL_CIPHER_RSA_WITH_AES_256_CBC_SHA NBIT6 +#define SSL_CIPHER_RSA_WITH_AES_256_CBC_SHA256 NBIT7 +#define SSL_CIPHER_DHE_RSA_WITH_AES_256_CBC_SHA NBIT8 +#define SSL_CIPHER_DHE_RSA_WITH_AES_256_CBC_SHA256 NBIT9 +#define SSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA NBIT10 +#define SSL_CIPHER_ECDHE_RSA_WITH_AES_256_CBC_SHA NBIT11 +#define SSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA256 NBIT12 +#define SSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 NBIT13 +#define SSL_CIPHER_ECDHE_RSA_WITH_AES_128_GCM_SHA256 NBIT14 +#define SSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 NBIT15 -#define SSL_ECC_ONLY_CIPHERS \ +#define SSL_ECC_ONLY_CIPHERS \ (\ - SSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 | \ - SSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 \ + SSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 | \ + SSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 \ ) /*!< - All ciphers that use ECC crypto only. This excludes ciphers that use RSA. They use ECDSA instead. - These ciphers are turned off by default at startup. - The application may enable them if it has an ECC math engine (like ATECC508). + All ciphers that use ECC crypto only. This excludes ciphers that use RSA. They use ECDSA instead. + These ciphers are turned off by default at startup. + The application may enable them if it has an ECC math engine (like ATECC508). */ -#define SSL_ECC_ALL_CIPHERS \ +#define SSL_ECC_ALL_CIPHERS \ (\ - SSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA | \ - SSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA256 | \ - SSL_CIPHER_ECDHE_RSA_WITH_AES_128_GCM_SHA256 | \ - SSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 | \ - SSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 \ + SSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA | \ + SSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA256 | \ + SSL_CIPHER_ECDHE_RSA_WITH_AES_128_GCM_SHA256 | \ + SSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 | \ + SSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 \ ) /*!< - All supported ECC Ciphers including those ciphers that depend on RSA and ECC. - These ciphers are turned off by default at startup. - The application may enable them if it has an ECC math engine (like ATECC508). + All supported ECC Ciphers including those ciphers that depend on RSA and ECC. + These ciphers are turned off by default at startup. + The application may enable them if it has an ECC math engine (like ATECC508). */ -#define SSL_NON_ECC_CIPHERS_AES_128 \ +#define SSL_NON_ECC_CIPHERS_AES_128 \ (\ - SSL_CIPHER_RSA_WITH_AES_128_CBC_SHA | \ - SSL_CIPHER_RSA_WITH_AES_128_CBC_SHA256 | \ - SSL_CIPHER_DHE_RSA_WITH_AES_128_CBC_SHA | \ - SSL_CIPHER_DHE_RSA_WITH_AES_128_CBC_SHA256 | \ - SSL_CIPHER_RSA_WITH_AES_128_GCM_SHA256 | \ - SSL_CIPHER_DHE_RSA_WITH_AES_128_GCM_SHA256 \ + SSL_CIPHER_RSA_WITH_AES_128_CBC_SHA | \ + SSL_CIPHER_RSA_WITH_AES_128_CBC_SHA256 | \ + SSL_CIPHER_DHE_RSA_WITH_AES_128_CBC_SHA | \ + SSL_CIPHER_DHE_RSA_WITH_AES_128_CBC_SHA256 | \ + SSL_CIPHER_RSA_WITH_AES_128_GCM_SHA256 | \ + SSL_CIPHER_DHE_RSA_WITH_AES_128_GCM_SHA256 \ ) /*!< - All supported AES-128 Ciphers (ECC ciphers are not counted). This is the default active group after startup. + All supported AES-128 Ciphers (ECC ciphers are not counted). This is the default active group after startup. */ -#define SSL_ECC_CIPHERS_AES_256 \ +#define SSL_ECC_CIPHERS_AES_256 \ (\ - SSL_CIPHER_ECDHE_RSA_WITH_AES_256_CBC_SHA \ + SSL_CIPHER_ECDHE_RSA_WITH_AES_256_CBC_SHA \ ) /*!< - ECC AES-256 supported ciphers. + ECC AES-256 supported ciphers. */ -#define SSL_NON_ECC_CIPHERS_AES_256 \ +#define SSL_NON_ECC_CIPHERS_AES_256 \ (\ - SSL_CIPHER_RSA_WITH_AES_256_CBC_SHA | \ - SSL_CIPHER_RSA_WITH_AES_256_CBC_SHA256 | \ - SSL_CIPHER_DHE_RSA_WITH_AES_256_CBC_SHA | \ - SSL_CIPHER_DHE_RSA_WITH_AES_256_CBC_SHA256 \ + SSL_CIPHER_RSA_WITH_AES_256_CBC_SHA | \ + SSL_CIPHER_RSA_WITH_AES_256_CBC_SHA256 | \ + SSL_CIPHER_DHE_RSA_WITH_AES_256_CBC_SHA | \ + SSL_CIPHER_DHE_RSA_WITH_AES_256_CBC_SHA256 \ ) /*!< - AES-256 Ciphers. + AES-256 Ciphers. This group is disabled by default at startup because the WINC HW Accelerator - supports only AES-128. If the application needs to force AES-256 cipher support, - it could enable them (or any of them) explicitly by calling m2m_ssl_set_active_ciphersuites. + supports only AES-128. If the application needs to force AES-256 cipher support, + it could enable them (or any of them) explicitly by calling m2m_ssl_set_active_ciphersuites. */ -#define SSL_CIPHER_ALL \ +#define SSL_CIPHER_ALL \ (\ - SSL_CIPHER_RSA_WITH_AES_128_CBC_SHA | \ - SSL_CIPHER_RSA_WITH_AES_128_CBC_SHA256 | \ - SSL_CIPHER_DHE_RSA_WITH_AES_128_CBC_SHA | \ - SSL_CIPHER_DHE_RSA_WITH_AES_128_CBC_SHA256 | \ - SSL_CIPHER_RSA_WITH_AES_128_GCM_SHA256 | \ - SSL_CIPHER_DHE_RSA_WITH_AES_128_GCM_SHA256 | \ - SSL_CIPHER_RSA_WITH_AES_256_CBC_SHA | \ - SSL_CIPHER_RSA_WITH_AES_256_CBC_SHA256 | \ - SSL_CIPHER_DHE_RSA_WITH_AES_256_CBC_SHA | \ - SSL_CIPHER_DHE_RSA_WITH_AES_256_CBC_SHA256 | \ - SSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA | \ - SSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA256 | \ - SSL_CIPHER_ECDHE_RSA_WITH_AES_128_GCM_SHA256 | \ - SSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 | \ - SSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 | \ - SSL_CIPHER_ECDHE_RSA_WITH_AES_256_CBC_SHA \ + SSL_CIPHER_RSA_WITH_AES_128_CBC_SHA | \ + SSL_CIPHER_RSA_WITH_AES_128_CBC_SHA256 | \ + SSL_CIPHER_DHE_RSA_WITH_AES_128_CBC_SHA | \ + SSL_CIPHER_DHE_RSA_WITH_AES_128_CBC_SHA256 | \ + SSL_CIPHER_RSA_WITH_AES_128_GCM_SHA256 | \ + SSL_CIPHER_DHE_RSA_WITH_AES_128_GCM_SHA256 | \ + SSL_CIPHER_RSA_WITH_AES_256_CBC_SHA | \ + SSL_CIPHER_RSA_WITH_AES_256_CBC_SHA256 | \ + SSL_CIPHER_DHE_RSA_WITH_AES_256_CBC_SHA | \ + SSL_CIPHER_DHE_RSA_WITH_AES_256_CBC_SHA256 | \ + SSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA | \ + SSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA256 | \ + SSL_CIPHER_ECDHE_RSA_WITH_AES_128_GCM_SHA256 | \ + SSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 | \ + SSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 | \ + SSL_CIPHER_ECDHE_RSA_WITH_AES_256_CBC_SHA \ ) /*!< - Turn On All TLS Ciphers. + Turn On All TLS Ciphers. */ /**@}*/ //SSLCipherSuiteID @@ -392,201 +493,247 @@ Socket Errors * Errors are listed in numerical order with the error macro name. * @{ */ -#define SOCK_ERR_NO_ERROR 0 +#define SOCK_ERR_NO_ERROR 0 /*!< - Successful socket operation. This code is also used with event @ref SOCKET_MSG_RECV if a socket connection is closed. - In that case, the application should call @ref close(). + Successful socket operation. This code is also used with event @ref SOCKET_MSG_RECV if a socket connection is closed. + In that case, the application should call @ref close(). */ -#define SOCK_ERR_INVALID_ADDRESS -1 +#define SOCK_ERR_INVALID_ADDRESS -1 /*!< - Socket address is invalid. The socket operation cannot be completed successfully without specifying a specific address - For example: bind is called without specifying a port number + Socket address is invalid. The socket operation cannot be completed successfully without specifying a specific address + For example: bind is called without specifying a port number */ -#define SOCK_ERR_ADDR_ALREADY_IN_USE -2 +#define SOCK_ERR_ADDR_ALREADY_IN_USE -2 /*!< - Socket operation cannot bind on the given address. With socket operations, only one IP address per socket is permitted. - Any attempt for a new socket to bind with an IP address already bound to another open socket, - will return the following error code. States that bind operation failed. + Socket operation cannot bind on the given address. With socket operations, only one IP address per socket is permitted. + Any attempt for a new socket to bind with an IP address already bound to another open socket, + will return the following error code. States that bind operation failed. */ -#define SOCK_ERR_MAX_TCP_SOCK -3 +#define SOCK_ERR_MAX_TCP_SOCK -3 /*!< - Exceeded the maximum number of TCP sockets. A maximum number of TCP sockets opened simultaneously is defined through TCP_SOCK_MAX. - It is not permitted to exceed that number at socket creation. Identifies that @ref socket operation failed. + Exceeded the maximum number of TCP sockets. A maximum number of TCP sockets opened simultaneously is defined through TCP_SOCK_MAX. + It is not permitted to exceed that number at socket creation. Identifies that @ref socket operation failed. */ -#define SOCK_ERR_MAX_UDP_SOCK -4 +#define SOCK_ERR_MAX_UDP_SOCK -4 /*!< - Exceeded the maximum number of UDP sockets. A maximum number of UDP sockets opened simultaneously is defined through UDP_SOCK_MAX. - It is not permitted to exceed that number at socket creation. Identifies that @ref socket operation failed + Exceeded the maximum number of UDP sockets. A maximum number of UDP sockets opened simultaneously is defined through UDP_SOCK_MAX. + It is not permitted to exceed that number at socket creation. Identifies that @ref socket operation failed */ -#define SOCK_ERR_INVALID_ARG -6 +#define SOCK_ERR_INVALID_ARG -6 /*!< - An invalid argument is passed to a function. + An invalid argument is passed to a socket function. Identifies that @ref socket operation failed */ -#define SOCK_ERR_MAX_LISTEN_SOCK -7 +#define SOCK_ERR_MAX_LISTEN_SOCK -7 /*!< - Exceeded the maximum number of TCP passive listening sockets. - Identifies Identifies that @ref listen operation failed. + Exceeded the maximum number of TCP passive listening sockets. + Identifies that @ref listen operation failed. */ -#define SOCK_ERR_INVALID -9 +#define SOCK_ERR_INVALID -9 /*!< - The requested socket operation is not valid in the - current socket state. - For example: @ref accept is called on a TCP socket before @ref bind or @ref listen. + The requested socket operation is not valid in the current socket state. + For example: @ref accept is called on a TCP socket before @ref bind or @ref listen. */ -#define SOCK_ERR_ADDR_IS_REQUIRED -11 +#define SOCK_ERR_ADDR_IS_REQUIRED -11 /*!< - Destination address is required. Failure to provide the socket address required for the socket operation to be completed. - It is generated as an error to the @ref sendto function when the address required to send the data to is not known. + Destination address is required. Failure to provide the socket address required for the socket operation to be completed. + It is generated as an error to the @ref sendto function when the address required to send the data to is not known. */ -#define SOCK_ERR_CONN_ABORTED -12 +#define SOCK_ERR_CONN_ABORTED -12 /*!< - The socket is closed (reset) by the peer. If this error is received, the application should call @ref close(). + The socket is closed (reset) by the peer. If this error is received, the application should call @ref close(). */ -#define SOCK_ERR_TIMEOUT -13 +#define SOCK_ERR_TIMEOUT -13 /*!< - The socket pending operation has timed out. The socket remains open. + The socket pending operation has timed out. The socket remains open. */ -#define SOCK_ERR_BUFFER_FULL -14 +#define SOCK_ERR_BUFFER_FULL -14 /*!< - No buffer space available to be used for the requested socket operation. + No buffer space available to be used for the requested socket operation. */ /**@}*/ //SocketErrorCode -/** @addtogroup SocketDefines - * @{ +/**@addtogroup SOCKETBYTEORDER Byte Order + * @ingroup SocketHeader + * The following list of macros are used to convert between host representation and network byte order. + * @{ */ #ifdef _NM_BSP_BIG_END -#define _htonl(m) (m) -#define _htons(A) (A) +#define _htonl(m) (m) +#define _htons(A) (A) #else -#define _htonl(m) \ - (uint32)(((uint32)(m << 24)) | ((uint32)((m & 0x0000FF00) << 8)) | ((uint32)((m & 0x00FF0000) >> 8)) | ((uint32)(((uint32)m) >> 24))) +#define _htonl(m) \ + (uint32)(((uint32)(m << 24)) | ((uint32)((m & 0x0000FF00) << 8)) | ((uint32)((m & 0x00FF0000) >> 8)) | ((uint32)(((uint32)m) >> 24))) /*!< - Convert a 4-byte integer from the host representation to the Network byte order representation. + Convert a 4-byte integer from the host representation to the Network byte order representation. */ -#define _htons(A) (uint16)((((uint16) (A)) << 8) | (((uint16) (A)) >> 8)) +#define _htons(A) (uint16)((((uint16) (A)) << 8) | (((uint16) (A)) >> 8)) /*!< - Convert a 2-byte integer (short) from the host representation to the Network byte order representation. + Convert a 2-byte integer (short) from the host representation to the Network byte order representation. */ #endif -#define _ntohl _htonl +#define _ntohl _htonl /*!< - Convert a 4-byte integer from the Network byte order representation to the host representation . + Convert a 4-byte integer from the Network byte order representation to the host representation . */ -#define _ntohs _htons +#define _ntohs _htons /*!< - Convert a 2-byte integer from the Network byte order representation to the host representation . + Convert a 2-byte integer from the Network byte order representation to the host representation . */ -/**@}*/ //SocketDefines +/**@}*/ //SOCKETBYTEORDER /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* DATA TYPES *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ -/** @defgroup SocketEnums DataTypes +/**@defgroup SocketEnums DataTypes * @ingroup SocketHeader * Specific Enumeration-typedefs used for socket operations - * @{ */ - + * @{ + */ /*! -@typedef \ - SOCKET +@typedef \ + SOCKET @brief - Definition for socket handler data type. - Socket ID,used with all socket operations to uniquely identify the socket handler. - Such an ID is uniquely assigned at socket creation when calling @ref socket operation. + Definition for socket handler data type. + Socket ID,used with all socket operations to uniquely identify the socket handler. + The ID is uniquely assigned at socket creation when calling @ref socket operation. */ typedef sint8 SOCKET; /*! -@struct \ - in_addr +@enum \ + tenuSockErrSource @brief - IPv4 address representation. + Source of socket error (local, remote or unknown). - This structure is used as a placeholder for IPV4 address in other structures. -@see - sockaddr_in +@see tstrSockErr */ -typedef struct{ - uint32 s_addr; - /*!< - Network Byte Order representation of the IPv4 address. For example, - the address "192.168.0.10" is represented as 0x0A00A8C0. - */ -}in_addr; + +typedef enum { + SOCKET_ERR_UNKNOWN = 0, + /*!< + No detail available (also used when there is no error). + */ + SOCKET_ERR_TLS_REMOTE, + /*!< + TLS Error Alert received from peer. + */ + SOCKET_ERR_TLS_LOCAL + /*!< + TLS Error Alert generated locally. + */ +} tenuSockErrSource; + /*! -@struct \ - sockaddr +@struct \ + in_addr @brief - Generic socket address structure. + IPv4 address representation. + + This structure is used as a placeholder for IPV4 address in other structures. +@see + sockaddr_in +*/ +typedef struct { + uint32 s_addr; + /*!< + Network Byte Order representation of the IPv4 address. For example, + the address "192.168.0.10" is represented as 0x0A00A8C0. + */ +} in_addr; + +/*! +@struct \ + sockaddr + +@brief + Generic socket address structure. @see sockaddr_in */ -struct sockaddr{ - uint16 sa_family; - /*!< -Socket address family. - */ - uint8 sa_data[14]; - /*!< - Maximum size of all the different socket address structures. - */ +struct sockaddr { + uint16 sa_family; + /*!< + Socket address family. + */ + uint8 sa_data[14]; + /*!< + Maximum size of all the different socket address structures. + */ }; /*! -@struct \ - sockaddr_in +@struct \ + sockaddr_in @brief - Socket address structure for IPV4 addresses. Used to specify socket address information to which to connect to. - Can be cast to @ref sockaddr structure. + Socket address structure for IPV4 addresses. Used to specify socket address information to connect to. + Can be cast to @ref sockaddr structure. */ -struct sockaddr_in{ - uint16 sin_family; - /*!< - Specifies the address family(AF). - Members of AF_INET address family are IPv4 addresses. - Hence,the only supported value for this is AF_INET. - */ - uint16 sin_port; - /*!< - Port number of the socket. - Network sockets are identified by a pair of IP addresses and port number. - It must be set in the Network Byte Order format , _htons (e.g. _htons(80)). - Can NOT have zero value. - */ - in_addr sin_addr; - /*!< - IP Address of the socket. - The IP address is of type @ref in_addr structure. - Can be set to "0" to accept any IP address for server operation. non zero otherwise. - */ - uint8 sin_zero[8]; - /*!< - Padding to make structure the same size as @ref sockaddr. - */ +struct sockaddr_in { + uint16 sin_family; + /*!< + Specifies the address family(AF). + Members of AF_INET address family are IPv4 addresses. + Hence,the only supported value for this is AF_INET. + */ + uint16 sin_port; + /*!< + Port number of the socket. + Network sockets are identified by a pair of IP addresses and port number. + It must be set in the Network Byte Order format , @ref _htons (e.g. _htons(80)). + Can NOT have zero value. + */ + in_addr sin_addr; + /*!< + IP Address of the socket. + The IP address is of type @ref in_addr structure. + Can be set to "0" to accept any IP address for server operation. + */ + uint8 sin_zero[8]; + /*!< + Padding to make structure the same size as @ref sockaddr. + */ }; + +/*! +@struct \ + tstrSockErr + +@brief + Detail about socket failures. Used with @ref get_error_detail. +*/ +typedef struct { + tenuSockErrSource enuErrSource; + /*!< + Source of socket error (local, remote or unknown). + */ + uint8 u8ErrCode; + /*!< + TLS Alert code as defined in + https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-6. + */ +} tstrSockErr; /**@}*/ //SocketEnums /**@defgroup AsyncCallback Asynchronous Events @@ -594,18 +741,18 @@ struct sockaddr_in{ * Specific Enumeration used for asynchronous operations * @{ */ /*! -@enum \ - tenuSocketCallbackMsgType +@enum \ + tenuSocketCallbackMsgType @brief - Asynchronous APIs make use of callback functions in-order to return back the results once the corresponding socket operation is completed. - Hence resuming the normal execution of the application code while the socket operation returns the results. - Callback functions expect event messages to be passed in, in-order to identify the operation they're returning the results for. - The following enum identifies the type of events that are received in the callback function. - - Application Use: - In order for application developers to handle the pending events from the network controller through the callback functions, - a function call must be made to the function @ref m2m_wifi_handle_events at least once for each socket operation. + Asynchronous APIs make use of callback functions in-order to return back the results once the corresponding socket operation is completed. + Hence resuming the normal execution of the application code while the socket operation returns the results. + Callback functions expect event messages to be passed in, in-order to identify the operation they're returning the results for. + The following enum identifies the type of events that are received in the callback function. + + Application Use: + In order for application developers to handle the pending events from the network controller through the callback functions, + a function call must be made to the function @ref m2m_wifi_handle_events at least once for each socket operation. @see bind @see listen @@ -614,336 +761,351 @@ struct sockaddr_in{ @see send @see recv */ - -typedef enum{ - SOCKET_MSG_BIND = 1, - /*!< - Bind socket event. - */ - SOCKET_MSG_LISTEN, - /*!< - Listen socket event. - */ - SOCKET_MSG_DNS_RESOLVE, - /*!< - DNS Resolution event. - */ - SOCKET_MSG_ACCEPT, - /*!< - Accept socket event. - */ - SOCKET_MSG_CONNECT, - /*!< - Connect socket event. - */ - SOCKET_MSG_RECV, - /*!< - Receive socket event. - */ - SOCKET_MSG_SEND, - /*!< - Send socket event. - */ - SOCKET_MSG_SENDTO, - /*!< - sendto socket event. - */ - SOCKET_MSG_RECVFROM - /*!< - Recvfrom socket event. - */ -}tenuSocketCallbackMsgType; +typedef enum { + SOCKET_MSG_BIND = 1, + /*!< + Bind socket event. + */ + SOCKET_MSG_LISTEN, + /*!< + Listen socket event. + */ + SOCKET_MSG_DNS_RESOLVE, + /*!< + DNS Resolution event. + */ + SOCKET_MSG_ACCEPT, + /*!< + Accept socket event. + */ + SOCKET_MSG_CONNECT, + /*!< + Connect socket event. + */ + SOCKET_MSG_RECV, + /*!< + Receive socket event. + */ + SOCKET_MSG_SEND, + /*!< + Send socket event. + */ + SOCKET_MSG_SENDTO, + /*!< + Sendto socket event. + */ + SOCKET_MSG_RECVFROM, + /*!< + Recvfrom socket event. + */ + SOCKET_MSG_SECURE +/*!< + Existing socket made secure event. +*/ +} tenuSocketCallbackMsgType; /*! -@struct \ - tstrSocketBindMsg +@struct \ + tstrSocketBindMsg -@brief Socket bind status. +@brief Socket bind status. - An asynchronous call to the @ref bind socket operation, returns information through this structure in response. - This structure together with the event @ref SOCKET_MSG_BIND are passed in parameters to the callback function. + An asynchronous call to the @ref bind socket operation, returns information through this structure in response. + This structure together with the event @ref SOCKET_MSG_BIND are passed in parameters to the callback function. @see bind */ -typedef struct{ - sint8 status; - /*!< - The result of the bind operation. - Holding a value of ZERO for a successful bind or otherwise a negative - error code corresponding to the type of error. - */ -}tstrSocketBindMsg; +typedef struct { + sint8 status; + /*!< + The result of the bind operation. + Holding a value of ZERO for a successful bind or otherwise a negative + error code corresponding to @ref SocketErrorCode. + */ +} tstrSocketBindMsg; /*! -@struct \ - tstrSocketListenMsg +@struct \ + tstrSocketListenMsg -@brief Socket listen status. +@brief Socket listen status. - Socket listen information is returned through this structure in response to the asynchronous call to the @ref listen function. - This structure together with the event @ref SOCKET_MSG_LISTEN are passed-in parameters to the callback function. + Socket listen information is returned through this structure in response to the asynchronous call to the @ref listen function. + This structure together with the event @ref SOCKET_MSG_LISTEN are passed-in parameters to the callback function. @see listen */ -typedef struct{ - sint8 status; - /*!< - Holding a value of ZERO for a successful listen or otherwise a negative - error code corresponding to the type of error. - */ -}tstrSocketListenMsg; +typedef struct { + sint8 status; + /*!< + Holding a value of ZERO for a successful listen or otherwise a negative + error code corresponding to @ref SocketErrorCode. + */ +} tstrSocketListenMsg; /*! -@struct \ - tstrSocketAcceptMsg +@struct \ + tstrSocketAcceptMsg -@brief Socket accept status. +@brief Socket accept status. - Socket accept information is returned through this structure in response to the asynchronous call to the @ref accept function. - This structure together with the event @ref SOCKET_MSG_ACCEPT are passed-in parameters to the callback function. + Socket accept information is returned through this structure in response to the asynchronous call to the @ref accept function. + This structure together with the event @ref SOCKET_MSG_ACCEPT are passed-in parameters to the callback function. */ -typedef struct{ - SOCKET sock; - /*!< - On a successful @ref accept operation, the return information is the socket ID for the accepted connection with the remote peer. - Otherwise a negative error code is returned to indicate failure of the accept operation. - */ - struct sockaddr_in strAddr; - /*!< - Socket address structure for the remote peer. - */ -}tstrSocketAcceptMsg; +typedef struct { + SOCKET sock; + /*!< + On a successful @ref accept operation, the return information is the socket ID for the accepted connection with the remote peer. + Otherwise a negative error code is returned to indicate failure of the accept operation. + */ + struct sockaddr_in strAddr; + /*!< + Socket address structure for the remote peer. + */ +} tstrSocketAcceptMsg; /*! -@struct \ - tstrSocketConnectMsg +@struct \ + tstrSocketConnectMsg -@brief Socket connect status. +@brief Socket connect status. - Socket connect information is returned through this structure in response to the asynchronous call to the @ref connect socket function. - This structure together with the event @ref SOCKET_MSG_CONNECT are passed-in parameters to the callback function. - If the application receives this structure with a negative value in s8Error, the application should call @ref close(). + Socket connect information is returned through this structure in response to an asynchronous call to the @ref connect socket function + or the @ref secure socket function. + This structure and the event @ref SOCKET_MSG_CONNECT or @ref SOCKET_MSG_SECURE are passed in parameters to the callback function. + If the application receives this structure with a negative value in s8Error, the application should call @ref close(). */ -typedef struct{ - SOCKET sock; - /*!< - Socket ID referring to the socket passed to the connect function call. - */ - sint8 s8Error; - /*!< - Connect error code. - Holding a value of ZERO for a successful connect or otherwise a negative - error code corresponding to the type of error. - */ -}tstrSocketConnectMsg; +typedef struct { + SOCKET sock; + /*!< + Socket ID referring to the socket passed to the @ref connect or @ref secure function call. + */ + sint8 s8Error; + /*!< + Connect error code:\n + - ZERO for a successful connect or successful secure. \n + - Otherwise a negative error code corresponding to the type of error. + */ +} tstrSocketConnectMsg; /*! -@struct \ - tstrSocketRecvMsg +@struct \ + tstrSocketRecvMsg -@brief Socket recv status. +@brief Socket recv status. - Socket receive information is returned through this structure in response to the asynchronous call to the recv or recvfrom socket functions. - This structure together with the events @ref SOCKET_MSG_RECV or @ref SOCKET_MSG_RECVFROM are passed-in parameters to the callback function. -@remark - In case the received data from the remote peer is larger than the USER buffer size defined during the asynchronous call to the @ref recv function, - delivered to the user in a number of consecutive chunks according to the USER Buffer size. - A negative or zero buffer size indicates an error with the following code: - @ref SOCK_ERR_NO_ERROR : Socket connection closed. The application should now call @ref close(). - @ref SOCK_ERR_CONN_ABORTED : Socket connection aborted. The application should now call @ref close(). - @ref SOCK_ERR_TIMEOUT : Socket receive timed out. The socket connection remains open. + Socket receive information is returned through this structure in response to the asynchronous call to the @ref recv or @ref recvfrom socket functions. + This structure, together with the events @ref SOCKET_MSG_RECV or @ref SOCKET_MSG_RECVFROM, is passed-in parameters to the callback function. +@remark + After receiving this structure, the application should issue a new call to @ref recv or @ref recvfrom in order to receive subsequent data.\n + In the case of @ref SOCKET_MSG_RECVFROM (UDP), any further data in the same datagram is dropped, then subsequent datagrams are buffered on the WINC until the application provides a buffer via a new call to @ref recvfrom \n + In the case of @ref SOCKET_MSG_RECV (TCP), all subsequent data is buffered on the WINC until the application provides a buffer via a new call to @ref recv \n + A negative or zero buffer size indicates an error with the following code: + @ref SOCK_ERR_NO_ERROR : Socket connection closed. The application should now call @ref close(). + @ref SOCK_ERR_CONN_ABORTED : Socket connection aborted. The application should now call @ref close(). + @ref SOCK_ERR_TIMEOUT : Socket receive timed out. The socket connection remains open. */ -typedef struct{ - uint8 *pu8Buffer; - /*!< - Pointer to the USER buffer (passed to @ref recv and @ref recvfrom function) containing the received data chunk. - */ - sint16 s16BufferSize; - /*!< - The received data chunk size. - Holds a negative value if there is a receive error or ZERO on success upon reception of close socket message. - */ - uint16 u16RemainingSize; - /*!< - The number of bytes remaining in the current @ref recv operation. - */ - struct sockaddr_in strRemoteAddr; - /*!< - Socket address structure for the remote peer. It is valid for @ref SOCKET_MSG_RECVFROM event. - */ -}tstrSocketRecvMsg; +typedef struct { + uint8 *pu8Buffer; + /*!< + Pointer to the USER buffer (passed to @ref recv and @ref recvfrom function) containing the received data chunk. + */ + sint16 s16BufferSize; + /*!< + The received data chunk size. + Holds a negative value if there is a receive error or ZERO on success upon reception of close socket message. + */ + uint16 u16RemainingSize; + /*!< + This field is used internally by the driver. In normal operation, this field will be 0 when the application receives this structure. + */ + struct sockaddr_in strRemoteAddr; + /*!< + Socket address structure for the remote peer. It is valid for @ref SOCKET_MSG_RECVFROM event. + */ +} tstrSocketRecvMsg; /**@}*/ //AsyncCallback -/** @defgroup SocketCallbacks Callbacks - * @ingroup SocketHeader +/**@defgroup SocketCallbacks Callbacks + * @ingroup SocketHeader + * @{ */ -/**@{*/ /*! @typedef \ - tpfAppSocketCb + tpfAppSocketCb @brief - The main socket application callback function. Applications register their main socket application callback through this function by calling @ref registerSocketCallback. - In response to events received, the following callback function is called to handle the corresponding asynchronous function called. Example: @ref bind, @ref connect,...etc. + The main socket application callback function. Applications register their main socket application callback through this function by calling @ref registerSocketCallback. + In response to events received, the following callback function is called to handle the corresponding asynchronous function called. Example: @ref bind, @ref connect,...etc. @param [in] sock - Socket ID for the callback. + Socket ID for the callback. + + The socket callback function is called whenever a new event is received in response + to socket operations. - The socket callback function is called whenever a new event is received in response - to socket operations. - @param [in] u8Msg - Socket event type. Possible values are: - - @ref SOCKET_MSG_BIND - - @ref SOCKET_MSG_LISTEN - - @ref SOCKET_MSG_ACCEPT - - @ref SOCKET_MSG_CONNECT - - @ref SOCKET_MSG_RECV - - @ref SOCKET_MSG_SEND - - @ref SOCKET_MSG_SENDTO - - @ref SOCKET_MSG_RECVFROM - + Socket event type. Possible values are: + - @ref SOCKET_MSG_BIND + - @ref SOCKET_MSG_LISTEN + - @ref SOCKET_MSG_ACCEPT + - @ref SOCKET_MSG_CONNECT + - @ref SOCKET_MSG_RECV + - @ref SOCKET_MSG_SEND + - @ref SOCKET_MSG_SENDTO + - @ref SOCKET_MSG_RECVFROM + - @ref SOCKET_MSG_SECURE + @param [in] pvMsg - Pointer to message structure. Existing types are: - - tstrSocketBindMsg - - tstrSocketListenMsg - - tstrSocketAcceptMsg - - tstrSocketConnectMsg - - tstrSocketRecvMsg + Pointer to message structure. Existing types are: + - tstrSocketBindMsg + - tstrSocketListenMsg + - tstrSocketAcceptMsg + - tstrSocketConnectMsg + - tstrSocketRecvMsg @see - tenuSocketCallbackMsgType - tstrSocketRecvMsg - tstrSocketConnectMsg - tstrSocketAcceptMsg - tstrSocketListenMsg - tstrSocketBindMsg + tenuSocketCallbackMsgType + tstrSocketRecvMsg + tstrSocketConnectMsg + tstrSocketAcceptMsg + tstrSocketListenMsg + tstrSocketBindMsg */ -typedef void (*tpfAppSocketCb) (SOCKET sock, uint8 u8Msg, void * pvMsg); +typedef void (*tpfAppSocketCb)(SOCKET sock, uint8 u8Msg, void *pvMsg); /*! -@typedef \ - tpfAppResolveCb +@typedef \ + tpfAppResolveCb @brief - DNS resolution callback function. - Applications requiring DNS resolution should register their callback through this function by calling @ref registerSocketCallback. - The following callback is triggered in response to asynchronous call to the @ref gethostbyname function (DNS Resolution callback). + DNS resolution callback function. + Applications requiring DNS resolution should register their callback through this function by calling @ref registerSocketCallback. + The following callback is triggered in response to an asynchronous call to the @ref gethostbyname function (DNS Resolution callback). -@param [in] pu8DomainName - Domain name of the host. +@param[in] pu8DomainName + Domain name of the host. -@param [in] u32ServerIP - Server IPv4 address encoded in NW byte order format. If it is Zero, then the DNS resolution failed. +@param[in] u32ServerIP + Server IPv4 address encoded in Network byte order format. If it is Zero, then the DNS resolution failed. */ -typedef void (*tpfAppResolveCb) (uint8* pu8DomainName, uint32 u32ServerIP); +typedef void (*tpfAppResolveCb)(uint8 *pu8DomainName, uint32 u32ServerIP); /*! @typedef \ - tpfPingCb + tpfPingCb -@brief PING Callback +@brief PING Callback - The function delivers the ping statistics for the sent ping triggered by calling - m2m_ping_req. + The function delivers the ping statistics for the sent ping triggered by calling + @ref m2m_ping_req. -@param [in] u32IPAddr - Destination IP. +@param[in] u32IPAddr + Destination IP. -@param [in] u32RTT - Round Trip Time. +@param[in] u32RTT + Round Trip Time. -@param [in] u8ErrorCode - Ping error code. It may be one of: - - PING_ERR_SUCCESS - - PING_ERR_DEST_UNREACH - - PING_ERR_TIMEOUT +@param[in] u8ErrorCode + Ping error code. It may be one of: + - PING_ERR_SUCCESS + - PING_ERR_DEST_UNREACH + - PING_ERR_TIMEOUT */ typedef void (*tpfPingCb)(uint32 u32IPAddr, uint32 u32RTT, uint8 u8ErrorCode); /**@}*/ //SocketCallbacks - + /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* FUNCTION PROTOTYPES *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ -/** \defgroup SocketAPI Function - * @ingroup SocketHeader +/** @defgroup SocketAPI Functions + * @ingroup SocketHeader */ /** @defgroup SocketInitializationFn socketInit * @ingroup SocketAPI * The function performs the necessary initializations for the socket library through the following steps: - - A check made by the global variable gbSocketInit, ensuring that initialization for sockets is performed only once, - in-order to prevent resetting the socket instances already created in the global socket array (gastrSockets). - - Zero initializations to the global socket array (gastrSockets), which holds the list of TCP sockets. - - Registers the socket (Host Interface)hif callback function through the call to the hif_register_cb function. - This facilitates handling all of the socket related functions received through interrupts from the firmware. + - A check made by the global variable gbSocketInit, ensuring that initialization for sockets is performed only once, + in-order to prevent resetting the socket instances already created in the global socket array (gastrSockets). + - Zero initializations to the global socket array (gastrSockets), which holds the list of TCP sockets. + - Registers the socket (Host Interface)hif callback function through the call to the hif_register_cb function. + This facilitates handling all of the socket related functions received through interrupts from the firmware. */ - /**@{*/ +/**@{*/ /*! -@fn \ - NMI_API void socketInit(void); +@fn \ + NMI_API void socketInit(void); -@return void +@return void -@remarks - This initialization function must be invoked before any socket operation is performed. - No error codes from this initialization function since the socket array is statically allocated based in the maximum number of - sockets @ref MAX_SOCKET based on the systems capability. -\section socketinit example +@remarks + This initialization function must be invoked before any socket operation is performed. + No error codes from this initialization function since the socket array is statically allocated based in the maximum number of + sockets @ref MAX_SOCKET based on the systems capability. +\section socketInit_Ex Example This example demonstrates the use of the socketinit for socket initialization for an mqtt chat application. \code - tstrWifiInitParam param; - tstrNetworkId strNetworkId; - tstrAuthPsk strAuthPsk; - int8_t ret; - char topic[strlen(MAIN_CHAT_TOPIC) + MAIN_CHAT_USER_NAME_SIZE + 1]; + tstrWifiInitParam param; + tstrNetworkId strNetworkId; + tstrAuthPsk strAuthPsk; + int8_t ret; + char topic[strlen(MAIN_CHAT_TOPIC) + MAIN_CHAT_USER_NAME_SIZE + 1]; - //Initialize the board. - system_init(); + //Initialize the board. + system_init(); - //Initialize the UART console. - configure_console(); + //Initialize the UART console. + configure_console(); - // Initialize the BSP. - nm_bsp_init(); - - ---------- - - // Initialize socket interface. - socketInit(); - registerSocketCallback(socket_event_handler, socket_resolve_handler); + // Initialize the BSP. + nm_bsp_init(); - // Connect to router. - strNetworkId.pu8Bssid = NULL; - strNetworkId.pu8Ssid = MAIN_WLAN_SSID; - strNetworkId.u8SsidLen = sizeof(MAIN_WLAN_SSID); - strNetworkId.u8Channel = M2M_WIFI_CH_ALL; + ---------- - strAuthPsk.pu8Psk = NULL; - strAuthPsk.pu8Passphrase = MAIN_WLAN_PSK; - strAuthPsk.u8PassphraseLen = (uint8)strlen((char*)MAIN_WLAN_PSK); + // Initialize socket interface. + socketInit(); + registerSocketCallback(socket_event_handler, socket_resolve_handler); - m2m_wifi_connect_psk(WIFI_CRED_SAVE_ENCRYPTED, &strNetworkId, &strAuthPsk); + // Connect to router. + strNetworkId.pu8Bssid = NULL; + strNetworkId.pu8Ssid = MAIN_WLAN_SSID; + strNetworkId.u8SsidLen = sizeof(MAIN_WLAN_SSID); + strNetworkId.u8Channel = M2M_WIFI_CH_ALL; + + strAuthPsk.pu8Psk = NULL; + strAuthPsk.pu8Passphrase = MAIN_WLAN_PSK; + strAuthPsk.u8PassphraseLen = (uint8)strlen((char*)MAIN_WLAN_PSK); + + m2m_wifi_connect_psk(WIFI_CRED_SAVE_ENCRYPTED, &strNetworkId, &strAuthPsk); \endcode */ NMI_API void socketInit(void); +/** @} */ //SocketInitializationFn - +/** @defgroup SocketDeInitializationFn socketDeInit + * @ingroup SocketAPI + */ +/**@{*/ /*! -@fn \ - NMI_API void socketDeinit(void); +@fn \ + NMI_API void socketDeinit(void); -@brief Socket Layer De-initialization +@brief Socket Layer De-initialization - The function performs the necessary cleanup for the socket library static data - It must be invoked as the last any socket operation is performed on any active sockets. + The function performs the necessary cleanup for the socket library static data + It must be invoked only after all desired socket operations have been performed on any active sockets. */ NMI_API void socketDeinit(void); +/** @} */ //SocketDeInitializationFn +/** @defgroup SocketStateFn socketState + * @ingroup SocketAPI + */ +/**@{*/ /*! -@fn \ - uint8 IsSocketReady(void); +@fn \ + uint8 IsSocketReady(void); @see socketInit socketDeinit @@ -951,161 +1113,167 @@ NMI_API void socketDeinit(void); Should return 1 after @ref socketInit and 0 after @ref socketDeinit */ NMI_API uint8 IsSocketReady(void); -/** @} */ //SocketInitializationFn +/** @} */ //SocketStateFn /** @defgroup SocketCallbackFn registerSocketCallback * @ingroup SocketAPI * Register two callback functions one for asynchronous socket events and the other one for DNS callback registering function. * The registered callback functions are used to retrieve information in response to the asynchronous socket functions called. */ - /**@{*/ - - +/**@{*/ /*! -@fn \ - NMI_API void registerSocketCallback(tpfAppSocketCb socket_cb, tpfAppResolveCb resolve_cb); - -@param [in] socket_cb tpfAppSocketCb - Assignment of callback function to the global callback @ref tpfAppSocketCb gpfAppSocketCb. Delivers - socket messages to the host application. In response to the asynchronous function calls, such as @ref bind - @ref listen @ref accept @ref connect - -@param [in] resolve_cb tpfAppResolveCb - Assignment of callback function to the global callback @ref tpfAppResolveCb gpfAppResolveCb. - Used for DNS resolving functionalities. The DNS resolving technique is determined by the application - registering the callback. - NULL is assigned when, DNS resolution is not required. - -@return void -@remarks - If any of the socket functionalities is not to be used, NULL is passed in as a parameter. - It must be invoked after socketinit and before other socket layer operations. - -\section registerSocketCallback example - This example demonstrates the use of the registerSocketCallback to register a socket callback function with DNS resolution CB set to null - for a simple UDP server example. +@fn \ + NMI_API void registerSocketCallback(tpfAppSocketCb socket_cb, tpfAppResolveCb resolve_cb); + +@param[in] socket_cb tpfAppSocketCb + Assignment of callback function to the global callback @ref tpfAppSocketCb gpfAppSocketCb. Delivers + socket messages to the host application. In response to the asynchronous function calls, such as @ref bind + @ref listen @ref accept @ref connect + +@param[in] resolve_cb tpfAppResolveCb + Assignment of callback function to the global callback @ref tpfAppResolveCb gpfAppResolveCb. + Used for DNS resolving. The DNS resolving technique is determined by the application + registering the callback. + NULL is assigned when, DNS resolution is not required. + +@return void +@remarks + If the socket functionality is not to be used, NULL is passed in as a parameter. + It must be invoked after socketinit and before other socket layer operations. + +\section registerSocketCallback_Ex Example + This example demonstrates the use of the registerSocketCallback to register a socket callback function with DNS resolution CB set to null + for a simple UDP server example. \code - tstrWifiInitParam param; - tstrNetworkId strNetworkId; - tstrAuthPsk strAuthPsk; - int8_t ret; - struct sockaddr_in addr; + tstrWifiInitParam param; + tstrNetworkId strNetworkId; + tstrAuthPsk strAuthPsk; + int8_t ret; + struct sockaddr_in addr; - // Initialize the board - system_init(); + // Initialize the board + system_init(); - //Initialize the UART console. - configure_console(); - - // Initialize the BSP. - nm_bsp_init(); + //Initialize the UART console. + configure_console(); - // Initialize socket address structure. - addr.sin_family = AF_INET; - addr.sin_port = _htons(MAIN_WIFI_M2M_SERVER_PORT); - addr.sin_addr.s_addr = _htonl(MAIN_WIFI_M2M_SERVER_IP); + // Initialize the BSP. + nm_bsp_init(); - // Initialize Wi-Fi parameters structure. - memset((uint8_t *)¶m, 0, sizeof(tstrWifiInitParam)); + // Initialize socket address structure. + addr.sin_family = AF_INET; + addr.sin_port = _htons(MAIN_WIFI_M2M_SERVER_PORT); + addr.sin_addr.s_addr = _htonl(MAIN_WIFI_M2M_SERVER_IP); - // Initialize Wi-Fi driver with data and status callbacks. - param.pfAppWifiCb = wifi_cb; - ret = m2m_wifi_init(¶m); - if (M2M_SUCCESS != ret) { - printf("main: m2m_wifi_init call error!(%d)\r\n", ret); - while (1) { - } - } + // Initialize Wi-Fi parameters structure. + memset((uint8_t *)¶m, 0, sizeof(tstrWifiInitParam)); - // Initialize socket module - socketInit(); - registerSocketCallback(socket_cb, NULL); + // Initialize Wi-Fi driver with data and status callbacks. + param.pfAppWifiCb = wifi_cb; + ret = m2m_wifi_init(¶m); + if (M2M_SUCCESS != ret) { + printf("main: m2m_wifi_init call error!(%d)\r\n", ret); + while (1) { + } + } - // Connect to router. - strNetworkId.pu8Bssid = NULL; - strNetworkId.pu8Ssid = MAIN_WLAN_SSID; - strNetworkId.u8SsidLen = sizeof(MAIN_WLAN_SSID); - strNetworkId.u8Channel = M2M_WIFI_CH_ALL; + // Initialize socket module + socketInit(); + registerSocketCallback(socket_cb, NULL); - strAuthPsk.pu8Psk = NULL; - strAuthPsk.pu8Passphrase = MAIN_WLAN_PSK; - strAuthPsk.u8PassphraseLen = (uint8)strlen((char*)MAIN_WLAN_PSK); + // Connect to router. + strNetworkId.pu8Bssid = NULL; + strNetworkId.pu8Ssid = MAIN_WLAN_SSID; + strNetworkId.u8SsidLen = sizeof(MAIN_WLAN_SSID); + strNetworkId.u8Channel = M2M_WIFI_CH_ALL; - m2m_wifi_connect_psk(WIFI_CRED_SAVE_ENCRYPTED, &strNetworkId, &strAuthPsk); - \endcode + strAuthPsk.pu8Psk = NULL; + strAuthPsk.pu8Passphrase = MAIN_WLAN_PSK; + strAuthPsk.u8PassphraseLen = (uint8)strlen((char*)MAIN_WLAN_PSK); + + m2m_wifi_connect_psk(WIFI_CRED_SAVE_ENCRYPTED, &strNetworkId, &strAuthPsk); +\endcode */ NMI_API void registerSocketCallback(tpfAppSocketCb socket_cb, tpfAppResolveCb resolve_cb); /** @} */ //SocketCallbackFn /** @defgroup SocketFn socket * @ingroup SocketAPI - * Synchronous socket allocation function based on the specified socket type. Created sockets are non-blocking and their possible types are either TCP or a UDP sockets. - * The maximum allowed number of TCP sockets is @ref TCP_SOCK_MAX sockets while the maximum number of UDP sockets that can be created simultaneously is @ref UDP_SOCK_MAX sockets. - * -*/ - /**@{*/ + * Synchronous socket allocation function based on the specified socket type. Created sockets are non-blocking and their possible types are either TCP or a UDP sockets. + * The maximum allowed number of TCP sockets is @ref TCP_SOCK_MAX sockets while the maximum number of UDP sockets that can be created simultaneously is @ref UDP_SOCK_MAX sockets. + * + */ +/**@{*/ /*! -@fn \ - NMI_API SOCKET socket(uint16 u16Domain, uint8 u8Type, uint8 u8Flags); +@fn \ + NMI_API SOCKET socket(uint16 u16Domain, uint8 u8Type, uint8 u8Config); - -@param [in] u16Domain - Socket family. The only allowed value is AF_INET (IPv4.0) for TCP/UDP sockets. -@param [in] u8Type - Socket type. Allowed values are: +@param [in] u16Domain + Socket family. The only allowed value is AF_INET (IPv4.0) for TCP/UDP sockets. + +@param[in] u8Type + Socket type. Allowed values are: - @ref SOCK_STREAM - @ref SOCK_DGRAM -@param [in] u8Flags - Used to specify the socket creation flags. It shall be set to zero for normal TCP/UDP sockets. - It could be @ref SOCKET_FLAGS_SSL if the socket is used for SSL session. The use of the flag - @ref SOCKET_FLAGS_SSL has no meaning in case of UDP sockets. +@param[in] u8Config + Used to specify the socket configuration. The following + configuration values are defined:\n + - @ref SOCKET_CONFIG_SSL_OFF : The socket is not secured by TLS.\n + - @ref SOCKET_CONFIG_SSL_ON : The socket is secured by TLS. + This value has no effect if u8Type is @ref SOCK_DGRAM + - @ref SOCKET_CONFIG_SSL_DELAY : The socket is not secured by + TLS, but may be secured later, by calling @ref secure. + This value has no effect if u8Type is @ref SOCK_DGRAM \n + All other configuration values are reserved and should not be + used. @pre - The @ref socketInit function must be called once at the beginning of the application to initialize the socket handler. - before any call to the socket function can be made. + The @ref socketInit function must be called once at the beginning of the application to initialize the socket handler. + before any call to the @ref socket function can be made. @see - connect - bind - listen - accept - recv - recvfrom - send - sendto - close - setsockopt - getsockopt - -@return - On successful socket creation, a non-blocking socket type is created and a socket ID is returned - In case of failure the function returns a negative value, identifying one of the socket error codes defined. + connect + secure + bind + listen + accept + recv + recvfrom + send + sendto + close + setsockopt + getsockopt + +@return + On successful socket creation, a non-blocking socket type is created and a socket ID is returned + In case of failure the function returns a negative value, identifying one of the socket error codes defined. For example: @ref SOCK_ERR_INVALID for invalid argument or @ref SOCK_ERR_MAX_TCP_SOCK if the number of TCP allocated sockets exceeds the number of available sockets. @remarks - The socket function must be called a priori to any other related socket functions "e.g. send, recv, close ..etc" -\section Socket allocation example - This example demonstrates the use of the socket function to allocate the socket, returning the socket handler to be used for other + The socket function must be called a priori to any other related socket functions "e.g. send, recv, close ..etc" +\section Socket_Ex Allocation example + This example demonstrates the use of the socket function to allocate the socket, returning the socket handler to be used for other socket operations. Socket creation is dependent on the socket type. UDP example @code - SOCKET UdpServerSocket = -1; - - UdpServerSocket = socket(AF_INET, SOCK_DGRAM, 0); - + SOCKET UdpServerSocket = -1; + + UdpServerSocket = socket(AF_INET, SOCK_DGRAM, 0); + @endcode TCP example @code - static SOCKET tcp_client_socket = -1; + static SOCKET tcp_client_socket = -1; - tcp_client_socket = socket(AF_INET, SOCK_STREAM, 0)); + tcp_client_socket = socket(AF_INET, SOCK_STREAM, 0)); @endcode SSL example @@ -1116,202 +1284,202 @@ static SOCKET ssl_socket = -1; ssl_socket = socket(AF_INET, SOCK_STREAM, SOCK_FLAGS_SSL)); @endcode */ -NMI_API SOCKET WINC1500_EXPORT(socket)(uint16 u16Domain, uint8 u8Type, uint8 u8Flags); +NMI_API SOCKET WINC1500_EXPORT(socket)(uint16 u16Domain, uint8 u8Type, uint8 u8Config); /** @} */ //SocketFn /** @defgroup BindFn bind * @ingroup SocketAPI -* Asynchronous bind function associates the provided address and local port to the socket. +* Asynchronous bind function associates the provided address and local port to the socket. * The function can be used with both TCP and UDP sockets. It is mandatory to call the @ref bind function before starting any UDP or TCP server operation. -* Upon socket bind completion, the application will receive a @ref SOCKET_MSG_BIND message in the socket callback. +* Upon socket bind completion, the application will receive a @ref SOCKET_MSG_BIND message in the socket callback. */ - /**@{*/ +/**@{*/ /*! -\fn \ - NMI_API sint8 bind(SOCKET sock, struct sockaddr *pstrAddr, uint8 u8AddrLen); - -@param [in] sock - Socket ID, must hold a non negative value. - A negative value will return a socket error @ref SOCK_ERR_INVALID_ARG. Indicating that an invalid argument is passed in. +\fn \ + NMI_API sint8 bind(SOCKET sock, struct sockaddr *pstrAddr, uint8 u8AddrLen); -@param [in] pstrAddr +@param[in] sock + Socket ID, must hold a non negative value. + A negative value will return a socket error @ref SOCK_ERR_INVALID_ARG. Indicating that an invalid argument is passed in. + +@param[in] pstrAddr Pointer to socket address structure @ref sockaddr_in. - -@param [in] u8AddrLen - Size of the given socket address structure in bytes. + +@param[in] u8AddrLen + Size of the given socket address structure in bytes. @pre - The socket function must be called to allocate a socket before passing the socket ID to the bind function. + The socket function must be called to allocate a socket before passing the socket ID to the bind function. -@see socket -@see connect -@see listen -@see accept -@see recv -@see recvfrom -@see send -@see sendto +@see socket +@see connect +@see listen +@see accept +@see recv +@see recvfrom +@see send +@see sendto -@return - The function returns ZERO for successful operations and a negative value otherwise. - The possible error values are: +@return + The function returns ZERO for successful operations and a negative value otherwise. + The possible error values are: - @ref SOCK_ERR_NO_ERROR - Indicating that the operation was successful. - + Indicating that the operation was successful. + - @ref SOCK_ERR_INVALID_ARG - Indicating passing invalid arguments such as negative socket ID or NULL socket address structure. + Indicating passing invalid arguments such as negative socket ID or NULL socket address structure. - @ref SOCK_ERR_INVALID - Indicate socket bind failure. -\section bind example - This example demonstrates the call of the bind socket operation after a successful socket operation. + Indicate socket bind failure. +\section bind_Ex Example + This example demonstrates the call of the bind socket operation after a successful socket operation. @code - struct sockaddr_in addr; - SOCKET udpServerSocket =-1; - int ret = -1; - - if(udpServerSocket == -1) - { - udpServerSocket = socket(AF_INET, SOCK_DGRAM, 0); - if(udpServerSocket >= 0) - { - addr.sin_family = AF_INET; - addr.sin_port = _htons(1234); - addr.sin_addr.s_addr = 0; - ret = bind(udpServerSocket,(struct sockaddr*)&addr,sizeof(addr)); + struct sockaddr_in addr; + SOCKET udpServerSocket =-1; + int ret = -1; - if(ret != 0) - { - printf("Bind Failed. Error code = %d\n",ret); - close(udpServerSocket); - } - } - else - { - printf("UDP Server Socket Creation Failed\n"); - return; - } - } -@endcode + if(udpServerSocket == -1) + { + udpServerSocket = socket(AF_INET, SOCK_DGRAM, 0); + if(udpServerSocket >= 0) + { + addr.sin_family = AF_INET; + addr.sin_port = _htons(1234); + addr.sin_addr.s_addr = 0; + ret = bind(udpServerSocket,(struct sockaddr*)&addr,sizeof(addr)); + + if(ret != 0) + { + printf("Bind Failed. Error code = %d\n",ret); + close(udpServerSocket); + } + } + else + { + printf("UDP Server Socket Creation Failed\n"); + return; + } + } +@endcode */ NMI_API sint8 WINC1500_EXPORT(bind)(SOCKET sock, struct sockaddr *pstrAddr, uint8 u8AddrLen); /**@}*/ //BindFn /** @defgroup ListenFn listen * @ingroup SocketAPI - * After successful socket binding to an IP address and port on the system, start listening on a passive socket for incoming connections. - The socket must be bound on a local port or the listen operation fails. + * After successful socket binding to an IP address and port on the system, start listening on a passive socket for incoming connections. + The socket must be bound on a local port or the listen operation fails. Upon the call to the asynchronous listen function, response is received through the event @ref SOCKET_MSG_LISTEN - in the socket callback. - A successful listen means the TCP server operation is active. If a connection is accepted, then the application socket callback function is - notified with the new connected socket through the event @ref SOCKET_MSG_ACCEPT. Hence there is no need to call the @ref accept function - after calling @ref listen. - - After a connection is accepted, the user is then required to call @ref recv to receive any packets transmitted by the remote host or to receive notification of socket connection - termination. + in the socket callback. + A successful listen means the TCP server operation is active. If a connection is accepted, then the application socket callback function is + notified with the new connected socket through the event @ref SOCKET_MSG_ACCEPT. Hence there is no need to call the @ref accept function + after calling @ref listen. + + After a connection is accepted, the user is then required to call @ref recv to receive any packets transmitted by the remote host or to receive notification of socket connection + termination. */ - /**@{*/ +/**@{*/ /*! -@fn \ - NMI_API sint8 listen(SOCKET sock, uint8 backlog); +@fn \ + NMI_API sint8 listen(SOCKET sock, uint8 backlog); -@param [in] sock - Socket ID, must hold a non negative value. - A negative value will return a socket error @ref SOCK_ERR_INVALID_ARG. Indicating that an invalid argument is passed in. +@param[in] sock + Socket ID, must hold a non negative value. + A negative value will return a socket error @ref SOCK_ERR_INVALID_ARG. Indicating that an invalid argument is passed in. + +@param[in] backlog + Not used by the current implementation. -@param [in] backlog - Not used by the current implementation. - @pre - The bind function must be called to assign the port number and IP address to the socket before the listen operation. + The bind function must be called to assign the port number and IP address to the socket before the listen operation. -@see bind -@see accept -@see recv -@see recvfrom -@see send -@see sendto +@see bind +@see accept +@see recv +@see recvfrom +@see send +@see sendto -@return - The function returns ZERO for successful operations and a negative value otherwise. - The possible error values are: +@return + The function returns ZERO for successful operations and a negative value otherwise. + The possible error values are: - @ref SOCK_ERR_NO_ERROR - Indicating that the operation was successful. - + Indicating that the operation was successful. + - @ref SOCK_ERR_INVALID_ARG - Indicating passing invalid arguments such as negative socket ID. + Indicating passing invalid arguments such as negative socket ID. - @ref SOCK_ERR_INVALID - Indicate socket listen failure. -\section listen example + Indicate socket listen failure. +\section listen_Ex Example This example demonstrates the call of the listen socket operation after a successful socket operation. @code - static void TCP_Socketcallback(SOCKET sock, uint8 u8Msg, void * pvMsg) - { - int ret =-1; - - switch(u8Msg) - { - case SOCKET_MSG_BIND: - { - tstrSocketBindMsg *pstrBind = (tstrSocketBindMsg*)pvMsg; - if(pstrBind != NULL) - { - if(pstrBind->status == 0) - { - ret = listen(sock, 0); - - if(ret <0) - printf("Listen failure! Error = %d\n",ret); - } - else - { - M2M_ERR("bind Failure!\n"); - close(sock); - } - } - } - break; + static void TCP_Socketcallback(SOCKET sock, uint8 u8Msg, void * pvMsg) + { + int ret =-1; - case SOCKET_MSG_LISTEN: - { + switch(u8Msg) + { + case SOCKET_MSG_BIND: + { + tstrSocketBindMsg *pstrBind = (tstrSocketBindMsg*)pvMsg; + if(pstrBind != NULL) + { + if(pstrBind->status == 0) + { + ret = listen(sock, 0); - tstrSocketListenMsg *pstrListen = (tstrSocketListenMsg*)pvMsg; - if(pstrListen != NULL) - { - if(pstrListen->status == 0) - { - ret = accept(sock,NULL,0); - } - else - { - M2M_ERR("listen Failure!\n"); - close(sock); - } - } - } - break; + if(ret <0) + printf("Listen failure! Error = %d\n",ret); + } + else + { + M2M_ERR("bind Failure!\n"); + close(sock); + } + } + } + break; - case SOCKET_MSG_ACCEPT: - { - tstrSocketAcceptMsg *pstrAccept = (tstrSocketAcceptMsg*)pvMsg; - - if(pstrAccept->sock >= 0) - { - TcpNotificationSocket = pstrAccept->sock; - recv(pstrAccept->sock,gau8RxBuffer,sizeof(gau8RxBuffer),TEST_RECV_TIMEOUT); - } - else - { - M2M_ERR("accept failure\n"); - } - } - break; - - default: - break; - } - } + case SOCKET_MSG_LISTEN: + { + + tstrSocketListenMsg *pstrListen = (tstrSocketListenMsg*)pvMsg; + if(pstrListen != NULL) + { + if(pstrListen->status == 0) + { + ret = accept(sock,NULL,0); + } + else + { + M2M_ERR("listen Failure!\n"); + close(sock); + } + } + } + break; + + case SOCKET_MSG_ACCEPT: + { + tstrSocketAcceptMsg *pstrAccept = (tstrSocketAcceptMsg*)pvMsg; + + if(pstrAccept->sock >= 0) + { + TcpNotificationSocket = pstrAccept->sock; + recv(pstrAccept->sock,gau8RxBuffer,sizeof(gau8RxBuffer),TEST_RECV_TIMEOUT); + } + else + { + M2M_ERR("accept failure\n"); + } + } + break; + + default: + break; + } + } @endcode */ NMI_API sint8 WINC1500_EXPORT(listen)(SOCKET sock, uint8 backlog); @@ -1319,247 +1487,287 @@ NMI_API sint8 WINC1500_EXPORT(listen)(SOCKET sock, uint8 backlog); /** @defgroup AcceptFn accept * @ingroup SocketAPI - * The function has no current implementation. An empty declaration is used to prevent errors when legacy application code is used. + * The function has no current implementation. An empty declaration is used to prevent errors when legacy application code is used. * For recent application use, the accept function can be safer as it has no effect and could be safely removed from any application using it. */ - /**@{*/ +/**@{*/ /*! -@fn \ - NMI_API sint8 accept(SOCKET sock, struct sockaddr *addr, uint8 *addrlen); +@fn \ + NMI_API sint8 accept(SOCKET sock, struct sockaddr *addr, uint8 *addrlen); -@param [in] sock - Socket ID, must hold a non negative value. - A negative value will return a socket error @ref SOCK_ERR_INVALID_ARG. Indicating that an invalid argument is passed in. -@param [in] addr - Not used in the current implementation. +@param[in] sock + Socket ID, must hold a non negative value. + A negative value will return a socket error @ref SOCK_ERR_INVALID_ARG. Indicating that an invalid argument is passed in. +@param[in] addr + Not used in the current implementation. -@param [in] addrlen - Not used in the current implementation. +@param[in] addrlen + Not used in the current implementation. -@return - The function returns ZERO for successful operations and a negative value otherwise. - The possible error values are: +@return + The function returns ZERO for successful operations and a negative value otherwise. + The possible error values are: - @ref SOCK_ERR_NO_ERROR - Indicating that the operation was successful. - + Indicating that the operation was successful. + - @ref SOCK_ERR_INVALID_ARG - Indicating passing invalid arguments such as negative socket ID. + Indicating passing invalid arguments such as negative socket ID. */ NMI_API sint8 WINC1500_EXPORT(accept)(SOCKET sock, struct sockaddr *addr, uint8 *addrlen); -/** @} */ +/** @} */ //AcceptFn + /** @defgroup ConnectFn connect @ingroup SocketAPI - Establishes a TCP connection with a remote server. - The asynchronous connect function must be called after receiving a valid socket ID from the @ref socket function. - The application socket callback function is notified of the result of the connection attempt through the event @ref SOCKET_MSG_CONNECT, - along with a structure @ref tstrSocketConnectMsg. - If socket connection fails, the application should call @ref close(). - A successful connect means the TCP session is active. The application is then required to make a call to the @ref recv - to receive any packets transmitted by the remote server, unless the application is interrupted by a notification of socket connection - termination. + Establishes a TCP connection with a remote server. + The asynchronous connect function must be called after receiving a valid socket ID from the @ref socket function. + The application socket callback function is notified of the result of the connection attempt through the event @ref SOCKET_MSG_CONNECT, + along with a structure @ref tstrSocketConnectMsg. + If socket connection fails, the application should call @ref close(). + A successful connect means the TCP session is active. The application is then required to make a call to the @ref recv + to receive any packets transmitted by the remote server, unless the application is interrupted by a notification of socket connection + termination. */ - /**@{*/ +/**@{*/ /*! -@fn \ - NMI_API sint8 connect(SOCKET sock, struct sockaddr *pstrAddr, uint8 u8AddrLen); +@fn \ + NMI_API sint8 connect(SOCKET sock, struct sockaddr *pstrAddr, uint8 u8AddrLen); -@param [in] sock - Socket ID, must hold a non negative value. - A negative value will return a socket error @ref SOCK_ERR_INVALID_ARG. Indicating that an invalid argument is passed in. +@param[in] sock + Socket ID, must hold a non negative value. + A negative value will return a socket error @ref SOCK_ERR_INVALID_ARG. Indicating that an invalid argument is passed in. -@param [in] pstrAddr - Address of the remote server. - -@param [in] pstrAddr +@param[in] pstrAddr Pointer to socket address structure @ref sockaddr_in. -@param [in] u8AddrLen - Size of the given socket address structure in bytes. - Not currently used, implemented for BSD compatibility only. +@param[in] u8AddrLen + Size of the given socket address structure in bytes. + Not currently used, implemented for BSD compatibility only. @pre - The socket function must be called to allocate a TCP socket before passing the socket ID to the bind function. - If the socket is not bound, you do NOT have to call bind before the "connect" function. + The socket function must be called to allocate a TCP socket before passing the socket ID to the bind function. + If the socket is not bound, you do NOT have to call bind before the "connect" function. @see - socket - recv - send - close - -@return - The function returns ZERO for successful operations and a negative value otherwise. - The possible error values are: + socket + recv + send + close + +@return + The function returns ZERO for successful operations and a negative value otherwise. + The possible error values are: - @ref SOCK_ERR_NO_ERROR - Indicating that the operation was successful. - + Indicating that the operation was successful. + - @ref SOCK_ERR_INVALID_ARG - Indicating passing invalid arguments such as negative socket ID or NULL socket address structure. + Indicating passing invalid arguments such as negative socket ID or NULL socket address structure. - @ref SOCK_ERR_INVALID - Indicate socket connect failure. -\section connect example - The example demonstrates a TCP application, showing how the asynchronous call to the connect function is made through the main function and how the + Indicate socket connect failure. +\section connect_Ex Example + The example demonstrates a TCP application, showing how the asynchronous call to the connect function is made through the main function and how the callback function handles the @ref SOCKET_MSG_CONNECT event. Main Function @code - struct sockaddr_in Serv_Addr; - SOCKET TcpClientSocket =-1; - int ret = -1 - - TcpClientSocket = socket(AF_INET,SOCK_STREAM,0); - Serv_Addr.sin_family = AF_INET; - Serv_Addr.sin_port = _htons(1234); - Serv_Addr.sin_addr.s_addr = inet_addr(SERVER); - printf("Connected to server via socket %u\n",TcpClientSocket); + struct sockaddr_in Serv_Addr; + SOCKET TcpClientSocket =-1; + int ret = -1 - do - { - ret = connect(TcpClientSocket,(sockaddr_in*)&Serv_Addr,sizeof(Serv_Addr)); - if(ret != 0) - { - printf("Connection Error\n"); - } - else - { - printf("Connection successful.\n"); - break; - } - }while(1) + TcpClientSocket = socket(AF_INET,SOCK_STREAM,0); + Serv_Addr.sin_family = AF_INET; + Serv_Addr.sin_port = _htons(1234); + Serv_Addr.sin_addr.s_addr = inet_addr(SERVER); + printf("Connected to server via socket %u\n",TcpClientSocket); + + do + { + ret = connect(TcpClientSocket,(sockaddr_in*)&Serv_Addr,sizeof(Serv_Addr)); + if(ret != 0) + { + printf("Connection Error\n"); + } + else + { + printf("Connection successful.\n"); + break; + } + }while(1) @endcode Socket Callback @code - if(u8Msg == SOCKET_MSG_CONNECT) - { - tstrSocketConnectMsg *pstrConnect = (tstrSocketConnectMsg*)pvMsg; - if(pstrConnect->s8Error == 0) - { - uint8 acBuffer[GROWL_MSG_SIZE]; - uint16 u16MsgSize; + if(u8Msg == SOCKET_MSG_CONNECT) + { + tstrSocketConnectMsg *pstrConnect = (tstrSocketConnectMsg*)pvMsg; + if(pstrConnect->s8Error == 0) + { + uint8 acBuffer[GROWL_MSG_SIZE]; + uint16 u16MsgSize; - printf("Connect success!\n"); - - u16MsgSize = FormatMsg(u8ClientID, acBuffer); - send(sock, acBuffer, u16MsgSize, 0); - recv(pstrNotification->Socket, (void*)au8Msg,GROWL_DESCRIPTION_MAX_LENGTH, GROWL_RX_TIMEOUT); - u8Retry = GROWL_CONNECT_RETRY; - } - else - { - M2M_DBG("Connection Failed, Error: %d\n",pstrConnect->s8Error"); - close(pstrNotification->Socket); - } - } + printf("Connect success!\n"); + + u16MsgSize = FormatMsg(u8ClientID, acBuffer); + send(sock, acBuffer, u16MsgSize, 0); + recv(pstrNotification->Socket, (void*)au8Msg,GROWL_DESCRIPTION_MAX_LENGTH, GROWL_RX_TIMEOUT); + u8Retry = GROWL_CONNECT_RETRY; + } + else + { + M2M_DBG("Connection Failed, Error: %d\n",pstrConnect->s8Error"); + close(pstrNotification->Socket); + } + } @endcode */ NMI_API sint8 WINC1500_EXPORT(connect)(SOCKET sock, struct sockaddr *pstrAddr, uint8 u8AddrLen); -/**@}*/ //AcceptFn + +/**@}*/ //ConnectFn + +/** @defgroup SecureFn secure + @ingroup SocketAPI + Converts an (insecure) TCP connection with a remote server into a secure TLS-over-TCP connection. + It may be called after both of the following:\n + - a TCP socket has been created by the @ref socket function, with u8Config parameter set to + @ref SOCKET_CONFIG_SSL_DELAY \n + - a successful connection has been made on the socket via the @ref connect function. + This is an asynchronous API; the application socket callback function is notified of the result + of the attempt to make the connection secure through the event @ref SOCKET_MSG_SECURE, along + with a structure @ref tstrSocketConnectMsg. + If the attempt to make the connection secure fails, the application should call @ref close() + */ +/**@{*/ +/*! +@fn \ + sint8 secure(SOCKET sock); + +@param[in] sock + Socket ID, corresponding to a connected TCP socket. + +@pre + @ref socket and @ref connect must be called to connect a TCP socket before passing the socket ID to this function. + Value @ref SOCKET_CONFIG_SSL_DELAY must have been set in the u8Config parameter that was passed to @ref socket. + +@see + socket + connect + +@return + The function returns SOCK_ERR_NO_ERROR for successful operations and a negative error value otherwise. + The possible error values are: + - @ref SOCK_ERR_INVALID_ARG + Indicating passing invalid arguments such as negative socket ID. + + - @ref SOCK_ERR_INVALID + Indicating failure to process the request. +*/ +sint8 WINC1500_EXPORT(secure)(SOCKET sock); + +/**@}*/ //SecureFn /** @defgroup ReceiveFn recv @ingroup SocketAPI An asynchronous receive function, used to retrieve data from a TCP stream. - Before calling the recv function, a successful socket connection status must have been received through any of the two socket events + Before calling the recv function, a successful socket connection status must have been received through any of the two socket events @ref SOCKET_MSG_CONNECT or @ref SOCKET_MSG_ACCEPT, from the socket callback. Hence, indicating that the socket is already connected to a remote - host. - The application receives the required data in response to this asynchronous call through the reception of the event @ref SOCKET_MSG_RECV in the - socket callback. + host. + The application receives the required data in response to this asynchronous call through the reception of the event @ref SOCKET_MSG_RECV in the + socket callback. - Receiving the SOCKET_MSG_RECV message in the callback with zero or negative buffer length indicates the following: - @ref SOCK_ERR_NO_ERROR : Socket connection closed. The application should now call @ref close(). - @ref SOCK_ERR_CONN_ABORTED : Socket connection aborted. The application should now call @ref close(). - @ref SOCK_ERR_TIMEOUT : Socket receive timed out. The socket connection remains open. - The application code is expected to close the socket through the call to the @ref close function upon the appearance of the above mentioned errors. + Receiving the SOCKET_MSG_RECV message in the callback with zero or negative buffer length indicates the following: + @ref SOCK_ERR_NO_ERROR : Socket connection closed. The application should now call @ref close(). + @ref SOCK_ERR_CONN_ABORTED : Socket connection aborted. The application should now call @ref close(). + @ref SOCK_ERR_TIMEOUT : Socket receive timed out. The socket connection remains open. + The application code is expected to close the socket through the call to the @ref close function upon the appearance of the above mentioned errors. */ - /**@{*/ +/**@{*/ /*! -@fn \ - NMI_API sint16 recv(SOCKET sock, void *pvRecvBuf, uint16 u16BufLen, uint32 u32Timeoutmsec); - -@param [in] sock - Socket ID, must hold a non negative value. - A negative value will return a socket error @ref SOCK_ERR_INVALID_ARG. Indicating that an invalid argument is passed in. +@fn \ + NMI_API sint16 recv(SOCKET sock, void *pvRecvBuf, uint16 u16BufLen, uint32 u32Timeoutmsec); + +@param[in] sock + Socket ID, must hold a non negative value. + A negative value will return a socket error @ref SOCK_ERR_INVALID_ARG. Indicating that an invalid argument is passed in. -@param [in] pvRecvBuf - Pointer to a buffer that will hold the received data. The buffer is used - in the recv callback to deliver the received data to the caller. The buffer must - be resident in memory (heap or global buffer). +@param[in] pvRecvBuf + Pointer to a buffer that will hold the received data. The buffer is used + in the recv callback to deliver the received data to the caller. The buffer must + be resident in memory (heap or global buffer). -@param [in] u16BufLen - The buffer size in bytes. +@param[in] u16BufLen + The buffer size in bytes. -@param [in] u32Timeoutmsec - Timeout for the recv function in milli-seconds. If the value is set to ZERO, the timeout - will be set to infinite (the recv function waits forever). If the timeout period is - elapsed with no data received, the socket will get a timeout error. +@param[in] u32Timeoutmsec + Timeout for the recv function in milli-seconds. If the value is set to ZERO, the timeout + will be set to infinite (the recv function waits forever). If the timeout period is + elapsed with no data received, the socket will get a timeout error. @pre - - The socket function must be called to allocate a TCP socket before passing the socket ID to the recv function. - - The socket in a connected state is expected to receive data through the socket interface. - -@see socket -@see connect -@see bind -@see listen -@see recvfrom -@see close + - The socket function must be called to allocate a TCP socket before passing the socket ID to the recv function. + - The socket in a connected state is expected to receive data through the socket interface. -@return - The function returns ZERO for successful operations and a negative value otherwise. - The possible error values are: +@see socket +@see connect +@see bind +@see listen +@see recvfrom +@see close + +@return + The function returns ZERO for successful operations and a negative value otherwise. + The possible error values are: - @ref SOCK_ERR_NO_ERROR - Indicating that the operation was successful. - + Indicating that the operation was successful. + - @ref SOCK_ERR_INVALID_ARG - Indicating passing invalid arguments such as negative socket ID or NULL Receive buffer. + Indicating passing invalid arguments such as negative socket ID or NULL Receive buffer. - @ref SOCK_ERR_BUFFER_FULL - Indicate socket receive failure. -\section recv example - The example demonstrates a code snippet for the calling of the recv function in the socket callback upon notification of the accept or connect events, and the parsing of the + Indicate socket receive failure. +\section recv_Ex Example + The example demonstrates a code snippet for the calling of the recv function in the socket callback upon notification of the accept or connect events, and the parsing of the received data when the @ref SOCKET_MSG_RECV event is received. @code - switch(u8Msg) - { + switch(u8Msg) + { - case SOCKET_MSG_ACCEPT: - { - tstrSocketAcceptMsg *pstrAccept = (tstrSocketAcceptMsg*)pvMsg; + case SOCKET_MSG_ACCEPT: + { + tstrSocketAcceptMsg *pstrAccept = (tstrSocketAcceptMsg*)pvMsg; - if(pstrAccept->sock >= 0) - { - recv(pstrAccept->sock,gau8RxBuffer,sizeof(gau8RxBuffer),TEST_RECV_TIMEOUT); - } - else - { - M2M_ERR("accept\n"); - } - } - break; + if(pstrAccept->sock >= 0) + { + recv(pstrAccept->sock,gau8RxBuffer,sizeof(gau8RxBuffer),TEST_RECV_TIMEOUT); + } + else + { + M2M_ERR("accept\n"); + } + } + break; - case SOCKET_MSG_RECV: - { - tstrSocketRecvMsg *pstrRx = (tstrSocketRecvMsg*)pvMsg; - - if(pstrRx->s16BufferSize > 0) - { - recv(sock,gau8RxBuffer,sizeof(gau8RxBuffer),TEST_RECV_TIMEOUT); - } - else - { - printf("Socket recv Error: %d\n",pstrRx->s16BufferSize); - close(sock); - } - } - break; - - default: - break; - } + case SOCKET_MSG_RECV: + { + tstrSocketRecvMsg *pstrRx = (tstrSocketRecvMsg*)pvMsg; + + if(pstrRx->s16BufferSize > 0) + { + recv(sock,gau8RxBuffer,sizeof(gau8RxBuffer),TEST_RECV_TIMEOUT); + } + else + { + printf("Socket recv Error: %d\n",pstrRx->s16BufferSize); + close(sock); + } + } + break; + + default: + break; + } } @endcode */ @@ -1568,111 +1776,111 @@ NMI_API sint16 WINC1500_EXPORT(recv)(SOCKET sock, void *pvRecvBuf, uint16 u16Buf /** @defgroup ReceiveFromSocketFn recvfrom * @ingroup SocketAPI - * Receives data from a UDP Socket. + * Receives data from a UDP Socket. * -* The asynchronous recvfrom function is used to retrieve data from a UDP socket. The socket must already be bound to -* a local port before a call to the recvfrom function is made (i.e message @ref SOCKET_MSG_BIND is received -* with successful status in the socket callback). +* The asynchronous recvfrom function is used to retrieve data from a UDP socket. The socket must already be bound to +* a local port before a call to the recvfrom function is made (i.e message @ref SOCKET_MSG_BIND is received +* with successful status in the socket callback). * -* Upon calling the recvfrom function with a successful return code, the application is expected to receive a notification -* in the socket callback whenever a message is received through the @ref SOCKET_MSG_RECVFROM event. +* Upon calling the recvfrom function with a successful return code, the application is expected to receive a notification +* in the socket callback whenever a message is received through the @ref SOCKET_MSG_RECVFROM event. * -* Receiving the SOCKET_MSG_RECVFROM message in the callback with zero, indicates that the socket is closed. -* Whereby a negative buffer length indicates one of the socket error codes such as socket timeout error @ref SOCK_ERR_TIMEOUT +* Receiving the SOCKET_MSG_RECVFROM message in the callback with zero, indicates that the socket is closed. +* Whereby a negative buffer length indicates one of the socket error codes such as socket timeout error @ref SOCK_ERR_TIMEOUT * -* The recvfrom callback can also be used to show the IP address of the remote host that sent the frame by -* using the "strRemoteAddr" element in the @ref tstrSocketRecvMsg structure. (refer to the code example) +* The recvfrom callback can also be used to show the IP address of the remote host that sent the frame by +* using the "strRemoteAddr" element in the @ref tstrSocketRecvMsg structure. (refer to the code example) */ - /**@{*/ +/**@{*/ /*! -@fn \ - NMI_API sint16 recvfrom(SOCKET sock, void *pvRecvBuf, uint16 u16BufLen, uint32 u32TimeoutSeconds); +@fn \ + NMI_API sint16 recvfrom(SOCKET sock, void *pvRecvBuf, uint16 u16BufLen, uint32 u32TimeoutSeconds); -@param [in] sock - Socket ID, must hold a non negative value. - A negative value will return a socket error @ref SOCK_ERR_INVALID_ARG. Indicating that an invalid argument is passed in. +@param[in] sock + Socket ID, must hold a non negative value. + A negative value will return a socket error @ref SOCK_ERR_INVALID_ARG. Indicating that an invalid argument is passed in. -@param [in] pvRecvBuf - Pointer to a buffer that will hold the received data. The buffer shall be used - in the recv callback to deliver the received data to the caller. The buffer must - be resident in memory (heap or global buffer). +@param[in] pvRecvBuf + Pointer to a buffer that will hold the received data. The buffer shall be used + in the recv callback to deliver the received data to the caller. The buffer must + be resident in memory (heap or global buffer). -@param [in] u16BufLen - The buffer size in bytes. +@param[in] u16BufLen + The buffer size in bytes. -@param [in] u32TimeoutSeconds - Timeout for the recv function in milli-seconds. If the value is set to ZERO, the timeout - will be set to infinite (the recv function waits forever). +@param[in] u32TimeoutSeconds + Timeout for the recv function in milli-seconds. If the value is set to ZERO, the timeout + will be set to infinite (the recv function waits forever). @pre - - The socket function must be called to allocate a UDP socket before passing the socket ID to the recvfrom function. - - The socket corresponding to the socket ID must be successfully bound to a local port through the call to a @ref bind function. - -@see - socket - bind - close + - The socket function must be called to allocate a UDP socket before passing the socket ID to the recvfrom function. + - The socket corresponding to the socket ID must be successfully bound to a local port through the call to a @ref bind function. -@return - The function returns ZERO for successful operations and a negative value otherwise. - The possible error values are: +@see + socket + bind + close + +@return + The function returns ZERO for successful operations and a negative value otherwise. + The possible error values are: - @ref SOCK_ERR_NO_ERROR - Indicating that the operation was successful. - + Indicating that the operation was successful. + - @ref SOCK_ERR_INVALID_ARG - Indicating passing invalid arguments such as negative socket ID or NULL Receive buffer. + Indicating passing invalid arguments such as negative socket ID or NULL Receive buffer. - @ref SOCK_ERR_BUFFER_FULL - Indicate socket receive failure. -\section recvfrom example - The example demonstrates a code snippet for the calling of the recvfrom function in the socket callback upon notification of a successful bind event, and the parsing of the + Indicate socket receive failure. +\section recvfrom_Ex Example + The example demonstrates a code snippet for the calling of the recvfrom function in the socket callback upon notification of a successful bind event, and the parsing of the received data when the @ref SOCKET_MSG_RECVFROM event is received. @code - switch(u8Msg) - { - case SOCKET_MSG_BIND: - { - tstrSocketBindMsg *pstrBind = (tstrSocketBindMsg*)pvMsg; + switch(u8Msg) + { + case SOCKET_MSG_BIND: + { + tstrSocketBindMsg *pstrBind = (tstrSocketBindMsg*)pvMsg; - if(pstrBind != NULL) - { - if(pstrBind->status == 0) - { - recvfrom(sock, gau8SocketTestBuffer, TEST_BUFFER_SIZE, 0); - } - else - { - M2M_ERR("bind\n"); - } - } - } - break; + if(pstrBind != NULL) + { + if(pstrBind->status == 0) + { + recvfrom(sock, gau8SocketTestBuffer, TEST_BUFFER_SIZE, 0); + } + else + { + M2M_ERR("bind\n"); + } + } + } + break; - case SOCKET_MSG_RECVFROM: - { - tstrSocketRecvMsg *pstrRx = (tstrSocketRecvMsg*)pvMsg; - - if(pstrRx->s16BufferSize > 0) - { - //get the remote host address and port number - uint16 u16port = pstrRx->strRemoteAddr.sin_port; - uint32 strRemoteHostAddr = pstrRx->strRemoteAddr.sin_addr.s_addr; + case SOCKET_MSG_RECVFROM: + { + tstrSocketRecvMsg *pstrRx = (tstrSocketRecvMsg*)pvMsg; - printf("Received frame with size = %d.\tHost address=%x, Port number = %d\n\n",pstrRx->s16BufferSize,strRemoteHostAddr, u16port); - - ret = recvfrom(sock,gau8SocketTestBuffer,sizeof(gau8SocketTestBuffer),TEST_RECV_TIMEOUT); - } - else - { - printf("Socket recv Error: %d\n",pstrRx->s16BufferSize); - ret = close(sock); - } - } - break; - - default: - break; - } + if(pstrRx->s16BufferSize > 0) + { + //get the remote host address and port number + uint16 u16port = pstrRx->strRemoteAddr.sin_port; + uint32 strRemoteHostAddr = pstrRx->strRemoteAddr.sin_addr.s_addr; + + printf("Received frame with size = %d.\tHost address=%x, Port number = %d\n\n",pstrRx->s16BufferSize,strRemoteHostAddr, u16port); + + ret = recvfrom(sock,gau8SocketTestBuffer,sizeof(gau8SocketTestBuffer),TEST_RECV_TIMEOUT); + } + else + { + printf("Socket recv Error: %d\n",pstrRx->s16BufferSize); + ret = close(sock); + } + } + break; + + default: + break; + } } @endcode */ @@ -1686,59 +1894,59 @@ NMI_API sint16 WINC1500_EXPORT(recvfrom)(SOCKET sock, void *pvRecvBuf, uint16 u1 * Called by the application code when there is outgoing data available required to be sent on a specific socket handler. * The only difference between this function and the similar @ref sendto function, is the type of socket the data is sent on and the parameters passed in. * @ref send function is most commonly called for sockets in a connected state. -* After the data is sent, the socket callback function registered using registerSocketCallback(), is expected to receive an event of type +* After the data is sent, the socket callback function registered using registerSocketCallback(), is expected to receive an event of type * @ref SOCKET_MSG_SEND holding information containing the number of data bytes sent. */ - /**@{*/ +/**@{*/ /*! -@fn \ - NMI_API sint16 send(SOCKET sock, void *pvSendBuffer, uint16 u16SendLength, uint16 u16Flags); +@fn \ + NMI_API sint16 send(SOCKET sock, void *pvSendBuffer, uint16 u16SendLength, uint16 u16Flags); -@param [in] sock - Socket ID, must hold a non negative value. - A negative value will return a socket error @ref SOCK_ERR_INVALID_ARG. Indicating that an invalid argument is passed in. - -@param [in] pvSendBuffer - Pointer to a buffer holding data to be transmitted. +@param[in] sock + Socket ID, must hold a non negative value. + A negative value will return a socket error @ref SOCK_ERR_INVALID_ARG. Indicating that an invalid argument is passed in. -@param [in] u16SendLength - The buffer size in bytes. +@param[in] pvSendBuffer + Pointer to a buffer holding data to be transmitted. + +@param[in] u16SendLength + The buffer size in bytes. + +@param[in] u16Flags + Not used in the current implementation. + +@pre + Sockets must be initialized using socketInit. \n + + For TCP Socket:\n + Must use a successfully connected Socket (so that the intended recipient address is known ahead of sending the data). + Hence this function is expected to be called after a successful socket connect operation(in client case or accept in the + the server case).\n + + For UDP Socket:\n + UDP sockets most commonly use @ref sendto function, where the destination address is defined. However, in-order to send outgoing data + using the @ref send function, at least one successful call must be made to the @ref sendto function a priori the consecutive calls to the @ref send function, + to ensure that the destination address is saved in the firmware. -@param [in] u16Flags - Not used in the current implementation. - -@pre - Sockets must be initialized using socketInit. \n - - For TCP Socket:\n - Must use a successfully connected Socket (so that the intended recipient address is known ahead of sending the data). - Hence this function is expected to be called after a successful socket connect operation(in client case or accept in the - the server case).\n - - For UDP Socket:\n - UDP sockets most commonly use @ref sendto function, where the destination address is defined. However, in-order to send outgoing data - using the @ref send function, at least one successful call must be made to the @ref sendto function a priori the consecutive calls to the @ref send function, - to ensure that the destination address is saved in the firmware. - @warning - u16SendLength must not exceed @ref SOCKET_BUFFER_MAX_LENGTH.\n + u16SendLength must not exceed @ref SOCKET_BUFFER_MAX_LENGTH \n Use a valid socket identifier through the a prior call to the @ref socket function. Must use a valid buffer pointer. Successful completion of a call to send() does not guarantee delivery of the message, - A negative return value indicates only locally-detected errors + A negative return value indicates only locally-detected errors. @see - socketInit - recv - sendto - socket - connect - accept - sendto - -@return - The function shall return @ref SOCK_ERR_NO_ERROR for successful operation and a negative value (indicating the error) otherwise. + socketInit + recv + sendto + socket + connect + accept + sendto + +@return + The function shall return @ref SOCK_ERR_NO_ERROR for successful operation and a negative value (indicating the error) otherwise. */ NMI_API sint16 WINC1500_EXPORT(send)(SOCKET sock, void *pvSendBuffer, uint16 u16SendLength, uint16 u16Flags); /**@}*/ //SendFn @@ -1747,58 +1955,58 @@ NMI_API sint16 WINC1500_EXPORT(send)(SOCKET sock, void *pvSendBuffer, uint16 u16 * @ingroup SocketAPI * Asynchronous sending function, used to send data on a UDP socket. * Called by the application code when there is data required to be sent on a UDP socket handler. -* The application code is expected to receive data from a successful bounded socket node. +* The application code is expected to receive data from a successful bounded socket node. * The only difference between this function and the similar @ref send function, is the type of socket the data is received on. This function works * only with UDP sockets. -* After the data is sent, the socket callback function registered using registerSocketCallback(), is expected to receive an event of type +* After the data is sent, the socket callback function registered using registerSocketCallback(), is expected to receive an event of type * @ref SOCKET_MSG_SENDTO. */ - /**@{*/ +/**@{*/ /*! -@fn \ - NMI_API sint16 sendto(SOCKET sock, void *pvSendBuffer, uint16 u16SendLength, uint16 flags, struct sockaddr *pstrDestAddr, uint8 u8AddrLen); +@fn \ + NMI_API sint16 sendto(SOCKET sock, void *pvSendBuffer, uint16 u16SendLength, uint16 flags, struct sockaddr *pstrDestAddr, uint8 u8AddrLen); -@param [in] sock - Socket ID, must hold a non negative value. - A negative value will return a socket error @ref SOCK_ERR_INVALID_ARG. Indicating that an invalid argument is passed in. +@param[in] sock + Socket ID, must hold a non negative value. + A negative value will return a socket error @ref SOCK_ERR_INVALID_ARG. Indicating that an invalid argument is passed in. -@param [in] pvSendBuffer - Pointer to a buffer holding data to be transmitted. - A NULL value will return a socket error @ref SOCK_ERR_INVALID_ARG. Indicating that an invalid argument is passed in. +@param[in] pvSendBuffer + Pointer to a buffer holding data to be transmitted. + A NULL value will return a socket error @ref SOCK_ERR_INVALID_ARG. Indicating that an invalid argument is passed in. -@param [in] u16SendLength - The buffer size in bytes. It must not exceed @ref SOCKET_BUFFER_MAX_LENGTH. +@param[in] u16SendLength + The buffer size in bytes. It must not exceed @ref SOCKET_BUFFER_MAX_LENGTH. -@param [in] flags - Not used in the current implementation +@param[in] flags + Not used in the current implementation -@param [in] pstrDestAddr - The destination address. +@param[in] pstrDestAddr + The destination address. + +@param[in] u8AddrLen + Destination address length in bytes. + Not used in the current implementation, only included for BSD compatibility. +@pre + Sockets must be initialized using socketInit. -@param [in] u8AddrLen - Destination address length in bytes. - Not used in the current implementation, only included for BSD compatibility. -@pre - Sockets must be initialized using socketInit. - @warning u16SendLength must not exceed @ref SOCKET_BUFFER_MAX_LENGTH. \n Use a valid socket (returned from socket ). - A valid buffer pointer must be used (not NULL). \n + A valid buffer pointer must be used (not NULL).\n Successful completion of a call to sendto() does not guarantee delivery of the message, - A negative return value indicates only locally-detected errors + A negative return value indicates only locally-detected errors. @see - socketInit - recvfrom - sendto - socket - connect - accept - send - + socketInit + recvfrom + sendto + socket + connect + accept + send + @return - The function returns @ref SOCK_ERR_NO_ERROR for successful operation and a negative value (indicating the error) otherwise. + The function returns @ref SOCK_ERR_NO_ERROR for successful operation and a negative value (indicating the error) otherwise. */ NMI_API sint16 WINC1500_EXPORT(sendto)(SOCKET sock, void *pvSendBuffer, uint16 u16SendLength, uint16 flags, struct sockaddr *pstrDestAddr, uint8 u8AddrLen); /**@}*/ //SendToSocketFn @@ -1807,28 +2015,28 @@ NMI_API sint16 WINC1500_EXPORT(sendto)(SOCKET sock, void *pvSendBuffer, uint16 u * @ingroup SocketAPI * Synchronous close function, releases all the socket assigned resources. */ - /**@{*/ +/**@{*/ /*! -@fn \ - NMI_API sint8 close(SOCKET sock); +@fn \ + NMI_API sint8 close(SOCKET sock); + +@param[in] sock + Socket ID, must hold a non negative value. + A negative value will return a socket error @ref SOCK_ERR_INVALID_ARG. Indicating that an invalid argument is passed in. + +@pre + Sockets must be initialized through the call of the socketInit function. + @ref close is called only for valid socket identifiers created through the @ref socket function. -@param [in] sock - Socket ID, must hold a non negative value. - A negative value will return a socket error @ref SOCK_ERR_INVALID_ARG. Indicating that an invalid argument is passed in. - -@pre - Sockets must be initialized through the call of the socketInit function. - @ref close is called only for valid socket identifiers created through the @ref socket function. - @warning - If @ref close is called while there are still pending messages (sent or received ) they will be discarded. + If @ref close is called while there are still pending messages (sent or received ) they will be discarded. @see - socketInit - socket + socketInit + socket -@return - The function returned @ref SOCK_ERR_NO_ERROR for successful operation and a negative value (indicating the error) otherwise. +@return + The function returned @ref SOCK_ERR_NO_ERROR for successful operation and a negative value (indicating the error) otherwise. */ NMI_API sint8 WINC1500_EXPORT(close)(SOCKET sock); /**@}*/ //CloseSocketFn @@ -1838,65 +2046,65 @@ NMI_API sint8 WINC1500_EXPORT(close)(SOCKET sock); * Synchronous function which returns a BSD socket compliant Internet Protocol (IPv4) socket address. * This IPv4 address in the input string parameter could either be specified as a host name, or as a numeric string representation like n.n.n.n known as the IPv4 dotted-decimal format * (i.e. "192.168.10.1"). -* This function is used whenever an ip address needs to be set in the proper format -* (i.e. for the @ref tstrM2MIPConfig structure). +* This function is used whenever an ip address needs to be set in the proper format +* (i.e. for the @ref tstrM2MIPConfig structure). */ - /**@{*/ +/**@{*/ /*! -@fn \ - NMI_API uint32 nmi_inet_addr(char *pcIpAddr); +@fn \ + NMI_API uint32 nmi_inet_addr(char *pcIpAddr); -@param [in] pcIpAddr - A null terminated string containing the IP address in IPv4 dotted-decimal address. +@param[in] pcIpAddr + A null terminated string containing the IP address in IPv4 dotted-decimal address. @return - Unsigned 32-bit integer representing the IP address in Network byte order - (eg. "192.168.10.1" will be expressed as 0x010AA8C0). + Unsigned 32-bit integer representing the IP address in Network byte order + (eg. "192.168.10.1" will be expressed as 0x010AA8C0). */ NMI_API uint32 nmi_inet_addr(char *pcIpAddr); /**@}*/ //InetAddressFn /** @defgroup gethostbynameFn gethostbyname * @ingroup SocketAPI -* Asynchronous DNS resolving function. This function use DNS to resolve a domain name into the corresponding IP address. -* A call to this function will cause a DNS request to be sent and the response will be delivered to the DNS callback function registered using registerSocketCallback() +* Asynchronous DNS resolving function. This function use DNS to resolve a domain name into the corresponding IP address. +* A call to this function will cause a DNS request to be sent and the response will be delivered to the DNS callback function registered using registerSocketCallback() */ - /**@{*/ +/**@{*/ /*! -@fn \ - NMI_API sint8 gethostbyname(uint8 * pcHostName); +@fn \ + NMI_API sint8 gethostbyname(uint8 * pcHostName); -@param [in] pcHostName - NULL terminated string containing the domain name for the remote host. +@param[in] pcHostName + NULL terminated string containing the domain name for the remote host. Its size must not exceed @ref HOSTNAME_MAX_SIZE. - + @warning - Successful completion of a call to gethostbyname() does not guarantee success of the DNS request, - a negative return value indicates only locally-detected errors - + Successful completion of a call to gethostbyname() does not guarantee success of the DNS request, + a negative return value indicates only locally-detected errors + @see registerSocketCallback -@return +@return - @ref SOCK_ERR_NO_ERROR - @ref SOCK_ERR_INVALID_ARG */ -NMI_API sint8 WINC1500_EXPORT(gethostbyname)(uint8 * pcHostName); +NMI_API sint8 WINC1500_EXPORT(gethostbyname)(uint8 *pcHostName); /**@}*/ //gethostbynameFn /** @defgroup sslEnableCertExpirationCheckFn sslEnableCertExpirationCheck * @ingroup SocketAPI -* Configure the behavior of the SSL Library for Certificate Expiry Validation. + * Configure the behavior of the SSL Library for Certificate Expiry Validation. */ - /**@{*/ +/**@{*/ /*! -@fn \ +@fn \ NMI_API sint8 sslEnableCertExpirationCheck(tenuSslCertExpSettings enuValidationSetting); -@param [in] enuValidationSetting - See @ref tenuSslCertExpSettings for details. +@param [in] enuValidationSetting + See @ref tenuSslCertExpSettings for details. -@sa tenuSslCertExpSettings +@sa tenuSslCertExpSettings @return - @ref SOCK_ERR_NO_ERROR for successful operation and negative error code otherwise. @@ -1906,87 +2114,50 @@ NMI_API sint8 WINC1500_EXPORT(sslEnableCertExpirationCheck)(tenuSslCertExpSettin /** @defgroup SetSocketOptionFn setsockopt * @ingroup SocketAPI -*The setsockopt() function shall set the option specified by the option_name -* argument, at the protocol level specified by the level argument, to the value -* pointed to by the option_value argument for the socket specified by the socket argument. -* -*

Possible protocol level values supported are @ref SOL_SOCKET and @ref SOL_SSL_SOCKET. -* Possible options when the protocol level is @ref SOL_SOCKET :

-* -* -* -* -* -* -* -* -* -* -* -* -* -*
@ref SO_SET_UDP_SEND_CALLBACKEnable/Disable callback messages for sendto(). -* Since UDP is unreliable by default the user maybe interested (or not) in -* receiving a message of @ref SOCKET_MSG_SENDTO for each call of sendto(). -* Enabled if option value equals TRUE, disabled otherwise.
@ref IP_ADD_MEMBERSHIPValid for UDP sockets. This option is used to receive frames sent to -* a multicast group. option_value shall be a pointer to Unsigned 32-bit -* integer containing the multicast IPv4 address.
@ref IP_DROP_MEMBERSHIPValid for UDP sockets. This option is used to stop receiving frames -* sent to a multicast group. option_value shall be a pointer to Unsigned -* 32-bit integer containing the multicast IPv4 address.
-*

Possible options when the protocol level  is @ref SOL_SSL_SOCKET

-* -* -* -* -* -* -* -* -* -* -* -* -* -*
-* @ref SO_SSL_BYPASS_X509_VERIFAllow an opened SSL socket to bypass the X509 -* certificate verification process. It is highly recommended NOT to use -* this socket option in production software applications. The option is -* supported for debugging and testing purposes. The option value should be -* casted to int type and it is handled as a boolean flag.
@ref SO_SSL_SNISet the Server Name Indicator (SNI) for an SSL socket. The SNI is a -* null terminated string containing the server name associated with the -* connection. It must not exceed the size of @ref HOSTNAME_MAX_SIZE.
@ref SO_SSL_ENABLE_SESSION_CACHINGThis option allow the TLS to cache the session information for fast -* TLS session establishment in future connections using the TLS Protocol -* session resume features.
+ * The setsockopt() function shall set the option specified by the option_name + * argument, at the protocol level specified by the level argument, to the value + * pointed to by the option_value argument for the socket specified by the socket argument. */ - /**@{*/ +/**@{*/ /*! -@fn \ - NMI_API sint8 setsockopt(SOCKET socket, uint8 u8Level, uint8 option_name, +@fn \ + NMI_API sint8 setsockopt(SOCKET socket, uint8 u8Level, uint8 option_name, const void *option_value, uint16 u16OptionLen); -@param [in] socket - Socket handler. +@param[in] socket + Socket handler. -@param [in] u8Level - protocol level. See description above. +@param[in] u8Level + Protocol level.\n + Supported protocol levels are @ref SOL_SOCKET and @ref SOL_SSL_SOCKET. -@param [in] option_name - option to be set. See description above. +@param[in] option_name + Option to be set.\n + For protocol level @ref SOL_SOCKET, the supported option names are:\n + @ref SO_SET_UDP_SEND_CALLBACK \n + @ref SO_TCP_KEEPALIVE \n + @ref SO_TCP_KEEPIDLE \n + @ref SO_TCP_KEEPINTVL \n + @ref SO_TCP_KEEPCNT \n + For protocol level @ref SOL_SSL_SOCKET, the supported option names are:\n + @ref SO_SSL_BYPASS_X509_VERIF \n + @ref SO_SSL_SNI \n + @ref SO_SSL_ENABLE_SESSION_CACHING \n + @ref SO_SSL_ENABLE_SNI_VALIDATION \n + @ref SO_SSL_ALPN \n -@param [in] option_value - pointer to user provided value. +@param[in] option_value + Pointer to user provided value. -@param [in] u16OptionLen - length of the option value in bytes. +@param[in] u16OptionLen + Length of the option value in bytes. Refer to each option documentation for the required length. -@sa SOL_SOCKET, SOL_SSL_SOCKET, IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP - -@return - The function shall return \ref SOCK_ERR_NO_ERROR for successful operation - and a negative value (indicating the error) otherwise. +@return + The function shall return \ref SOCK_ERR_NO_ERROR for successful operation + and a negative value (indicating the error) otherwise. */ NMI_API sint8 WINC1500_EXPORT(setsockopt)(SOCKET socket, uint8 u8Level, uint8 option_name, - const void *option_value, uint16 u16OptionLen); + const void *option_value, uint16 u16OptionLen); /**@}*/ //SetSocketOptionFn /** @defgroup GetSocketOptionsFn getsockopt @@ -1994,54 +2165,163 @@ NMI_API sint8 WINC1500_EXPORT(setsockopt)(SOCKET socket, uint8 u8Level, uint8 op * Get socket options retrieves * This Function isn't implemented yet but this is the form that will be released later. */ - /**@{*/ +/**@{*/ /*! -@fn \ - sint8 getsockopt(SOCKET sock, uint8 u8Level, uint8 u8OptName, const void *pvOptValue, uint8 * pu8OptLen); +@fn \ + sint8 getsockopt(SOCKET sock, uint8 u8Level, uint8 u8OptName, const void *pvOptValue, uint8 * pu8OptLen); @brief -@param [in] sock - Socket Identifier. -@param [in] u8Level - The protocol level of the option. -@param [in] u8OptName - The u8OptName argument specifies a single option to get. -@param [out] pvOptValue - The pvOptValue argument contains pointer to a buffer containing the option value. -@param [out] pu8OptLen - Option value buffer length. +@param[in] sock + Socket Identifier. +@param[in] u8Level + The protocol level of the option. +@param[in] u8OptName + The u8OptName argument specifies a single option to get. +@param[out] pvOptValue + The pvOptValue argument contains pointer to a buffer containing the option value. +@param[out] pu8OptLen + Option value buffer length. @return - The function shall return ZERO for successful operation and a negative value otherwise. + The function shall return ZERO for successful operation and a negative value otherwise. */ -NMI_API sint8 WINC1500_EXPORT(getsockopt)(SOCKET sock, uint8 u8Level, uint8 u8OptName, const void *pvOptValue, uint8* pu8OptLen); +NMI_API sint8 WINC1500_EXPORT(getsockopt)(SOCKET sock, uint8 u8Level, uint8 u8OptName, const void *pvOptValue, uint8 *pu8OptLen); /**@}*/ //GetSocketOptionsFn -/**@}*/ /** @defgroup PingFn m2m_ping_req * @ingroup SocketAPI - * The function sends ping request to the given IP Address. + * The function sends ping request to the given IP Address. */ - /**@{*/ +/**@{*/ /*! -@fn \ - NMI_API sint8 m2m_ping_req(uint32 u32DstIP, uint8 u8TTL, tpfPingCb fpPingCb); +@fn \ + NMI_API sint8 m2m_ping_req(uint32 u32DstIP, uint8 u8TTL, tpfPingCb fpPingCb); -@param [in] u32DstIP - Target Destination IP Address for the ping request. It must be represented in Network byte order. - The function nmi_inet_addr could be used to translate the dotted decimal notation IP - to its Network bytes order integer representative. - -@param [in] u8TTL - IP TTL value for the ping request. If set to ZERO, the default value SHALL be used. +@param[in] u32DstIP + Target Destination IP Address for the ping request. It must be represented in Network byte order. + The function nmi_inet_addr could be used to translate the dotted decimal notation IP + to its Network bytes order integer representative. -@param [in] fpPingCb - Callback will be called to deliver the ping statistics. +@param[in] u8TTL + IP TTL value for the ping request. If set to ZERO, the default value SHALL be used. -@see nmi_inet_addr -@return The function returns @ref M2M_SUCCESS for successful operations and a negative value otherwise. +@param[in] fpPingCb + Callback will be called to deliver the ping statistics. + +@warning This API should only be used to request one ping at a time; calling this API invalidates callbacks + for previous ping requests. +@see nmi_inet_addr +@return The function returns @ref M2M_SUCCESS for successful operations and a negative value otherwise. */ NMI_API sint8 m2m_ping_req(uint32 u32DstIP, uint8 u8TTL, tpfPingCb fpPingCb); + +/*! + * @fn sint8 set_alpn_list(SOCKET sock, const char *pcProtocolList); + * + * This function sets the protocol list used for application-layer protocol negotiation (ALPN). + * If used, it must be called after creating a SSL socket (using @ref socket) and before + * connecting/binding (using @ref connect or @ref bind) or securing (using @ref secure). + * + * @param[in] sock + * Socket ID obtained by a call to @ref socket. This is the SSL socket to which + * the ALPN list applies. + * + * @param[in] pcProtocolList + * Pointer to the list of protocols available in the application. \n + * The entries in the list must: \n + * - be separated with ' ' (space). \n + * - not contain ' ' (space) or '\0' (NUL). \n + * - be non-zero length. \n + * . + * The list itself must: \n + * - be terminated with '\0' (NUL). \n + * - be no longer than @ref ALPN_LIST_MAX_APP_LENGTH, including separators (spaces) and terminator (NUL). \n + * - contain at least one entry. + * + * @return The function returns @ref M2M_SUCCESS for successful operations and a negative value otherwise. + * + * \section SocketExample9 Example + * The example demonstrates an application using @ref set_alpn_list and @ref get_alpn_index to negotiate secure HTTP/2 + * (with fallback option of HTTP/1.1). + + * \subsection sub5 Main Function + * @code + * SOCKET TcpClientSocket = socket(AF_INET, SOCK_STREAM, SOCKET_CONFIG_SSL_ON); + * if (TcpClientSocket >= 0) + * { + * struct sockaddr_in Serv_Addr = { + * .sin_family = AF_INET, + * .sin_port = _htons(1234), + * .sin_addr.s_addr = inet_addr(SERVER) + * }; + * set_alpn_list(TcpClientSocket, "h2 http/1.1"); + * connect(TcpClientSocket, &Serv_Addr, sizeof(Serv_Addr)); + * } + * @endcode + * \subsection sub6 Socket Callback + * @code + * if(u8Msg == SOCKET_MSG_CONNECT) + * { + * tstrSocketConnectMsg *pstrConnect = (tstrSocketConnectMsg*)pvMsg; + * if(pstrConnect->s8Error == 0) + * { + * uint8 alpn_index = get_alpn_index(pstrConnect->sock); + * switch (alpn_index) + * { + * case 1: + * printf("Negotiated HTTP/2\n"); + * break; + * case 2: + * printf("Negotiated HTTP/1.1\n"); + * break; + * case 0: + * printf("Protocol negotiation did not occur\n"); + * break; + * } + * } + * } + * @endcode +*/ +sint8 set_alpn_list(SOCKET sock, const char *pcProtocolList); +/*! + * @fn sint8 get_alpn_index(SOCKET sock); + * + * This function gets the index of the protocol negotiated via ALPN. + * It should be called when a SSL socket connection succeeds, in order to determine which + * application-layer protocol must be used. + * + * @param[in] sock + * Socket ID obtained by a call to @ref socket. This is the SSL socket to which + * the ALPN applies. + * + * @return The function returns:\n + * - >0: 1-based index of negotiated protocol with respect to the list previously provided to @ref set_alpn_list \n + * - 0: No negotiation occurred (eg TLS peer did not support ALPN).\n + * - <0: Invalid parameters (socket is not in use, or not an SSL socket).\n + * + * @see @ref SocketExample9 +*/ +sint8 get_alpn_index(SOCKET sock); + +/*! + *@fn sint8 get_error_detail(SOCKET sock, tstrSockErr *pstrErr); + * + * This function gets detail about a socket failure. The application can call this when notified + * of a socket failure via @ref SOCKET_MSG_CONNECT or @ref SOCKET_MSG_RECV. + * If used, it must be called before @ref close. + + * @param[in] sock + * Socket ID obtained by a call to @ref socket. + * + * @param[out] pstrErr + * Pointer to structure to be populated with the details of the socket failure. + * + * @return The function returns @ref SOCK_ERR_NO_ERROR if the request is successful. In this case pstrErr + * has been populated. + * The function returns a negative value if the request is not successful. In this case pstrErr + * has not been populated. +*/ +sint8 get_error_detail(SOCKET sock, tstrSockErr *pstrErr); /**@}*/ //PingFn #ifdef __cplusplus diff --git a/src/drivers/winc1500/src/m2m_ota.c b/src/drivers/winc1500/src/m2m_ota.c index 5f0bcc158..ba41ffd5a 100644 --- a/src/drivers/winc1500/src/m2m_ota.c +++ b/src/drivers/winc1500/src/m2m_ota.c @@ -4,7 +4,7 @@ * * \brief NMC1500 IoT OTA Interface. * - * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries. + * Copyright (c) 2016-2021 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * @@ -32,12 +32,11 @@ * */ - - /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* INCLUDES *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ #include +#include #include "common/include/nm_common.h" #include "driver/include/m2m_types.h" #include "driver/include/m2m_ota.h" @@ -48,11 +47,12 @@ INCLUDES /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* MACROS *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* DATA TYPES *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ static tpfOtaUpdateCb gpfOtaUpdateCb = NULL; -static tpfOtaNotifCb gpfOtaNotifCb = NULL; +static tpfOtaNotifCb gpfOtaNotifCb = NULL; static tpfFileGetCb gpfHFDGetCb = NULL; static tpfFileReadCb gpfHFDReadCb = NULL; static tpfFileEraseCb gpfHFDEraseCb = NULL; @@ -60,55 +60,62 @@ static tpfFileEraseCb gpfHFDEraseCb = NULL; typedef struct { uint32 u32Offset; uint32 u32Size; -}FileBlockDescriptor; +} FileBlockDescriptor; static FileBlockDescriptor FileBlock; static uint8 gu8CurrFileHandlerID = HFD_INVALID_HANDLER; +static uint8 gu8OTASSLOpts = 0; +static uint8 gu8SNIServerName[64] = {0}; -/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* +/* Map OTA SSL flags to SSL socket options */ +#define WIFI_OTA_SSL_FLAG_BYPASS_SERVER_AUTH NBIT1 +#define WIFI_OTA_SSL_FLAG_SNI_VALIDATION NBIT6 + +/*=*=*=*=*=*=*=*=*=*=*=*= +*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* FUNCTION PROTOTYPES *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ /** -* @fn m2m_ota_cb(uint8 u8OpCode, uint16 u16DataSize, uint32 u32Addr) -* @brief OTA call back function -* @param [in] u8OpCode -* HIF Opcode type. -* @param [in] u16DataSize -* HIF data length. -* @param [in] u32Addr -* HIF address. +@fn m2m_ota_cb(uint8 u8OpCode, uint16 u16DataSize, uint32 u32Addr) +@brief Internal OTA call back function. +@param[in] u8OpCode + HIF Opcode type. +@param[in] u16DataSize + HIF data length. +@param[in] u32Addr + HIF address. */ static void m2m_ota_cb(uint8 u8OpCode, uint16 u16DataSize, uint32 u32Addr) { - sint8 s8Ret = M2M_SUCCESS; - if(u8OpCode == M2M_OTA_RESP_NOTIF_UPDATE_INFO) - { - tstrOtaUpdateInfo strOtaUpdateInfo; - m2m_memset((uint8*)&strOtaUpdateInfo,0,sizeof(tstrOtaUpdateInfo)); - s8Ret = hif_receive(u32Addr,(uint8*)&strOtaUpdateInfo,sizeof(tstrOtaUpdateInfo),0); - if(s8Ret == M2M_SUCCESS) - { - if(gpfOtaNotifCb) - gpfOtaNotifCb(&strOtaUpdateInfo); - } - } - else if (u8OpCode == M2M_OTA_RESP_UPDATE_STATUS) - { - tstrOtaUpdateStatusResp strOtaUpdateStatusResp; - m2m_memset((uint8*)&strOtaUpdateStatusResp,0,sizeof(tstrOtaUpdateStatusResp)); - s8Ret = hif_receive(u32Addr, (uint8*) &strOtaUpdateStatusResp,sizeof(tstrOtaUpdateStatusResp), 0); - if(s8Ret == M2M_SUCCESS) - { - if(gpfOtaUpdateCb) - gpfOtaUpdateCb(strOtaUpdateStatusResp.u8OtaUpdateStatusType,strOtaUpdateStatusResp.u8OtaUpdateStatus); - } - } - else if (u8OpCode == M2M_OTA_RESP_HOST_FILE_STATUS) + sint8 s8Ret = M2M_SUCCESS; + if(u8OpCode == M2M_OTA_RESP_NOTIF_UPDATE_INFO) + { + tstrOtaUpdateInfo strOtaUpdateInfo; + m2m_memset((uint8 *)&strOtaUpdateInfo, 0, sizeof(tstrOtaUpdateInfo)); + s8Ret = hif_receive(u32Addr, (uint8 *)&strOtaUpdateInfo, sizeof(tstrOtaUpdateInfo), 0); + if(s8Ret == M2M_SUCCESS) + { + if(gpfOtaNotifCb) + gpfOtaNotifCb(&strOtaUpdateInfo); + } + } + else if(u8OpCode == M2M_OTA_RESP_UPDATE_STATUS) + { + tstrOtaUpdateStatusResp strOtaUpdateStatusResp; + m2m_memset((uint8 *)&strOtaUpdateStatusResp, 0, sizeof(tstrOtaUpdateStatusResp)); + s8Ret = hif_receive(u32Addr, (uint8 *) &strOtaUpdateStatusResp, sizeof(tstrOtaUpdateStatusResp), 0); + if(s8Ret == M2M_SUCCESS) + { + if(gpfOtaUpdateCb) + gpfOtaUpdateCb(strOtaUpdateStatusResp.u8OtaUpdateStatusType, strOtaUpdateStatusResp.u8OtaUpdateStatus); + } + } + else if(u8OpCode == M2M_OTA_RESP_HOST_FILE_STATUS) { tstrOtaHostFileGetStatusResp strOtaHostFileGetStatusResp = {0}; - s8Ret = hif_receive(u32Addr, (uint8*)&strOtaHostFileGetStatusResp, sizeof(tstrOtaHostFileGetStatusResp), 1); + s8Ret = hif_receive(u32Addr, (uint8 *)&strOtaHostFileGetStatusResp, sizeof(tstrOtaHostFileGetStatusResp), 1); if(M2M_SUCCESS == s8Ret) { if(strOtaHostFileGetStatusResp.u8OtaFileGetStatus == OTA_STATUS_SUCCESS) { @@ -116,10 +123,10 @@ static void m2m_ota_cb(uint8 u8OpCode, uint16 u16DataSize, uint32 u32Addr) } } } - else if (u8OpCode == M2M_OTA_RESP_HOST_FILE_DOWNLOAD) + else if(u8OpCode == M2M_OTA_RESP_HOST_FILE_DOWNLOAD) { tstrOtaHostFileGetStatusResp strOtaHostFileGetStatusResp = {0}; - s8Ret = hif_receive(u32Addr, (uint8*)&strOtaHostFileGetStatusResp, sizeof(tstrOtaHostFileGetStatusResp), 1); + s8Ret = hif_receive(u32Addr, (uint8 *)&strOtaHostFileGetStatusResp, sizeof(tstrOtaHostFileGetStatusResp), 1); if(M2M_SUCCESS == s8Ret) { if(strOtaHostFileGetStatusResp.u8OtaFileGetStatus == OTA_STATUS_SUCCESS) { @@ -133,19 +140,19 @@ static void m2m_ota_cb(uint8 u8OpCode, uint16 u16DataSize, uint32 u32Addr) } } } - else if (u8OpCode == M2M_OTA_RESP_HOST_FILE_READ) + else if(u8OpCode == M2M_OTA_RESP_HOST_FILE_READ) { tstrOtaHostFileReadStatusResp strOtaHostFileReadStatusResp; - m2m_memset((uint8*)&strOtaHostFileReadStatusResp, 0, sizeof(tstrOtaHostFileReadStatusResp)); - s8Ret = hif_receive(u32Addr, (uint8*)&strOtaHostFileReadStatusResp, sizeof(tstrOtaHostFileReadStatusResp), 1); + m2m_memset((uint8 *)&strOtaHostFileReadStatusResp, 0, sizeof(tstrOtaHostFileReadStatusResp)); + s8Ret = hif_receive(u32Addr, (uint8 *)&strOtaHostFileReadStatusResp, sizeof(tstrOtaHostFileReadStatusResp), 1); if(M2M_SUCCESS == s8Ret) if(gpfHFDReadCb) - gpfHFDReadCb(strOtaHostFileReadStatusResp.u8OtaFileReadStatus, strOtaHostFileReadStatusResp.pFileBuf, strOtaHostFileReadStatusResp.FileBlockSz); - } - else if (u8OpCode == M2M_OTA_RESP_HOST_FILE_ERASE) + gpfHFDReadCb(strOtaHostFileReadStatusResp.u8OtaFileReadStatus, strOtaHostFileReadStatusResp.pFileBuf, strOtaHostFileReadStatusResp.u16FileBlockSz); + } + else if(u8OpCode == M2M_OTA_RESP_HOST_FILE_ERASE) { tstrOtaHostFileEraseStatusResp strOtaHostFileEraseStatusResp = {0}; - s8Ret = hif_receive(u32Addr, (uint8*)&strOtaHostFileEraseStatusResp, sizeof(tstrOtaHostFileEraseStatusResp), 1); + s8Ret = hif_receive(u32Addr, (uint8 *)&strOtaHostFileEraseStatusResp, sizeof(tstrOtaHostFileEraseStatusResp), 1); if(M2M_SUCCESS == s8Ret) { if(gpfHFDEraseCb) @@ -155,277 +162,180 @@ static void m2m_ota_cb(uint8 u8OpCode, uint16 u16DataSize, uint32 u32Addr) } } } - else - { - M2M_ERR("Invalid OTA resp %d ?\n",u8OpCode); - } + else + { + M2M_ERR("Invalid OTA resp %d ?\n", u8OpCode); + } } + /*! -@fn \ - NMI_API sint8 m2m_ota_init(tpfOtaUpdateCb pfOtaUpdateCb, tpfOtaNotifCb pfOtaNotifCb); - -@brief - Initialize the OTA layer. - -@param [in] pfOtaUpdateCb - OTA Update callback function - -@param [in] pfOtaNotifCb - OTA Notify callback function - -@return - The function SHALL return 0 for success and a negative value otherwise. +@fn NMI_API sint8 m2m_ota_init(tpfOtaUpdateCb pfOtaUpdateCb, tpfOtaNotifCb pfOtaNotifCb) +@brief Initialize the OTA layer. +@param[in] pfOtaUpdateCb + OTA Update callback function. +@param[in] pfOtaNotifCb + OTA Notify callback function. +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. */ NMI_API sint8 m2m_ota_init(tpfOtaUpdateCb pfOtaUpdateCb, tpfOtaNotifCb pfOtaNotifCb) { - sint8 ret = M2M_SUCCESS; + sint8 ret = M2M_SUCCESS; + if(pfOtaUpdateCb) { + gpfOtaUpdateCb = pfOtaUpdateCb; + } else { + M2M_ERR("Invalid Ota update cb\n"); + } + if(pfOtaNotifCb) { + gpfOtaNotifCb = pfOtaNotifCb; + } else { + M2M_ERR("Invalid Ota notify cb\n"); + } - if(pfOtaUpdateCb){ - gpfOtaUpdateCb = pfOtaUpdateCb; - }else{ - M2M_ERR("Invalid Ota update cb\n"); - } - if(pfOtaNotifCb){ - gpfOtaNotifCb = pfOtaNotifCb; - }else{ - M2M_ERR("Invalid Ota notify cb\n"); - } + hif_register_cb(M2M_REQ_GROUP_OTA, m2m_ota_cb); + ret = hif_send(M2M_REQ_GROUP_OTA, M2M_OTA_REQ_HOST_FILE_STATUS, NULL, 0, NULL, 0, 0); - hif_register_cb(M2M_REQ_GROUP_OTA,m2m_ota_cb); - ret = hif_send(M2M_REQ_GROUP_OTA, M2M_OTA_REQ_HOST_FILE_STATUS, NULL, 0, NULL, 0, 0); - - return ret; + return ret; } + /*! -@fn \ - NMI_API sint8 m2m_ota_notif_set_url(uint8 * u8Url); - -@brief - Set the OTA url - -@param [in] u8Url - The url server address - -@return - The function SHALL return 0 for success and a negative value otherwise. +@fn NMI_API sint8 m2m_ota_notif_set_url(uint8 * u8Url) +@brief Set the OTA url. +@param[in] u8Url + The url server address. +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. */ -NMI_API sint8 m2m_ota_notif_set_url(uint8 * u8Url) +NMI_API sint8 m2m_ota_notif_set_url(uint8 *u8Url) { - sint8 ret = M2M_SUCCESS; - uint16 u16UrlSize = m2m_strlen(u8Url) + 1; - /*Todo: we may change it to data pkt but we need to give it higher priority - but the priority is not implemented yet in data pkt - */ - ret = hif_send(M2M_REQ_GROUP_OTA,M2M_OTA_REQ_NOTIF_SET_URL,u8Url,u16UrlSize,NULL,0,0); - return ret; - + sint8 ret = M2M_SUCCESS; + uint16 u16UrlSize = m2m_strlen(u8Url) + 1; + /*Todo: we may change it to data pkt but we need to give it higher priority + but the priority is not implemented yet in data pkt + */ + ret = hif_send(M2M_REQ_GROUP_OTA, M2M_OTA_REQ_NOTIF_SET_URL, u8Url, u16UrlSize, NULL, 0, 0); + return ret; } /*! -@fn \ - NMI_API sint8 m2m_ota_notif_check_for_update(void); - -@brief - check for ota update - -@return - The function SHALL return 0 for success and a negative value otherwise. +@fn NMI_API sint8 m2m_ota_notif_check_for_update(void) +@brief Check for OTA update. +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. */ NMI_API sint8 m2m_ota_notif_check_for_update(void) { - sint8 ret = M2M_SUCCESS; - ret = hif_send(M2M_REQ_GROUP_OTA,M2M_OTA_REQ_NOTIF_CHECK_FOR_UPDATE,NULL,0,NULL,0,0); - return ret; + sint8 ret = M2M_SUCCESS; + ret = hif_send(M2M_REQ_GROUP_OTA, M2M_OTA_REQ_NOTIF_CHECK_FOR_UPDATE, NULL, 0, NULL, 0, 0); + return ret; } /*! -@fn \ - NMI_API sint8 m2m_ota_notif_sched(uint32 u32Period); - -@brief - Schedule OTA update - -@param [in] u32Period - Period in days - -@return - The function SHALL return 0 for success and a negative value otherwise. +@fn NMI_API sint8 m2m_ota_notif_sched(uint32 u32Period) +@brief Schedule OTA update. +@param[in] u32Period + Period in days +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. */ NMI_API sint8 m2m_ota_notif_sched(uint32 u32Period) { - sint8 ret = M2M_SUCCESS; - ret = hif_send(M2M_REQ_GROUP_OTA,M2M_OTA_REQ_NOTIF_CHECK_FOR_UPDATE,NULL,0,NULL,0,0); - return ret; + sint8 ret = M2M_SUCCESS; + ret = hif_send(M2M_REQ_GROUP_OTA, M2M_OTA_REQ_NOTIF_CHECK_FOR_UPDATE, NULL, 0, NULL, 0, 0); + return ret; } /*! -@fn \ - NMI_API sint8 m2m_ota_start_update(unsigned char * pcDownloadUrl); - -@brief - Request OTA start update using the downloaded url - -@param [in] pcDownloadUrl - The download firmware url, you get it from device info - -@return - The function SHALL return 0 for success and a negative value otherwise. - +@fn NMI_API sint8 m2m_ota_start_update(unsigned char * pcDownloadUrl) +@brief Request OTA start update using the downloaded URL. +@param[in] pcDownloadUrl + The download firmware URL, you get it from device info. +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. */ -NMI_API sint8 m2m_ota_start_update(unsigned char * pcDownloadUrl) +NMI_API sint8 m2m_ota_start_update(unsigned char *pcDownloadUrl) { - sint8 ret = M2M_SUCCESS; - uint16 u16DurlSize = m2m_strlen(pcDownloadUrl) + 1; - /*Todo: we may change it to data pkt but we need to give it higher priority - but the priority is not implemented yet in data pkt - */ - ret = hif_send(M2M_REQ_GROUP_OTA,M2M_OTA_REQ_START_FW_UPDATE,pcDownloadUrl,u16DurlSize,NULL,0,0); - return ret; + tstrOtaStart strOtaStart; + uint16 u16UrlLen = m2m_strlen(pcDownloadUrl); + if (u16UrlLen >= 255) + { + return M2M_ERR_INVALID_ARG; + } + + m2m_memset((uint8*)&strOtaStart, 0, sizeof(strOtaStart)); + m2m_memcpy((uint8*)&strOtaStart.acUrl, (uint8*)pcDownloadUrl, u16UrlLen); + + /* Convert SSL options to flags */ + if (gu8OTASSLOpts & WIFI_OTA_SSL_OPT_BYPASS_SERVER_AUTH) + strOtaStart.u8SSLFlags |= WIFI_OTA_SSL_FLAG_BYPASS_SERVER_AUTH; + + if (gu8OTASSLOpts & WIFI_OTA_SSL_OPT_SNI_VALIDATION) + strOtaStart.u8SSLFlags |= WIFI_OTA_SSL_FLAG_SNI_VALIDATION; + + m2m_memcpy((uint8*)&strOtaStart.acSNI, gu8SNIServerName, m2m_strlen(gu8SNIServerName)); + + strOtaStart.u32TotalLen = sizeof(strOtaStart); + + return hif_send(M2M_REQ_GROUP_OTA, M2M_OTA_REQ_START_FW_UPDATE_V2 | M2M_REQ_DATA_PKT, (uint8*)&strOtaStart, strOtaStart.u32TotalLen, NULL, 0, 0); } /*! -@fn \ - NMI_API sint8 m2m_ota_rollback(void); - -@brief - Request OTA Rollback image - -@return - The function SHALL return 0 for success and a negative value otherwise. +@fn NMI_API sint8 m2m_ota_rollback(void) +@brief Request OTA Rollback image. +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. */ NMI_API sint8 m2m_ota_rollback(void) { - sint8 ret = M2M_SUCCESS; - ret = hif_send(M2M_REQ_GROUP_OTA,M2M_OTA_REQ_ROLLBACK_FW,NULL,0,NULL,0,0); - return ret; + sint8 ret = M2M_SUCCESS; + ret = hif_send(M2M_REQ_GROUP_OTA, M2M_OTA_REQ_ROLLBACK_FW, NULL, 0, NULL, 0, 0); + return ret; } /*! -@fn \ - NMI_API sint8 m2m_ota_abort(void); - -@brief - Request OTA Abort - -@return - The function SHALL return 0 for success and a negative value otherwise. +@fn NMI_API sint8 m2m_ota_abort(void) +@brief Request OTA Abort. +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. */ NMI_API sint8 m2m_ota_abort(void) { - sint8 ret = M2M_SUCCESS; - ret = hif_send(M2M_REQ_GROUP_OTA,M2M_OTA_REQ_ABORT,NULL,0,NULL,0,0); - return ret; + sint8 ret = M2M_SUCCESS; + ret = hif_send(M2M_REQ_GROUP_OTA, M2M_OTA_REQ_ABORT, NULL, 0, NULL, 0, 0); + return ret; } - /*! -@fn \ - NMI_API sint8 m2m_ota_switch_firmware(void); - -@brief - Switch to the upgraded Firmware - -@return - The function SHALL return 0 for success and a negative value otherwise. +@fn NMI_API sint8 m2m_ota_switch_firmware(void) +@brief Switch to the upgraded Firmware. +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. */ NMI_API sint8 m2m_ota_switch_firmware(void) { - sint8 ret = M2M_SUCCESS; - ret = hif_send(M2M_REQ_GROUP_OTA,M2M_OTA_REQ_SWITCH_FIRMWARE,NULL,0,NULL,0,0); - return ret; + sint8 ret = M2M_SUCCESS; + ret = hif_send(M2M_REQ_GROUP_OTA, M2M_OTA_REQ_SWITCH_FIRMWARE, NULL, 0, NULL, 0, 0); + return ret; } /*! -@fn \ - NMI_API sint8 m2m_ota_get_firmware_version(tstrM2mRev * pstrRev); - -@brief - Get the OTA Firmware version. - -@return - The function SHALL return 0 for success and a negative value otherwise. +@fn NMI_API sint8 m2m_ota_get_firmware_version(tstrM2mRev * pstrRev) +@brief Get the OTA Firmware version. +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. */ -NMI_API sint8 m2m_ota_get_firmware_version(tstrM2mRev * pstrRev) +NMI_API sint8 m2m_ota_get_firmware_version(tstrM2mRev *pstrRev) { - sint8 ret = M2M_SUCCESS; - ret = hif_chip_wake(); - if(ret == M2M_SUCCESS) - { - ret = nm_get_ota_firmware_info(pstrRev); - hif_chip_sleep(); - } - return ret; + sint8 ret = M2M_SUCCESS; + ret = hif_chip_wake(); + if(ret == M2M_SUCCESS) + { + ret = nm_get_ota_firmware_info(pstrRev); + hif_chip_sleep(); + } + return ret; } -#if 0 -#define M2M_OTA_FILE "../../../m2m_ota.dat" -NMI_API sint8 m2m_ota_test(void) -{ - uint32 page = 0; - uint8 buffer[1500]; - uint32 u32Sz = 0; - sint8 ret = M2M_SUCCESS; - FILE *fp =NULL; - fp = fopen(M2M_OTA_FILE,"rb"); - if(fp) - { - fseek(fp, 0L, SEEK_END); - u32Sz = ftell(fp); - fseek(fp, 0L, SEEK_SET); - - while(u32Sz > 0) - { - { - page = (rand()%1400); - - if((page<100)||(page>1400)) page = 1400; - } - - if(u32Sz>page) - { - u32Sz-=page; - } - else - { - page = u32Sz; - u32Sz = 0; - } - printf("page %d\n", (int)page); - fread(buffer,page,1,fp); - ret = hif_send(M2M_REQ_GROUP_OTA,M2M_OTA_REQ_TEST|M2M_REQ_DATA_PKT,NULL,0,(uint8*)&buffer,page,0); - if(ret != M2M_SUCCESS) - { - M2M_ERR("\n"); - } - nm_bsp_sleep(1); - } - - } - else - { - M2M_ERR("nO err\n"); - } - return ret; -} -#endif /*! -@fn \ - NMI_API m2m_ota_host_file_get(unsigned char *pcDownloadUrl, tpfFileGetCb pfHFDGetCb); - -@brief - Download a file from a remote location and store it in the WINC's Flash. - +@fn NMI_API m2m_ota_host_file_get(unsigned char *pcDownloadUrl, tpfFileGetCb pfHFDGetCb) +@brief Download a file from a remote location and store it in the WINC's Flash. @param[in] pcDownloadUrl Url pointing to the remote file. HTTP/HTTPS only. - @param[in] pfHFDGetCb Pointer to a callback to be executed when the download finishes. - -@return - Status of the get operation - -@warning 1. Providing a callback is mandatory. - 2. This functionality is only supported from WINC release 19.6.0 onwards. +@return Status of the get operation. +@warning Providing a callback is mandatory. */ NMI_API sint8 m2m_ota_host_file_get(unsigned char *pcDownloadUrl, tpfFileGetCb pfHFDGetCb) { @@ -457,28 +367,18 @@ EXIT: } /*! -@fn \ - NMI_API m2m_ota_host_file_read_hif(uint8 u8Handler, uint32 u32Offset, uint32 u32Size, tpfFileReadCb pfHFDReadCb); -@brief - Read a certain amount of bytes from a file in WINC's Flash using HIF transfer. - +@fn NMI_API m2m_ota_host_file_read_hif(uint8 u8Handler, uint32 u32Offset, uint32 u32Size, tpfFileReadCb pfHFDReadCb) +@brief Read a certain amount of bytes from a file in WINC's Flash using HIF transfer. @param[in] u8Handler ID of the file we are trying to read from. Must be valid. - @param[in] u32Offset Offset from start of the file to read from (in bytes). - @param[in] u32Size The amount of data to read (in bytes). - @param[in] pfHFDReadCb Callback to be executed when the read operation completes. - -@return - Status of the read operation - -@warning 1. Providing a callback is mandatory. - 2. This functionality is only supported from WINC release 19.6.0 onwards. +@return Status of the read operation. +@warning Providing a callback is mandatory. */ NMI_API sint8 m2m_ota_host_file_read_hif(uint8 u8Handler, uint32 u32Offset, uint32 u32Size, tpfFileReadCb pfHFDReadCb) { @@ -486,7 +386,7 @@ NMI_API sint8 m2m_ota_host_file_read_hif(uint8 u8Handler, uint32 u32Offset, uint FileBlock.u32Offset = u32Offset; FileBlock.u32Size = u32Size; - if((u8Handler != gu8CurrFileHandlerID) || (HFD_INVALID_HANDLER == gu8CurrFileHandlerID) || (NULL == pfHFDReadCb)) goto EXIT; + if((u8Handler != gu8CurrFileHandlerID) || (HFD_INVALID_HANDLER == gu8CurrFileHandlerID) || (NULL == pfHFDReadCb)) goto EXIT; s8Ret = hif_send(M2M_REQ_GROUP_OTA, M2M_OTA_REQ_HOST_FILE_READ, (uint8 *) &FileBlock, sizeof(FileBlockDescriptor), NULL, 0, 0); if(M2M_SUCCESS == s8Ret) @@ -496,38 +396,27 @@ EXIT: } /*! -@fn \ - NMI_API m2m_ota_host_file_read_spi(uint8 u8Handler, uint8 *pu8Buff, uint32 u32Offset, uint32 u32Size); -@brief - Read a certain amount of bytes from a file in WINC's Flash using SPI transfer. - +@fn NMI_API m2m_ota_host_file_read_spi(uint8 u8Handler, uint8 *pu8Buff, uint32 u32Offset, uint32 u32Size) +@brief Read a certain amount of bytes from a file in WINC's Flash using SPI transfer. @param[in] u8Handler ID of the file we are trying to read from. Must be valid. - @param[in] pu8Buff Pointer to a buffer to store the data being read. Must be valid. - @param[in] u32Offset Offset from start of the file to read from (in bytes). - @param[in] u32Size The amount of data to read (in Bytes). - -@return - Status of the read operation - -@warning 1. Before using m2m_ota_host_file_read_spi, the WINC needs to be put in a special - mode to allow for a safe access to the Flash. This can be done by calling - @ref m2m_wifi_download_mode or @ref m2m_wifi_reinit_hold before trying to read. - - 2. This functionality is only supported from WINC release 19.6.0 onwards. +@return Status of the read operation. +@warning Before using m2m_ota_host_file_read_spi, the WINC needs to be put in a special + mode to allow for a safe access to the Flash. This can be done by calling + @ref m2m_wifi_download_mode or @ref m2m_wifi_reinit_hold before trying to read. */ NMI_API sint8 m2m_ota_host_file_read_spi(uint8 u8Handler, uint8 *pu8Buff, uint32 u32Offset, uint32 u32Size) { static uint32 u32FlashHFDStart = 0; static uint32 u32FlashHFDSize = 0; sint8 s8Ret = M2M_ERR_INVALID_ARG; - if((u8Handler != gu8CurrFileHandlerID) || (HFD_INVALID_HANDLER == gu8CurrFileHandlerID) || (NULL == pu8Buff)) goto EXIT; + if((u8Handler == HFD_INVALID_HANDLER) || (NULL == pu8Buff)) goto EXIT; if(WIFI_STATE_INIT != m2m_wifi_get_state()) { @@ -542,9 +431,13 @@ NMI_API sint8 m2m_ota_host_file_read_spi(uint8 u8Handler, uint8 *pu8Buff, uint32 if(M2M_SUCCESS != s8Ret) goto EXIT; } + s8Ret = spi_flash_read(pu8Buff, u32FlashHFDStart, 4); + + if((M2M_SUCCESS != s8Ret) || (pu8Buff[0] != u8Handler)) goto EXIT; + if((u32Offset >= u32FlashHFDSize) || - (u32Size > u32FlashHFDSize) || - ((u32Offset + u32Size) >= u32FlashHFDSize)) + (u32Size > u32FlashHFDSize) || + ((u32Offset + u32Size) >= u32FlashHFDSize)) { s8Ret = M2M_ERR_FAIL; goto EXIT; @@ -560,20 +453,13 @@ EXIT: } /*! -@fn \ - NMI_API m2m_ota_host_file_erase(uint8 u8Handler, tpfFileEraseCb pfHFDEraseCb); -@brief - Erase any traces of an existing file, this means from host driver and WINC firmware. - +@fn NMI_API m2m_ota_host_file_erase(uint8 u8Handler, tpfFileEraseCb pfHFDEraseCb) +@brief Erase any traces of an existing file, this means from host driver and WINC firmware. @param[in] u8Handler ID of the file we are trying to erase. Must be valid. - @param[in] pfHFDEraseCb Pointer to callback to execute when the file erase in the WINC completes. - -@return - Status of the erase operation - +@return Status of the erase operation. @note Providing a callback is optional. If the current handler is invalid at this point, it means one of the three: 1. The file never existed; @@ -581,8 +467,6 @@ EXIT: 3. The request to get the file hasn't fully completed. For 1. and 2. there is no need to signal the WINC to erase the file in Flash. For 3. the Flash can't be erased while a file download is ongoing. - -@warning This functionality is only supported from WINC release 19.6.0 onwards. */ NMI_API sint8 m2m_ota_host_file_erase(uint8 u8Handler, tpfFileEraseCb pfHFDEraseCb) { @@ -597,3 +481,95 @@ NMI_API sint8 m2m_ota_host_file_erase(uint8 u8Handler, tpfFileEraseCb pfHFDErase EXIT: return s8Ret; } + +/*! +@fn NMI_API sint8 m2m_ota_set_ssl_option(tenuOTASSLOption enuOptionName, const void *pOptionValue, size_t OptionLen) +@brief Sets SSL related options for OTA via https connections +@param[in] enuOptionName +The SSL option to set, from the set defined in tenuOTASSLOption +@param[in] pOptionValue +Pointer to the option value to set. Either a pointer to a uint32 with the value of 0 or 1, or a pointer to a string for the +WIFI_OTA_SSL_OPT_SNI_SERVERNAME option. +@param[in] OptionLen +The size of the option referred to in pOptionValue +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. +*/ +NMI_API sint8 m2m_ota_set_ssl_option(tenuOTASSLOption enuOptionName, const void *pOptionValue, size_t OptionLen) +{ + if((pOptionValue == NULL) && (OptionLen > 0)) + return M2M_ERR_INVALID_ARG; + + switch(enuOptionName) + { + case WIFI_OTA_SSL_OPT_SNI_SERVERNAME: + if(OptionLen > 64) + return M2M_ERR_INVALID_ARG; + if (m2m_strlen((uint8*)pOptionValue)+1 != OptionLen) + return M2M_ERR_INVALID_ARG; + + m2m_memcpy(gu8SNIServerName, (uint8*)pOptionValue, OptionLen); + break; + + case WIFI_OTA_SSL_OPT_SNI_VALIDATION: + case WIFI_OTA_SSL_OPT_BYPASS_SERVER_AUTH: + if(OptionLen != sizeof(int)) + return M2M_ERR_INVALID_ARG; + switch(*(int*)pOptionValue) + { + case 1: + gu8OTASSLOpts |= enuOptionName; + break; + case 0: + gu8OTASSLOpts &= ~enuOptionName; + break; + default: + return M2M_ERR_INVALID_ARG; + } + break; + + default: + return M2M_ERR_INVALID_ARG; + } + return M2M_SUCCESS; +} + +/*! +@fn NMI_API sint8 m2m_ota_get_ssl_option(tenuOTASSLOption enuOptionName, void *pOptionValue, size_t *OptionLen) +@brief Gets the status of SSL related options for OTA via https connections +@param[in] enuOptionName +The SSL option to obtain the status of, from the set defined in tenuOTASSLOption +@param[in] pOptionValue +Pointer to the option value to be updated by the function. Either a pointer to a uint32, or a pointer to a buffer for the +WIFI_OTA_SSL_OPT_SNI_SERVERNAME option. +@param[in] OptionLen +A pointer to a size_t type variable which will be updated to contain the size of the returned option. +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. +*/ +NMI_API sint8 m2m_ota_get_ssl_option(tenuOTASSLOption enuOptionName, void *pOptionValue, size_t *pOptionLen) +{ + if((pOptionValue == NULL) || (pOptionLen == NULL)) + return M2M_ERR_INVALID_ARG; + + switch(enuOptionName) + { + case WIFI_OTA_SSL_OPT_SNI_VALIDATION: + case WIFI_OTA_SSL_OPT_BYPASS_SERVER_AUTH: + if(*pOptionLen < sizeof(int)) + return M2M_ERR_INVALID_ARG; + *pOptionLen = sizeof(int); + *(int*)pOptionValue = (gu8OTASSLOpts & enuOptionName) ? 1 : 0; + break; + case WIFI_OTA_SSL_OPT_SNI_SERVERNAME: + { + uint16 sni_len = m2m_strlen(gu8SNIServerName)+1; + if(*pOptionLen < sni_len) + return M2M_ERR_INVALID_ARG; + *pOptionLen = sni_len; + m2m_memcpy((uint8*)pOptionValue, gu8SNIServerName, sni_len); + } + break; + default: + return M2M_ERR_INVALID_ARG; + } + return M2M_SUCCESS; +} diff --git a/src/drivers/winc1500/src/m2m_periph.c b/src/drivers/winc1500/src/m2m_periph.c index b58d5c406..2738eebb6 100644 --- a/src/drivers/winc1500/src/m2m_periph.c +++ b/src/drivers/winc1500/src/m2m_periph.c @@ -4,7 +4,7 @@ * * \brief NMC1500 Peripherials Application Interface. * - * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries. + * Copyright (c) 2016-2021 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * @@ -32,7 +32,6 @@ * */ - /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* INCLUDES *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ @@ -42,7 +41,6 @@ INCLUDES #include "driver/include/m2m_hif.h" #ifdef CONF_PERIPH - /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* MACROS *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ @@ -56,95 +54,52 @@ DATA TYPES /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* STATIC FUNCTIONS *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ -static sint8 get_gpio_idx(uint8 u8GpioNum) -{ - if(u8GpioNum >= M2M_PERIPH_GPIO_MAX) return -1; - if(u8GpioNum == M2M_PERIPH_GPIO15) { return 15; - } else if(u8GpioNum == M2M_PERIPH_GPIO16) { return 16; - } else if(u8GpioNum == M2M_PERIPH_GPIO18) { return 18; - } else if(u8GpioNum == M2M_PERIPH_GPIO3) { return 3; - } else if(u8GpioNum == M2M_PERIPH_GPIO4) { return 4; - } else if(u8GpioNum == M2M_PERIPH_GPIO5) { return 5; - } else if(u8GpioNum == M2M_PERIPH_GPIO6) { return 6; - } else { - return -2; - } -} /* * GPIO read/write skeleton with wakeup/sleep capability. */ static sint8 gpio_ioctl(uint8 op, uint8 u8GpioNum, uint8 u8InVal, uint8 * pu8OutVal) { - sint8 ret, gpio; + sint8 s8Ret = hif_chip_wake(); + if(s8Ret != M2M_SUCCESS) goto _EXIT; - ret = hif_chip_wake(); - if(ret != M2M_SUCCESS) goto _EXIT; + if(u8GpioNum >= M2M_PERIPH_GPIO_MAX) goto _EXIT1; - gpio = get_gpio_idx(u8GpioNum); - if(gpio < 0) goto _EXIT1; - - if(op == GPIO_OP_DIR) { - ret = set_gpio_dir((uint8)gpio, u8InVal); - } else if(op == GPIO_OP_SET) { - ret = set_gpio_val((uint8)gpio, u8InVal); - } else if(op == GPIO_OP_GET) { - ret = get_gpio_val((uint8)gpio, pu8OutVal); - } - if(ret != M2M_SUCCESS) goto _EXIT1; + if(op == GPIO_OP_DIR) { + s8Ret = set_gpio_dir(u8GpioNum, u8InVal); + } else if(op == GPIO_OP_SET) { + s8Ret = set_gpio_val(u8GpioNum, u8InVal); + } else if(op == GPIO_OP_GET) { + s8Ret = get_gpio_val(u8GpioNum, pu8OutVal); + } _EXIT1: - ret = hif_chip_sleep(); + s8Ret = hif_chip_sleep(); + _EXIT: - return ret; + return s8Ret; } + /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* FUNCTION IMPLEMENTATION *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ - -sint8 m2m_periph_init(tstrPerphInitParam * param) -{ - return M2M_SUCCESS; -} - sint8 m2m_periph_gpio_set_dir(uint8 u8GpioNum, uint8 u8GpioDir) { - return gpio_ioctl(GPIO_OP_DIR, u8GpioNum, u8GpioDir, NULL); + return gpio_ioctl(GPIO_OP_DIR, u8GpioNum, u8GpioDir, NULL); } sint8 m2m_periph_gpio_set_val(uint8 u8GpioNum, uint8 u8GpioVal) { - return gpio_ioctl(GPIO_OP_SET, u8GpioNum, u8GpioVal, NULL); + return gpio_ioctl(GPIO_OP_SET, u8GpioNum, u8GpioVal, NULL); } sint8 m2m_periph_gpio_get_val(uint8 u8GpioNum, uint8 * pu8GpioVal) { - return gpio_ioctl(GPIO_OP_GET, u8GpioNum, 0, pu8GpioVal); + return gpio_ioctl(GPIO_OP_GET, u8GpioNum, 0, pu8GpioVal); } -sint8 m2m_periph_gpio_pullup_ctrl(uint8 u8GpioNum, uint8 u8PullupEn) -{ - return M2M_SUCCESS; -} - -sint8 m2m_periph_i2c_master_init(tstrI2cMasterInitParam * param) -{ - return M2M_SUCCESS; -} - -sint8 m2m_periph_i2c_master_write(uint8 u8SlaveAddr, uint8 * pu8Buf, uint16 u16BufLen, uint8 flags) -{ - return M2M_SUCCESS; -} - -sint8 m2m_periph_i2c_master_read(uint8 u8SlaveAddr, uint8 * pu8Buf, uint16 u16BufLen, uint16 * pu16ReadLen, uint8 flags) -{ - return M2M_SUCCESS; -} - - sint8 m2m_periph_pullup_ctrl(uint32 pinmask, uint8 enable) { - return pullup_ctrl(pinmask, enable); + return pullup_ctrl(pinmask, enable); } #endif /* CONF_PERIPH */ diff --git a/src/drivers/winc1500/src/m2m_ssl.c b/src/drivers/winc1500/src/m2m_ssl.c index 79b0dfb48..eb861e960 100644 --- a/src/drivers/winc1500/src/m2m_ssl.c +++ b/src/drivers/winc1500/src/m2m_ssl.c @@ -4,7 +4,7 @@ * * \brief This module contains M2M Wi-Fi SSL APIs implementation. * - * Copyright (c) 2017-2018 Microchip Technology Inc. and its subsidiaries. + * Copyright (c) 2017-2021 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * @@ -32,8 +32,6 @@ * */ - - /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* INCLUDES *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ @@ -41,10 +39,6 @@ INCLUDES #include "driver/include/m2m_ssl.h" #include "driver/include/m2m_hif.h" #include "driver/include/nmasic.h" -#define min(a,b) \ - ({ __typeof__ (a) _a = (a); \ - __typeof__ (b) _b = (b); \ - _a < _b ? _a : _b; }) /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* MACROS @@ -53,8 +47,8 @@ MACROS /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* DATA TYPES *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ -static tpfAppSSLCb gpfAppSSLCb = NULL; -static uint32 gu32HIFAddr = 0; +static tpfAppSSLCb gpfAppSSLCb = NULL; +static uint32 gu32HIFAddr = 0; static tenuTlsFlashStatus genuStatus = TLS_FLASH_ERR_UNKNOWN; /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* @@ -62,348 +56,443 @@ FUNCTION PROTOTYPES *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ /*! - @fn \ m2m_ssl_cb(uint8 u8OpCode, uint16 u16DataSize, uint32 u32Addr) - @brief SSL callback function - @param [in] u8OpCode - HIF Opcode type. - @param [in] u16DataSize - HIF data length. - @param [in] u32Addr - HIF address. +@fn void m2m_ssl_cb(uint8 u8OpCode, uint16 u16DataSize, uint32 u32Addr) +@brief Internal SSL callback function. +@param [in] u8OpCode + HIF Opcode type. +@param [in] u16DataSize + HIF data length. +@param [in] u32Addr + HIF address. */ static void m2m_ssl_cb(uint8 u8OpCode, uint16 u16DataSize, uint32 u32Addr) { - sint8 s8tmp = M2M_SUCCESS; - switch(u8OpCode) - { - case M2M_SSL_REQ_ECC: - { - tstrEccReqInfo strEccREQ; - s8tmp = hif_receive(u32Addr, (uint8*)&strEccREQ, sizeof(tstrEccReqInfo), 0); - if(s8tmp == M2M_SUCCESS) - { - if (gpfAppSSLCb) - { - gu32HIFAddr = u32Addr + sizeof(tstrEccReqInfo); - gpfAppSSLCb(M2M_SSL_REQ_ECC, &strEccREQ); - } - } - } - break; - case M2M_SSL_RESP_SET_CS_LIST: - { - tstrSslSetActiveCsList strCsList; - s8tmp = hif_receive(u32Addr, (uint8*)&strCsList, sizeof(tstrSslSetActiveCsList), 0); - if(s8tmp == M2M_SUCCESS) - { - if (gpfAppSSLCb) - gpfAppSSLCb(M2M_SSL_RESP_SET_CS_LIST, &strCsList); - } - } - break; - case M2M_SSL_RESP_WRITE_OWN_CERTS: - { - tstrTlsSrvChunkHdr strTlsSrvChunkRsp; - uint8 bCallApp = 1; + sint8 s8tmp = M2M_SUCCESS; + switch(u8OpCode) + { + case M2M_SSL_REQ_ECC: + { + tstrEccReqInfo strEccREQ; + s8tmp = hif_receive(u32Addr, (uint8 *)&strEccREQ, sizeof(tstrEccReqInfo), 0); + if(s8tmp == M2M_SUCCESS) + { + if(gpfAppSSLCb) + { + gu32HIFAddr = u32Addr + sizeof(tstrEccReqInfo); + gpfAppSSLCb(M2M_SSL_REQ_ECC, &strEccREQ); + } + } + } + break; + case M2M_SSL_RESP_SET_CS_LIST: + { + tstrSslSetActiveCsList strCsList; + s8tmp = hif_receive(u32Addr, (uint8 *)&strCsList, sizeof(tstrSslSetActiveCsList), 0); + if(s8tmp == M2M_SUCCESS) + { + if(gpfAppSSLCb) + gpfAppSSLCb(M2M_SSL_RESP_SET_CS_LIST, &strCsList); + } + } + break; + case M2M_SSL_RESP_WRITE_OWN_CERTS: + { + tstrTlsSrvChunkHdr strTlsSrvChunkRsp; + uint8 bCallApp = 1; - s8tmp = hif_receive(u32Addr, (uint8*)&strTlsSrvChunkRsp, sizeof(tstrTlsSrvChunkHdr), 0); - if(s8tmp == M2M_SUCCESS) - { - uint16 offset = strTlsSrvChunkRsp.u16Offset32; - uint16 chunk_size = strTlsSrvChunkRsp.u16Size32; - uint16 total_size = strTlsSrvChunkRsp.u16TotalSize32; - tenuTlsFlashStatus status = (tenuTlsFlashStatus)(strTlsSrvChunkRsp.u16Sig); - - /* If first chunk, reset status. */ - if (offset == 0) - genuStatus = TLS_FLASH_OK_NO_CHANGE; - /* Only send status to app when processing last chunk. */ - if (offset + chunk_size != total_size) - bCallApp = 0; + s8tmp = hif_receive(u32Addr, (uint8 *)&strTlsSrvChunkRsp, sizeof(tstrTlsSrvChunkHdr), 0); + if(s8tmp == M2M_SUCCESS) + { + uint16 offset = strTlsSrvChunkRsp.u16Offset32; + uint16 chunk_size = strTlsSrvChunkRsp.u16Size32; + uint16 total_size = strTlsSrvChunkRsp.u16TotalSize32; + tenuTlsFlashStatus status = (tenuTlsFlashStatus)(strTlsSrvChunkRsp.u16Sig); - switch (status) - { - case TLS_FLASH_OK: - // Good flash write. Update status if no errors yet. - if (genuStatus == TLS_FLASH_OK_NO_CHANGE) - genuStatus = status; - break; - case TLS_FLASH_OK_NO_CHANGE: - // No change, don't update status. - break; - case TLS_FLASH_ERR_CORRUPT: - // Corrupt. Always update status. - genuStatus = status; - break; - case TLS_FLASH_ERR_NO_CHANGE: - // Failed flash write. Update status if no more serious error. - if ((genuStatus != TLS_FLASH_ERR_CORRUPT) && (genuStatus != TLS_FLASH_ERR_UNKNOWN)) - genuStatus = status; - break; - default: - // Don't expect any other case. Ensure we don't mask a previous corrupt error. - if (genuStatus != TLS_FLASH_ERR_CORRUPT) - genuStatus = TLS_FLASH_ERR_UNKNOWN; - break; - } - } - if (bCallApp && gpfAppSSLCb) - gpfAppSSLCb(M2M_SSL_RESP_WRITE_OWN_CERTS, &genuStatus); - } - break; - } - if(s8tmp != M2M_SUCCESS) - { - M2M_ERR("Error receiving SSL from the HIF\n"); - } -} + /* If first chunk, reset status. */ + if(offset == 0) + genuStatus = TLS_FLASH_OK_NO_CHANGE; + /* Only send status to app when processing last chunk. */ + if(offset + chunk_size != total_size) + bCallApp = 0; - -/*! - @fn \ m2m_ssl_handshake_rsp(tstrEccReqInfo* strECCResp, uint8* pu8RspDataBuff, uint16 u16RspDataSz) - @brief Sends ECC responses to the WINC - @param [in] strECCResp - ECC Response struct. - @param [in] pu8RspDataBuffe - Pointer of the response data to be sent. - @param [in] u16RspDataSz - Response data size. - @return The function SHALL return 0 for success and a negative value otherwise. -*/ -NMI_API sint8 m2m_ssl_handshake_rsp(tstrEccReqInfo* strECCResp, uint8* pu8RspDataBuff, uint16 u16RspDataSz) -{ - sint8 s8Ret = M2M_SUCCESS; - - s8Ret = hif_send(M2M_REQ_GROUP_SSL, (M2M_SSL_RESP_ECC | M2M_REQ_DATA_PKT), (uint8*)strECCResp, sizeof(tstrEccReqInfo), pu8RspDataBuff, u16RspDataSz, sizeof(tstrEccReqInfo)); - - return s8Ret; + switch(status) + { + case TLS_FLASH_OK: + // Good flash write. Update status if no errors yet. + if(genuStatus == TLS_FLASH_OK_NO_CHANGE) + genuStatus = status; + break; + case TLS_FLASH_OK_NO_CHANGE: + // No change, don't update status. + break; + case TLS_FLASH_ERR_CORRUPT: + // Corrupt. Always update status. + genuStatus = status; + break; + case TLS_FLASH_ERR_NO_CHANGE: + // Failed flash write. Update status if no more serious error. + if((genuStatus != TLS_FLASH_ERR_CORRUPT) && (genuStatus != TLS_FLASH_ERR_UNKNOWN)) + genuStatus = status; + break; + default: + // Don't expect any other case. Ensure we don't mask a previous corrupt error. + if(genuStatus != TLS_FLASH_ERR_CORRUPT) + genuStatus = TLS_FLASH_ERR_UNKNOWN; + break; + } + } + if(bCallApp && gpfAppSSLCb) + gpfAppSSLCb(M2M_SSL_RESP_WRITE_OWN_CERTS, &genuStatus); + } + break; + } + if(s8tmp != M2M_SUCCESS) + { + M2M_ERR("Error receiving SSL from the HIF\n"); + } } /*! - @fn \ m2m_ssl_send_certs_to_winc(uint8* sector_buffer, uint32 sector_size) - @brief Sends certificates to the WINC - @param [in] pu8Buffer - Pointer to the certificates. - @param [in] u32BufferSz - Size of the certificates. - @return The function SHALL return 0 for success and a negative value otherwise. -*/ -NMI_API sint8 m2m_ssl_send_certs_to_winc(uint8* pu8Buffer, uint32 u32BufferSz) -{ - sint8 s8Ret = M2M_SUCCESS; - #define TXLIMIT (256 * 6) - - if(u32BufferSz <= TXLIMIT) - { - // set chunk header for one chunk - tstrTlsSrvChunkHdr *pchkhdr = (tstrTlsSrvChunkHdr *)pu8Buffer; - pchkhdr->u16Sig = TLS_CERTS_CHUNKED_SIG_VALUE; - pchkhdr->u16TotalSize32 = (u32BufferSz + 3) >> 2; - pchkhdr->u16Offset32 = 0; - pchkhdr->u16Size32 = (u32BufferSz + 3) >> 2; - s8Ret = hif_send(M2M_REQ_GROUP_SSL, (M2M_SSL_REQ_WRITE_OWN_CERTS | M2M_REQ_DATA_PKT), NULL, 0, pu8Buffer, u32BufferSz, 0); - M2M_INFO("Transferred %lu bytes of cert data NON-CHUNKED\n", u32BufferSz); - } - else - { - // chunk it - // We are sneaking in a header - tstrTlsSrvChunkHdr - #define CHUNKHDRSZ (sizeof(tstrTlsSrvChunkHdr)) - #define CHUNKSZ (TXLIMIT - 256) // divisible by 4 - uint8 saveblob[CHUNKHDRSZ]; - uint32 ofs = 0; - uint32 thischunksz = 0; - - // first is special - over writing our header - m2m_memcpy(saveblob, &pu8Buffer[ofs], CHUNKHDRSZ); - thischunksz = min(CHUNKSZ,u32BufferSz-ofs); // no need to round up to quad words this time - - tstrTlsSrvChunkHdr* pchkhdr = (tstrTlsSrvChunkHdr*)&pu8Buffer[ofs]; - pchkhdr->u16Sig = TLS_CERTS_CHUNKED_SIG_VALUE; - pchkhdr->u16TotalSize32 = ((u32BufferSz + 3) >> 2); - pchkhdr->u16Offset32 = ((ofs + 3) >> 2); - pchkhdr->u16Size32 = ((thischunksz + 3) >> 2); - s8Ret = hif_send(M2M_REQ_GROUP_SSL, (M2M_SSL_REQ_WRITE_OWN_CERTS | M2M_REQ_DATA_PKT), NULL, 0, &pu8Buffer[ofs], thischunksz, 0); - M2M_INFO("Transferred %lu bytes of cert data CHUNKED to offset %lu total %lu\n", thischunksz, ofs, u32BufferSz); - m2m_memcpy(&pu8Buffer[ofs], saveblob, CHUNKHDRSZ); - ofs += thischunksz; - - while (ofs < u32BufferSz) - { - // Subsequent chunks write header before and send a little more - m2m_memcpy(saveblob, &pu8Buffer[ofs-CHUNKHDRSZ], CHUNKHDRSZ); - thischunksz = min(CHUNKSZ,u32BufferSz-ofs); - thischunksz = (thischunksz + 3) & 0xFFFFFFFC; // needs to round up to quad word length - pchkhdr = (tstrTlsSrvChunkHdr*)&pu8Buffer[ofs - CHUNKHDRSZ]; - pchkhdr->u16Sig = TLS_CERTS_CHUNKED_SIG_VALUE; - pchkhdr->u16TotalSize32 = ((u32BufferSz + 3) >> 2); - pchkhdr->u16Offset32 = ((ofs + 3) >> 2); - pchkhdr->u16Size32 = ((thischunksz + 3) >> 2); - s8Ret = hif_send(M2M_REQ_GROUP_SSL, (M2M_SSL_REQ_WRITE_OWN_CERTS | M2M_REQ_DATA_PKT), NULL, 0, &pu8Buffer[ofs - CHUNKHDRSZ], thischunksz + CHUNKHDRSZ, 0); - M2M_INFO("Transferred %lu bytes of cert data CHUNKED to offset %lu total %lu\n", thischunksz, ofs, u32BufferSz); - m2m_memcpy(&pu8Buffer[ofs - CHUNKHDRSZ], saveblob, CHUNKHDRSZ); - ofs += thischunksz; - } - } - - return s8Ret; -} - -/*! - @fn \ m2m_ssl_retrieve_cert(uint32 u32ReadAddr, uint16* pu16CurveType, uint8* pu8Hash, uint8* pu8Sig, tstrECPoint* pu8Key) - @brief Retrieve the certificate to be verified from the WINC - @param [in] pu16CurveType - Pointer to the certificate curve type. - @param [in] pu8Hash - Pointer to the certificate hash. - @param [in] pu8Sig - Pointer to the certificate signature. - @param [in] pu8Key - Pointer to the certificate Key. - @return The function SHALL return 0 for success and a negative value otherwise. -*/ -NMI_API sint8 m2m_ssl_retrieve_cert(uint16* pu16CurveType, uint8* pu8Hash, uint8* pu8Sig, tstrECPoint* pu8Key) -{ - uint8 bSetRxDone = 1; - uint16 u16HashSz, u16SigSz, u16KeySz; - sint8 s8Ret = M2M_SUCCESS; - - if(gu32HIFAddr == 0) return M2M_ERR_FAIL; - - if(hif_receive(gu32HIFAddr, (uint8*)pu16CurveType, 2, 0) != M2M_SUCCESS) goto __ERR; - gu32HIFAddr += 2; - - if(hif_receive(gu32HIFAddr, (uint8*)&u16KeySz, 2, 0) != M2M_SUCCESS) goto __ERR; - gu32HIFAddr += 2; - - if(hif_receive(gu32HIFAddr, (uint8*)&u16HashSz, 2, 0) != M2M_SUCCESS) goto __ERR; - gu32HIFAddr += 2; - - if(hif_receive(gu32HIFAddr, (uint8*)&u16SigSz, 2, 0) != M2M_SUCCESS) goto __ERR; - gu32HIFAddr += 2; - - (*pu16CurveType)= _htons((*pu16CurveType)); - pu8Key->u16Size = _htons(u16KeySz); - u16HashSz = _htons(u16HashSz); - u16SigSz = _htons(u16SigSz); - - if(hif_receive(gu32HIFAddr, pu8Key->X, pu8Key->u16Size * 2, 0) != M2M_SUCCESS) goto __ERR; - gu32HIFAddr += (pu8Key->u16Size * 2); - - if(hif_receive(gu32HIFAddr, pu8Hash, u16HashSz, 0) != M2M_SUCCESS) goto __ERR; - gu32HIFAddr += u16HashSz; - - if(hif_receive(gu32HIFAddr, pu8Sig, u16SigSz, 0) != M2M_SUCCESS) goto __ERR; - gu32HIFAddr += u16SigSz; - - bSetRxDone = 0; - -__ERR: - if(bSetRxDone) - { - s8Ret = M2M_ERR_FAIL; - hif_receive(0, NULL, 0, 1); - } - return s8Ret; -} - -/*! - @fn \ m2m_ssl_retrieve_hash(uint32 u32ReadAddr, uint8* pu8Hash, uint16 u16HashSz) - @brief Retrieve the certificate hash - @param [in] pu8Hash - Pointer to the certificate hash. - @param [in] u16HashSz - Hash size. - @return The function SHALL return 0 for success and a negative value otherwise. -*/ -NMI_API sint8 m2m_ssl_retrieve_hash(uint8* pu8Hash, uint16 u16HashSz) -{ - uint8 bSetRxDone = 1; - sint8 s8Ret = M2M_SUCCESS; - - if(gu32HIFAddr == 0) return M2M_ERR_FAIL; - - if(hif_receive(gu32HIFAddr, pu8Hash, u16HashSz, 0) != M2M_SUCCESS) goto __ERR; - - bSetRxDone = 0; - -__ERR: - if(bSetRxDone) - { - s8Ret = M2M_ERR_FAIL; - hif_receive(0, NULL, 0, 1); - } - return s8Ret; -} - -/*! - @fn \ m2m_ssl_stop_processing_certs(void) - @brief Stops receiving from the HIF -*/ -NMI_API void m2m_ssl_stop_processing_certs(void) -{ - hif_receive(0, NULL, 0, 1); -} - -/*! - @fn \ m2m_ssl_ecc_process_done(void) - @brief Stops receiving from the HIF -*/ -NMI_API void m2m_ssl_ecc_process_done(void) -{ - gu32HIFAddr = 0; -} - -/*! -@fn \ - m2m_ssl_set_active_ciphersuites(uint32 u32SslCsBMP); - Override the default Active SSL ciphers in the SSL module with a certain combination selected by the caller in the form of - a bitmap containing the required ciphers to be on. - There is no need to call this function if the application will not change the default ciphersuites. - -@param [in] u32SslCsBMP - Bitmap containing the desired ciphers to be enabled for the SSL module. The ciphersuites are defined in - @ref SSLCipherSuiteID. - The default ciphersuites are all ciphersuites supported by the firmware with the exception of ECC ciphersuites. - The caller can override the default with any desired combination, except for combinations involving both RSA - and ECC; if any RSA ciphersuite is enabled, then firmware will disable all ECC ciphersuites. - If u32SslCsBMP does not contain any ciphersuites supported by firmware, then the current active list will not - be changed. - -@return - - [SOCK_ERR_NO_ERROR](@ref SOCK_ERR_NO_ERROR) - - [SOCK_ERR_INVALID_ARG](@ref SOCK_ERR_INVALID_ARG) -*/ -sint8 m2m_ssl_set_active_ciphersuites(uint32 u32SslCsBMP) -{ - sint8 s8Ret = M2M_SUCCESS; - tstrSslSetActiveCsList strCsList; - - strCsList.u32CsBMP = u32SslCsBMP; - s8Ret = hif_send(M2M_REQ_GROUP_SSL, M2M_SSL_REQ_SET_CS_LIST, (uint8*)&strCsList, sizeof(tstrSslSetActiveCsList), NULL, 0, 0); - - return s8Ret; -} - -/*! - @fn \ m2m_ssl_init(tpfAppSslCb pfAppSslCb); - @brief Initializes the SSL layer. - @param [in] pfAppSslCb - Application SSL callback function. - @return The function SHALL return 0 for success and a negative value otherwise. +@fn NMI_API sint8 m2m_ssl_init(tpfAppSSLCb pfAppSSLCb) +@brief Initializes the SSL layer. +@param [in] pfAppSslCb + Application SSL callback function. +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. */ NMI_API sint8 m2m_ssl_init(tpfAppSSLCb pfAppSSLCb) { - sint8 s8Ret = M2M_SUCCESS; - - gpfAppSSLCb = pfAppSSLCb; - gu32HIFAddr = 0; - genuStatus = TLS_FLASH_ERR_UNKNOWN; - - s8Ret = hif_register_cb(M2M_REQ_GROUP_SSL,m2m_ssl_cb); - if (s8Ret != M2M_SUCCESS) - { - M2M_ERR("hif_register_cb() failed with ret=%d", s8Ret); - } - return s8Ret; + sint8 s8Ret = M2M_SUCCESS; + gpfAppSSLCb = pfAppSSLCb; + gu32HIFAddr = 0; + genuStatus = TLS_FLASH_ERR_UNKNOWN; + s8Ret = hif_register_cb(M2M_REQ_GROUP_SSL, m2m_ssl_cb); + if(s8Ret != M2M_SUCCESS) + { + M2M_ERR("hif_register_cb() failed with ret=%d", s8Ret); + } + return s8Ret; +} + +/*! +@fn NMI_API sint8 m2m_ssl_handshake_rsp(tstrEccReqInfo* strECCResp, uint8* pu8RspDataBuff, uint16 u16RspDataSz) +@brief Sends ECC responses to the WINC. +@param[in] strECCResp + ECC Response struct. +@param[in] pu8RspDataBuff + Pointer of the response data to be sent. +@param[in] u16RspDataSz + Response data size. +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. +*/ +NMI_API sint8 m2m_ssl_handshake_rsp(tstrEccReqInfo *strECCResp, uint8 *pu8RspDataBuff, uint16 u16RspDataSz) +{ + sint8 s8Ret = M2M_SUCCESS; + + s8Ret = hif_send(M2M_REQ_GROUP_SSL, (M2M_SSL_RESP_ECC | M2M_REQ_DATA_PKT), (uint8 *)strECCResp, sizeof(tstrEccReqInfo), pu8RspDataBuff, u16RspDataSz, sizeof(tstrEccReqInfo)); + + return s8Ret; +} + +/*! +@fn NMI_API sint8 m2m_ssl_send_certs_to_winc(uint8 *pu8Buffer, uint32 u32BufferSz) +@brief Sends certificates to the WINC +@param[in] pu8Buffer + Pointer to the certificates. +@param[in] u32BufferSz + Size of the certificates. +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. +*/ +NMI_API sint8 m2m_ssl_send_certs_to_winc(uint8 *pu8Buffer, uint32 u32BufferSz) +{ + sint8 s8Ret = M2M_SUCCESS; +#define TXLIMIT (256 * 6) + + if(u32BufferSz <= TXLIMIT) + { + // set chunk header for one chunk +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + tstrTlsSrvChunkHdr *pchkhdr = (tstrTlsSrvChunkHdr *)pu8Buffer; +#pragma GCC diagnostic pop + pchkhdr->u16Sig = TLS_CERTS_CHUNKED_SIG_VALUE; + pchkhdr->u16TotalSize32 = (u32BufferSz + 3) >> 2; + pchkhdr->u16Offset32 = 0; + pchkhdr->u16Size32 = (u32BufferSz + 3) >> 2; + s8Ret = hif_send(M2M_REQ_GROUP_SSL, (M2M_SSL_REQ_WRITE_OWN_CERTS | M2M_REQ_DATA_PKT), NULL, 0, pu8Buffer, u32BufferSz, 0); + M2M_INFO("Transferred %lu bytes of cert data NON-CHUNKED\n", u32BufferSz); + } + else + { + // chunk it + // We are sneaking in a header - tstrTlsSrvChunkHdr +#define CHUNKHDRSZ (sizeof(tstrTlsSrvChunkHdr)) +#define CHUNKSZ (TXLIMIT - 256) // divisible by 4 + uint8 saveblob[CHUNKHDRSZ]; + uint32 ofs = 0; + uint32 thischunksz = 0; + + // first is special - over writing our header + m2m_memcpy(saveblob, &pu8Buffer[ofs], CHUNKHDRSZ); + thischunksz = min(CHUNKSZ, u32BufferSz-ofs); // no need to round up to quad words this time +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + tstrTlsSrvChunkHdr *pchkhdr = (tstrTlsSrvChunkHdr *)&pu8Buffer[ofs]; +#pragma GCC diagnostic pop + pchkhdr->u16Sig = TLS_CERTS_CHUNKED_SIG_VALUE; + pchkhdr->u16TotalSize32 = ((u32BufferSz + 3) >> 2); + pchkhdr->u16Offset32 = ((ofs + 3) >> 2); + pchkhdr->u16Size32 = ((thischunksz + 3) >> 2); + s8Ret = hif_send(M2M_REQ_GROUP_SSL, (M2M_SSL_REQ_WRITE_OWN_CERTS | M2M_REQ_DATA_PKT), NULL, 0, &pu8Buffer[ofs], thischunksz, 0); + M2M_INFO("Transferred %u bytes of cert data CHUNKED to offset %u total %u\n", thischunksz, ofs, u32BufferSz); + m2m_memcpy(&pu8Buffer[ofs], saveblob, CHUNKHDRSZ); + ofs += thischunksz; + + while(ofs < u32BufferSz) + { + // Subsequent chunks write header before and send a little more + m2m_memcpy(saveblob, &pu8Buffer[ofs-CHUNKHDRSZ], CHUNKHDRSZ); + thischunksz = min(CHUNKSZ, u32BufferSz-ofs); + thischunksz = (thischunksz + 3) & 0xFFFFFFFC; // needs to round up to quad word length +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + pchkhdr = (tstrTlsSrvChunkHdr *)&pu8Buffer[ofs - CHUNKHDRSZ]; +#pragma GCC diagnostic pop + pchkhdr->u16Sig = TLS_CERTS_CHUNKED_SIG_VALUE; + pchkhdr->u16TotalSize32 = ((u32BufferSz + 3) >> 2); + pchkhdr->u16Offset32 = ((ofs + 3) >> 2); + pchkhdr->u16Size32 = ((thischunksz + 3) >> 2); + s8Ret = hif_send(M2M_REQ_GROUP_SSL, (M2M_SSL_REQ_WRITE_OWN_CERTS | M2M_REQ_DATA_PKT), NULL, 0, &pu8Buffer[ofs - CHUNKHDRSZ], thischunksz + CHUNKHDRSZ, 0); + M2M_INFO("Transferred %lu bytes of cert data CHUNKED to offset %u total %u\n", thischunksz, ofs, u32BufferSz); + m2m_memcpy(&pu8Buffer[ofs - CHUNKHDRSZ], saveblob, CHUNKHDRSZ); + ofs += thischunksz; + } + } + + return s8Ret; +} + +/*! +@fn NMI_API sint8 m2m_ssl_retrieve_next_for_verifying(tenuEcNamedCurve *penuCurve, uint8 *pu8Value, uint16 *pu16ValueSz, uint8 *pu8Sig, uint16 *pu16SigSz, tstrECPoint *pstrKey); +@brief Retrieve the next set of information from the WINC for ECDSA verification. +@param[out] penuCurve + The named curve. +@param[out] pu8Value + Value retrieved for verification. This is the digest of the message, truncated/prepended to the appropriate size. +@param[inout] pu16ValueSz + in: Size of value buffer provided by caller. + out: Size of value retrieved (provided for convenience; the value size is in fact determined by the curve). +@param[out] pu8Sig + Signature retrieved for verification. +@param[inout] pu16SigSz + in: Size of signature buffer provided by caller. + out: Size of signature retrieved (provided for convenience; the signature size is in fact determined by the curve). +@param[out] pstrKey + Public key retrieved for verification. +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. + +@pre This function should only be called after the application has been notified that + verification information is ready via @ref ECC_REQ_SIGN_VERIFY. + +@warning If this function returns @ref M2M_ERR_FAIL, then any remaining verification info from + the WINC is lost. +*/ +NMI_API sint8 m2m_ssl_retrieve_next_for_verifying(tenuEcNamedCurve *penuCurve, uint8 *pu8Value, uint16 *pu16ValueSz, uint8 *pu8Sig, uint16 *pu16SigSz, tstrECPoint *pstrKey) +{ + sint8 s8Ret = M2M_ERR_FAIL; + uint16 u16HashSz, u16SigSz, u16KeySz; + + if(gu32HIFAddr == 0) return M2M_ERR_FAIL; + + if((NULL == penuCurve) || (NULL == pu8Value) || (NULL == pu16ValueSz) || (NULL == pu8Sig) || (NULL == pu16SigSz) || (NULL == pstrKey)) + { + s8Ret = M2M_ERR_INVALID_ARG; + goto __ERR; + } + + if(hif_receive(gu32HIFAddr, (uint8 *)&u16KeySz, 2, 0) != M2M_SUCCESS) goto __ERR; + *penuCurve = _htons(u16KeySz); + gu32HIFAddr += 2; + + if(hif_receive(gu32HIFAddr, (uint8 *)&u16KeySz, 2, 0) != M2M_SUCCESS) goto __ERR; + u16KeySz = _htons(u16KeySz); + if(u16KeySz > sizeof(pstrKey->X)) goto __ERR; + pstrKey->u16Size = u16KeySz; + gu32HIFAddr += 2; + + if(hif_receive(gu32HIFAddr, (uint8 *)&u16HashSz, 2, 0) != M2M_SUCCESS) goto __ERR; + u16HashSz = _htons(u16HashSz); + if(u16HashSz > *pu16ValueSz) goto __ERR; + *pu16ValueSz = u16HashSz; + gu32HIFAddr += 2; + + if(hif_receive(gu32HIFAddr, (uint8 *)&u16SigSz, 2, 0) != M2M_SUCCESS) goto __ERR; + u16SigSz = _htons(u16SigSz); + if(u16SigSz > *pu16SigSz) goto __ERR; + *pu16SigSz = u16SigSz; + gu32HIFAddr += 2; + + if(hif_receive(gu32HIFAddr, pstrKey->X, u16KeySz, 0) != M2M_SUCCESS) goto __ERR; + gu32HIFAddr += u16KeySz; + if(hif_receive(gu32HIFAddr, pstrKey->Y, u16KeySz, 0) != M2M_SUCCESS) goto __ERR; + gu32HIFAddr += u16KeySz; + + if(hif_receive(gu32HIFAddr, pu8Value, u16HashSz, 0) != M2M_SUCCESS) goto __ERR; + gu32HIFAddr += u16HashSz; + + if(hif_receive(gu32HIFAddr, pu8Sig, u16SigSz, 0) != M2M_SUCCESS) goto __ERR; + gu32HIFAddr += u16SigSz; + + return M2M_SUCCESS; + +__ERR: + if(hif_receive(0, NULL, 0, 1) != M2M_SUCCESS) M2M_ERR("hif rx done failed\n"); + return s8Ret; +} + +/*! +@fn NMI_API sint8 m2m_ssl_retrieve_cert(uint16* pu16Curve, uint8* pu8Value, uint8* pu8Sig, tstrECPoint* pstrKey); +@brief Retrieve the next set of information from the WINC for ECDSA verification. +@param[out] pu16Curve + The named curve, to be cast to type @ref tenuEcNamedCurve. +@param[out] pu8Value + Value retrieved for verification. This is the digest of the message, truncated/prepended to the appropriate size. + The size of the value is equal to the field size of the curve, hence is determined by pu16Curve. +@param[out] pu8Sig + Signature retrieved for verification. + The size of the signature is equal to twice the field size of the curve, hence is determined by pu16Curve. +@param[out] pstrKey + Public key retrieved for verification. +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. + +@pre This function should only be called after the application has been notified that + verification information is ready via @ref ECC_REQ_SIGN_VERIFY. + +@warning If this function returns @ref M2M_ERR_FAIL, then any remaining verification info from + the WINC is lost. + +@warning This API has been deprecated and is kept for legacy purposes only. It is recommended + that @ref m2m_ssl_retrieve_next_for_verifying is used instead. +*/ +NMI_API sint8 m2m_ssl_retrieve_cert(uint16 *pu16Curve, uint8 *pu8Value, uint8 *pu8Sig, tstrECPoint *pstrKey) +{ + uint16 u16ValueSz = 32, u16SigSz = 64; + + return m2m_ssl_retrieve_next_for_verifying((tenuEcNamedCurve *)pu16Curve, pu8Value, &u16ValueSz, pu8Sig, &u16SigSz, pstrKey); +} + +/*! +@fn NMI_API sint8 m2m_ssl_retrieve_hash(uint8* pu8Value, uint16 u16ValueSz) +@brief Retrieve the value from the WINC for ECDSA signing. +@param[out] pu8Value + Value retrieved for signing. This is the digest of the message, truncated/prepended to the appropriate size. +@param[in] u16ValueSz + Size of value to be retrieved. (The application should obtain this information, + along with the curve, from the associated @ref ECC_REQ_SIGN_GEN notification.) +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. + +@pre This function should only be called after the application has been notified that + signing information is ready via @ref ECC_REQ_SIGN_GEN. + +@warning If this function returns @ref M2M_ERR_FAIL, then the value for signing is lost. +*/ +NMI_API sint8 m2m_ssl_retrieve_hash(uint8 *pu8Value, uint16 u16ValueSz) +{ + sint8 s8Ret = M2M_ERR_FAIL; + + if(gu32HIFAddr == 0) return M2M_ERR_FAIL; + + if(NULL == pu8Value) + { + s8Ret = M2M_ERR_INVALID_ARG; + goto __ERR; + } + + if(hif_receive(gu32HIFAddr, pu8Value, u16ValueSz, 0) != M2M_SUCCESS) goto __ERR; + + return M2M_SUCCESS; + +__ERR: + if(hif_receive(0, NULL, 0, 1) != M2M_SUCCESS) M2M_ERR("hif rx done failed\n"); + return s8Ret; +} + +/*! +@fn NMI_API void m2m_ssl_stop_retrieving(void); +@brief Allow SSL driver to tidy up when the application chooses not to retrieve all available + information. + +@return None. + +@warning The application must call this function if it has been notified (via + @ref ECC_REQ_SIGN_GEN or @ref ECC_REQ_SIGN_VERIFY) that information is available for + retrieving from the WINC, but chooses not to retrieve it all. + The application must not call this function if it has retrieved all the available + information, or if a retrieve function returned @ref M2M_ERR_FAIL indicating that any + remaining information has been lost. + +@see m2m_ssl_retrieve_next_for_verifying\n + m2m_ssl_retrieve_cert\n + m2m_ssl_retrieve_hash +*/ +NMI_API void m2m_ssl_stop_retrieving(void) +{ + if(hif_receive(0, NULL, 0, 1) != M2M_SUCCESS) M2M_ERR("hif rx done failed\n"); +} + +/*! +@fn NMI_API void m2m_ssl_stop_processing_certs(void); +@brief Allow SSL driver to tidy up in case application does not read all available certificates. +@return None. + +@warning This API has been deprecated and is kept for legacy purposes only. It is recommended + that @ref m2m_ssl_stop_retrieving is used instead. +*/ +NMI_API void m2m_ssl_stop_processing_certs(void) +{ + m2m_ssl_stop_retrieving(); +} + +/*! +@fn NMI_API void m2m_ssl_ecc_process_done(void); +@brief Allow SSL driver to tidy up after application has finished processing ECC message. + +@return None. + +@warning The application should call this function after receiving an SSL callback with message + type @ref M2M_SSL_REQ_ECC, after retrieving any related information, and before + calling @ref m2m_ssl_handshake_rsp. +*/ +NMI_API void m2m_ssl_ecc_process_done(void) +{ + gu32HIFAddr = 0; +} + +/*! +@fn NMI_API sint8 m2m_ssl_set_active_ciphersuites(uint32 u32SslCsBMP) +@brief Sets the active ciphersuites. +@details Override the default Active SSL ciphers in the SSL module with a certain combination selected by the caller in the form of + a bitmap containing the required ciphers to be on. + There is no need to call this function if the application will not change the default ciphersuites. + +@param [in] u32SslCsBMP + Bitmap containing the desired ciphers to be enabled for the SSL module. The ciphersuites are defined in + @ref SSLCipherSuiteID. + The default ciphersuites are all ciphersuites supported by the firmware with the exception of ECC ciphersuites. + The caller can override the default with any desired combination. + If u32SslCsBMP does not contain any ciphersuites supported by firmware, then the current active list will not + change. + +@return + - @ref SOCK_ERR_NO_ERROR + - @ref SOCK_ERR_INVALID_ARG +*/ +NMI_API sint8 m2m_ssl_set_active_ciphersuites(uint32 u32SslCsBMP) +{ + sint8 s8Ret = M2M_SUCCESS; + tstrSslSetActiveCsList strCsList; + + strCsList.u32CsBMP = u32SslCsBMP; + s8Ret = hif_send(M2M_REQ_GROUP_SSL, M2M_SSL_REQ_SET_CS_LIST, (uint8 *)&strCsList, sizeof(tstrSslSetActiveCsList), NULL, 0, 0); + + return s8Ret; } diff --git a/src/drivers/winc1500/src/m2m_wifi.c b/src/drivers/winc1500/src/m2m_wifi.c index 643d8b067..d655f0187 100644 --- a/src/drivers/winc1500/src/m2m_wifi.c +++ b/src/drivers/winc1500/src/m2m_wifi.c @@ -4,7 +4,7 @@ * * \brief This module contains M2M Wi-Fi APIs implementation. * - * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries. + * Copyright (c) 2016-2021 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * @@ -39,415 +39,408 @@ #include "driver/include/m2m_hif.h" #include "driver/include/nmasic.h" -/** - * \defgroup winc1500_group WINC1500 (Wi-Fi) - * - * \{ - */ +/* Require authentication of server. */ +#define WIFI_1X_TLS_HS_FLAGS_PEER_AUTH NBIT1 +/* Enable expiry checking of server certificate chain. */ +#define WIFI_1X_TLS_HS_FLAGS_PEER_CERTTIMECHECK NBIT2 +/* Require local system time to be known (i.e. fail expiry checking if time is not known locally). */ +#define WIFI_1X_TLS_HS_FLAGS_REQUIRE_TIME NBIT3 +/* Enable TLS session caching. */ +#define WIFI_1X_TLS_HS_FLAGS_SESSION_CACHING NBIT4 +/* Reserved, this bit must be clear. */ +#define WIFI_1X_TLS_HS_FLAGS_RSV5 NBIT5 +/* Require server authentication to be against a specified root certificate. */ +#define WIFI_1X_TLS_HS_FLAGS_SPECIFY_ROOTCERT NBIT6 +/* Reserved, this bit must be clear. */ +#define WIFI_1X_TLS_HS_FLAGS_RSV7 NBIT7 + +#define WIFI_1X_TLS_HS_FLAGS_DEFAULT ( \ + WIFI_1X_TLS_HS_FLAGS_PEER_AUTH \ + | WIFI_1X_TLS_HS_FLAGS_PEER_CERTTIMECHECK \ + | WIFI_1X_TLS_HS_FLAGS_SESSION_CACHING \ +) -static volatile uint8 gu8WifiState = WIFI_STATE_DEINIT; static volatile uint8 gu8ChNum; +static volatile uint8 gu8WifiState = WIFI_STATE_DEINIT; +static tpfAppWifiCb gpfAppWifiCb = NULL; static volatile uint8 gu8scanInProgress = 0; -static tpfAppWifiCb gpfAppWifiCb = NULL; +static uint32 gu321xTlsHsFlags = WIFI_1X_TLS_HS_FLAGS_DEFAULT; +static uint8 gau81xRootSha1[20] = {0}; #ifdef ETH_MODE -static tpfAppEthCb gpfAppEthCb = NULL; -static uint8* gau8ethRcvBuf=NULL; -static uint16 gu16ethRcvBufSize ; +static tpfAppEthCb gpfAppEthCb = NULL; +static uint8 *gau8ethRcvBuf = NULL; +static uint16 gu16ethRcvBufSize; #endif /** -* @fn m2m_wifi_cb(uint8 u8OpCode, uint16 u16DataSize, uint32 u32Addr, uint8 grp) -* @brief WiFi call back function -* @param [in] u8OpCode -* HIF Opcode type. -* @param [in] u16DataSize -* HIF data length. -* @param [in] u32Addr -* HIF address. -* @param [in] grp -* HIF group type. -* @author -* @date -* @version 1.0 +@fn void m2m_wifi_cb(uint8 u8OpCode, uint16 u16DataSize, uint32 u32Addr, uint8 grp) +@brief Internal WiFi callback function. +@param[in] u8OpCode + HIF Opcode type. +@param[in] u16DataSize + HIF data length. +@param[in] u32Addr + HIF address. +@param[in] grp + HIF group type. */ static void m2m_wifi_cb(uint8 u8OpCode, uint16 u16DataSize, uint32 u32Addr) { - uint8 rx_buf[8]; - if (u8OpCode == M2M_WIFI_RESP_CON_STATE_CHANGED) - { - tstrM2mWifiStateChanged strState; - if (hif_receive(u32Addr, (uint8*) &strState,sizeof(tstrM2mWifiStateChanged), 0) == M2M_SUCCESS) - { - if (gpfAppWifiCb) - gpfAppWifiCb(M2M_WIFI_RESP_CON_STATE_CHANGED, &strState); - } - } - else if (u8OpCode == M2M_WIFI_RESP_GET_SYS_TIME) - { - tstrSystemTime strSysTime; - if (hif_receive(u32Addr, (uint8*) &strSysTime,sizeof(tstrSystemTime), 0) == M2M_SUCCESS) - { - if (gpfAppWifiCb) - gpfAppWifiCb(M2M_WIFI_RESP_GET_SYS_TIME, &strSysTime); - } - } - else if(u8OpCode == M2M_WIFI_RESP_CONN_INFO) - { - tstrM2MConnInfo strConnInfo; - if(hif_receive(u32Addr, (uint8*)&strConnInfo, sizeof(tstrM2MConnInfo), 1) == M2M_SUCCESS) - { - if(gpfAppWifiCb) - gpfAppWifiCb(M2M_WIFI_RESP_CONN_INFO, &strConnInfo); - } - } - else if (u8OpCode == M2M_WIFI_RESP_MEMORY_RECOVER) - { + uint8 rx_buf[8]; + if(u8OpCode == M2M_WIFI_RESP_CON_STATE_CHANGED) + { + tstrM2mWifiStateChanged strState; + if(hif_receive(u32Addr, (uint8 *) &strState, sizeof(tstrM2mWifiStateChanged), 0) == M2M_SUCCESS) + { + if(gpfAppWifiCb) + gpfAppWifiCb(M2M_WIFI_RESP_CON_STATE_CHANGED, &strState); + } + } + else if(u8OpCode == M2M_WIFI_RESP_GET_SYS_TIME) + { + tstrSystemTime strSysTime; + if(hif_receive(u32Addr, (uint8 *) &strSysTime, sizeof(tstrSystemTime), 0) == M2M_SUCCESS) + { + if(gpfAppWifiCb) + gpfAppWifiCb(M2M_WIFI_RESP_GET_SYS_TIME, &strSysTime); + } + } + else if(u8OpCode == M2M_WIFI_RESP_CONN_INFO) + { + tstrM2MConnInfo strConnInfo; + if(hif_receive(u32Addr, (uint8 *)&strConnInfo, sizeof(tstrM2MConnInfo), 1) == M2M_SUCCESS) + { + if(gpfAppWifiCb) + gpfAppWifiCb(M2M_WIFI_RESP_CONN_INFO, &strConnInfo); + } + } + else if(u8OpCode == M2M_WIFI_RESP_MEMORY_RECOVER) + { #if 0 - if (hif_receive(u32Addr, rx_buf, 4, 1) == M2M_SUCCESS) - { - tstrM2mWifiStateChanged strState; - m2m_memcpy((uint8*) &strState, rx_buf,sizeof(tstrM2mWifiStateChanged)); - if (app_wifi_recover_cb) - app_wifi_recover_cb(strState.u8CurrState); - } + if(hif_receive(u32Addr, rx_buf, 4, 1) == M2M_SUCCESS) + { + tstrM2mWifiStateChanged strState; + m2m_memcpy((uint8 *) &strState, rx_buf, sizeof(tstrM2mWifiStateChanged)); + if(app_wifi_recover_cb) + app_wifi_recover_cb(strState.u8CurrState); + } #endif - } - else if (u8OpCode == M2M_WIFI_REQ_DHCP_CONF) - { - tstrM2MIPConfig strIpConfig; - if (hif_receive(u32Addr, (uint8 *)&strIpConfig, sizeof(tstrM2MIPConfig), 0) == M2M_SUCCESS) - { - if (gpfAppWifiCb) - gpfAppWifiCb(M2M_WIFI_REQ_DHCP_CONF, (uint8 *)&strIpConfig); - } - } - else if (u8OpCode == M2M_WIFI_REQ_DHCP_FAILURE) - { - if (hif_receive(u32Addr, NULL, 0, 1) == M2M_SUCCESS) - { - if (gpfAppWifiCb) - gpfAppWifiCb(M2M_WIFI_REQ_DHCP_FAILURE, NULL); - } - } - else if (u8OpCode == M2M_WIFI_REQ_WPS) - { - tstrM2MWPSInfo strWps; - m2m_memset((uint8*)&strWps,0,sizeof(tstrM2MWPSInfo)); - if(hif_receive(u32Addr, (uint8*)&strWps, sizeof(tstrM2MWPSInfo), 0) == M2M_SUCCESS) - { - if (gpfAppWifiCb) - gpfAppWifiCb(M2M_WIFI_REQ_WPS, &strWps); - } - } - else if (u8OpCode == M2M_WIFI_RESP_IP_CONFLICT) - { - uint32 u32ConflictedIP; - if(hif_receive(u32Addr, (uint8 *)&u32ConflictedIP, sizeof(u32ConflictedIP), 0) == M2M_SUCCESS) - { - M2M_INFO("Conflicted IP \" %u.%u.%u.%u \" \n", - BYTE_0(u32ConflictedIP),BYTE_1(u32ConflictedIP),BYTE_2(u32ConflictedIP),BYTE_3(u32ConflictedIP)); - if (gpfAppWifiCb) - gpfAppWifiCb(M2M_WIFI_RESP_IP_CONFLICT, NULL); - - } - } - else if (u8OpCode == M2M_WIFI_RESP_SCAN_DONE) - { - tstrM2mScanDone strState; - gu8scanInProgress = 0; - if(hif_receive(u32Addr, (uint8*)&strState, sizeof(tstrM2mScanDone), 0) == M2M_SUCCESS) - { - gu8ChNum = strState.u8NumofCh; - if (gpfAppWifiCb) - gpfAppWifiCb(M2M_WIFI_RESP_SCAN_DONE, &strState); - } - } - else if (u8OpCode == M2M_WIFI_RESP_SCAN_RESULT) - { - tstrM2mWifiscanResult strScanResult; - if(hif_receive(u32Addr, (uint8*)&strScanResult, sizeof(tstrM2mWifiscanResult), 0) == M2M_SUCCESS) - { - if (gpfAppWifiCb) - gpfAppWifiCb(M2M_WIFI_RESP_SCAN_RESULT, &strScanResult); - } - } - else if (u8OpCode == M2M_WIFI_RESP_CURRENT_RSSI) - { - if (hif_receive(u32Addr, rx_buf, 4, 0) == M2M_SUCCESS) - { - if (gpfAppWifiCb) - gpfAppWifiCb(M2M_WIFI_RESP_CURRENT_RSSI, rx_buf); - } - } - else if (u8OpCode == M2M_WIFI_RESP_CLIENT_INFO) - { - if (hif_receive(u32Addr, rx_buf, 4, 0) == M2M_SUCCESS) - { - if (gpfAppWifiCb) - gpfAppWifiCb(M2M_WIFI_RESP_CLIENT_INFO, rx_buf); - } - } - else if(u8OpCode == M2M_WIFI_RESP_PROVISION_INFO) - { - tstrM2MProvisionInfo strProvInfo; - if(hif_receive(u32Addr, (uint8*)&strProvInfo, sizeof(tstrM2MProvisionInfo), 1) == M2M_SUCCESS) - { - if(gpfAppWifiCb) - gpfAppWifiCb(M2M_WIFI_RESP_PROVISION_INFO, &strProvInfo); - } - } - else if(u8OpCode == M2M_WIFI_RESP_DEFAULT_CONNECT) - { - tstrM2MDefaultConnResp strResp; - if(hif_receive(u32Addr, (uint8*)&strResp, sizeof(tstrM2MDefaultConnResp), 1) == M2M_SUCCESS) - { - if(gpfAppWifiCb) - gpfAppWifiCb(M2M_WIFI_RESP_DEFAULT_CONNECT, &strResp); - } - } - else if (u8OpCode == M2M_WIFI_REQRSP_DELETE_APID) - { - tstrM2MGenericResp strResp; - if (hif_receive(u32Addr, (uint8*)&strResp, sizeof(tstrM2MGenericResp), 0) == M2M_SUCCESS) - { - if (gpfAppWifiCb) - gpfAppWifiCb(M2M_WIFI_REQRSP_DELETE_APID, &strResp); - } - } - else if(u8OpCode == M2M_WIFI_RESP_GET_PRNG) - { - tstrPrng strPrng; - if(hif_receive(u32Addr, (uint8*)&strPrng,sizeof(tstrPrng), 0) == M2M_SUCCESS) - { - if(hif_receive(u32Addr + sizeof(tstrPrng),strPrng.pu8RngBuff,strPrng.u16PrngSize, 1) == M2M_SUCCESS) - { - if(gpfAppWifiCb) - gpfAppWifiCb(M2M_WIFI_RESP_GET_PRNG,&strPrng); - } - } - } + } + else if(u8OpCode == M2M_WIFI_REQ_DHCP_CONF) + { + tstrM2MIPConfig strIpConfig; + if(hif_receive(u32Addr, (uint8 *)&strIpConfig, sizeof(tstrM2MIPConfig), 0) == M2M_SUCCESS) + { + if(gpfAppWifiCb) + gpfAppWifiCb(M2M_WIFI_REQ_DHCP_CONF, (uint8 *)&strIpConfig); + } + } + else if(u8OpCode == M2M_WIFI_REQ_DHCP_FAILURE) + { + if(hif_receive(u32Addr, NULL, 0, 1) == M2M_SUCCESS) + { + if(gpfAppWifiCb) + gpfAppWifiCb(M2M_WIFI_REQ_DHCP_FAILURE, NULL); + } + } + else if(u8OpCode == M2M_WIFI_REQ_WPS) + { + tstrM2MWPSInfo strWps; + m2m_memset((uint8 *)&strWps, 0, sizeof(tstrM2MWPSInfo)); + if(hif_receive(u32Addr, (uint8 *)&strWps, sizeof(tstrM2MWPSInfo), 0) == M2M_SUCCESS) + { + if(gpfAppWifiCb) + gpfAppWifiCb(M2M_WIFI_REQ_WPS, &strWps); + } + } + else if(u8OpCode == M2M_WIFI_RESP_IP_CONFLICT) + { + uint32 u32ConflictedIP; + if(hif_receive(u32Addr, (uint8 *)&u32ConflictedIP, sizeof(u32ConflictedIP), 0) == M2M_SUCCESS) + { + M2M_INFO("Conflicted IP \" %u.%u.%u.%u \" \n", + BYTE_0(u32ConflictedIP), BYTE_1(u32ConflictedIP), BYTE_2(u32ConflictedIP), BYTE_3(u32ConflictedIP)); + if(gpfAppWifiCb) + gpfAppWifiCb(M2M_WIFI_RESP_IP_CONFLICT, NULL); + } + } + else if(u8OpCode == M2M_WIFI_RESP_SCAN_DONE) + { + tstrM2mScanDone strState; + gu8scanInProgress = 0; + if(hif_receive(u32Addr, (uint8 *)&strState, sizeof(tstrM2mScanDone), 0) == M2M_SUCCESS) + { + gu8ChNum = strState.u8NumofCh; + if(gpfAppWifiCb) + gpfAppWifiCb(M2M_WIFI_RESP_SCAN_DONE, &strState); + } + } + else if(u8OpCode == M2M_WIFI_RESP_SCAN_RESULT) + { + tstrM2mWifiscanResult strScanResult; + if(hif_receive(u32Addr, (uint8 *)&strScanResult, sizeof(tstrM2mWifiscanResult), 0) == M2M_SUCCESS) + { + if(gpfAppWifiCb) + gpfAppWifiCb(M2M_WIFI_RESP_SCAN_RESULT, &strScanResult); + } + } + else if(u8OpCode == M2M_WIFI_RESP_CURRENT_RSSI) + { + if(hif_receive(u32Addr, rx_buf, 4, 0) == M2M_SUCCESS) + { + if(gpfAppWifiCb) + gpfAppWifiCb(M2M_WIFI_RESP_CURRENT_RSSI, rx_buf); + } + } + else if(u8OpCode == M2M_WIFI_RESP_CLIENT_INFO) + { + if(hif_receive(u32Addr, rx_buf, 4, 0) == M2M_SUCCESS) + { + if(gpfAppWifiCb) + gpfAppWifiCb(M2M_WIFI_RESP_CLIENT_INFO, rx_buf); + } + } + else if(u8OpCode == M2M_WIFI_RESP_PROVISION_INFO) + { + tstrM2MProvisionInfo strProvInfo; + if(hif_receive(u32Addr, (uint8 *)&strProvInfo, sizeof(tstrM2MProvisionInfo), 1) == M2M_SUCCESS) + { + if(gpfAppWifiCb) + gpfAppWifiCb(M2M_WIFI_RESP_PROVISION_INFO, &strProvInfo); + } + } + else if(u8OpCode == M2M_WIFI_RESP_DEFAULT_CONNECT) + { + tstrM2MDefaultConnResp strResp; + if(hif_receive(u32Addr, (uint8 *)&strResp, sizeof(tstrM2MDefaultConnResp), 1) == M2M_SUCCESS) + { + if(gpfAppWifiCb) + gpfAppWifiCb(M2M_WIFI_RESP_DEFAULT_CONNECT, &strResp); + } + } + else if(u8OpCode == M2M_WIFI_REQRSP_DELETE_APID) + { + tstrM2MGenericResp strResp; + if(hif_receive(u32Addr, (uint8 *)&strResp, sizeof(tstrM2MGenericResp), 0) == M2M_SUCCESS) + { + if(gpfAppWifiCb) + gpfAppWifiCb(M2M_WIFI_REQRSP_DELETE_APID, &strResp); + } + } + else if(u8OpCode == M2M_WIFI_RESP_GET_PRNG) + { + tstrPrng strPrng; + if(hif_receive(u32Addr, (uint8 *)&strPrng, sizeof(tstrPrng), 0) == M2M_SUCCESS) + { + if(hif_receive(u32Addr + sizeof(tstrPrng), strPrng.pu8RngBuff, strPrng.u16PrngSize, 1) == M2M_SUCCESS) + { + if(gpfAppWifiCb) + gpfAppWifiCb(M2M_WIFI_RESP_GET_PRNG, &strPrng); + } + } + } #ifdef ETH_MODE - else if(u8OpCode == M2M_WIFI_RESP_ETHERNET_RX_PACKET) - { - uint8 u8SetRxDone; - tstrM2mIpRsvdPkt strM2mRsvd; - if(hif_receive(u32Addr, (uint8 *)&strM2mRsvd ,sizeof(tstrM2mIpRsvdPkt), 0) == M2M_SUCCESS) - { - tstrM2mIpCtrlBuf strM2mIpCtrlBuf; - uint16 u16Offset = strM2mRsvd.u16PktOffset; - strM2mIpCtrlBuf.u16RemainingDataSize = strM2mRsvd.u16PktSz; - if((gpfAppEthCb) && (gau8ethRcvBuf) && (gu16ethRcvBufSize > 0)) - { - do - { - u8SetRxDone = 1; - if(strM2mIpCtrlBuf.u16RemainingDataSize > gu16ethRcvBufSize) - { - u8SetRxDone = 0; - strM2mIpCtrlBuf.u16DataSize = gu16ethRcvBufSize; - } - else - { - strM2mIpCtrlBuf.u16DataSize = strM2mIpCtrlBuf.u16RemainingDataSize; - } + else if(u8OpCode == M2M_WIFI_RESP_ETHERNET_RX_PACKET) + { + uint8 u8SetRxDone; + tstrM2mIpRsvdPkt strM2mRsvd; + if(hif_receive(u32Addr, &strM2mRsvd, sizeof(tstrM2mIpRsvdPkt), 0) == M2M_SUCCESS) + { + tstrM2mIpCtrlBuf strM2mIpCtrlBuf; + uint16 u16Offset = strM2mRsvd.u16PktOffset; + strM2mIpCtrlBuf.u16RemainingDataSize = strM2mRsvd.u16PktSz; + if((gpfAppEthCb) && (gau8ethRcvBuf) && (gu16ethRcvBufSize > 0)) + { + do + { + u8SetRxDone = 1; + if(strM2mIpCtrlBuf.u16RemainingDataSize > gu16ethRcvBufSize) + { + u8SetRxDone = 0; + strM2mIpCtrlBuf.u16DataSize = gu16ethRcvBufSize; + } + else + { + strM2mIpCtrlBuf.u16DataSize = strM2mIpCtrlBuf.u16RemainingDataSize; + } - if(hif_receive(u32Addr + u16Offset, gau8ethRcvBuf, strM2mIpCtrlBuf.u16DataSize, u8SetRxDone) == M2M_SUCCESS) - { - strM2mIpCtrlBuf.u16RemainingDataSize -= strM2mIpCtrlBuf.u16DataSize; - u16Offset += strM2mIpCtrlBuf.u16DataSize; - gpfAppEthCb(M2M_WIFI_RESP_ETHERNET_RX_PACKET, gau8ethRcvBuf, &(strM2mIpCtrlBuf)); - } - else - { - break; - } - }while (strM2mIpCtrlBuf.u16RemainingDataSize > 0); - } - } - } -#endif /* ETH_MODE */ - else - { - M2M_ERR("REQ Not defined %d\n",u8OpCode); - } + if(hif_receive(u32Addr + u16Offset, gau8ethRcvBuf, strM2mIpCtrlBuf.u16DataSize, u8SetRxDone) == M2M_SUCCESS) + { + strM2mIpCtrlBuf.u16RemainingDataSize -= strM2mIpCtrlBuf.u16DataSize; + u16Offset += strM2mIpCtrlBuf.u16DataSize; + gpfAppEthCb(M2M_WIFI_RESP_ETHERNET_RX_PACKET, gau8ethRcvBuf, &(strM2mIpCtrlBuf)); + } + else + { + break; + } + } while(strM2mIpCtrlBuf.u16RemainingDataSize > 0); + } + } + } +#endif /* ETH_MODE */ + else + { + M2M_ERR("REQ Not defined %d\n", u8OpCode); + } } sint8 m2m_wifi_download_mode() { - sint8 ret = M2M_SUCCESS; - /* Apply device specific initialization. */ - ret = nm_drv_init_download_mode(); - if(ret != M2M_SUCCESS) goto _EXIT0; + sint8 ret = M2M_SUCCESS; + /* Apply device specific initialization. */ + ret = nm_drv_init_download_mode(); + if(ret != M2M_SUCCESS) goto _EXIT0; - enable_interrupts(); + enable_interrupts(); gu8WifiState = WIFI_STATE_INIT; _EXIT0: - return ret; + return ret; } -static sint8 m2m_validate_ap_parameters(CONST tstrM2MAPModeConfig* pstrM2MAPModeConfig) +static sint8 m2m_validate_ap_parameters(CONST tstrM2MAPModeConfig *pstrM2MAPModeConfig) { - sint8 s8Ret = M2M_SUCCESS; - /* Check for incoming pointer */ - if(pstrM2MAPModeConfig == NULL) - { - M2M_ERR("INVALID POINTER\n"); - s8Ret = M2M_ERR_FAIL; - goto ERR1; - } - /* Check for SSID */ - if((m2m_strlen((uint8 *)pstrM2MAPModeConfig->strApConfig.au8SSID) <= 0) || (m2m_strlen((uint8 *)pstrM2MAPModeConfig->strApConfig.au8SSID) >= M2M_MAX_SSID_LEN)) - { - M2M_ERR("INVALID SSID\n"); - s8Ret = M2M_ERR_FAIL; - goto ERR1; - } - /* Check for Channel */ - if(pstrM2MAPModeConfig->strApConfig.u8ListenChannel > M2M_WIFI_CH_14 || pstrM2MAPModeConfig->strApConfig.u8ListenChannel < M2M_WIFI_CH_1) - { - M2M_ERR("INVALID CH\n"); - s8Ret = M2M_ERR_FAIL; - goto ERR1; - } - /* Check for DHCP Server IP address */ - if(!(pstrM2MAPModeConfig->strApConfig.au8DHCPServerIP[0] || pstrM2MAPModeConfig->strApConfig.au8DHCPServerIP[1])) - { - if(!(pstrM2MAPModeConfig->strApConfig.au8DHCPServerIP[2])) - { - M2M_ERR("INVALID DHCP SERVER IP\n"); - s8Ret = M2M_ERR_FAIL; - goto ERR1; - } - } - /* Check for Security */ - if(pstrM2MAPModeConfig->strApConfig.u8SecType == M2M_WIFI_SEC_OPEN) - { - goto ERR1; - } - else if(pstrM2MAPModeConfig->strApConfig.u8SecType == M2M_WIFI_SEC_WEP) - { - /* Check for WEP Key index */ - if((pstrM2MAPModeConfig->strApConfig.u8KeyIndx == 0) || (pstrM2MAPModeConfig->strApConfig.u8KeyIndx > WEP_KEY_MAX_INDEX)) - { - M2M_ERR("INVALID KEY INDEX\n"); - s8Ret = M2M_ERR_FAIL; - goto ERR1; - } - /* Check for WEP Key size */ - if( (pstrM2MAPModeConfig->strApConfig.u8KeySz != WEP_40_KEY_STRING_SIZE) && - (pstrM2MAPModeConfig->strApConfig.u8KeySz != WEP_104_KEY_STRING_SIZE) - ) - { - M2M_ERR("INVALID KEY STRING SIZE\n"); - s8Ret = M2M_ERR_FAIL; - goto ERR1; - } + sint8 s8Ret = M2M_SUCCESS; + /* Check for incoming pointer */ + if(pstrM2MAPModeConfig == NULL) + { + M2M_ERR("INVALID POINTER\n"); + s8Ret = M2M_ERR_FAIL; + goto ERR1; + } + /* Check for SSID */ + if((m2m_strlen((uint8 *)pstrM2MAPModeConfig->strApConfig.au8SSID) <= 0) || (m2m_strlen((uint8 *)pstrM2MAPModeConfig->strApConfig.au8SSID) >= M2M_MAX_SSID_LEN)) + { + M2M_ERR("INVALID SSID\n"); + s8Ret = M2M_ERR_FAIL; + goto ERR1; + } + /* Check for Channel */ + if(pstrM2MAPModeConfig->strApConfig.u8ListenChannel > M2M_WIFI_CH_14 || pstrM2MAPModeConfig->strApConfig.u8ListenChannel < M2M_WIFI_CH_1) + { + M2M_ERR("INVALID CH\n"); + s8Ret = M2M_ERR_FAIL; + goto ERR1; + } + /* Check for DHCP Server IP address */ + if(!(pstrM2MAPModeConfig->strApConfig.au8DHCPServerIP[0] || pstrM2MAPModeConfig->strApConfig.au8DHCPServerIP[1])) + { + if(!(pstrM2MAPModeConfig->strApConfig.au8DHCPServerIP[2])) + { + M2M_ERR("INVALID DHCP SERVER IP\n"); + s8Ret = M2M_ERR_FAIL; + goto ERR1; + } + } + /* Check for Security */ + if(pstrM2MAPModeConfig->strApConfig.u8SecType == M2M_WIFI_SEC_OPEN) + { + goto ERR1; + } + else if(pstrM2MAPModeConfig->strApConfig.u8SecType == M2M_WIFI_SEC_WEP) + { + /* As of 19.7.5 the WEP protocol is deprecated */ + s8Ret = M2M_ERR_FAIL; + goto ERR1; + } + else if(pstrM2MAPModeConfig->strApConfig.u8SecType == M2M_WIFI_SEC_WPA_PSK) + { + /* Check for WPA Key size */ + if(((pstrM2MAPModeConfig->strApConfig.u8KeySz + 1) < M2M_MIN_PSK_LEN) || ((pstrM2MAPModeConfig->strApConfig.u8KeySz + 1) > M2M_MAX_PSK_LEN)) + { + M2M_ERR("INVALID WPA KEY SIZE\n"); + s8Ret = M2M_ERR_FAIL; + goto ERR1; + } + } + else + { + M2M_ERR("INVALID AUTHENTICATION MODE\n"); + s8Ret = M2M_ERR_FAIL; + goto ERR1; + } - if((m2m_strlen((uint8 *)pstrM2MAPModeConfig->strApConfig.au8WepKey) <= 0) || (m2m_strlen((uint8 *)pstrM2MAPModeConfig->strApConfig.au8WepKey) > WEP_104_KEY_STRING_SIZE)) - { - M2M_ERR("INVALID KEY SIZE\n"); - s8Ret = M2M_ERR_FAIL; - goto ERR1; - } - } - else if(pstrM2MAPModeConfig->strApConfig.u8SecType == M2M_WIFI_SEC_WPA_PSK) - { - /* Check for WPA Key size */ - if( ((pstrM2MAPModeConfig->strApConfig.u8KeySz + 1) < M2M_MIN_PSK_LEN) || ((pstrM2MAPModeConfig->strApConfig.u8KeySz + 1) > M2M_MAX_PSK_LEN)) - { - M2M_ERR("INVALID WPA KEY SIZE\n"); - s8Ret = M2M_ERR_FAIL; - goto ERR1; - } - } - else - { - M2M_ERR("INVALID AUTHENTICATION MODE\n"); - s8Ret = M2M_ERR_FAIL; - goto ERR1; - } - ERR1: - return s8Ret; + return s8Ret; } -static sint8 m2m_validate_scan_options(tstrM2MScanOption* ptstrM2MScanOption) + +static sint8 m2m_validate_scan_options(tstrM2MScanOption *ptstrM2MScanOption) { - sint8 s8Ret = M2M_SUCCESS; - /* Check for incoming pointer */ - if(ptstrM2MScanOption == NULL) - { - M2M_ERR("INVALID POINTER\n"); - s8Ret = M2M_ERR_FAIL; - goto ERR; - } - /* Check for valid No of slots */ - if(ptstrM2MScanOption->u8NumOfSlot == 0) - { - M2M_ERR("INVALID No of scan slots! %d\n",ptstrM2MScanOption->u8NumOfSlot); - s8Ret = M2M_ERR_FAIL; - goto ERR; - } - /* Check for valid time of slots */ - if(ptstrM2MScanOption->u8SlotTime < 10 || ptstrM2MScanOption->u8SlotTime > 250) - { - M2M_ERR("INVALID scan slot time! %d\n",ptstrM2MScanOption->u8SlotTime); - s8Ret = M2M_ERR_FAIL; - goto ERR; - } - /* Check for valid No of probe requests per slot */ - if((ptstrM2MScanOption->u8ProbesPerSlot == 0)||(ptstrM2MScanOption->u8ProbesPerSlot > M2M_SCAN_DEFAULT_NUM_PROBE)) - { - M2M_ERR("INVALID No of probe requests per scan slot %d\n",ptstrM2MScanOption->u8ProbesPerSlot); - s8Ret = M2M_ERR_FAIL; - goto ERR; - } - /* Check for valid RSSI threshold */ - if(ptstrM2MScanOption->s8RssiThresh >= 0) - { - M2M_ERR("INVALID RSSI threshold %d \n",ptstrM2MScanOption->s8RssiThresh); - s8Ret = M2M_ERR_FAIL; - } + sint8 s8Ret = M2M_SUCCESS; + /* Check for incoming pointer */ + if(ptstrM2MScanOption == NULL) + { + M2M_ERR("INVALID POINTER\n"); + s8Ret = M2M_ERR_FAIL; + goto ERR; + } + /* Check for valid No of slots */ + if(ptstrM2MScanOption->u8NumOfSlot == 0) + { + M2M_ERR("INVALID No of scan slots! %d\n", ptstrM2MScanOption->u8NumOfSlot); + s8Ret = M2M_ERR_FAIL; + goto ERR; + } + /* Check for valid time of slots */ + if(ptstrM2MScanOption->u8SlotTime < 10 || ptstrM2MScanOption->u8SlotTime > 250) + { + M2M_ERR("INVALID scan slot time! %d\n", ptstrM2MScanOption->u8SlotTime); + s8Ret = M2M_ERR_FAIL; + goto ERR; + } + /* Check for valid No of probe requests per slot */ + if((ptstrM2MScanOption->u8ProbesPerSlot == 0) || (ptstrM2MScanOption->u8ProbesPerSlot > M2M_SCAN_DEFAULT_NUM_PROBE)) + { + M2M_ERR("INVALID No of probe requests per scan slot %d\n", ptstrM2MScanOption->u8ProbesPerSlot); + s8Ret = M2M_ERR_FAIL; + goto ERR; + } + /* Check for valid RSSI threshold */ + if(ptstrM2MScanOption->s8RssiThresh >= 0) + { + M2M_ERR("INVALID RSSI threshold %d \n", ptstrM2MScanOption->s8RssiThresh); + s8Ret = M2M_ERR_FAIL; + } ERR: - return s8Ret; + return s8Ret; } -sint8 m2m_wifi_send_crl(tstrTlsCrlInfo* pCRL) +sint8 m2m_wifi_send_crl(tstrTlsCrlInfo *pCRL) { - sint8 s8Ret = M2M_ERR_FAIL; - s8Ret = hif_send(M2M_REQ_GROUP_SSL, M2M_SSL_IND_CRL|M2M_REQ_DATA_PKT, NULL, 0, (uint8*)pCRL, sizeof(tstrTlsCrlInfo), 0); - return s8Ret; + sint8 s8Ret = M2M_ERR_FAIL; + s8Ret = hif_send(M2M_REQ_GROUP_SSL, M2M_SSL_IND_CRL|M2M_REQ_DATA_PKT, NULL, 0, (uint8 *)pCRL, sizeof(tstrTlsCrlInfo), 0); + return s8Ret; } -sint8 m2m_wifi_init_hold() +sint8 m2m_wifi_init_hold(void) { - sint8 ret = M2M_ERR_FAIL; + sint8 ret = M2M_ERR_FAIL; - /* Apply device specific initialization. */ - ret = nm_drv_init_hold(); + /* Apply device specific initialization. */ + ret = nm_drv_init_hold(); - if(M2M_SUCCESS == ret) { + if(M2M_SUCCESS == ret) gu8WifiState = WIFI_STATE_INIT; - } - return ret; + return ret; } -sint8 m2m_wifi_init_start(tstrWifiInitParam * pWifiInitParam) +sint8 m2m_wifi_init_start(tstrWifiInitParam *pWifiInitParam) { - tstrM2mRev strtmp; - sint8 ret = M2M_SUCCESS; - uint8 u8WifiMode = M2M_WIFI_MODE_NORMAL; - - if(pWifiInitParam == NULL) { - ret = M2M_ERR_FAIL; - goto _EXIT0; - } - - gpfAppWifiCb = pWifiInitParam->pfAppWifiCb; + tstrM2mRev strtmp; + sint8 ret = M2M_SUCCESS; + uint8 u8WifiMode = M2M_WIFI_MODE_NORMAL; + + if(pWifiInitParam == NULL) { + ret = M2M_ERR_FAIL; + goto _EXIT0; + } + + gpfAppWifiCb = pWifiInitParam->pfAppWifiCb; #ifdef ETH_MODE gpfAppEthCb = pWifiInitParam->strEthInitParam.pfAppEthCb; @@ -457,20 +450,20 @@ sint8 m2m_wifi_init_start(tstrWifiInitParam * pWifiInitParam) u8WifiMode = M2M_WIFI_MODE_ETHERNET; #endif /* ETH_MODE */ - gu8scanInProgress = 0; - /* Apply device specific initialization. */ - ret = nm_drv_init_start(&u8WifiMode); - if(ret != M2M_SUCCESS) goto _EXIT0; + gu8scanInProgress = 0; + /* Apply device specific initialization. */ + ret = nm_drv_init_start(&u8WifiMode); + if(ret != M2M_SUCCESS) goto _EXIT0; gu8WifiState = WIFI_STATE_START; - /* Initialize host interface module */ - ret = hif_init(NULL); - if(ret != M2M_SUCCESS) goto _EXIT1; + /* Initialize host interface module */ + ret = hif_init(NULL); + if(ret != M2M_SUCCESS) goto _EXIT1; - hif_register_cb(M2M_REQ_GROUP_WIFI,m2m_wifi_cb); + hif_register_cb(M2M_REQ_GROUP_WIFI, m2m_wifi_cb); - ret = nm_get_firmware_full_info(&strtmp); + ret = nm_get_firmware_full_info(&strtmp); M2M_INFO("Firmware ver : %u.%u.%u Svnrev %u\n", strtmp.u8FirmwareMajor, strtmp.u8FirmwareMinor, strtmp.u8FirmwarePatch,strtmp.u16FirmwareSvnNum); M2M_INFO("Firmware Build %s Time %s\n",strtmp.BuildDate,strtmp.BuildTime); @@ -482,453 +475,538 @@ sint8 m2m_wifi_init_start(tstrWifiInitParam * pWifiInitParam) M2M_ERR("Mismatch Firmware Version\n"); } - goto _EXIT0; + goto _EXIT0; _EXIT1: gu8WifiState = WIFI_STATE_DEINIT; - nm_drv_deinit(NULL); + nm_drv_deinit(NULL); _EXIT0: - return ret; + return ret; } -sint8 m2m_wifi_init(tstrWifiInitParam * pWifiInitParam) +sint8 m2m_wifi_init(tstrWifiInitParam *pWifiInitParam) { - sint8 ret = M2M_SUCCESS; + sint8 ret = M2M_SUCCESS; - ret = m2m_wifi_init_hold(); - if (ret == M2M_SUCCESS) -{ - ret = m2m_wifi_init_start(pWifiInitParam); - } - return ret; + ret = m2m_wifi_init_hold(); + if(ret == M2M_SUCCESS) + { + ret = m2m_wifi_init_start(pWifiInitParam); + } + return ret; } -sint8 m2m_wifi_deinit(void * arg) +sint8 m2m_wifi_deinit(void *arg) { gu8WifiState = WIFI_STATE_DEINIT; - hif_deinit(NULL); + hif_deinit(NULL); - nm_drv_deinit(NULL); + nm_drv_deinit(NULL); - return M2M_SUCCESS; + return M2M_SUCCESS; } void m2m_wifi_yield(void) { - hif_yield(); + hif_yield(); } -sint8 m2m_wifi_handle_events(void * arg) +sint8 m2m_wifi_handle_events(void *arg) { - return hif_handle_isr(); + return hif_handle_isr(); } sint8 m2m_wifi_delete_sc(char *pcSsid, uint8 u8SsidLen) { - tstrM2mWifiApId strApId; - m2m_memset((uint8*)&strApId, 0, sizeof(strApId)); + tstrM2mWifiApId strApId; + m2m_memset((uint8 *)&strApId, 0, sizeof(strApId)); #if 0 - if (pcSsid != NULL) - { - if (u8SsidLen >= M2M_MAX_SSID_LEN) - return M2M_ERR_INVALID_ARG; - strApId.au8SSID[0] = u8SsidLen; - m2m_memcpy(&strApId.au8SSID[1], (uint8*)pcSsid, u8SsidLen); - } - else + if(pcSsid != NULL) + { + if(u8SsidLen >= M2M_MAX_SSID_LEN) + return M2M_ERR_INVALID_ARG; + strApId.au8SSID[0] = u8SsidLen; + m2m_memcpy(&strApId.au8SSID[1], (uint8 *)pcSsid, u8SsidLen); + } + else #endif - strApId.au8SSID[0] = 0xFF; // Special value used to cause fw to delete all entries. - return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQRSP_DELETE_APID, (uint8*)&strApId, sizeof(tstrM2mWifiApId), NULL, 0, 0); + strApId.au8SSID[0] = 0xFF; // Special value used to cause fw to delete all entries. + return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQRSP_DELETE_APID, (uint8 *)&strApId, sizeof(tstrM2mWifiApId), NULL, 0, 0); } sint8 m2m_wifi_default_connect(void) { - return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_DEFAULT_CONNECT, NULL, 0,NULL, 0,0); + return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_DEFAULT_CONNECT, NULL, 0, NULL, 0, 0); } /*************************************************************************************************/ /* WIFI CONNECT INTERNAL FUNCTIONS */ /*************************************************************************************************/ static sint8 m2m_wifi_connect_prepare_msg( - tenuCredStoreOption enuCredStoreOption, - tenuM2mSecType enuAuthType, - uint16 u16AuthSize, - tstrNetworkId *pstrNetworkId, - tstrM2mWifiConnHdr *pstrWifiConn + tenuCredStoreOption enuCredStoreOption, + tenuM2mSecType enuAuthType, + uint16 u16AuthSize, + tstrNetworkId *pstrNetworkId, + tstrM2mWifiConnHdr *pstrWifiConn ) { - sint8 ret = M2M_ERR_FAIL; - uint16 u16CredSize = sizeof(tstrM2mConnCredCmn) + u16AuthSize; + sint8 ret = M2M_ERR_FAIL; + uint16 u16CredSize = sizeof(tstrM2mConnCredCmn) + u16AuthSize; - /* Check application params. */ - if ( - (pstrNetworkId == NULL) - || (pstrNetworkId->pu8Ssid == NULL) - || (pstrNetworkId->u8SsidLen >= M2M_MAX_SSID_LEN) - ) - goto INVALID_ARG; + /* Check application params. */ + if( + (pstrNetworkId == NULL) + || (pstrNetworkId->pu8Ssid == NULL) + || (pstrNetworkId->u8SsidLen >= M2M_MAX_SSID_LEN) + ) + goto INVALID_ARG; - if (pstrWifiConn != NULL) - { - tstrM2mConnCredHdr *pstrHdr = &pstrWifiConn->strConnCredHdr; - tstrM2mConnCredCmn *pstrCmn = &pstrWifiConn->strConnCredCmn; + if(pstrWifiConn != NULL) + { + tstrM2mConnCredHdr *pstrHdr = &pstrWifiConn->strConnCredHdr; + tstrM2mConnCredCmn *pstrCmn = &pstrWifiConn->strConnCredCmn; - m2m_memset((uint8*)pstrWifiConn, 0, sizeof(tstrM2mWifiConnHdr)); + m2m_memset((uint8 *)pstrWifiConn, 0, sizeof(tstrM2mWifiConnHdr)); - pstrHdr->u16CredSize = u16CredSize; - switch (enuCredStoreOption) - { - case WIFI_CRED_SAVE_ENCRYPTED: - pstrHdr->u8CredStoreFlags |= M2M_CRED_ENCRYPT_FLAG; - // intentional fall through... - case WIFI_CRED_SAVE_UNENCRYPTED: - pstrHdr->u8CredStoreFlags |= M2M_CRED_STORE_FLAG; - // intentional fall through... - case WIFI_CRED_DONTSAVE: - break; - default: - goto INVALID_ARG; - } + pstrHdr->u16CredSize = u16CredSize; + switch(enuCredStoreOption) + { + case WIFI_CRED_SAVE_ENCRYPTED: + pstrHdr->u8CredStoreFlags |= M2M_CRED_ENCRYPT_FLAG; + // intentional fall through... + case WIFI_CRED_SAVE_UNENCRYPTED: + pstrHdr->u8CredStoreFlags |= M2M_CRED_STORE_FLAG; + // intentional fall through... + case WIFI_CRED_DONTSAVE: + break; + default: + goto INVALID_ARG; + } - if (pstrNetworkId->enuChannel == M2M_WIFI_CH_ALL) - pstrHdr->u8Channel = (uint8)(pstrNetworkId->enuChannel); - else if ((pstrNetworkId->enuChannel <= M2M_WIFI_CH_14) && (pstrNetworkId->enuChannel >= M2M_WIFI_CH_1)) - pstrHdr->u8Channel = (uint8)(pstrNetworkId->enuChannel) - 1; - else - goto INVALID_ARG; + if(pstrNetworkId->enuChannel == M2M_WIFI_CH_ALL) + pstrHdr->u8Channel = (uint8)(pstrNetworkId->enuChannel); + else if((pstrNetworkId->enuChannel <= M2M_WIFI_CH_14) && (pstrNetworkId->enuChannel >= M2M_WIFI_CH_1)) + pstrHdr->u8Channel = (uint8)(pstrNetworkId->enuChannel) - 1; + else + goto INVALID_ARG; - if ((enuAuthType == M2M_WIFI_SEC_INVALID) || (enuAuthType >= M2M_WIFI_NUM_AUTH_TYPES)) - goto INVALID_ARG; - pstrCmn->u8AuthType = (uint8)enuAuthType; + if((enuAuthType == M2M_WIFI_SEC_INVALID) || (enuAuthType >= M2M_WIFI_NUM_AUTH_TYPES)) + goto INVALID_ARG; + pstrCmn->u8AuthType = (uint8)enuAuthType; - pstrCmn->u8SsidLen = pstrNetworkId->u8SsidLen; - m2m_memcpy(pstrCmn->au8Ssid, pstrNetworkId->pu8Ssid, pstrNetworkId->u8SsidLen); - if (pstrNetworkId->pu8Bssid != NULL) - { - pstrCmn->u8Options = M2M_WIFI_CONN_BSSID_FLAG; - m2m_memcpy(pstrCmn->au8Bssid, pstrNetworkId->pu8Bssid, M2M_MAC_ADDRES_LEN); - } - /* Everything is ok, set return value. */ - ret = M2M_SUCCESS; - } - return ret; + pstrCmn->u8SsidLen = pstrNetworkId->u8SsidLen; + m2m_memcpy(pstrCmn->au8Ssid, pstrNetworkId->pu8Ssid, pstrNetworkId->u8SsidLen); + if(pstrNetworkId->pu8Bssid != NULL) + { + pstrCmn->u8Options = M2M_WIFI_CONN_BSSID_FLAG; + m2m_memcpy(pstrCmn->au8Bssid, pstrNetworkId->pu8Bssid, M2M_MAC_ADDRES_LEN); + } + /* Everything is ok, set return value. */ + ret = M2M_SUCCESS; + } + return ret; INVALID_ARG: - return M2M_ERR_INVALID_ARG; + return M2M_ERR_INVALID_ARG; } -/* Convert hexchar to value 0-15 */ -static uint8 hexchar_2_val(uint8 ch) -{ - ch -= 0x30; - if (ch <= 9) - return ch; - ch |= 0x20; - ch -= 0x31; - if (ch <= 5) - return ch + 10; - return 0xFF; - } -/* Convert hexstring to bytes */ -static sint8 hexstr_2_bytes(uint8 *pu8Out, uint8 *pu8In, uint8 u8SizeOut) - { - while (u8SizeOut--) - { - uint8 u8Out = hexchar_2_val(*pu8In++); - if (u8Out > 0xF) - return M2M_ERR_INVALID_ARG; - *pu8Out = u8Out * 0x10; - u8Out = hexchar_2_val(*pu8In++); - if (u8Out > 0xF) - return M2M_ERR_INVALID_ARG; - *pu8Out += u8Out; - pu8Out++; - } - return M2M_SUCCESS; - } - /*************************************************************************************************/ /* WIFI CONNECT APIS */ /*************************************************************************************************/ sint8 m2m_wifi_connect_open( - tenuCredStoreOption enuCredStoreOption, - tstrNetworkId *pstrNetworkId + tenuCredStoreOption enuCredStoreOption, + tstrNetworkId *pstrNetworkId ) - { - sint8 ret = M2M_ERR_INVALID_ARG; - tstrM2mWifiConnHdr strConnHdr; +{ + sint8 ret = M2M_ERR_INVALID_ARG; + tstrM2mWifiConnHdr strConnHdr; - ret = m2m_wifi_connect_prepare_msg(enuCredStoreOption, M2M_WIFI_SEC_OPEN, 0, pstrNetworkId, &strConnHdr); - if (ret == M2M_SUCCESS) - { - ret = hif_send( M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_CONN, - (uint8*)&strConnHdr, sizeof(strConnHdr), - NULL, 0, 0); - } - return ret; - } + ret = m2m_wifi_connect_prepare_msg(enuCredStoreOption, M2M_WIFI_SEC_OPEN, 0, pstrNetworkId, &strConnHdr); + if(ret == M2M_SUCCESS) + { + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_CONN, + (uint8 *)&strConnHdr, sizeof(strConnHdr), + NULL, 0, 0); + } + return ret; +} sint8 m2m_wifi_connect_wep( - tenuCredStoreOption enuCredStoreOption, - tstrNetworkId *pstrNetworkId, - tstrAuthWep *pstrAuthWep + tenuCredStoreOption enuCredStoreOption, + tstrNetworkId *pstrNetworkId, + tstrAuthWep *pstrAuthWep ) { - sint8 ret = M2M_ERR_INVALID_ARG; - - if ( - (pstrAuthWep != NULL) && (pstrAuthWep->pu8WepKey != NULL) - && (pstrAuthWep->u8KeyIndx > 0) && (pstrAuthWep->u8KeyIndx <= WEP_KEY_MAX_INDEX) - && ((pstrAuthWep->u8KeySz == WEP_104_KEY_STRING_SIZE) || (pstrAuthWep->u8KeySz == WEP_40_KEY_STRING_SIZE)) - ) - { - tstrM2mWifiConnHdr strConnHdr; - - ret = m2m_wifi_connect_prepare_msg( enuCredStoreOption, - M2M_WIFI_SEC_WEP, - sizeof(tstrM2mWifiWep), - pstrNetworkId, - &strConnHdr); - - if (ret == M2M_SUCCESS) - { - tstrM2mWifiWep pstrWep; - pstrWep.u8KeyIndex = pstrAuthWep->u8KeyIndx - 1; - pstrWep.u8KeyLen = pstrAuthWep->u8KeySz/2; - hexstr_2_bytes(pstrWep.au8WepKey, (pstrAuthWep->pu8WepKey), pstrWep.u8KeyLen); - - ret = hif_send( M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_CONN | M2M_REQ_DATA_PKT, - (uint8*)&strConnHdr, sizeof(tstrM2mWifiConnHdr), - (uint8*)&pstrWep, sizeof(tstrM2mWifiWep), sizeof(tstrM2mWifiConnHdr)); - } - } - return ret; - } + /* As of 19.7.5 the WEP protocol is deprecated */ + return M2M_ERR_INVALID; +} sint8 m2m_wifi_connect_psk( - tenuCredStoreOption enuCredStoreOption, - tstrNetworkId *pstrNetworkId, - tstrAuthPsk *pstrAuthPsk + tenuCredStoreOption enuCredStoreOption, + tstrNetworkId *pstrNetworkId, + tstrAuthPsk *pstrAuthPsk ) { - sint8 ret = M2M_ERR_INVALID_ARG; + sint8 ret = M2M_ERR_INVALID_ARG; - if (pstrAuthPsk != NULL) - { - tstrM2mWifiConnHdr strConnHdr; + if(pstrAuthPsk != NULL) + { + tstrM2mWifiConnHdr strConnHdr; - ret = m2m_wifi_connect_prepare_msg( enuCredStoreOption, - M2M_WIFI_SEC_WPA_PSK, - sizeof(tstrM2mWifiPsk), - pstrNetworkId, - &strConnHdr); + ret = m2m_wifi_connect_prepare_msg(enuCredStoreOption, + M2M_WIFI_SEC_WPA_PSK, + sizeof(tstrM2mWifiPsk), + pstrNetworkId, + &strConnHdr); - if (ret == M2M_SUCCESS) - { - tstrM2mWifiPsk pstrPsk; - m2m_memset((uint8*)&pstrPsk, 0, sizeof(tstrM2mWifiPsk)); - if (pstrAuthPsk->pu8Psk != NULL) + if(ret == M2M_SUCCESS) + { + tstrM2mWifiPsk *pstrPsk = (tstrM2mWifiPsk *) fb_alloc(sizeof(tstrM2mWifiPsk), 0); + if(pstrPsk != NULL) { - if (pstrAuthPsk->pu8Passphrase != NULL) - ret = M2M_ERR_INVALID_ARG; - else + m2m_memset((uint8 *)pstrPsk, 0, sizeof(tstrM2mWifiPsk)); + if(pstrAuthPsk->pu8Psk != NULL) { - pstrPsk.u8PassphraseLen = M2M_MAX_PSK_LEN-1; - /* Use hexstr_2_bytes to verify pu8Psk input. */ - if (M2M_SUCCESS != hexstr_2_bytes(pstrPsk.au8Passphrase, pstrAuthPsk->pu8Psk, pstrPsk.u8PassphraseLen/2)) + if(pstrAuthPsk->pu8Passphrase != NULL) ret = M2M_ERR_INVALID_ARG; - m2m_memcpy(pstrPsk.au8Passphrase, pstrAuthPsk->pu8Psk, pstrPsk.u8PassphraseLen); + else + { + pstrPsk->u8PassphraseLen = M2M_MAX_PSK_LEN-1; + /* Use hexstr_2_bytes to verify pu8Psk input. */ + if(M2M_SUCCESS != hexstr_2_bytes(pstrPsk->au8Passphrase, pstrAuthPsk->pu8Psk, pstrPsk->u8PassphraseLen/2)) + ret = M2M_ERR_INVALID_ARG; + m2m_memcpy(pstrPsk->au8Passphrase, pstrAuthPsk->pu8Psk, pstrPsk->u8PassphraseLen); + } } - } - else if (pstrAuthPsk->pu8Passphrase != NULL) - { - if (pstrAuthPsk->u8PassphraseLen > M2M_MAX_PSK_LEN-1) - ret = M2M_ERR_INVALID_ARG; - else + else if(pstrAuthPsk->pu8Passphrase != NULL) { - pstrPsk.u8PassphraseLen = pstrAuthPsk->u8PassphraseLen; - m2m_memcpy(pstrPsk.au8Passphrase, pstrAuthPsk->pu8Passphrase, pstrPsk.u8PassphraseLen); + if(pstrAuthPsk->u8PassphraseLen > M2M_MAX_PSK_LEN-1) + ret = M2M_ERR_INVALID_ARG; + else + { + pstrPsk->u8PassphraseLen = pstrAuthPsk->u8PassphraseLen; + m2m_memcpy(pstrPsk->au8Passphrase, pstrAuthPsk->pu8Passphrase, pstrPsk->u8PassphraseLen); + } } + else + ret = M2M_ERR_INVALID_ARG; + if(ret == M2M_SUCCESS) + { + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_CONN | M2M_REQ_DATA_PKT, + (uint8 *)&strConnHdr, sizeof(tstrM2mWifiConnHdr), + (uint8 *)pstrPsk, sizeof(tstrM2mWifiPsk), sizeof(tstrM2mWifiConnHdr)); + } + fb_free(); } else - ret = M2M_ERR_INVALID_ARG; - if (ret == M2M_SUCCESS) - { - ret = hif_send( M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_CONN | M2M_REQ_DATA_PKT, - (uint8*)&strConnHdr, sizeof(tstrM2mWifiConnHdr), - (uint8*)&pstrPsk, sizeof(tstrM2mWifiPsk), sizeof(tstrM2mWifiConnHdr)); - } + ret = M2M_ERR_MEM_ALLOC; } - } - return ret; + } + return ret; +} + +sint8 m2m_wifi_1x_set_option(tenu1xOption enuOptionName, const void *pOptionValue, size_t OptionLen) +{ + if((pOptionValue == NULL) && (OptionLen > 0)) + return M2M_ERR_INVALID_ARG; + switch(enuOptionName) + { + case WIFI_1X_BYPASS_SERVER_AUTH: + if(OptionLen != sizeof(int)) + return M2M_ERR_INVALID_ARG; + switch(*(int*)pOptionValue) + { + case 1: + gu321xTlsHsFlags &= ~WIFI_1X_TLS_HS_FLAGS_PEER_AUTH; + break; + case 0: + gu321xTlsHsFlags |= WIFI_1X_TLS_HS_FLAGS_PEER_AUTH; + break; + default: + return M2M_ERR_INVALID_ARG; + } + break; + case WIFI_1X_TIME_VERIF_MODE: + if(OptionLen != sizeof(tenuSslCertExpSettings)) + return M2M_ERR_INVALID_ARG; + switch(*(tenuSslCertExpSettings*)pOptionValue) + { + case SSL_CERT_EXP_CHECK_DISABLE: + gu321xTlsHsFlags &= ~WIFI_1X_TLS_HS_FLAGS_PEER_CERTTIMECHECK; + gu321xTlsHsFlags &= ~WIFI_1X_TLS_HS_FLAGS_REQUIRE_TIME; + break; + case SSL_CERT_EXP_CHECK_ENABLE: + gu321xTlsHsFlags |= WIFI_1X_TLS_HS_FLAGS_PEER_CERTTIMECHECK; + gu321xTlsHsFlags |= WIFI_1X_TLS_HS_FLAGS_REQUIRE_TIME; + break; + case SSL_CERT_EXP_CHECK_EN_IF_SYS_TIME: + gu321xTlsHsFlags |= WIFI_1X_TLS_HS_FLAGS_PEER_CERTTIMECHECK; + gu321xTlsHsFlags &= ~WIFI_1X_TLS_HS_FLAGS_REQUIRE_TIME; + break; + default: + return M2M_ERR_INVALID_ARG; + } + break; + case WIFI_1X_SESSION_CACHING: + if(OptionLen != sizeof(int)) + return M2M_ERR_INVALID_ARG; + switch(*(int*)pOptionValue) + { + case 1: + gu321xTlsHsFlags |= WIFI_1X_TLS_HS_FLAGS_SESSION_CACHING; + break; + case 0: + gu321xTlsHsFlags &= ~WIFI_1X_TLS_HS_FLAGS_SESSION_CACHING; + break; + default: + return M2M_ERR_INVALID_ARG; + } + break; + case WIFI_1X_SPECIFIC_ROOTCERT: + switch(OptionLen) + { + case 20: + gu321xTlsHsFlags |= WIFI_1X_TLS_HS_FLAGS_SPECIFY_ROOTCERT; + m2m_memcpy(gau81xRootSha1, (uint8*)pOptionValue, sizeof(gau81xRootSha1)); + break; + case 0: + gu321xTlsHsFlags &= ~WIFI_1X_TLS_HS_FLAGS_SPECIFY_ROOTCERT; + m2m_memset(gau81xRootSha1, 0, sizeof(gau81xRootSha1)); + break; + default: + return M2M_ERR_INVALID_ARG; + } + break; + default: + return M2M_ERR_INVALID_ARG; + } + return M2M_SUCCESS; +} + +sint8 m2m_wifi_1x_get_option(tenu1xOption enuOptionName, void *pOptionValue, size_t *pOptionLen) +{ + if(pOptionValue == NULL) + return M2M_ERR_INVALID_ARG; + switch(enuOptionName) + { + case WIFI_1X_BYPASS_SERVER_AUTH: + if(*pOptionLen < sizeof(int)) + return M2M_ERR_INVALID_ARG; + *pOptionLen = sizeof(int); + *(int*)pOptionValue = (gu321xTlsHsFlags & WIFI_1X_TLS_HS_FLAGS_PEER_AUTH) ? 0 : 1; + break; + case WIFI_1X_TIME_VERIF_MODE: + if(*pOptionLen < sizeof(tenuSslCertExpSettings)) + return M2M_ERR_INVALID_ARG; + *pOptionLen = sizeof(tenuSslCertExpSettings); + if(!(gu321xTlsHsFlags & WIFI_1X_TLS_HS_FLAGS_PEER_CERTTIMECHECK)) + *(tenuSslCertExpSettings*)pOptionValue = SSL_CERT_EXP_CHECK_DISABLE; + else if(gu321xTlsHsFlags & WIFI_1X_TLS_HS_FLAGS_REQUIRE_TIME) + *(tenuSslCertExpSettings*)pOptionValue = SSL_CERT_EXP_CHECK_ENABLE; + else + *(tenuSslCertExpSettings*)pOptionValue = SSL_CERT_EXP_CHECK_EN_IF_SYS_TIME; + break; + case WIFI_1X_SESSION_CACHING: + if(*pOptionLen < sizeof(int)) + return M2M_ERR_INVALID_ARG; + *pOptionLen = sizeof(int); + *(int*)pOptionValue = (gu321xTlsHsFlags & WIFI_1X_TLS_HS_FLAGS_SESSION_CACHING) ? 1 : 0; + break; + case WIFI_1X_SPECIFIC_ROOTCERT: + if(gu321xTlsHsFlags & WIFI_1X_TLS_HS_FLAGS_SPECIFY_ROOTCERT) + { + if(*pOptionLen < sizeof(gau81xRootSha1)) + return M2M_ERR_INVALID_ARG; + *pOptionLen = sizeof(gau81xRootSha1); + m2m_memcpy((uint8*)pOptionValue, gau81xRootSha1, sizeof(gau81xRootSha1)); + } + else + *pOptionLen = 0; + break; + default: + return M2M_ERR_INVALID_ARG; + } + return M2M_SUCCESS; } sint8 m2m_wifi_connect_1x_mschap2( - tenuCredStoreOption enuCredStoreOption, - tstrNetworkId *pstrNetworkId, - tstrAuth1xMschap2 *pstrAuth1xMschap2 + tenuCredStoreOption enuCredStoreOption, + tstrNetworkId *pstrNetworkId, + tstrAuth1xMschap2 *pstrAuth1xMschap2 ) { - sint8 ret = M2M_ERR_INVALID_ARG; - if (pstrAuth1xMschap2 != NULL) - { - if (pstrAuth1xMschap2->pu8Domain == NULL) - pstrAuth1xMschap2->u16DomainLen = 0; - if ( - (pstrAuth1xMschap2->pu8UserName != NULL) - && (pstrAuth1xMschap2->pu8Password != NULL) - && ((uint32)(pstrAuth1xMschap2->u16DomainLen) + pstrAuth1xMschap2->u16UserNameLen <= M2M_AUTH_1X_USER_LEN_MAX) - && (pstrAuth1xMschap2->u16PasswordLen <= M2M_AUTH_1X_PASSWORD_LEN_MAX) - ) - { - tstrM2mWifiConnHdr strConnHdr; - uint16 u16AuthSize = sizeof(tstrM2mWifi1xHdr) + - pstrAuth1xMschap2->u16DomainLen + - pstrAuth1xMschap2->u16UserNameLen + - pstrAuth1xMschap2->u16PasswordLen; + sint8 ret = M2M_ERR_INVALID_ARG; + if(pstrAuth1xMschap2 != NULL) + { + if(pstrAuth1xMschap2->pu8Domain == NULL) + pstrAuth1xMschap2->u16DomainLen = 0; + if( + (pstrAuth1xMschap2->pu8UserName != NULL) + && (pstrAuth1xMschap2->pu8Password != NULL) + && ((uint32)(pstrAuth1xMschap2->u16DomainLen) + pstrAuth1xMschap2->u16UserNameLen <= M2M_AUTH_1X_USER_LEN_MAX) + && (pstrAuth1xMschap2->u16PasswordLen <= M2M_AUTH_1X_PASSWORD_LEN_MAX) + ) + { + tstrM2mWifiConnHdr strConnHdr; + uint16 u16AuthSize = sizeof(tstrM2mWifi1xHdr) + + pstrAuth1xMschap2->u16DomainLen + + pstrAuth1xMschap2->u16UserNameLen + + pstrAuth1xMschap2->u16PasswordLen; - ret = m2m_wifi_connect_prepare_msg( enuCredStoreOption, - M2M_WIFI_SEC_802_1X, - u16AuthSize, - pstrNetworkId, - &strConnHdr); + ret = m2m_wifi_connect_prepare_msg(enuCredStoreOption, + M2M_WIFI_SEC_802_1X, + u16AuthSize, + pstrNetworkId, + &strConnHdr); - if (ret == M2M_SUCCESS) - { - tstrM2mWifi1xHdr *pstr1xHdr = (tstrM2mWifi1xHdr*)fb_alloc(u16AuthSize, 0); - if (pstr1xHdr != NULL) - { - uint8 *pu8AuthPtr = pstr1xHdr->au81xAuthDetails; - m2m_memset((uint8*)pstr1xHdr, 0, u16AuthSize); + if(ret == M2M_SUCCESS) + { + tstrM2mWifi1xHdr *pstr1xHdr = (tstrM2mWifi1xHdr *) fb_alloc(u16AuthSize, 0); + if(pstr1xHdr != NULL) + { + uint8 *pu8AuthPtr = pstr1xHdr->au81xAuthDetails; + m2m_memset((uint8 *)pstr1xHdr, 0, u16AuthSize); - pstr1xHdr->u8Flags = M2M_802_1X_MSCHAP2_FLAG; - if (pstrAuth1xMschap2->bUnencryptedUserName == true) - pstr1xHdr->u8Flags |= M2M_802_1X_UNENCRYPTED_USERNAME_FLAG; - if (pstrAuth1xMschap2->bPrependDomain == true) - pstr1xHdr->u8Flags |= M2M_802_1X_PREPEND_DOMAIN_FLAG; + pstr1xHdr->u8Flags = M2M_802_1X_MSCHAP2_FLAG; + if(pstrAuth1xMschap2->bUnencryptedUserName == true) + pstr1xHdr->u8Flags |= M2M_802_1X_UNENCRYPTED_USERNAME_FLAG; + if(pstrAuth1xMschap2->bPrependDomain == true) + pstr1xHdr->u8Flags |= M2M_802_1X_PREPEND_DOMAIN_FLAG; - pstr1xHdr->u8DomainLength = 0; - if (pstrAuth1xMschap2->pu8Domain != NULL) - { - pstr1xHdr->u8DomainLength = (uint8)(pstrAuth1xMschap2->u16DomainLen); - m2m_memcpy(pu8AuthPtr, pstrAuth1xMschap2->pu8Domain, pstr1xHdr->u8DomainLength); - pu8AuthPtr += pstr1xHdr->u8DomainLength; - } + pstr1xHdr->u8HdrLength = sizeof(tstrM2mWifi1xHdr); + pstr1xHdr->u32TlsHsFlags = gu321xTlsHsFlags; + m2m_memcpy(pstr1xHdr->au8TlsSpecificRootNameSha1, gau81xRootSha1, sizeof(gau81xRootSha1)); - pstr1xHdr->u16UserNameLength = (pstrAuth1xMschap2->u16UserNameLen); - m2m_memcpy(pu8AuthPtr, pstrAuth1xMschap2->pu8UserName, pstr1xHdr->u16UserNameLength); - pu8AuthPtr += pstr1xHdr->u16UserNameLength; + pstr1xHdr->u8DomainLength = 0; + if(pstrAuth1xMschap2->pu8Domain != NULL) + { + pstr1xHdr->u8DomainLength = (uint8)(pstrAuth1xMschap2->u16DomainLen); + m2m_memcpy(pu8AuthPtr, pstrAuth1xMschap2->pu8Domain, pstr1xHdr->u8DomainLength); + pu8AuthPtr += pstr1xHdr->u8DomainLength; + } - pstr1xHdr->u16PrivateKeyOffset = pu8AuthPtr - pstr1xHdr->au81xAuthDetails; - pstr1xHdr->u16PrivateKeyLength = pstrAuth1xMschap2->u16PasswordLen; - m2m_memcpy(pu8AuthPtr, pstrAuth1xMschap2->pu8Password, pstr1xHdr->u16PrivateKeyLength); + pstr1xHdr->u8UserNameLength = (pstrAuth1xMschap2->u16UserNameLen); + m2m_memcpy(pu8AuthPtr, pstrAuth1xMschap2->pu8UserName, pstr1xHdr->u8UserNameLength); + pu8AuthPtr += pstr1xHdr->u8UserNameLength; - ret = hif_send( M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_CONN | M2M_REQ_DATA_PKT, - (uint8*)&strConnHdr, sizeof(tstrM2mWifiConnHdr), - (uint8*)pstr1xHdr, u16AuthSize, - sizeof(tstrM2mWifiConnHdr)); - fb_free(); - } - } - } - } - return ret; + pstr1xHdr->u16PrivateKeyOffset = pu8AuthPtr - pstr1xHdr->au81xAuthDetails; + pstr1xHdr->u16PrivateKeyLength = pstrAuth1xMschap2->u16PasswordLen; + m2m_memcpy(pu8AuthPtr, pstrAuth1xMschap2->pu8Password, pstr1xHdr->u16PrivateKeyLength); + + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_CONN | M2M_REQ_DATA_PKT, + (uint8 *)&strConnHdr, sizeof(tstrM2mWifiConnHdr), + (uint8 *)pstr1xHdr, u16AuthSize, + sizeof(tstrM2mWifiConnHdr)); + fb_free(); + } + else + ret = M2M_ERR_MEM_ALLOC; + } + } + } + return ret; } sint8 m2m_wifi_connect_1x_tls( -tenuCredStoreOption enuCredStoreOption, -tstrNetworkId *pstrNetworkId, -tstrAuth1xTls *pstrAuth1xTls + tenuCredStoreOption enuCredStoreOption, + tstrNetworkId *pstrNetworkId, + tstrAuth1xTls *pstrAuth1xTls ) { - sint8 ret = M2M_ERR_INVALID_ARG; - if (pstrAuth1xTls != NULL) - { - if (pstrAuth1xTls->pu8Domain == NULL) - pstrAuth1xTls->u16DomainLen = 0; - if ( - (pstrAuth1xTls->pu8UserName != NULL) - && (pstrAuth1xTls->pu8PrivateKey_Mod != NULL) - && (pstrAuth1xTls->pu8PrivateKey_Exp != NULL) - && (pstrAuth1xTls->pu8Certificate != NULL) - && ((uint32)(pstrAuth1xTls->u16DomainLen) + pstrAuth1xTls->u16UserNameLen <= M2M_AUTH_1X_USER_LEN_MAX) - && (pstrAuth1xTls->u16PrivateKeyLen <= M2M_AUTH_1X_PRIVATEKEY_LEN_MAX) - && (pstrAuth1xTls->u16CertificateLen <= M2M_AUTH_1X_CERT_LEN_MAX) - ) - { - tstrM2mWifiConnHdr strConnHdr; - uint16 u16AuthSize = sizeof(tstrM2mWifi1xHdr) + - pstrAuth1xTls->u16DomainLen + - pstrAuth1xTls->u16UserNameLen + - (2 * pstrAuth1xTls->u16PrivateKeyLen) + - pstrAuth1xTls->u16CertificateLen; + sint8 ret = M2M_ERR_INVALID_ARG; + if(pstrAuth1xTls != NULL) + { + if(pstrAuth1xTls->pu8Domain == NULL) + pstrAuth1xTls->u16DomainLen = 0; + if( + (pstrAuth1xTls->pu8UserName != NULL) + && (pstrAuth1xTls->pu8PrivateKey_Mod != NULL) + && (pstrAuth1xTls->pu8PrivateKey_Exp != NULL) + && (pstrAuth1xTls->pu8Certificate != NULL) + && ((uint32)(pstrAuth1xTls->u16DomainLen) + pstrAuth1xTls->u16UserNameLen <= M2M_AUTH_1X_USER_LEN_MAX) + && (pstrAuth1xTls->u16PrivateKeyLen <= M2M_AUTH_1X_PRIVATEKEY_LEN_MAX) + && (pstrAuth1xTls->u16CertificateLen <= M2M_AUTH_1X_CERT_LEN_MAX) + ) + { + tstrM2mWifiConnHdr strConnHdr; + uint16 u16AuthSize = sizeof(tstrM2mWifi1xHdr) + + pstrAuth1xTls->u16DomainLen + + pstrAuth1xTls->u16UserNameLen + + (2 * pstrAuth1xTls->u16PrivateKeyLen) + + pstrAuth1xTls->u16CertificateLen; - ret = m2m_wifi_connect_prepare_msg( enuCredStoreOption, - M2M_WIFI_SEC_802_1X, - u16AuthSize, - pstrNetworkId, - &strConnHdr); + ret = m2m_wifi_connect_prepare_msg(enuCredStoreOption, + M2M_WIFI_SEC_802_1X, + u16AuthSize, + pstrNetworkId, + &strConnHdr); - if (ret == M2M_SUCCESS) - { - uint16 u16Payload1Size = u16AuthSize - pstrAuth1xTls->u16CertificateLen; - tstrM2mWifi1xHdr *pstr1xHdr = (tstrM2mWifi1xHdr*)fb_alloc(u16Payload1Size, 0); - if (pstr1xHdr != NULL) - { - tstrM2mWifiAuthInfoHdr strInfoHdr = {0}; + if(ret == M2M_SUCCESS) + { + uint16 u16Payload1Size = u16AuthSize - pstrAuth1xTls->u16CertificateLen; + tstrM2mWifi1xHdr *pstr1xHdr = (tstrM2mWifi1xHdr *) fb_alloc(u16Payload1Size, 0); + if(pstr1xHdr != NULL) + { + tstrM2mWifiAuthInfoHdr strInfoHdr = {0}; - uint8 *pu8AuthPtr = pstr1xHdr->au81xAuthDetails; - m2m_memset((uint8*)pstr1xHdr, 0, u16Payload1Size); + uint8 *pu8AuthPtr = pstr1xHdr->au81xAuthDetails; + m2m_memset((uint8 *)pstr1xHdr, 0, u16Payload1Size); - pstr1xHdr->u8Flags = M2M_802_1X_TLS_FLAG; - if (pstrAuth1xTls->bUnencryptedUserName == true) - pstr1xHdr->u8Flags |= M2M_802_1X_UNENCRYPTED_USERNAME_FLAG; - if (pstrAuth1xTls->bPrependDomain == true) - pstr1xHdr->u8Flags |= M2M_802_1X_PREPEND_DOMAIN_FLAG; + pstr1xHdr->u8Flags = M2M_802_1X_TLS_FLAG; + if(pstrAuth1xTls->bUnencryptedUserName == true) + pstr1xHdr->u8Flags |= M2M_802_1X_UNENCRYPTED_USERNAME_FLAG; + if(pstrAuth1xTls->bPrependDomain == true) + pstr1xHdr->u8Flags |= M2M_802_1X_PREPEND_DOMAIN_FLAG; - pstr1xHdr->u8DomainLength = 0; - if (pstrAuth1xTls->pu8Domain != NULL) - { - pstr1xHdr->u8DomainLength = (uint8)(pstrAuth1xTls->u16DomainLen); - m2m_memcpy(pu8AuthPtr, pstrAuth1xTls->pu8Domain, pstr1xHdr->u8DomainLength); - pu8AuthPtr += pstr1xHdr->u8DomainLength; - } + pstr1xHdr->u8HdrLength = sizeof(tstrM2mWifi1xHdr); + pstr1xHdr->u32TlsHsFlags = gu321xTlsHsFlags; + m2m_memcpy(pstr1xHdr->au8TlsSpecificRootNameSha1, gau81xRootSha1, sizeof(gau81xRootSha1)); - pstr1xHdr->u16UserNameLength = (pstrAuth1xTls->u16UserNameLen); - m2m_memcpy(pu8AuthPtr, pstrAuth1xTls->pu8UserName, pstr1xHdr->u16UserNameLength); - pu8AuthPtr += pstr1xHdr->u16UserNameLength; + pstr1xHdr->u8DomainLength = 0; + if(pstrAuth1xTls->pu8Domain != NULL) + { + pstr1xHdr->u8DomainLength = (uint8)(pstrAuth1xTls->u16DomainLen); + m2m_memcpy(pu8AuthPtr, pstrAuth1xTls->pu8Domain, pstr1xHdr->u8DomainLength); + pu8AuthPtr += pstr1xHdr->u8DomainLength; + } - pstr1xHdr->u16PrivateKeyOffset = pu8AuthPtr - pstr1xHdr->au81xAuthDetails; - pstr1xHdr->u16PrivateKeyLength = pstrAuth1xTls->u16PrivateKeyLen; - m2m_memcpy(pu8AuthPtr, pstrAuth1xTls->pu8PrivateKey_Mod, pstr1xHdr->u16PrivateKeyLength); - pu8AuthPtr += pstr1xHdr->u16PrivateKeyLength; - m2m_memcpy(pu8AuthPtr, pstrAuth1xTls->pu8PrivateKey_Exp, pstr1xHdr->u16PrivateKeyLength); - pu8AuthPtr += pstr1xHdr->u16PrivateKeyLength; + pstr1xHdr->u8UserNameLength = (pstrAuth1xTls->u16UserNameLen); + m2m_memcpy(pu8AuthPtr, pstrAuth1xTls->pu8UserName, pstr1xHdr->u8UserNameLength); + pu8AuthPtr += pstr1xHdr->u8UserNameLength; - pstr1xHdr->u16CertificateOffset = pu8AuthPtr - pstr1xHdr->au81xAuthDetails; - pstr1xHdr->u16CertificateLength = pstrAuth1xTls->u16CertificateLen; + pstr1xHdr->u16PrivateKeyOffset = pu8AuthPtr - pstr1xHdr->au81xAuthDetails; + pstr1xHdr->u16PrivateKeyLength = pstrAuth1xTls->u16PrivateKeyLen; + m2m_memcpy(pu8AuthPtr, pstrAuth1xTls->pu8PrivateKey_Mod, pstr1xHdr->u16PrivateKeyLength); + pu8AuthPtr += pstr1xHdr->u16PrivateKeyLength; + m2m_memcpy(pu8AuthPtr, pstrAuth1xTls->pu8PrivateKey_Exp, pstr1xHdr->u16PrivateKeyLength); + pu8AuthPtr += pstr1xHdr->u16PrivateKeyLength; - strInfoHdr.u8Type = M2M_802_1X_TLS_CLIENT_CERTIFICATE; - strInfoHdr.u16InfoPos = pstr1xHdr->u16CertificateOffset; - strInfoHdr.u16InfoLen = pstr1xHdr->u16CertificateLength; - ret = hif_send( M2M_REQ_GROUP_WIFI, M2M_WIFI_IND_CONN_PARAM | M2M_REQ_DATA_PKT, - (uint8*)&strInfoHdr, sizeof(tstrM2mWifiAuthInfoHdr), - pstrAuth1xTls->pu8Certificate, pstrAuth1xTls->u16CertificateLen, - sizeof(tstrM2mWifiAuthInfoHdr)); + pstr1xHdr->u16CertificateOffset = pu8AuthPtr - pstr1xHdr->au81xAuthDetails; + pstr1xHdr->u16CertificateLength = pstrAuth1xTls->u16CertificateLen; - if (ret == M2M_SUCCESS) - { - ret = hif_send( M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_CONN | M2M_REQ_DATA_PKT, - (uint8*)&strConnHdr, sizeof(tstrM2mWifiConnHdr), - (uint8*)pstr1xHdr, u16Payload1Size, - sizeof(tstrM2mWifiConnHdr)); - } - fb_free(); - } - } - } - } - return ret; + strInfoHdr.u8Type = M2M_802_1X_TLS_CLIENT_CERTIFICATE; + strInfoHdr.u16InfoPos = pstr1xHdr->u16CertificateOffset; + strInfoHdr.u16InfoLen = pstr1xHdr->u16CertificateLength; + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_IND_CONN_PARAM | M2M_REQ_DATA_PKT, + (uint8 *)&strInfoHdr, sizeof(tstrM2mWifiAuthInfoHdr), + pstrAuth1xTls->pu8Certificate, pstrAuth1xTls->u16CertificateLen, + sizeof(tstrM2mWifiAuthInfoHdr)); + + if(ret == M2M_SUCCESS) + { + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_CONN | M2M_REQ_DATA_PKT, + (uint8 *)&strConnHdr, sizeof(tstrM2mWifiConnHdr), + (uint8 *)pstr1xHdr, u16Payload1Size, + sizeof(tstrM2mWifiConnHdr)); + } + fb_free(); + } + else + ret = M2M_ERR_MEM_ALLOC; + } + } + } + return ret; } sint8 m2m_wifi_connect(char *pcSsid, uint8 u8SsidLen, uint8 u8SecType, void *pvAuthInfo, uint16 u16Ch) @@ -939,923 +1017,960 @@ sint8 m2m_wifi_connect(char *pcSsid, uint8 u8SsidLen, uint8 u8SecType, void *pvA sint8 m2m_wifi_connect_sc(char *pcSsid, uint8 u8SsidLen, uint8 u8SecType, void *pvAuthInfo, uint16 u16Ch, uint8 u8NoSaveCred) { sint8 s8Ret = M2M_ERR_INVALID_ARG; - tstrNetworkId strNetworkId = {NULL, (uint8*)pcSsid, u8SsidLen, (tenuM2mScanCh)u16Ch}; + tstrNetworkId strNetworkId = {NULL, (uint8 *)pcSsid, u8SsidLen, (tenuM2mScanCh)u16Ch}; tenuCredStoreOption enuCredStoreOption = u8NoSaveCred ? WIFI_CRED_DONTSAVE : WIFI_CRED_SAVE_ENCRYPTED; /* This API does not support SSIDs which contain '\0'. If there is a '\0' character within the * first u8SsidLen characters, then assume that the input u8SsidLen was incorrect - set length * to strlen(pcSsid) and continue. This is to avoid a change from the behaviour of previously * released drivers. */ - if (u8SsidLen < M2M_MAX_SSID_LEN) - { - while (u8SsidLen--) - if (strNetworkId.pu8Ssid[u8SsidLen] == 0){ - strNetworkId.u8SsidLen = u8SsidLen;} - } + if(u8SsidLen < M2M_MAX_SSID_LEN) + while(u8SsidLen--) + if(strNetworkId.pu8Ssid[u8SsidLen] == 0) + strNetworkId.u8SsidLen = u8SsidLen; - switch ((tenuM2mSecType)u8SecType) - { - case M2M_WIFI_SEC_OPEN: + switch((tenuM2mSecType)u8SecType) + { + case M2M_WIFI_SEC_OPEN: s8Ret = m2m_wifi_connect_open(enuCredStoreOption, &strNetworkId); - break; - case M2M_WIFI_SEC_WPA_PSK: - if (pvAuthInfo != NULL) - { - tstrAuthPsk strAuthPsk = {NULL, NULL, 0}; - uint16 len = m2m_strlen((uint8*)pvAuthInfo); + break; + case M2M_WIFI_SEC_WPA_PSK: + if(pvAuthInfo != NULL) + { + tstrAuthPsk strAuthPsk = {NULL, NULL, 0}; + uint16 len = m2m_strlen((uint8 *)pvAuthInfo); - if (len == M2M_MAX_PSK_LEN-1) - { - strAuthPsk.pu8Psk = (uint8*)pvAuthInfo; - } - else - { - strAuthPsk.pu8Passphrase = (uint8*)pvAuthInfo; - strAuthPsk.u8PassphraseLen = len; - } + if(len == M2M_MAX_PSK_LEN-1) + { + strAuthPsk.pu8Psk = (uint8 *)pvAuthInfo; + } + else + { + strAuthPsk.pu8Passphrase = (uint8 *)pvAuthInfo; + strAuthPsk.u8PassphraseLen = len; + } s8Ret = m2m_wifi_connect_psk(enuCredStoreOption, &strNetworkId, &strAuthPsk); - } - break; - case M2M_WIFI_SEC_WEP: - if (pvAuthInfo != NULL) - { - tstrM2mWifiWepParams *pstrWepParams = (tstrM2mWifiWepParams*)pvAuthInfo; - tstrAuthWep strAuthWep = {pstrWepParams->au8WepKey, pstrWepParams->u8KeySz-1, pstrWepParams->u8KeyIndx}; + } + break; + case M2M_WIFI_SEC_WEP: + if(pvAuthInfo != NULL) + { + tstrM2mWifiWepParams *pstrWepParams = (tstrM2mWifiWepParams *)pvAuthInfo; + tstrAuthWep strAuthWep = {pstrWepParams->au8WepKey, pstrWepParams->u8KeySz-1, pstrWepParams->u8KeyIndx}; s8Ret = m2m_wifi_connect_wep(enuCredStoreOption, &strNetworkId, &strAuthWep); - } - break; - case M2M_WIFI_SEC_802_1X: - if (pvAuthInfo != NULL) - { - tstr1xAuthCredentials *pstr1xParams = (tstr1xAuthCredentials*)pvAuthInfo; - tstrAuth1xMschap2 strAuth1xMschap2 = {NULL, - pstr1xParams->au8UserName, - pstr1xParams->au8Passwd, - 0, + } + break; + case M2M_WIFI_SEC_802_1X: + if(pvAuthInfo != NULL) + { + tstr1xAuthCredentials *pstr1xParams = (tstr1xAuthCredentials *)pvAuthInfo; + tstrAuth1xMschap2 strAuth1xMschap2 = {NULL, + pstr1xParams->au8UserName, + pstr1xParams->au8Passwd, + 0, m2m_strlen(pstr1xParams->au8UserName), m2m_strlen(pstr1xParams->au8Passwd), false}; s8Ret = m2m_wifi_connect_1x_mschap2(enuCredStoreOption, &strNetworkId, &strAuth1xMschap2); - } - break; - default: - break; - } + } + break; + default: + break; + } return s8Ret; } sint8 m2m_wifi_disconnect(void) { - return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_DISCONNECT, NULL, 0, NULL, 0,0); + return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_DISCONNECT, NULL, 0, NULL, 0, 0); } sint8 m2m_wifi_set_mac_address(uint8 au8MacAddress[6]) { - tstrM2mSetMacAddress strTmp; - m2m_memcpy((uint8*) strTmp.au8Mac, (uint8*) au8MacAddress, 6); - return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_MAC_ADDRESS, - (uint8*) &strTmp, sizeof(tstrM2mSetMacAddress), NULL, 0,0); + tstrM2mSetMacAddress strTmp; + m2m_memcpy((uint8 *) strTmp.au8Mac, (uint8 *) au8MacAddress, 6); + return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_MAC_ADDRESS, + (uint8 *) &strTmp, sizeof(tstrM2mSetMacAddress), NULL, 0, 0); } -sint8 m2m_wifi_set_static_ip(tstrM2MIPConfig * pstrStaticIPConf) +sint8 m2m_wifi_set_static_ip(tstrM2MIPConfig *pstrStaticIPConf) { - pstrStaticIPConf->u32DNS = NM_BSP_B_L_32(pstrStaticIPConf->u32DNS); - pstrStaticIPConf->u32Gateway = NM_BSP_B_L_32(pstrStaticIPConf->u32Gateway); - pstrStaticIPConf->u32StaticIP = NM_BSP_B_L_32( - pstrStaticIPConf->u32StaticIP); - pstrStaticIPConf->u32SubnetMask = NM_BSP_B_L_32( - pstrStaticIPConf->u32SubnetMask); - return hif_send(M2M_REQ_GROUP_IP, M2M_IP_REQ_STATIC_IP_CONF, - (uint8*) pstrStaticIPConf, sizeof(tstrM2MIPConfig), NULL, 0,0); + pstrStaticIPConf->u32DNS = NM_BSP_B_L_32(pstrStaticIPConf->u32DNS); + pstrStaticIPConf->u32Gateway = NM_BSP_B_L_32(pstrStaticIPConf->u32Gateway); + pstrStaticIPConf->u32StaticIP = NM_BSP_B_L_32( + pstrStaticIPConf->u32StaticIP); + pstrStaticIPConf->u32SubnetMask = NM_BSP_B_L_32( + pstrStaticIPConf->u32SubnetMask); + return hif_send(M2M_REQ_GROUP_IP, M2M_IP_REQ_STATIC_IP_CONF, + (uint8 *) pstrStaticIPConf, sizeof(tstrM2MIPConfig), NULL, 0, 0); } sint8 m2m_wifi_request_dhcp_client(void) { - /*legacy API should be removed */ - return 0; + /*legacy API should be removed */ + return 0; } -sint8 m2m_wifi_request_dhcp_server(uint8* addr) +sint8 m2m_wifi_request_dhcp_server(uint8 *addr) { /*legacy API should be removed */ - return 0; + return 0; } + /*! -@fn NMI_API sint8 m2m_wifi_set_lsn_int(tstrM2mLsnInt * pstrM2mLsnInt); -@brief Set the Wi-Fi listen interval for power save operation. It is represented in units - of AP Beacon periods. -@param [in] pstrM2mLsnInt - Structure holding the listen interval configurations. -@return The function SHALL return 0 for success and a negative value otherwise. -@sa tstrM2mLsnInt , m2m_wifi_set_sleep_mode -@pre m2m_wifi_set_sleep_mode shall be called first -@warning The Function called once after initialization. +@fn NMI_API sint8 m2m_wifi_set_lsn_int(tstrM2mLsnInt * pstrM2mLsnInt); +@brief Set the Wi-Fi listen interval for power save operation. It is represented in units + of AP Beacon periods. +@param [in] pstrM2mLsnInt + Structure holding the listen interval configurations. +@return The function SHALL return 0 for success and a negative value otherwise. +@sa tstrM2mLsnInt , m2m_wifi_set_sleep_mode +@pre m2m_wifi_set_sleep_mode shall be called first +@warning The Function called once after initialization. */ -sint8 m2m_wifi_enable_dhcp(uint8 u8DhcpEn ) +sint8 m2m_wifi_enable_dhcp(uint8 u8DhcpEn) { - - uint8 u8Req; - u8Req = u8DhcpEn ? M2M_IP_REQ_ENABLE_DHCP : M2M_IP_REQ_DISABLE_DHCP; - return hif_send(M2M_REQ_GROUP_IP, u8Req, NULL, 0, NULL, 0, 0); - - + uint8 u8Req; + u8Req = u8DhcpEn ? M2M_IP_REQ_ENABLE_DHCP : M2M_IP_REQ_DISABLE_DHCP; + return hif_send(M2M_REQ_GROUP_IP, u8Req, NULL, 0, NULL, 0, 0); } -sint8 m2m_wifi_set_lsn_int(tstrM2mLsnInt* pstrM2mLsnInt) +sint8 m2m_wifi_set_lsn_int(tstrM2mLsnInt *pstrM2mLsnInt) { - return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_LSN_INT, (uint8*)pstrM2mLsnInt, sizeof(tstrM2mLsnInt), NULL, 0, 0); + return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_LSN_INT, (uint8 *)pstrM2mLsnInt, sizeof(tstrM2mLsnInt), NULL, 0, 0); } -sint8 m2m_wifi_set_cust_InfoElement(uint8* pau8M2mCustInfoElement) +sint8 m2m_wifi_set_cust_InfoElement(uint8 *pau8M2mCustInfoElement) { - - sint8 ret = M2M_ERR_FAIL; - if(pau8M2mCustInfoElement != NULL) - { - if((pau8M2mCustInfoElement[0] + 1) < M2M_CUST_IE_LEN_MAX) - { - ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_CUST_INFO_ELEMENT|M2M_REQ_DATA_PKT, (uint8*)pau8M2mCustInfoElement, pau8M2mCustInfoElement[0]+1, NULL, 0, 0); - } - } - return ret; + sint8 ret = M2M_ERR_FAIL; + if(pau8M2mCustInfoElement != NULL) + { + if((pau8M2mCustInfoElement[0] + 1) < M2M_CUST_IE_LEN_MAX) + { + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_CUST_INFO_ELEMENT|M2M_REQ_DATA_PKT, (uint8 *)pau8M2mCustInfoElement, pau8M2mCustInfoElement[0]+1, NULL, 0, 0); + } + } + return ret; } -sint8 m2m_wifi_set_scan_options(tstrM2MScanOption* ptstrM2MScanOption) +sint8 m2m_wifi_set_scan_options(tstrM2MScanOption *ptstrM2MScanOption) { - sint8 s8Ret = M2M_ERR_FAIL; - if(m2m_validate_scan_options (ptstrM2MScanOption) == M2M_SUCCESS) - { - s8Ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_SCAN_OPTION, (uint8*)ptstrM2MScanOption, sizeof(tstrM2MScanOption),NULL, 0,0); - } - return s8Ret; + sint8 s8Ret = M2M_ERR_FAIL; + if(m2m_validate_scan_options(ptstrM2MScanOption) == M2M_SUCCESS) + { + s8Ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_SCAN_OPTION, (uint8 *)ptstrM2MScanOption, sizeof(tstrM2MScanOption), NULL, 0, 0); + } + return s8Ret; } + +sint8 m2m_wifi_set_stop_scan_on_first(uint8 u8StopScanOption) +{ + sint8 s8Ret = M2M_ERR_FAIL; + + tstrM2MStopScanOption StopScanOption = { 0 }; + + if(1 >= u8StopScanOption) + { + StopScanOption.u8StopOnFirstResult = u8StopScanOption; + + s8Ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_STOP_SCAN_OPTION, (uint8 *)&StopScanOption, sizeof(tstrM2MStopScanOption), NULL, 0, 0); + + M2M_INFO("Scan will %s stop on first result.\n", StopScanOption.u8StopOnFirstResult ? "" : "NOT"); + } + else + { + s8Ret = M2M_ERR_INVALID_ARG; + } + + return s8Ret; +} + sint8 m2m_wifi_set_scan_region(uint16 ScanRegion) { - sint8 s8Ret = M2M_ERR_FAIL; - tstrM2MScanRegion strScanRegion; - strScanRegion.u16ScanRegion = ScanRegion; - s8Ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_SCAN_REGION, (uint8*)&strScanRegion, sizeof(tstrM2MScanRegion),NULL, 0,0); - return s8Ret; + sint8 s8Ret = M2M_ERR_FAIL; + tstrM2MScanRegion strScanRegion; + strScanRegion.u16ScanRegion = ScanRegion; + s8Ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_SCAN_REGION, (uint8 *)&strScanRegion, sizeof(tstrM2MScanRegion), NULL, 0, 0); + return s8Ret; } sint8 m2m_wifi_request_scan(uint8 ch) { - sint8 s8Ret = M2M_SUCCESS; + sint8 s8Ret = M2M_SUCCESS; - if(!gu8scanInProgress) - { - if(((ch >= M2M_WIFI_CH_1) && (ch <= M2M_WIFI_CH_14)) || (ch == M2M_WIFI_CH_ALL)) - { - tstrM2MScan strtmp; - strtmp.u8ChNum = ch; - s8Ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SCAN, (uint8*)&strtmp, sizeof(tstrM2MScan),NULL, 0,0); - if(s8Ret == M2M_SUCCESS) - { - gu8scanInProgress = 1; - } - } - else - { - s8Ret = M2M_ERR_INVALID_ARG; - } - } - else - { - s8Ret = M2M_ERR_SCAN_IN_PROGRESS; - } - return s8Ret; + if(!gu8scanInProgress) + { + if(((ch >= M2M_WIFI_CH_1) && (ch <= M2M_WIFI_CH_14)) || (ch == M2M_WIFI_CH_ALL)) + { + tstrM2MScan strtmp; + strtmp.u8ChNum = ch; + s8Ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SCAN, (uint8 *)&strtmp, sizeof(tstrM2MScan), NULL, 0, 0); + if(s8Ret == M2M_SUCCESS) + { + gu8scanInProgress = 1; + } + } + else + { + s8Ret = M2M_ERR_INVALID_ARG; + } + } + else + { + s8Ret = M2M_ERR_SCAN_IN_PROGRESS; + } + return s8Ret; } sint8 m2m_wifi_request_scan_passive(uint8 ch, uint16 scan_time) { - sint8 s8Ret = M2M_SUCCESS; + sint8 s8Ret = M2M_SUCCESS; - if(!gu8scanInProgress) - { - if(((ch >= M2M_WIFI_CH_1) && (ch <= M2M_WIFI_CH_14)) || (ch == M2M_WIFI_CH_ALL)) - { - tstrM2MScan strtmp; - strtmp.u8ChNum = ch; - - strtmp.u16PassiveScanTime = scan_time; - - s8Ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_PASSIVE_SCAN, (uint8*)&strtmp, sizeof(tstrM2MScan),NULL, 0,0); - if(s8Ret == M2M_SUCCESS) - { - gu8scanInProgress = 1; - } - } - else - { - s8Ret = M2M_ERR_INVALID_ARG; - } - } - else - { - s8Ret = M2M_ERR_SCAN_IN_PROGRESS; - } - return s8Ret; + if(!gu8scanInProgress) + { + if(((ch >= M2M_WIFI_CH_1) && (ch <= M2M_WIFI_CH_14)) || (ch == M2M_WIFI_CH_ALL)) + { + tstrM2MScan strtmp; + strtmp.u8ChNum = ch; + + strtmp.u16PassiveScanTime = scan_time; + + s8Ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_PASSIVE_SCAN, (uint8 *)&strtmp, sizeof(tstrM2MScan), NULL, 0, 0); + if(s8Ret == M2M_SUCCESS) + { + gu8scanInProgress = 1; + } + } + else + { + s8Ret = M2M_ERR_INVALID_ARG; + } + } + else + { + s8Ret = M2M_ERR_SCAN_IN_PROGRESS; + } + return s8Ret; } -sint8 m2m_wifi_request_scan_ssid_list(uint8 ch,uint8 * u8Ssidlist) +sint8 m2m_wifi_request_scan_ssid_list(uint8 ch, uint8 *u8Ssidlist) { - sint8 s8Ret = M2M_ERR_INVALID_ARG; + sint8 s8Ret = M2M_ERR_INVALID_ARG; - if(!gu8scanInProgress) - { - if((((ch >= M2M_WIFI_CH_1) && (ch <= M2M_WIFI_CH_14)) || (ch == M2M_WIFI_CH_ALL))&&(u8Ssidlist != NULL)) - { - tstrM2MScan strtmp; - uint16 u16Lsize = 0; - uint8 u8Apnum = u8Ssidlist[u16Lsize]; - if(u8Apnum <= MAX_HIDDEN_SITES) - { - u16Lsize++; - while(u8Apnum) - { - if(u8Ssidlist[u16Lsize] >= M2M_MAX_SSID_LEN){ - goto EXIT; - }else { - u16Lsize += u8Ssidlist[u16Lsize] + 1; - u8Apnum--; - } - } - strtmp.u8ChNum = ch; - s8Ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SCAN_SSID_LIST|M2M_REQ_DATA_PKT, (uint8*)&strtmp, sizeof(tstrM2MScan),u8Ssidlist, u16Lsize,sizeof(tstrM2MScan)); - if(s8Ret == M2M_SUCCESS) - { - gu8scanInProgress = 1; - } - } - } - } - else - { - s8Ret = M2M_ERR_SCAN_IN_PROGRESS; - } + if(!gu8scanInProgress) + { + if((((ch >= M2M_WIFI_CH_1) && (ch <= M2M_WIFI_CH_14)) || (ch == M2M_WIFI_CH_ALL))&&(u8Ssidlist != NULL)) + { + tstrM2MScan strtmp; + uint16 u16Lsize = 0; + uint8 u8Apnum = u8Ssidlist[u16Lsize]; + if(u8Apnum <= MAX_HIDDEN_SITES) + { + u16Lsize++; + while(u8Apnum) + { + if(u8Ssidlist[u16Lsize] >= M2M_MAX_SSID_LEN) { + goto EXIT; + } else { + u16Lsize += u8Ssidlist[u16Lsize] + 1; + u8Apnum--; + } + } + strtmp.u8ChNum = ch; + s8Ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SCAN_SSID_LIST|M2M_REQ_DATA_PKT, (uint8 *)&strtmp, sizeof(tstrM2MScan), u8Ssidlist, u16Lsize, sizeof(tstrM2MScan)); + if(s8Ret == M2M_SUCCESS) + { + gu8scanInProgress = 1; + } + } + } + } + else + { + s8Ret = M2M_ERR_SCAN_IN_PROGRESS; + } EXIT: - return s8Ret; + return s8Ret; } -sint8 m2m_wifi_wps(uint8 u8TriggerType,const char *pcPinNumber) +sint8 m2m_wifi_wps(uint8 u8TriggerType, const char *pcPinNumber) { - tstrM2MWPSConnect strtmp; + tstrM2MWPSConnect strtmp; - /* Stop Scan if it is ongoing. - */ - gu8scanInProgress = 0; - strtmp.u8TriggerType = u8TriggerType; - /*If WPS is using PIN METHOD*/ - if (u8TriggerType == WPS_PIN_TRIGGER) - m2m_memcpy ((uint8*)strtmp.acPinNumber,(uint8*) pcPinNumber,8); - return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_WPS, (uint8*)&strtmp,sizeof(tstrM2MWPSConnect), NULL, 0,0); + /* Stop Scan if it is ongoing. + */ + gu8scanInProgress = 0; + strtmp.u8TriggerType = u8TriggerType; + /*If WPS is using PIN METHOD*/ + if(u8TriggerType == WPS_PIN_TRIGGER) + m2m_memcpy((uint8 *)strtmp.acPinNumber, (uint8 *) pcPinNumber, 8); + return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_WPS, (uint8 *)&strtmp, sizeof(tstrM2MWPSConnect), NULL, 0, 0); } sint8 m2m_wifi_wps_disable(void) { - sint8 ret = M2M_SUCCESS; - ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_DISABLE_WPS, NULL,0, NULL, 0, 0); - return ret; + sint8 ret = M2M_SUCCESS; + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_DISABLE_WPS, NULL, 0, NULL, 0, 0); + return ret; } + /*! -@fn NMI_API sint8 m2m_wifi_req_client_ctrl(uint8 cmd); -@brief Send a command to the PS Client (An WINC1500 board running the ps_firmware), - if the PS client send any commands it will be received in wifi_cb M2M_WIFI_RESP_CLIENT_INFO -@param [in] cmd - Control command sent from PS Server to PS Client (command values defined by the application) -@return The function SHALL return M2M_SUCCESS for success and a negative value otherwise. -@sa m2m_wifi_req_server_init, M2M_WIFI_RESP_CLIENT_INFO -@pre m2m_wifi_req_server_init should be called first -@warning +@fn NMI_API sint8 m2m_wifi_req_client_ctrl(uint8 cmd) +@brief Send a command to the PS Client (An WINC board running the ps_firmware), + if the PS client send any commands it will be received in wifi_cb @ref M2M_WIFI_RESP_CLIENT_INFO. +@param[in] cmd + Control command sent from PS Server to PS Client (command values defined by the application). +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. +@sa m2m_wifi_req_server_init, M2M_WIFI_RESP_CLIENT_INFO +@pre m2m_wifi_req_server_init should be called first */ sint8 m2m_wifi_req_client_ctrl(uint8 u8Cmd) { - - sint8 ret = M2M_SUCCESS; + sint8 ret = M2M_SUCCESS; #ifdef _PS_SERVER_ - tstrM2Mservercmd strCmd; - strCmd.u8cmd = u8Cmd; - ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_CLIENT_CTRL, (uint8*)&strCmd, sizeof(tstrM2Mservercmd), NULL, 0, 0); + tstrM2Mservercmd strCmd; + strCmd.u8cmd = u8Cmd; + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_CLIENT_CTRL, (uint8 *)&strCmd, sizeof(tstrM2Mservercmd), NULL, 0, 0); #else - M2M_ERR("_PS_SERVER_ is not defined\n"); + M2M_ERR("_PS_SERVER_ is not defined\n"); #endif - return ret; + return ret; } + /*! -@fn NMI_API sint8 m2m_wifi_req_server_init(uint8 ch); -@brief Initialize the PS Server, The WINC1500 support Non secure communication with another WINC1500, - (SERVER/CLIENT) through one byte command (probe request and probe response) without any connection setup -@param [in] ch - Server listening channel -@return The function SHALL return M2M_SUCCESS for success and a negative value otherwise -@sa m2m_wifi_req_client_ctrl -@warning The server mode can't be used with any other modes (STA/P2P/AP) +@fn NMI_API sint8 m2m_wifi_req_server_init(uint8 ch) +@brief Initialize the PS Server, The WINC support non secure communication with another WINC, + (SERVER/CLIENT) through one byte command (probe request and probe response) without any connection setup. +@param[in] ch + Server listening channel +@return The function returns @ref M2M_SUCCESS for success and a negative value otherwise. +@sa m2m_wifi_req_client_ctrl +@warning The server mode can't be used with any other modes (STA/AP). */ sint8 m2m_wifi_req_server_init(uint8 ch) { - sint8 ret = M2M_SUCCESS; + sint8 ret = M2M_SUCCESS; #ifdef _PS_SERVER_ - tstrM2mServerInit strServer; - strServer.u8Channel = ch; - ret = hif_send(M2M_REQ_GROUP_WIFI,M2M_WIFI_REQ_SERVER_INIT, (uint8*)&strServer, sizeof(tstrM2mServerInit), NULL, 0, 0); + tstrM2mServerInit strServer; + strServer.u8Channel = ch; + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SERVER_INIT, (uint8 *)&strServer, sizeof(tstrM2mServerInit), NULL, 0, 0); #else - M2M_ERR("_PS_SERVER_ is not defined\n"); + M2M_ERR("_PS_SERVER_ is not defined\n"); #endif - return ret; + return ret; } + sint8 m2m_wifi_p2p(uint8 u8Channel) { - sint8 ret = M2M_SUCCESS; - if((u8Channel == M2M_WIFI_CH_1) || (u8Channel == M2M_WIFI_CH_6) || (u8Channel == M2M_WIFI_CH_11)) - { - tstrM2MP2PConnect strtmp; - strtmp.u8ListenChannel = u8Channel; - ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_ENABLE_P2P, (uint8*)&strtmp, sizeof(tstrM2MP2PConnect), NULL, 0,0); - } - else - { - M2M_ERR("Listen channel should only be M2M_WIFI_CH_1/6/11 \n"); - ret = M2M_ERR_FAIL; - } - return ret; + sint8 ret = M2M_SUCCESS; + if((u8Channel == M2M_WIFI_CH_1) || (u8Channel == M2M_WIFI_CH_6) || (u8Channel == M2M_WIFI_CH_11)) + { + tstrM2MP2PConnect strtmp; + strtmp.u8ListenChannel = u8Channel; + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_ENABLE_P2P, (uint8 *)&strtmp, sizeof(tstrM2MP2PConnect), NULL, 0, 0); + } + else + { + M2M_ERR("Listen channel should only be M2M_WIFI_CH_1/6/11 \n"); + ret = M2M_ERR_FAIL; + } + return ret; } + sint8 m2m_wifi_p2p_disconnect(void) { - sint8 ret = M2M_SUCCESS; - ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_DISABLE_P2P, NULL, 0, NULL, 0, 0); - return ret; + sint8 ret = M2M_SUCCESS; + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_DISABLE_P2P, NULL, 0, NULL, 0, 0); + return ret; } -sint8 m2m_wifi_enable_ap(CONST tstrM2MAPConfig* pstrM2MAPConfig) +sint8 m2m_wifi_enable_ap(CONST tstrM2MAPConfig *pstrM2MAPConfig) { - tstrM2MAPModeConfig strM2MAPModeConfig; + tstrM2MAPModeConfig strM2MAPModeConfig; - m2m_memcpy((uint8 *)&strM2MAPModeConfig.strApConfig, (uint8 *)pstrM2MAPConfig, sizeof(tstrM2MAPConfig)); - - m2m_memcpy(strM2MAPModeConfig.strApConfigExt.au8DefRouterIP, (uint8 *)pstrM2MAPConfig->au8DHCPServerIP, 4); - m2m_memcpy(strM2MAPModeConfig.strApConfigExt.au8DNSServerIP, (uint8 *)pstrM2MAPConfig->au8DHCPServerIP, 4); - strM2MAPModeConfig.strApConfigExt.au8SubnetMask[0] = 0; - - return m2m_wifi_enable_ap_ext(&strM2MAPModeConfig); + m2m_memcpy((uint8 *)&strM2MAPModeConfig.strApConfig, (uint8 *)pstrM2MAPConfig, sizeof(tstrM2MAPConfig)); + m2m_memcpy(strM2MAPModeConfig.strApConfigExt.au8DefRouterIP, pstrM2MAPConfig->au8DHCPServerIP, 4); + m2m_memcpy(strM2MAPModeConfig.strApConfigExt.au8DNSServerIP, pstrM2MAPConfig->au8DHCPServerIP, 4); + + strM2MAPModeConfig.strApConfigExt.au8SubnetMask[0] = 0; + + return m2m_wifi_enable_ap_ext(&strM2MAPModeConfig); } -sint8 m2m_wifi_enable_ap_ext(CONST tstrM2MAPModeConfig* pstrM2MAPModeConfig) +sint8 m2m_wifi_enable_ap_ext(CONST tstrM2MAPModeConfig *pstrM2MAPModeConfig) { - sint8 ret = M2M_ERR_FAIL; - if(M2M_SUCCESS == m2m_validate_ap_parameters(pstrM2MAPModeConfig)) - { - ret = hif_send(M2M_REQ_GROUP_WIFI, (M2M_REQ_DATA_PKT|M2M_WIFI_REQ_ENABLE_AP), NULL, 0, (uint8 *)pstrM2MAPModeConfig, sizeof(tstrM2MAPModeConfig), 0); - } - return ret; + sint8 ret = M2M_ERR_FAIL; + if(M2M_SUCCESS == m2m_validate_ap_parameters(pstrM2MAPModeConfig)) + { + ret = hif_send(M2M_REQ_GROUP_WIFI, (M2M_REQ_DATA_PKT|M2M_WIFI_REQ_ENABLE_AP), NULL, 0, (uint8 *)pstrM2MAPModeConfig, sizeof(tstrM2MAPModeConfig), 0); + } + return ret; } -sint8 m2m_wifi_set_gains(tstrM2mWifiGainsParams* pstrM2mGain) +sint8 m2m_wifi_set_gains(tstrM2mWifiGainsParams *pstrM2mGain) { - sint8 ret = M2M_ERR_FAIL; - if(pstrM2mGain != NULL) - { - ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_GAINS, (uint8 *)pstrM2mGain, sizeof(tstrM2mWifiGainsParams), NULL, 0, 0); - } - return ret; + sint8 ret = M2M_ERR_FAIL; + if(pstrM2mGain != NULL) + { + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_GAINS, (uint8 *)pstrM2mGain, sizeof(tstrM2mWifiGainsParams), NULL, 0, 0); + } + return ret; } + sint8 m2m_wifi_disable_ap(void) { - sint8 ret = M2M_SUCCESS; - ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_DISABLE_AP, NULL, 0, NULL, 0, 0); - return ret; + sint8 ret = M2M_SUCCESS; + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_DISABLE_AP, NULL, 0, NULL, 0, 0); + return ret; } + /*! @fn NMI_API sint8 m2m_wifi_req_curr_rssi(void); -@brief Request the current RSSI for the current connected AP, - the response received in wifi_cb M2M_WIFI_RESP_CURRENT_RSSI -@sa M2M_WIFI_RESP_CURRENT_RSSI +@brief Request the current RSSI for the current connected AP, + the response received in wifi_cb M2M_WIFI_RESP_CURRENT_RSSI +@sa M2M_WIFI_RESP_CURRENT_RSSI @return The function shall return M2M_SUCCESS for success and a negative value otherwise. */ sint8 m2m_wifi_req_curr_rssi(void) { - sint8 ret = M2M_SUCCESS; - ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_CURRENT_RSSI, NULL, 0, NULL,0, 0); - return ret; + sint8 ret = M2M_SUCCESS; + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_CURRENT_RSSI, NULL, 0, NULL, 0, 0); + return ret; } -sint8 m2m_wifi_send_ethernet_pkt(uint8* pu8Packet,uint16 u16PacketSize) +sint8 m2m_wifi_send_ethernet_pkt(uint8 *pu8Packet, uint16 u16PacketSize) { - sint8 s8Ret = -1; - if((pu8Packet != NULL)&&(u16PacketSize>0)) - { - tstrM2MWifiTxPacketInfo strTxPkt; + sint8 s8Ret = -1; + if((pu8Packet != NULL)&&(u16PacketSize>0)) + { + tstrM2MWifiTxPacketInfo strTxPkt; - strTxPkt.u16PacketSize = u16PacketSize; - strTxPkt.u16HeaderLength = M2M_ETHERNET_HDR_LEN; - s8Ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SEND_ETHERNET_PACKET | M2M_REQ_DATA_PKT, - (uint8*)&strTxPkt, sizeof(tstrM2MWifiTxPacketInfo), pu8Packet, u16PacketSize, M2M_ETHERNET_HDR_OFFSET - M2M_HIF_HDR_OFFSET); - } - return s8Ret; + strTxPkt.u16PacketSize = u16PacketSize; + strTxPkt.u16HeaderLength = M2M_ETHERNET_HDR_LEN; + s8Ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SEND_ETHERNET_PACKET | M2M_REQ_DATA_PKT, + (uint8 *)&strTxPkt, sizeof(tstrM2MWifiTxPacketInfo), pu8Packet, u16PacketSize, M2M_ETHERNET_HDR_OFFSET - M2M_HIF_HDR_OFFSET); + } + return s8Ret; } + /*! @fn NMI_API sint8 m2m_wifi_get_otp_mac_address(uint8 *pu8MacAddr, uint8 * pu8IsValid); @brief Request the MAC address stored on the OTP (one time programmable) memory of the device. - (the function is Blocking until response received) + (the function is Blocking until response received) @param [out] pu8MacAddr - Output MAC address buffer of 6 bytes size. Valid only if *pu8Valid=1. + Output MAC address buffer of 6 bytes size. Valid only if *pu8Valid=1. @param [out] pu8IsValid - A output boolean value to indicate the validity of pu8MacAddr in OTP. - Output zero if the OTP memory is not programmed, non-zero otherwise. + A output boolean value to indicate the validity of pu8MacAddr in OTP. + Output zero if the OTP memory is not programmed, non-zero otherwise. @return The function shall return M2M_SUCCESS for success and a negative value otherwise. -@sa m2m_wifi_get_mac_address +@sa m2m_wifi_get_mac_address @pre m2m_wifi_init required to call any WIFI/socket function */ -sint8 m2m_wifi_get_otp_mac_address(uint8 *pu8MacAddr, uint8* pu8IsValid) +sint8 m2m_wifi_get_otp_mac_address(uint8 *pu8MacAddr, uint8 *pu8IsValid) { - sint8 ret = M2M_SUCCESS; - ret = hif_chip_wake(); - if(ret == M2M_SUCCESS) - { - ret = nmi_get_otp_mac_address(pu8MacAddr, pu8IsValid); - if(ret == M2M_SUCCESS) - { - ret = hif_chip_sleep(); - } - } - return ret; + sint8 ret = M2M_SUCCESS; + ret = hif_chip_wake(); + if(ret == M2M_SUCCESS) + { + ret = nmi_get_otp_mac_address(pu8MacAddr, pu8IsValid); + if(ret == M2M_SUCCESS) + { + ret = hif_chip_sleep(); + } + } + return ret; } + /*! @fn NMI_API sint8 m2m_wifi_get_mac_address(uint8 *pu8MacAddr) @brief Request the current MAC address of the device (the working mac address). - (the function is Blocking until response received) + (the function is Blocking until response received) @param [out] pu8MacAddr - Output MAC address buffer of 6 bytes size. + Output MAC address buffer of 6 bytes size. @return The function shall return M2M_SUCCESS for success and a negative value otherwise. -@sa m2m_wifi_get_otp_mac_address +@sa m2m_wifi_get_otp_mac_address @pre m2m_wifi_init required to call any WIFI/socket function */ sint8 m2m_wifi_get_mac_address(uint8 *pu8MacAddr) { - sint8 ret = M2M_SUCCESS; - ret = hif_chip_wake(); - if(ret == M2M_SUCCESS) - { - ret = nmi_get_mac_address(pu8MacAddr); - if(ret == M2M_SUCCESS) - { - ret = hif_chip_sleep(); - } - } - - return ret; + sint8 ret = M2M_SUCCESS; + ret = hif_chip_wake(); + if(ret == M2M_SUCCESS) + { + ret = nmi_get_mac_address(pu8MacAddr); + if(ret == M2M_SUCCESS) + { + ret = hif_chip_sleep(); + } + } + return ret; } + /*! @fn NMI_API sint8 m2m_wifi_req_scan_result(uint8 index); -@brief Reads the AP information from the Scan Result list with the given index, - the response received in wifi_cb M2M_WIFI_RESP_SCAN_RESULT, - the response pointer should be casted with tstrM2mWifiscanResult structure -@param [in] index - Index for the requested result, the index range start from 0 till number of AP's found -@sa tstrM2mWifiscanResult,m2m_wifi_get_num_ap_found,m2m_wifi_request_scan +@brief Reads the AP information from the Scan Result list with the given index, + the response received in wifi_cb M2M_WIFI_RESP_SCAN_RESULT, + the response pointer should be casted with tstrM2mWifiscanResult structure +@param [in] index + Index for the requested result, the index range start from 0 till number of AP's found +@sa tstrM2mWifiscanResult,m2m_wifi_get_num_ap_found,m2m_wifi_request_scan @return The function shall return M2M_SUCCESS for success and a negative value otherwise -@pre m2m_wifi_request_scan need to be called first, then m2m_wifi_get_num_ap_found - to get the number of AP's found +@pre m2m_wifi_request_scan need to be called first, then m2m_wifi_get_num_ap_found + to get the number of AP's found @warning Function used only in STA mode only. the scan result updated only if scan request called, - else it will be cashed in firmware for the host scan request result, - which mean if large delay occur between the scan request and the scan result request, - the result will not be up-to-date + else it will be cashed in firmware for the host scan request result, + which mean if large delay occur between the scan request and the scan result request, + the result will not be up-to-date */ - sint8 m2m_wifi_req_scan_result(uint8 index) { - sint8 ret = M2M_SUCCESS; - tstrM2mReqScanResult strReqScanRlt; - strReqScanRlt.u8Index = index; - ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SCAN_RESULT, (uint8*) &strReqScanRlt, sizeof(tstrM2mReqScanResult), NULL, 0, 0); - return ret; + sint8 ret = M2M_SUCCESS; + tstrM2mReqScanResult strReqScanRlt; + strReqScanRlt.u8Index = index; + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SCAN_RESULT, (uint8 *) &strReqScanRlt, sizeof(tstrM2mReqScanResult), NULL, 0, 0); + return ret; } + /*! @fn NMI_API uint8 m2m_wifi_get_num_ap_found(void); -@brief Reads the number of AP's found in the last Scan Request, - The function read the number of AP's from global variable which updated in the - wifi_cb in M2M_WIFI_RESP_SCAN_DONE. -@sa m2m_wifi_request_scan +@brief Reads the number of AP's found in the last Scan Request, + The function read the number of AP's from global variable which updated in the + wifi_cb in M2M_WIFI_RESP_SCAN_DONE. +@sa m2m_wifi_request_scan @return Return the number of AP's found in the last Scan Request. -@pre m2m_wifi_request_scan need to be called first -@warning That function need to be called in the wifi_cb in M2M_WIFI_RESP_SCAN_DONE, - calling that function in any other place will return undefined/undated numbers. - Function used only in STA mode only. +@pre m2m_wifi_request_scan need to be called first +@warning That function need to be called in the wifi_cb in M2M_WIFI_RESP_SCAN_DONE, + calling that function in any other place will return undefined/undated numbers. + Function used only in STA mode only. */ uint8 m2m_wifi_get_num_ap_found(void) { - return gu8ChNum; + return gu8ChNum; } + /*! -@fn NMI_API uint8 m2m_wifi_get_sleep_mode(void); -@brief Get the current Power save mode. -@return The current operating power saving mode. -@sa tenuPowerSaveModes , m2m_wifi_set_sleep_mode +@fn NMI_API uint8 m2m_wifi_get_sleep_mode(void); +@brief Get the current Power save mode. +@return The current operating power saving mode. +@sa tenuPowerSaveModes , m2m_wifi_set_sleep_mode */ uint8 m2m_wifi_get_sleep_mode(void) { - return hif_get_sleep_mode(); + return hif_get_sleep_mode(); } + /*! -@fn NMI_API sint8 m2m_wifi_set_sleep_mode(uint8 PsTyp, uint8 BcastEn); -@brief Set the power saving mode for the WINC1500. -@param [in] PsTyp - Desired power saving mode. Supported types are defined in tenuPowerSaveModes. -@param [in] BcastEn - Broadcast reception enable flag. - If it is 1, the WINC1500 must be awake each DTIM Beacon for receiving Broadcast traffic. - If it is 0, the WINC1500 will not wakeup at the DTIM Beacon, but its wakeup depends only - on the the configured Listen Interval. +@fn NMI_API sint8 m2m_wifi_set_sleep_mode(uint8 PsTyp, uint8 BcastEn); +@brief Set the power saving mode for the WINC1500. +@param [in] PsTyp + Desired power saving mode. Supported types are defined in tenuPowerSaveModes. +@param [in] BcastEn + Broadcast reception enable flag. + If it is 1, the WINC1500 must be awake each DTIM Beacon for receiving Broadcast traffic. + If it is 0, the WINC1500 will not wakeup at the DTIM Beacon, but its wakeup depends only + on the the configured Listen Interval. @return The function SHALL return 0 for success and a negative value otherwise. -@sa tenuPowerSaveModes -@warning The function called once after initialization. +@sa tenuPowerSaveModes +@warning The function called once after initialization. */ sint8 m2m_wifi_set_sleep_mode(uint8 PsTyp, uint8 BcastEn) { - sint8 ret = M2M_SUCCESS; - tstrM2mPsType strPs; - strPs.u8PsType = PsTyp; - strPs.u8BcastEn = BcastEn; - ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SLEEP, (uint8*) &strPs,sizeof(tstrM2mPsType), NULL, 0, 0); - M2M_INFO("POWER SAVE %d\n",PsTyp); - hif_set_sleep_mode(PsTyp); - return ret; + sint8 ret = M2M_SUCCESS; + tstrM2mPsType strPs; + strPs.u8PsType = PsTyp; + strPs.u8BcastEn = BcastEn; + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SLEEP, (uint8 *) &strPs, sizeof(tstrM2mPsType), NULL, 0, 0); + M2M_INFO("POWER SAVE %d\n", PsTyp); + hif_set_sleep_mode(PsTyp); + return ret; } + /*! -@fn NMI_API sint8 m2m_wifi_request_sleep(void) -@brief Request from WINC1500 device to Sleep for specific time in the M2M_PS_MANUAL Power save mode (only). -@param [in] u32SlpReqTime - Request Sleep in ms +@fn NMI_API sint8 m2m_wifi_request_sleep(void) +@brief Request from WINC device to Sleep for specific time in the M2M_PS_MANUAL Power save mode (only). +@param [in] u32SlpReqTime + Request Sleep in ms @return The function SHALL return M2M_SUCCESS for success and a negative value otherwise. @sa tenuPowerSaveModes , m2m_wifi_set_sleep_mode -@warning the Function should be called in M2M_PS_MANUAL power save only +@warning the Function should be called in M2M_PS_MANUAL power save only */ sint8 m2m_wifi_request_sleep(uint32 u32SlpReqTime) { - sint8 ret = M2M_SUCCESS; - uint8 psType; - psType = hif_get_sleep_mode(); - if(psType == M2M_PS_MANUAL) - { - tstrM2mSlpReqTime strPs; - strPs.u32SleepTime = u32SlpReqTime; - ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_DOZE, (uint8*) &strPs,sizeof(tstrM2mSlpReqTime), NULL, 0, 0); - } - return ret; + sint8 ret = M2M_SUCCESS; + uint8 psType; + psType = hif_get_sleep_mode(); + if(psType == M2M_PS_MANUAL) + { + tstrM2mSlpReqTime strPs; + strPs.u32SleepTime = u32SlpReqTime; + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_DOZE, (uint8 *) &strPs, sizeof(tstrM2mSlpReqTime), NULL, 0, 0); + } + return ret; } + /*! -@fn NMI_API sint8 m2m_wifi_set_device_name(uint8 *pu8DeviceName, uint8 u8DeviceNameLength); -@brief Set the WINC1500 device name which is used as P2P device name. -@param [in] pu8DeviceName - Buffer holding the device name. -@param [in] u8DeviceNameLength - Length of the device name. -@return The function SHALL return M2M_SUCCESS for success and a negative value otherwise. -@warning The Function called once after initialization. +@fn NMI_API sint8 m2m_wifi_set_device_name(uint8 *pu8DeviceName, uint8 u8DeviceNameLength); +@brief Sets the WINC device name. The name string is used as a device name in DHCP hostname (option 12). +@param [in] pu8DeviceName + Buffer holding the device name. +@param [in] u8DeviceNameLength + Length of the device name. +@return The function SHALL return M2M_SUCCESS for success and a negative value otherwise. +@warning The Function called once after initialization. */ sint8 m2m_wifi_set_device_name(uint8 *pu8DeviceName, uint8 u8DeviceNameLength) { - tstrM2MDeviceNameConfig strDeviceName; - if(u8DeviceNameLength >= M2M_DEVICE_NAME_MAX) - { - u8DeviceNameLength = M2M_DEVICE_NAME_MAX; - } - //pu8DeviceName[u8DeviceNameLength] = '\0'; - u8DeviceNameLength ++; - m2m_memcpy(strDeviceName.au8DeviceName, pu8DeviceName, u8DeviceNameLength); - return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_DEVICE_NAME, - (uint8*)&strDeviceName, sizeof(tstrM2MDeviceNameConfig), NULL, 0,0); + tstrM2MDeviceNameConfig strDeviceName; + if(u8DeviceNameLength >= M2M_DEVICE_NAME_MAX) + { + u8DeviceNameLength = M2M_DEVICE_NAME_MAX; + } + //pu8DeviceName[u8DeviceNameLength] = '\0'; + u8DeviceNameLength ++; + m2m_memcpy(strDeviceName.au8DeviceName, pu8DeviceName, u8DeviceNameLength); + return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_DEVICE_NAME, + (uint8 *)&strDeviceName, sizeof(tstrM2MDeviceNameConfig), NULL, 0, 0); } /*! -@fn NMI_API sint8 m2m_wifi_configure_sntp(uint8 *pu8NTPServerName, uint8 u8NTPServerNameLength, tenuSNTPUseDHCP useDHCP); -@brief Configures what NTP server the SNTP client should use. -@param [in] pu8NTPServerName - Buffer holding the NTP server name. If the first character is an asterisk (*) then it will be treated as a server pool, where the asterisk will - be replaced with an incrementing value from 0 to 3 each time a server fails (example: *.pool.ntp.org). -@param [in] u8NTPServerNameLength - Length of the NTP server name. Should not exceed the maximum NTP server name length of @ref M2M_NTP_MAX_SERVER_NAME_LENGTH -@param [in] useDHCP - Should the NTP server provided by the DHCP server be used. -@return The function SHALL return M2M_SUCCESS for success and a negative value otherwise. +@fn NMI_API sint8 m2m_wifi_configure_sntp(uint8 *pu8NTPServerName, uint8 u8NTPServerNameLength, tenuSNTPUseDHCP useDHCP); +@brief Configures what NTP server the SNTP client should use. +@param [in] pu8NTPServerName + Buffer holding the NTP server name. If the first character is an asterisk (*) then it will be treated as a server pool, where the asterisk will + be replaced with an incrementing value from 0 to 3 each time a server fails (example: *.pool.ntp.org). +@param [in] u8NTPServerNameLength + Length of the NTP server name. Should not exceed the maximum NTP server name length of @ref M2M_NTP_MAX_SERVER_NAME_LENGTH +@param [in] useDHCP + Should the NTP server provided by the DHCP server be used. +@return The function SHALL return M2M_SUCCESS for success and a negative value otherwise. */ sint8 m2m_wifi_configure_sntp(uint8 *pu8NTPServerName, uint8 u8NTPServerNameLength, tenuSNTPUseDHCP useDHCP) { - tstrM2MSNTPConfig strSNTPConfig; - if(u8NTPServerNameLength > M2M_NTP_MAX_SERVER_NAME_LENGTH) - return M2M_ERR_FAIL; + tstrM2MSNTPConfig strSNTPConfig; + if(u8NTPServerNameLength > M2M_NTP_MAX_SERVER_NAME_LENGTH) + return M2M_ERR_FAIL; - m2m_memcpy((uint8*)strSNTPConfig.acNTPServer, pu8NTPServerName, u8NTPServerNameLength); - strSNTPConfig.acNTPServer[u8NTPServerNameLength] = '\0'; + m2m_memcpy((uint8 *)strSNTPConfig.acNTPServer, pu8NTPServerName, u8NTPServerNameLength); + strSNTPConfig.acNTPServer[u8NTPServerNameLength] = '\0'; + strSNTPConfig.enuUseDHCP = useDHCP; - strSNTPConfig.enuUseDHCP = useDHCP; - - return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_CONFIG_SNTP, - (uint8*)&strSNTPConfig, sizeof(tstrM2MSNTPConfig), NULL, 0,0); + return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_CONFIG_SNTP, + (uint8 *)&strSNTPConfig, sizeof(tstrM2MSNTPConfig), NULL, 0, 0); } sint8 m2m_wifi_get_firmware_version(tstrM2mRev *pstrRev) { - sint8 ret = M2M_SUCCESS; - ret = hif_chip_wake(); - if(ret == M2M_SUCCESS) - { - ret = nm_get_firmware_full_info(pstrRev); - hif_chip_sleep(); - } - return ret; + sint8 ret = M2M_SUCCESS; + ret = hif_chip_wake(); + if(ret == M2M_SUCCESS) + { + ret = nm_get_firmware_full_info(pstrRev); + hif_chip_sleep(); + } + return ret; } sint8 m2m_wifi_start_provision_mode(tstrM2MAPConfig *pstrM2MAPConfig, char *pcHttpServerDomainName, uint8 bEnableHttpRedirect) { - tstrM2MAPModeConfig strM2MAPModeConfig; + tstrM2MAPModeConfig strM2MAPModeConfig; - m2m_memcpy((uint8 *)&strM2MAPModeConfig.strApConfig, (uint8 *)pstrM2MAPConfig, sizeof(tstrM2MAPConfig)); + m2m_memcpy((uint8 *)&strM2MAPModeConfig.strApConfig, (uint8 *)pstrM2MAPConfig, sizeof(tstrM2MAPConfig)); + m2m_memcpy(strM2MAPModeConfig.strApConfigExt.au8DefRouterIP, pstrM2MAPConfig->au8DHCPServerIP, 4); + m2m_memcpy(strM2MAPModeConfig.strApConfigExt.au8DNSServerIP, pstrM2MAPConfig->au8DHCPServerIP, 4); - m2m_memcpy(strM2MAPModeConfig.strApConfigExt.au8DefRouterIP, pstrM2MAPConfig->au8DHCPServerIP, 4); - m2m_memcpy(strM2MAPModeConfig.strApConfigExt.au8DNSServerIP, pstrM2MAPConfig->au8DHCPServerIP, 4); - strM2MAPModeConfig.strApConfigExt.au8SubnetMask[0] = 0; + strM2MAPModeConfig.strApConfigExt.au8SubnetMask[0] = 0; - return m2m_wifi_start_provision_mode_ext(&strM2MAPModeConfig, pcHttpServerDomainName, bEnableHttpRedirect); + return m2m_wifi_start_provision_mode_ext(&strM2MAPModeConfig, pcHttpServerDomainName, bEnableHttpRedirect); } sint8 m2m_wifi_start_provision_mode_ext(tstrM2MAPModeConfig *pstrAPModeConfig, char *pcHttpServerDomainName, uint8 bEnableHttpRedirect) { - sint8 s8Ret = M2M_ERR_FAIL; + sint8 s8Ret = M2M_ERR_FAIL; - if(pstrAPModeConfig != NULL) - { - tstrM2MProvisionModeConfig strProvConfig; - if(M2M_SUCCESS == m2m_validate_ap_parameters(pstrAPModeConfig)) - { - m2m_memcpy((uint8*)&strProvConfig.strApConfig, (uint8*)&pstrAPModeConfig->strApConfig, sizeof(tstrM2MAPConfig)); - m2m_memcpy((uint8*)&strProvConfig.strApConfigExt, (uint8*)&pstrAPModeConfig->strApConfigExt, sizeof(tstrM2MAPConfigExt)); - if((m2m_strlen((uint8 *)pcHttpServerDomainName) <= 0) || (NULL == pcHttpServerDomainName)) - { - M2M_ERR("INVALID DOMAIN NAME\n"); - goto ERR1; - } - m2m_memcpy((uint8*)strProvConfig.acHttpServerDomainName, (uint8*)pcHttpServerDomainName, 64); - strProvConfig.u8EnableRedirect = bEnableHttpRedirect; - - /* Stop Scan if it is ongoing. - */ - gu8scanInProgress = 0; - s8Ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_START_PROVISION_MODE | M2M_REQ_DATA_PKT, - (uint8*)&strProvConfig, sizeof(tstrM2MProvisionModeConfig), NULL, 0, 0); - } - else - { - /*goto ERR1;*/ - } - } + if(pstrAPModeConfig != NULL) + { + tstrM2MProvisionModeConfig strProvConfig; + if(M2M_SUCCESS == m2m_validate_ap_parameters(pstrAPModeConfig)) + { + m2m_memcpy((uint8 *)&strProvConfig.strApConfig, (uint8 *)&pstrAPModeConfig->strApConfig, sizeof(tstrM2MAPConfig)); + m2m_memcpy((uint8 *)&strProvConfig.strApConfigExt, (uint8 *)&pstrAPModeConfig->strApConfigExt, sizeof(tstrM2MAPConfigExt)); + if((m2m_strlen((uint8 *)pcHttpServerDomainName) <= 0) || (NULL == pcHttpServerDomainName)) + { + M2M_ERR("INVALID DOMAIN NAME\n"); + goto ERR1; + } + m2m_memcpy((uint8 *)strProvConfig.acHttpServerDomainName, (uint8 *)pcHttpServerDomainName, 64); + strProvConfig.u8EnableRedirect = bEnableHttpRedirect; + + /* Stop Scan if it is ongoing. + */ + gu8scanInProgress = 0; + s8Ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_START_PROVISION_MODE | M2M_REQ_DATA_PKT, + (uint8 *)&strProvConfig, sizeof(tstrM2MProvisionModeConfig), NULL, 0, 0); + } + else + { + /*goto ERR1;*/ + } + } ERR1: - return s8Ret; + return s8Ret; } sint8 m2m_wifi_stop_provision_mode(void) { - return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_STOP_PROVISION_MODE, NULL, 0, NULL, 0, 0); + return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_STOP_PROVISION_MODE, NULL, 0, NULL, 0, 0); } sint8 m2m_wifi_get_connection_info(void) { - return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_GET_CONN_INFO, NULL, 0, NULL, 0, 0); + return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_GET_CONN_INFO, NULL, 0, NULL, 0, 0); } sint8 m2m_wifi_set_system_time(uint32 u32UTCSeconds) { - /* - The firmware accepts timestamps relative to 1900 like NTP Timestamp. - */ - return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_SYS_TIME, (uint8*)&u32UTCSeconds, sizeof(tstrSystemTime), NULL, 0, 0); + /* + The firmware accepts timestamps relative to 1900 like NTP Timestamp. + */ + return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_SYS_TIME, (uint8 *)&u32UTCSeconds, sizeof(tstrSystemTime), NULL, 0, 0); } /*! - * @fn NMI_API sint8 m2m_wifi_get_system_time(void); + * @fn NMI_API sint8 m2m_wifi_get_system_time(void); * @see m2m_wifi_enable_sntp - tstrSystemTime + tstrSystemTime * @note get the system time from the sntp client - * using the API \ref m2m_wifi_get_system_time. + * using the API \ref m2m_wifi_get_system_time. * @return The function returns @ref M2M_SUCCESS for successful operations and a negative value otherwise. */ sint8 m2m_wifi_get_system_time(void) { - return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_GET_SYS_TIME, NULL,0, NULL, 0, 0); + return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_GET_SYS_TIME, NULL, 0, NULL, 0, 0); } sint8 m2m_wifi_enable_sntp(uint8 bEnable) { - uint8 u8Req; - - u8Req = bEnable ? M2M_WIFI_REQ_ENABLE_SNTP_CLIENT : M2M_WIFI_REQ_DISABLE_SNTP_CLIENT; - return hif_send(M2M_REQ_GROUP_WIFI, u8Req, NULL, 0, NULL, 0, 0); + uint8 u8Req; + u8Req = bEnable ? M2M_WIFI_REQ_ENABLE_SNTP_CLIENT : M2M_WIFI_REQ_DISABLE_SNTP_CLIENT; + return hif_send(M2M_REQ_GROUP_WIFI, u8Req, NULL, 0, NULL, 0, 0); } + /*! -@fn NMI_API sint8 m2m_wifi_set_power_profile(uint8 u8PwrMode); -@brief Change the power profile mode -@param [in] u8PwrMode - Change the WINC power profile to different mode - PWR_LOW1/PWR_LOW2/PWR_HIGH/PWR_AUTO (tenuM2mPwrMode) -@return The function SHALL return M2M_SUCCESS for success and a negative value otherwise. -@sa tenuM2mPwrMode -@pre m2m_wifi_init -@warning must be called after the initializations and before any connection request and can't be changed in run time, +@fn NMI_API sint8 m2m_wifi_set_power_profile(uint8 u8PwrMode); +@brief Change the power profile mode +@param [in] u8PwrMode + Change the WINC power profile to different mode + PWR_LOW1/PWR_LOW2/PWR_HIGH/PWR_AUTO (tenuM2mPwrMode) +@return The function SHALL return M2M_SUCCESS for success and a negative value otherwise. +@sa tenuM2mPwrMode +@pre m2m_wifi_init +@warning must be called after the initializations and before any connection request and can't be changed in run time, */ sint8 m2m_wifi_set_power_profile(uint8 u8PwrMode) { - sint8 ret = M2M_SUCCESS; - tstrM2mPwrMode strM2mPwrMode; - strM2mPwrMode.u8PwrMode = u8PwrMode; - ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_POWER_PROFILE, (uint8*)&strM2mPwrMode,sizeof(tstrM2mPwrMode), NULL, 0, 0); - return ret; + sint8 ret = M2M_SUCCESS; + tstrM2mPwrMode strM2mPwrMode; + strM2mPwrMode.u8PwrMode = u8PwrMode; + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_POWER_PROFILE, (uint8 *)&strM2mPwrMode, sizeof(tstrM2mPwrMode), NULL, 0, 0); + return ret; } + /*! -@fn NMI_API sint8 m2m_wifi_set_tx_power(uint8 u8TxPwrLevel); -@brief set the TX power tenuM2mTxPwrLevel -@param [in] u8TxPwrLevel - change the TX power tenuM2mTxPwrLevel -@return The function SHALL return M2M_SUCCESS for success and a negative value otherwise. -@sa tenuM2mTxPwrLevel -@pre m2m_wifi_init -@warning +@fn NMI_API sint8 m2m_wifi_set_tx_power(uint8 u8TxPwrLevel); +@brief set the TX power tenuM2mTxPwrLevel +@param [in] u8TxPwrLevel + change the TX power tenuM2mTxPwrLevel +@return The function SHALL return M2M_SUCCESS for success and a negative value otherwise. +@sa tenuM2mTxPwrLevel +@pre m2m_wifi_init +@warning */ sint8 m2m_wifi_set_tx_power(uint8 u8TxPwrLevel) { - sint8 ret = M2M_SUCCESS; - tstrM2mTxPwrLevel strM2mTxPwrLevel; - strM2mTxPwrLevel.u8TxPwrLevel = u8TxPwrLevel; - ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_TX_POWER, (uint8*)&strM2mTxPwrLevel,sizeof(tstrM2mTxPwrLevel), NULL, 0, 0); - return ret; + sint8 ret = M2M_SUCCESS; + tstrM2mTxPwrLevel strM2mTxPwrLevel; + strM2mTxPwrLevel.u8TxPwrLevel = u8TxPwrLevel; + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_TX_POWER, (uint8 *)&strM2mTxPwrLevel, sizeof(tstrM2mTxPwrLevel), NULL, 0, 0); + return ret; } /*! -@fn NMI_API sint8 m2m_wifi_set_gain_table_idx(uint8 u8GainTableIdx); -@brief set the gain table index corresponding to a specific WiFi region -@param [in] u8GainTableIdx - change the gain table index -@return The function SHALL return M2M_SUCCESS for success and a negative value otherwise. -@pre The gain tables must be written to the flash through gain builder tool. - m2m_wifi_init -@warning +@fn NMI_API sint8 m2m_wifi_set_gain_table_idx(uint8 u8GainTableIdx); +@brief set the gain table index corresponding to a specific WiFi region +@param [in] u8GainTableIdx + change the gain table index +@return The function SHALL return M2M_SUCCESS for success and a negative value otherwise. +@pre The gain tables must be written to the flash through gain builder tool. + m2m_wifi_init +@warning */ sint8 m2m_wifi_set_gain_table_idx(uint8 u8GainTableIdx) { - sint8 ret = M2M_SUCCESS; - tstrM2mWiFiGainIdx strM2mGainTableIdx; - strM2mGainTableIdx.u8GainTableIdx = u8GainTableIdx; - ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_GAIN_TABLE_IDX, (uint8*)&strM2mGainTableIdx,sizeof(tstrM2mWiFiGainIdx), NULL, 0, 0); - return ret; + sint8 ret = M2M_SUCCESS; + tstrM2mWiFiGainIdx strM2mGainTableIdx; + strM2mGainTableIdx.u8GainTableIdx = u8GainTableIdx; + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_GAIN_TABLE_IDX, (uint8 *)&strM2mGainTableIdx, sizeof(tstrM2mWiFiGainIdx), NULL, 0, 0); + return ret; } /*! -@fn NMI_API sint8 m2m_wifi_enable_firmware_logs(uint8 u8Enable); -@brief Enable or Disable logs in run time (Disable Firmware logs will - enhance the firmware start-up time and performance) -@param [in] u8Enable - Set 1 to enable the logs 0 for disable -@return The function SHALL return M2M_SUCCESS for success and a negative value otherwise. -@sa __DISABLE_FIRMWARE_LOGS__ (build option to disable logs from initializations) -@pre m2m_wifi_init -@warning +@fn NMI_API sint8 m2m_wifi_enable_firmware_logs(uint8 u8Enable); +@brief Enable or Disable logs in run time (Disable Firmware logs will + enhance the firmware start-up time and performance) +@param [in] u8Enable + Set 1 to enable the logs 0 for disable +@return The function SHALL return M2M_SUCCESS for success and a negative value otherwise. +@sa __DISABLE_FIRMWARE_LOGS__ (build option to disable logs from initializations) +@pre m2m_wifi_init +@warning */ sint8 m2m_wifi_enable_firmware_logs(uint8 u8Enable) { - sint8 ret = M2M_SUCCESS; - tstrM2mEnableLogs strM2mEnableLogs; - strM2mEnableLogs.u8Enable = u8Enable; - ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_ENABLE_LOGS, (uint8*)&strM2mEnableLogs,sizeof(tstrM2mEnableLogs), NULL, 0, 0); - return ret; + sint8 ret = M2M_SUCCESS; + tstrM2mEnableLogs strM2mEnableLogs; + strM2mEnableLogs.u8Enable = u8Enable; + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_ENABLE_LOGS, (uint8 *)&strM2mEnableLogs, sizeof(tstrM2mEnableLogs), NULL, 0, 0); + return ret; } /*! -@fn NMI_API sint8 m2m_wifi_set_battery_voltage(uint16 u16BattVoltx100); -@brief Enable or Disable logs in run time (Disable Firmware logs will - enhance the firmware start-up time and performance) -@param [in] u16BattVoltx100 - battery voltage multiplied by 100 -@return The function SHALL return M2M_SUCCESS for success and a negative value otherwise. -@sa __DISABLE_FIRMWARE_LOGS__ (build option to disable logs from initializations) -@pre m2m_wifi_init -@warning +@fn NMI_API sint8 m2m_wifi_set_battery_voltage(uint16 u16BattVoltx100); +@brief Enable or Disable logs in run time (Disable Firmware logs will + enhance the firmware start-up time and performance) +@param [in] u16BattVoltx100 + battery voltage multiplied by 100 +@return The function SHALL return M2M_SUCCESS for success and a negative value otherwise. +@sa __DISABLE_FIRMWARE_LOGS__ (build option to disable logs from initializations) +@pre m2m_wifi_init +@warning */ sint8 m2m_wifi_set_battery_voltage(uint16 u16BattVoltx100) { - sint8 ret = M2M_SUCCESS; - tstrM2mBatteryVoltage strM2mBattVol = {0}; - strM2mBattVol.u16BattVolt = u16BattVoltx100; - ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_BATTERY_VOLTAGE, (uint8*)&strM2mBattVol,sizeof(tstrM2mBatteryVoltage), NULL, 0, 0); - return ret; + sint8 ret = M2M_SUCCESS; + tstrM2mBatteryVoltage strM2mBattVol = {0}; + strM2mBattVol.u16BattVolt = u16BattVoltx100; + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_BATTERY_VOLTAGE, (uint8 *)&strM2mBattVol, sizeof(tstrM2mBatteryVoltage), NULL, 0, 0); + return ret; } + /*! -@fn sint8 m2m_wifi_prng_get_random_bytes(uint8 * pu8PrngBuff,uint16 u16PrngSize) -@brief Get random bytes using the PRNG bytes. +@fn sint8 m2m_wifi_prng_get_random_bytes(uint8 * pu8PrngBuff,uint16 u16PrngSize) +@brief Get random bytes using the PRNG bytes. @param [in] u16PrngSize - Size of the required random bytes to be generated. + Size of the required random bytes to be generated. @param [in] pu8PrngBuff - Pointer to user allocated buffer. + Pointer to user allocated buffer. @return The function SHALL return M2M_SUCCESS for success and a negative value otherwise. */ -sint8 m2m_wifi_prng_get_random_bytes(uint8 * pu8PrngBuff,uint16 u16PrngSize) +sint8 m2m_wifi_prng_get_random_bytes(uint8 *pu8PrngBuff, uint16 u16PrngSize) { - sint8 ret = M2M_ERR_FAIL; - tstrPrng strRng = {0}; - if((u16PrngSize < (M2M_BUFFER_MAX_SIZE - sizeof(tstrPrng)))&&(pu8PrngBuff != NULL)) - { - strRng.u16PrngSize = u16PrngSize; - strRng.pu8RngBuff = pu8PrngBuff; - ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_GET_PRNG|M2M_REQ_DATA_PKT,(uint8 *)&strRng, sizeof(tstrPrng),NULL,0, 0); - } - else - { - M2M_ERR("PRNG Buffer exceeded maximum size %d or NULL Buffer\n",u16PrngSize); - } - return ret; + sint8 ret = M2M_ERR_FAIL; + tstrPrng strRng = {0}; + if( + (u16PrngSize <= (M2M_HIF_MAX_PACKET_SIZE - (M2M_HIF_HDR_OFFSET + sizeof(tstrPrng)))) + && (pu8PrngBuff != NULL) + ) + { + strRng.u16PrngSize = u16PrngSize; + strRng.pu8RngBuff = pu8PrngBuff; + ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_GET_PRNG|M2M_REQ_DATA_PKT, (uint8 *)&strRng, sizeof(tstrPrng), NULL, 0, 0); + } + else + { + M2M_ERR("PRNG Buffer exceeded maximum size %d or NULL Buffer\n", u16PrngSize); + } + return ret; } /*! -@fn \ - NMI_API sint8 m2m_wifi_conf_auto_rate(tstrConfAutoRate * pstrConfAutoRate) +@fn \ + NMI_API sint8 m2m_wifi_conf_auto_rate(tstrConfAutoRate * pstrConfAutoRate) @brief - Allow the host MCU app to configure auto TX rate selection algorithm. The application can use this - API to tweak the algorithm performance. Moreover, it allows the application to force a specific WLAN - PHY rate for transmitted data packets to favor range vs. throughput needs. + Allow the host MCU app to configure auto TX rate selection algorithm. The application can use this + API to tweak the algorithm performance. Moreover, it allows the application to force a specific WLAN + PHY rate for transmitted data packets to favor range vs. throughput needs. + +@param [in] pstrConfAutoRate + The Auto rate configuration parameters as listed in tstrConfAutoRate. + +@warning + The application will be responsible for keeping track of what values are set and, if required, + going back to default values. Changes will become permanent until the chip is power-cycled. -@param [in] pstrConfAutoRate - The Auto rate configuration parameters as listed in tstrConfAutoRate. @sa - tstrConfAutoRate + tstrConfAutoRate + @return - The function SHALL return 0 for success and a negative value otherwise. + The function SHALL return 0 for success and a negative value otherwise. */ -NMI_API sint8 m2m_wifi_conf_auto_rate(tstrConfAutoRate * pstrConfAutoRate) +NMI_API sint8 m2m_wifi_conf_auto_rate(tstrConfAutoRate *pstrConfAutoRate) { - sint8 s8ret = M2M_ERR_FAIL; - - s8ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_CONG_AUTO_RATE, (uint8 *)pstrConfAutoRate,sizeof(tstrConfAutoRate),NULL,0,0); + sint8 s8ret = M2M_ERR_FAIL; + s8ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_CONG_AUTO_RATE, (uint8 *)pstrConfAutoRate, sizeof(tstrConfAutoRate), NULL, 0, 0); - return s8ret; + return s8ret; } #ifdef ETH_MODE /*! -@fn \ - NMI_API sint8 m2m_wifi_enable_mac_mcast(uint8* pu8MulticastMacAddress, uint8 u8AddRemove) +@fn \ + NMI_API sint8 m2m_wifi_enable_mac_mcast(uint8* pu8MulticastMacAddress, uint8 u8AddRemove) @brief - Add MAC filter to receive Multicast packets. + Add MAC filter to receive Multicast packets. -@param [in] pu8MulticastMacAddress - Pointer to the MAC address. +@param [in] pu8MulticastMacAddress + Pointer to the MAC address. @param [in] u8AddRemove - Flag to Add/Remove MAC address. + Flag to Add/Remove MAC address. @return - The function SHALL return 0 for success and a negative value otherwise. + The function SHALL return 0 for success and a negative value otherwise. */ -NMI_API sint8 m2m_wifi_enable_mac_mcast(uint8* pu8MulticastMacAddress, uint8 u8AddRemove) +NMI_API sint8 m2m_wifi_enable_mac_mcast(uint8 *pu8MulticastMacAddress, uint8 u8AddRemove) { - sint8 s8ret = M2M_ERR_FAIL; - tstrM2MMulticastMac strMulticastMac; + sint8 s8ret = M2M_ERR_FAIL; + tstrM2MMulticastMac strMulticastMac; - if(pu8MulticastMacAddress != NULL ) - { - strMulticastMac.u8AddRemove = u8AddRemove; - m2m_memcpy(strMulticastMac.au8macaddress,pu8MulticastMacAddress,M2M_MAC_ADDRES_LEN); - M2M_DBG("mac multicast: %x:%x:%x:%x:%x:%x\r\n",strMulticastMac.au8macaddress[0],strMulticastMac.au8macaddress[1],strMulticastMac.au8macaddress[2],strMulticastMac.au8macaddress[3],strMulticastMac.au8macaddress[4],strMulticastMac.au8macaddress[5]); - s8ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_MAC_MCAST, (uint8 *)&strMulticastMac,sizeof(tstrM2MMulticastMac),NULL,0,0); - } + if(pu8MulticastMacAddress != NULL) + { + strMulticastMac.u8AddRemove = u8AddRemove; + m2m_memcpy(strMulticastMac.au8macaddress, pu8MulticastMacAddress, M2M_MAC_ADDRES_LEN); + M2M_DBG("mac multicast: %x:%x:%x:%x:%x:%x\r\n", strMulticastMac.au8macaddress[0], strMulticastMac.au8macaddress[1], strMulticastMac.au8macaddress[2], strMulticastMac.au8macaddress[3], strMulticastMac.au8macaddress[4], strMulticastMac.au8macaddress[5]); + s8ret = hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_SET_MAC_MCAST, (uint8 *)&strMulticastMac, sizeof(tstrM2MMulticastMac), NULL, 0, 0); + } - return s8ret; + return s8ret; } /*! -@fn \ - NMI_API sint8 m2m_wifi_set_receive_buffer(void* pvBuffer,uint16 u16BufferLen); +@fn \ + NMI_API sint8 m2m_wifi_set_receive_buffer(void* pvBuffer,uint16 u16BufferLen); @brief - set the ethernet receive buffer, should be called in the receive call back. + set the ethernet receive buffer, should be called in the receive call back. -@param [in] pvBuffer - Pointer to the ethernet receive buffer. +@param [in] pvBuffer + Pointer to the ethernet receive buffer. @param [in] u16BufferLen - Length of the buffer. + Length of the buffer. @return - The function SHALL return 0 for success and a negative value otherwise. + The function SHALL return 0 for success and a negative value otherwise. */ -NMI_API sint8 m2m_wifi_set_receive_buffer(void* pvBuffer,uint16 u16BufferLen) +NMI_API sint8 m2m_wifi_set_receive_buffer(void *pvBuffer, uint16 u16BufferLen) { - sint8 s8ret = M2M_SUCCESS; - if(pvBuffer != NULL) - { - gau8ethRcvBuf = pvBuffer; - gu16ethRcvBufSize= u16BufferLen; - } - else - { - s8ret = M2M_ERR_FAIL; - M2M_ERR("Buffer NULL pointer\r\n"); - } - return s8ret; + sint8 s8ret = M2M_SUCCESS; + if(pvBuffer != NULL) + { + gau8ethRcvBuf = pvBuffer; + gu16ethRcvBufSize= u16BufferLen; + } + else + { + s8ret = M2M_ERR_FAIL; + M2M_ERR("Buffer NULL pointer\r\n"); + } + return s8ret; } #endif /* ETH_MODE */ @@ -1886,22 +2001,39 @@ uint8 m2m_wifi_get_state(void) sint8 m2m_wifi_enable_roaming(uint8 bEnableDhcp) { - tstrM2mWiFiRoaming strWiFiRoaming; - strWiFiRoaming.u8EnableRoaming = 1; - if(0 == bEnableDhcp || 1 == bEnableDhcp) - { - strWiFiRoaming.u8EnableDhcp = bEnableDhcp; - return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_ROAMING, - (uint8 *) &strWiFiRoaming, sizeof(tstrM2mWiFiRoaming), NULL, 0, 0); - } - else - { - return M2M_ERR_INVALID_ARG; - } + tstrM2mWiFiRoaming strWiFiRoaming; + strWiFiRoaming.u8EnableRoaming = 1; + if(0 == bEnableDhcp || 1 == bEnableDhcp) + { + strWiFiRoaming.u8EnableDhcp = bEnableDhcp; + return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_ROAMING, + (uint8 *) &strWiFiRoaming, sizeof(tstrM2mWiFiRoaming), NULL, 0, 0); + } + else + { + return M2M_ERR_INVALID_ARG; + } } + sint8 m2m_wifi_disable_roaming(void) { - tstrM2mWiFiRoaming strWiFiRoaming; - strWiFiRoaming.u8EnableRoaming = 0; - return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_ROAMING, (uint8 *) &strWiFiRoaming, sizeof(tstrM2mWiFiRoaming), NULL, 0,0); + tstrM2mWiFiRoaming strWiFiRoaming; + strWiFiRoaming.u8EnableRoaming = 0; + return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_ROAMING, (uint8 *) &strWiFiRoaming, sizeof(tstrM2mWiFiRoaming), NULL, 0, 0); +} + +sint8 m2m_wifi_enable_XO_during_sleep(uint8 bXOSleepEnable) +{ + tstrM2mWiFiXOSleepEnable strM2mWiFiXOSleepEnable; + + if(0 == bXOSleepEnable || 1 == bXOSleepEnable) + { + strM2mWiFiXOSleepEnable.u8EnableXODuringSleep = bXOSleepEnable; + return hif_send(M2M_REQ_GROUP_WIFI, M2M_WIFI_REQ_XO_SLEEP_ENABLE, + (uint8 *) &strM2mWiFiXOSleepEnable, sizeof(strM2mWiFiXOSleepEnable), NULL, 0, 0); + } + else + { + return M2M_ERR_INVALID_ARG; + } } diff --git a/src/drivers/winc1500/src/nm_bus_wrapper.c b/src/drivers/winc1500/src/nm_bus_wrapper.c index 4e78f4f7e..4bcb66c27 100644 --- a/src/drivers/winc1500/src/nm_bus_wrapper.c +++ b/src/drivers/winc1500/src/nm_bus_wrapper.c @@ -55,7 +55,20 @@ sint8 nm_bus_deinit(void) { return M2M_SUCCESS; } -static sint8 nm_bus_rw(uint8 *txbuf, uint8 *rxbuf, uint16 size) { +sint8 nm_bus_reinit(void* config) +{ + return M2M_SUCCESS; +} + +sint8 nm_bus_speed(uint8 level) +{ + if ((level == HIGH) || (level == LOW)) + return M2M_SUCCESS; + + return M2M_ERR_INVALID_ARG; +} + +sint8 nm_spi_rw(uint8 *txbuf, uint8 *rxbuf, uint16 size) { sint8 result = M2M_SUCCESS; omv_spi_transfer_t spi_xfer = { .txbuf = txbuf, @@ -85,7 +98,7 @@ sint8 nm_bus_ioctl(uint8 cmd, void *arg) { switch (cmd) { case NM_BUS_IOCTL_RW: { tstrNmSpiRw *spi_rw = (tstrNmSpiRw *) arg; - ret = nm_bus_rw(spi_rw->pu8InBuf, spi_rw->pu8OutBuf, spi_rw->u16Sz); + ret = nm_spi_rw(spi_rw->pu8InBuf, spi_rw->pu8OutBuf, spi_rw->u16Sz); } break; default: diff --git a/src/drivers/winc1500/src/nm_common.c b/src/drivers/winc1500/src/nm_common.c index c626aca14..ab662671c 100644 --- a/src/drivers/winc1500/src/nm_common.c +++ b/src/drivers/winc1500/src/nm_common.c @@ -4,7 +4,7 @@ * * \brief This module contains common APIs declarations. * - * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries. + * Copyright (c) 2016-2021 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * @@ -33,56 +33,57 @@ */ #include "common/include/nm_common.h" -void m2m_memcpy(uint8* pDst,uint8* pSrc,uint32 sz) +void m2m_memcpy(uint8 *pDst, const uint8 *pSrc, uint32 sz) { - if(sz == 0) return; - do - { - *pDst = *pSrc; - pDst++; - pSrc++; - }while(--sz); -} -uint8 m2m_checksum(uint8* buf, int sz) -{ - uint8 cs = 0; - while(--sz) - { - cs ^= *buf; - buf++; - } - - return cs; + if(sz == 0) return; + do + { + *pDst = *pSrc; + pDst++; + pSrc++; + } while(--sz); } -void m2m_memset(uint8* pBuf,uint8 val,uint32 sz) +uint8 m2m_checksum(uint8 *buf, int sz) { - if(sz == 0) return; - do - { - *pBuf = val; - pBuf++; - }while(--sz); + uint8 cs = 0; + while(--sz) + { + cs ^= *buf; + buf++; + } + + return cs; } -uint16 m2m_strlen(uint8 * pcStr) +void m2m_memset(uint8 *pBuf, uint8 val, uint32 sz) { - uint16 u16StrLen = 0; - while(*pcStr) - { - u16StrLen ++; - pcStr++; - } - return u16StrLen; + if(sz == 0) return; + do + { + *pBuf = val; + pBuf++; + } while(--sz); +} + +uint16 m2m_strlen(uint8 *pcStr) +{ + uint16 u16StrLen = 0; + while(*pcStr) + { + u16StrLen ++; + pcStr++; + } + return u16StrLen; } uint8 m2m_strncmp(uint8 *pcS1, uint8 *pcS2, uint16 u16Len) { - for ( ; u16Len > 0; pcS1++, pcS2++, --u16Len) - if (*pcS1 != *pcS2) - return ((*(uint8 *)pcS1 < *(uint8 *)pcS2) ? -1 : +1); - else if (*pcS1 == '\0') - return 0; + for(; u16Len > 0; pcS1++, pcS2++, --u16Len) + if(*pcS1 != *pcS2) + return ((*(uint8 *)pcS1 < *(uint8 *)pcS2) ? -1 : +1); + else if(*pcS1 == '\0') + return 0; return 0; } @@ -90,14 +91,14 @@ uint8 m2m_strncmp(uint8 *pcS1, uint8 *pcS2, uint16 u16Len) If pcStr is part of pcIn it returns a valid pointer to the start of pcStr within pcIn. Otherwise a NULL Pointer is returned. */ -uint8 * m2m_strstr(uint8 *pcIn, uint8 *pcStr) +uint8 *m2m_strstr(uint8 *pcIn, uint8 *pcStr) { uint8 u8c; uint16 u16StrLen; u8c = *pcStr++; - if (!u8c) - return (uint8 *) pcIn; // Trivial empty string case + if(!u8c) + return (uint8 *) pcIn; // Trivial empty string case u16StrLen = m2m_strlen(pcStr); do { @@ -105,25 +106,59 @@ uint8 * m2m_strstr(uint8 *pcIn, uint8 *pcStr) do { u8Sc = *pcIn++; - if (!u8Sc) + if(!u8Sc) return (uint8 *) 0; - } while (u8Sc != u8c); - } while (m2m_strncmp(pcIn, pcStr, u16StrLen) != 0); + } while(u8Sc != u8c); + } while(m2m_strncmp(pcIn, pcStr, u16StrLen) != 0); - return (uint8 *) (pcIn - 1); + return (uint8 *)(pcIn - 1); } -sint8 m2m_memcmp(uint8 *pu8Buff1,uint8 *pu8Buff2 ,uint32 u32Size) +sint8 m2m_memcmp(uint8 *pu8Buff1, uint8 *pu8Buff2, uint32 u32Size) { - uint32 i; - sint8 s8Result = 0; - for(i = 0 ; i < u32Size ; i++) - { - if(pu8Buff1[i] != pu8Buff2[i]) - { - s8Result = 1; - break; - } - } - return s8Result; + uint32 i; + sint8 s8Result = 0; + for(i = 0 ; i < u32Size ; i++) + { + if(pu8Buff1[i] != pu8Buff2[i]) + { + s8Result = 1; + break; + } + } + return s8Result; +} + +/* Convert hexchar to value 0-15 */ +static uint8 hexchar_2_val(uint8 ch) +{ + /* ch -= '0' */ + ch -= 0x30; + if(ch <= 9) + return ch; + /* OR with 0x20 to convert upper case to lower case. */ + ch |= 0x20; + /* ch -= ('a'-'0') */ + ch -= 0x31; + if(ch <= 5) + return ch + 10; + return 0xFF; +} + +/* Convert hexstring to bytes */ +sint8 hexstr_2_bytes(uint8 *pu8Out, uint8 *pu8In, uint8 u8SizeOut) +{ + while(u8SizeOut--) + { + uint8 u8Out = hexchar_2_val(*pu8In++); + if(u8Out > 0xF) + return M2M_ERR_INVALID_ARG; + *pu8Out = u8Out * 0x10; + u8Out = hexchar_2_val(*pu8In++); + if(u8Out > 0xF) + return M2M_ERR_INVALID_ARG; + *pu8Out += u8Out; + pu8Out++; + } + return M2M_SUCCESS; } diff --git a/src/drivers/winc1500/src/nmasic.c b/src/drivers/winc1500/src/nmasic.c index 474e78b82..b268d217e 100644 --- a/src/drivers/winc1500/src/nmasic.c +++ b/src/drivers/winc1500/src/nmasic.c @@ -310,6 +310,8 @@ sint8 chip_wake(void) sint8 ret = M2M_SUCCESS; uint32 reg = 0, clk_status_reg = 0,trials = 0; + nm_bus_speed(LOW); + ret = nm_read_reg_with_ret(HOST_CORT_COMM, ®); if(ret != M2M_SUCCESS)goto _WAKE_EXIT; @@ -353,6 +355,7 @@ sint8 chip_wake(void) nm_bus_reset(); _WAKE_EXIT: + nm_bus_speed(HIGH); return ret; } sint8 cpu_halt(void) diff --git a/src/drivers/winc1500/src/nmdrv.c b/src/drivers/winc1500/src/nmdrv.c index eeec117cc..047a121bf 100644 --- a/src/drivers/winc1500/src/nmdrv.c +++ b/src/drivers/winc1500/src/nmdrv.c @@ -262,7 +262,7 @@ ERR1: sint8 nm_drv_init_hold(void) { sint8 ret = M2M_SUCCESS; - + ret = nm_bus_iface_init(NULL); if (M2M_SUCCESS != ret) { M2M_ERR("[nmi start]: fail init bus\n"); @@ -272,7 +272,7 @@ sint8 nm_drv_init_hold(void) #ifdef BUS_ONLY return; #endif - + #ifdef NO_HW_CHIP_EN ret = chip_wake(); @@ -297,8 +297,8 @@ sint8 nm_drv_init_hold(void) return ret; #ifdef NO_HW_CHIP_EN ERR2: - nm_bus_iface_deinit(); #endif + nm_bus_iface_deinit(); ERR1: return ret; } @@ -319,18 +319,18 @@ sint8 nm_drv_init_start(void * arg) if (M2M_SUCCESS != ret) { goto ERR2; } - + ret = wait_for_firmware_start(u8Mode); if (M2M_SUCCESS != ret) { goto ERR2; } - + if((M2M_WIFI_MODE_ATE_HIGH == u8Mode)||(M2M_WIFI_MODE_ATE_LOW == u8Mode)) { goto ERR1; } else { /*continue running*/ } - + ret = enable_interrupts(); if (M2M_SUCCESS != ret) { M2M_ERR("failed to enable interrupts..\n"); diff --git a/src/drivers/winc1500/src/nmspi.c b/src/drivers/winc1500/src/nmspi.c index 0a3167f7d..a3a4df27a 100644 --- a/src/drivers/winc1500/src/nmspi.c +++ b/src/drivers/winc1500/src/nmspi.c @@ -4,7 +4,7 @@ * * \brief This module contains NMC1000 SPI protocol bus APIs implementation. * - * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries. + * Copyright (c) 2016-2021 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * @@ -35,8 +35,6 @@ #ifdef CONF_WINC_USE_SPI -#define USE_OLD_SPI_SW - #include "bus_wrapper/include/nm_bus_wrapper.h" #include "driver/include/nmspi.h" @@ -73,10 +71,10 @@ #define CMD_SINGLE_READ 0xca #define CMD_RESET 0xcf -#define N_OK 1 -#define N_FAIL 0 -#define N_RESET -1 -#define N_RETRY -2 +#define N_OK 0 +#define N_FAIL -1 +#define N_RESET -2 +#define N_RETRY -3 #define SPI_RESP_RETRY_COUNT (10) #define SPI_RETRY_COUNT (10) @@ -89,33 +87,19 @@ static uint8 gu8Crc_off = 0; -static sint8 nmi_spi_read(uint8* b, uint16 sz) +static inline sint8 nmi_spi_read(uint8 *b, uint16 sz) { - tstrNmSpiRw spi; - spi.pu8InBuf = NULL; - spi.pu8OutBuf = b; - spi.u16Sz = sz; - return nm_bus_ioctl(NM_BUS_IOCTL_RW, &spi); + return nm_spi_rw(NULL, b, sz); +} +static inline sint8 nmi_spi_write(uint8 *b, uint16 sz) +{ + return nm_spi_rw(b, NULL, sz); +} +static sint8 nmi_spi_writeread(uint8 *bw, uint8 *br, uint16 sz) +{ + return nm_spi_rw(bw, br, sz); } -static sint8 nmi_spi_write(uint8* b, uint16 sz) -{ - tstrNmSpiRw spi; - spi.pu8InBuf = b; - spi.pu8OutBuf = NULL; - spi.u16Sz = sz; - return nm_bus_ioctl(NM_BUS_IOCTL_RW, &spi); -} -#ifndef USE_OLD_SPI_SW -static sint8 nmi_spi_rw(uint8 *bin,uint8* bout,uint16 sz) -{ - tstrNmSpiRw spi; - spi.pu8InBuf = bin; - spi.pu8OutBuf = bout; - spi.u16Sz = sz; - return nm_bus_ioctl(NM_BUS_IOCTL_RW, &spi); -} -#endif /******************************************** Crc7 @@ -158,12 +142,12 @@ static const uint8 crc7_syndrome_table[256] = { }; -static uint8 crc7_byte(uint8 crc, uint8 data) +static inline uint8 crc7_byte(uint8 crc, uint8 data) { return crc7_syndrome_table[(crc << 1) ^ data]; } -static uint8 crc7(uint8 crc, const uint8 *buffer, uint32 len) +static inline uint8 crc7(uint8 crc, const uint8 *buffer, uint32 len) { while (len--) crc = crc7_byte(crc, *buffer++); @@ -176,25 +160,6 @@ static uint8 crc7(uint8 crc, const uint8 *buffer, uint32 len) ********************************************/ -#define CMD_DMA_WRITE 0xc1 -#define CMD_DMA_READ 0xc2 -#define CMD_INTERNAL_WRITE 0xc3 -#define CMD_INTERNAL_READ 0xc4 -#define CMD_TERMINATE 0xc5 -#define CMD_REPEAT 0xc6 -#define CMD_DMA_EXT_WRITE 0xc7 -#define CMD_DMA_EXT_READ 0xc8 -#define CMD_SINGLE_WRITE 0xc9 -#define CMD_SINGLE_READ 0xca -#define CMD_RESET 0xcf - -#define DATA_PKT_SZ_256 256 -#define DATA_PKT_SZ_512 512 -#define DATA_PKT_SZ_1K 1024 -#define DATA_PKT_SZ_4K (4 * 1024) -#define DATA_PKT_SZ_8K (8 * 1024) -#define DATA_PKT_SZ DATA_PKT_SZ_8K - static sint8 spi_cmd(uint8 cmd, uint32 adr, uint32 u32data, uint32 sz,uint8 clockless) { uint8 bc[9]; @@ -216,24 +181,29 @@ static sint8 spi_cmd(uint8 cmd, uint32 adr, uint32 u32data, uint32 sz,uint8 cloc bc[3] = 0x00; len = 5; break; +#if defined(CMD_TERMINATE) case CMD_TERMINATE: /* termination */ bc[1] = 0x00; bc[2] = 0x00; bc[3] = 0x00; len = 5; break; +#endif +#if defined(CMD_REPEAT) case CMD_REPEAT: /* repeat */ bc[1] = 0x00; bc[2] = 0x00; bc[3] = 0x00; len = 5; break; +#endif case CMD_RESET: /* reset */ bc[1] = 0xff; bc[2] = 0xff; bc[3] = 0xff; len = 5; break; +#if defined(CMD_DMA_WRITE) || defined(CMD_DMA_READ) case CMD_DMA_WRITE: /* dma write */ case CMD_DMA_READ: /* dma read */ bc[1] = (uint8)(adr >> 16); @@ -243,6 +213,7 @@ static sint8 spi_cmd(uint8 cmd, uint32 adr, uint32 u32data, uint32 sz,uint8 cloc bc[5] = (uint8)(sz); len = 7; break; +#endif case CMD_DMA_EXT_WRITE: /* dma extended write */ case CMD_DMA_EXT_READ: /* dma extended read */ bc[1] = (uint8)(adr >> 16); @@ -278,7 +249,7 @@ static sint8 spi_cmd(uint8 cmd, uint32 adr, uint32 u32data, uint32 sz,uint8 cloc break; } - if (result) { + if(result == N_OK) { if (!gu8Crc_off) bc[len-1] = (crc7(0x7f, (const uint8 *)&bc[0], len-1)) << 1; else @@ -330,14 +301,22 @@ static sint8 spi_cmd_rsp(uint8 cmd) /** Command/Control response **/ - if ((cmd == CMD_RESET) || - (cmd == CMD_TERMINATE) || - (cmd == CMD_REPEAT)) { +#if defined(CMD_TERMINATE) + if(cmd == CMD_TERMINATE) { + if(M2M_SUCCESS != nmi_spi_read(&rsp, 1)) { + result = N_FAIL; + goto _fail_; + } + } +#endif +#if defined(CMD_REPEAT) + if(cmd == CMD_REPEAT) { if (M2M_SUCCESS != nmi_spi_read(&rsp, 1)) { result = N_FAIL; goto _fail_; } } +#endif /* wait for response */ s8RetryCnt = SPI_RESP_RETRY_COUNT; @@ -368,372 +347,27 @@ _fail_: return result; } -#ifndef USE_OLD_SPI_SW -static int spi_cmd_complete(uint8_t cmd, uint32_t adr, uint8_t *b, uint32_t sz, uint8_t clockless) + +sint8 nm_spi_reset(void) { - uint8_t wb[32], rb[32]; - uint8_t wix, rix; - uint32_t len2; - uint8_t rsp; - int len = 0; - int result = N_OK; + //M2M_INFO("Reset Spi\n"); + spi_cmd(CMD_RESET, 0, 0, 0, 0); - wb[0] = cmd; - switch (cmd) { - case CMD_SINGLE_READ: /* single word (4 bytes) read */ - wb[1] = (uint8_t)(adr >> 16); - wb[2] = (uint8_t)(adr >> 8); - wb[3] = (uint8_t)adr; - len = 5; - break; - case CMD_INTERNAL_READ: /* internal register read */ - wb[1] = (uint8_t)(adr >> 8); - if(clockless == 1) wb[1] |= (1 << 7); - wb[2] = (uint8_t)adr; - wb[3] = 0x00; - len = 5; - break; - case CMD_TERMINATE: /* termination */ - wb[1] = 0x00; - wb[2] = 0x00; - wb[3] = 0x00; - len = 5; - break; - case CMD_REPEAT: /* repeat */ - wb[1] = 0x00; - wb[2] = 0x00; - wb[3] = 0x00; - len = 5; - break; - case CMD_RESET: /* reset */ - wb[1] = 0xff; - wb[2] = 0xff; - wb[3] = 0xff; - len = 5; - break; - case CMD_DMA_WRITE: /* dma write */ - case CMD_DMA_READ: /* dma read */ - wb[1] = (uint8_t)(adr >> 16); - wb[2] = (uint8_t)(adr >> 8); - wb[3] = (uint8_t)adr; - wb[4] = (uint8_t)(sz >> 8); - wb[5] = (uint8_t)(sz); - len = 7; - break; - case CMD_DMA_EXT_WRITE: /* dma extended write */ - case CMD_DMA_EXT_READ: /* dma extended read */ - wb[1] = (uint8_t)(adr >> 16); - wb[2] = (uint8_t)(adr >> 8); - wb[3] = (uint8_t)adr; - wb[4] = (uint8_t)(sz >> 16); - wb[5] = (uint8_t)(sz >> 8); - wb[6] = (uint8_t)(sz); - len = 8; - break; - case CMD_INTERNAL_WRITE: /* internal register write */ - wb[1] = (uint8_t)(adr >> 8); - if(clockless == 1) wb[1] |= (1 << 7); - wb[2] = (uint8_t)(adr); - wb[3] = b[3]; - wb[4] = b[2]; - wb[5] = b[1]; - wb[6] = b[0]; - len = 8; - break; - case CMD_SINGLE_WRITE: /* single word write */ - wb[1] = (uint8_t)(adr >> 16); - wb[2] = (uint8_t)(adr >> 8); - wb[3] = (uint8_t)(adr); - wb[4] = b[3]; - wb[5] = b[2]; - wb[6] = b[1]; - wb[7] = b[0]; - len = 9; - break; - default: - result = N_FAIL; - break; - } - - if (result != N_OK) { - return result; - } - - if (!gu8Crc_off) { - wb[len-1] = (crc7(0x7f, (const uint8_t *)&wb[0], len-1)) << 1; - } else { - len -=1; - } - -#define NUM_SKIP_BYTES (1) -#define NUM_RSP_BYTES (2) -#define NUM_DATA_HDR_BYTES (1) -#define NUM_DATA_BYTES (4) -#define NUM_CRC_BYTES (2) -#define NUM_DUMMY_BYTES (3) - - if ((cmd == CMD_RESET) || - (cmd == CMD_TERMINATE) || - (cmd == CMD_REPEAT)) { - len2 = len + (NUM_SKIP_BYTES + NUM_RSP_BYTES + NUM_DUMMY_BYTES); - } else if ((cmd == CMD_INTERNAL_READ) || (cmd == CMD_SINGLE_READ)) { - if (!gu8Crc_off) { - len2 = len + (NUM_RSP_BYTES + NUM_DATA_HDR_BYTES + NUM_DATA_BYTES - + NUM_CRC_BYTES + NUM_DUMMY_BYTES); - } else { - len2 = len + (NUM_RSP_BYTES + NUM_DATA_HDR_BYTES + NUM_DATA_BYTES - + NUM_DUMMY_BYTES); + if(spi_cmd_rsp(CMD_RESET) != N_OK) { + // Reset command failed, need to send repeated 1's until reset occurs + uint8 w_buf[8] = {0xFF}; + uint8 r_buf[8]; + M2M_ERR("[nmi spi]: Failed rst cmd response\n"); + nmi_spi_writeread(w_buf, r_buf, 8); + if(r_buf[7] != 0xFF) + { + M2M_ERR("[nmi spi]: Failed repeated reset\n"); + return N_FAIL; } - } else { - len2 = len + (NUM_RSP_BYTES + NUM_DUMMY_BYTES); } -#undef NUM_DUMMY_BYTES - - if(len2 > (sizeof(wb)/sizeof(wb[0]))) { - M2M_ERR("[nmi spi]: spi buffer size too small (%d) (%d)\n", - len2, (sizeof(wb)/sizeof(wb[0]))); - result = N_FAIL; - return result; - } - /* zero spi write buffers. */ - for(wix = len; wix< len2; wix++) { - wb[wix] = 0; - } - rix = len; - - if (nmi_spi_rw(wb, rb, len2) != M2M_SUCCESS) { - M2M_ERR("[nmi spi]: Failed cmd write, bus error...\n"); - result = N_FAIL; - return result; - } - -#if 0 - { - int jj; - printk("--- cnd = %x, len=%d, len2=%d\n", cmd, len, len2); - for(jj=0; jj= len2) break; - if(((jj+1)%16) != 0) { - if((jj%16) == 0) { - printk("wb[%02x]: %02x ", jj, wb[jj]); - } else { - printk("%02x ", wb[jj]); - } - } else { - printk("%02x\n", wb[jj]); - } - } - printk("\n"); - - for(jj=0; jj= len2) break; - if(((jj+1)%16) != 0) { - if((jj%16) == 0) { - printk("rb[%02x]: %02x ", jj, rb[jj]); - } else { - printk("%02x ", rb[jj]); - } - } else { - printk("%02x\n", rb[jj]); - } - } - printk("\n"); - } -#endif - - /** - Command/Control response - **/ - if ((cmd == CMD_RESET) || - (cmd == CMD_TERMINATE) || - (cmd == CMD_REPEAT)) { - rix++; /* skip 1 byte */ - } - - rsp = rb[rix++]; - - - if (rsp != cmd) { - M2M_ERR("[nmi spi]: Failed cmd response, cmd (%02x), resp (%02x)\n", cmd, rsp); - result = N_FAIL; - return result; - } - - /** - State response - **/ - rsp = rb[rix++]; - if (rsp != 0x00) { - M2M_ERR("[nmi spi]: Failed cmd state response state (%02x)\n", rsp); - result = N_FAIL; - return result; - } - - if ((cmd == CMD_INTERNAL_READ) || (cmd == CMD_SINGLE_READ) - || (cmd == CMD_DMA_READ) || (cmd == CMD_DMA_EXT_READ)) { - int retry; - //uint16_t crc1, crc2; - uint8_t crc[2]; - /** - Data Response header - **/ - retry = SPI_RESP_RETRY_COUNT; - do { - /* ensure there is room in buffer later to read data and crc */ - if(rix < len2) { - rsp = rb[rix++]; - } else { - retry = 0; - break; - } - if (((rsp >> 4) & 0xf) == 0xf) - break; - } while (retry--); - - if (retry <= 0) { - M2M_ERR("[nmi spi]: Error, data read response (%02x)\n", rsp); - result = N_RESET; - return result; - } - - if ((cmd == CMD_INTERNAL_READ) || (cmd == CMD_SINGLE_READ)) { - /** - Read bytes - **/ - if((rix+3) < len2) { - b[0] = rb[rix++]; - b[1] = rb[rix++]; - b[2] = rb[rix++]; - b[3] = rb[rix++]; - } else { - M2M_ERR("[nmi spi]: buffer overrun when reading data.\n"); - result = N_FAIL; - return result; - } - - if (!gu8Crc_off) { - /** - Read Crc - **/ - if((rix+1) < len2) { - crc[0] = rb[rix++]; - crc[1] = rb[rix++]; - } else { - M2M_ERR("[nmi spi]: buffer overrun when reading crc.\n"); - result = N_FAIL; - return result; - } - } - } else if((cmd == CMD_DMA_READ) || (cmd == CMD_DMA_EXT_READ)) { - int ix; - - /* some data may be read in response to dummy bytes. */ - for(ix=0; (rix < len2) && (ix < sz);) { - b[ix++] = rb[rix++]; - } -#if 0 - if(ix) M2M_INFO("ttt %d %d\n", sz, ix); -#endif - sz -= ix; - - if(sz > 0) { - int nbytes; - - if (sz <= (DATA_PKT_SZ-ix)) { - nbytes = sz; - } else { - nbytes = DATA_PKT_SZ-ix; - } - - /** - Read bytes - **/ - if (nmi_spi_read(&b[ix], nbytes) != M2M_SUCCESS) { - M2M_ERR("[nmi spi]: Failed data block read, bus error...\n"); - result = N_FAIL; - goto _error_; - } - - /** - Read Crc - **/ - if (!gu8Crc_off) { - if (nmi_spi_read(crc, 2) != M2M_SUCCESS) { - M2M_ERR("[nmi spi]: Failed data block crc read, bus error...\n"); - result = N_FAIL; - goto _error_; - } - } - - - ix += nbytes; - sz -= nbytes; - } - - /* if any data in left unread, then read the rest using normal DMA code.*/ - while(sz > 0) { - int nbytes; - - if (sz <= DATA_PKT_SZ) { - nbytes = sz; - } else { - nbytes = DATA_PKT_SZ; - } - - /** - read data response only on the next DMA cycles not - the first DMA since data response header is already - handled above for the first DMA. - **/ - /** - Data Response header - **/ - retry = SPI_RESP_RETRY_COUNT; - do { - if (nmi_spi_read(&rsp, 1) != M2M_SUCCESS) { - M2M_ERR("[nmi spi]: Failed data response read, bus error...\n"); - result = N_FAIL; - break; - } - if (((rsp >> 4) & 0xf) == 0xf) - break; - } while (retry--); - - if (result == N_FAIL) - break; - - - /** - Read bytes - **/ - if (nmi_spi_read(&b[ix], nbytes) != M2M_SUCCESS) { - M2M_ERR("[nmi spi]: Failed data block read, bus error...\n"); - result = N_FAIL; - break; - } - - /** - Read Crc - **/ - if (!gu8Crc_off) { - if (nmi_spi_read(crc, 2) != M2M_SUCCESS) { - M2M_ERR("[nmi spi]: Failed data block crc read, bus error...\n"); - result = N_FAIL; - break; - } - } - - ix += nbytes; - sz -= nbytes; - } - } - } -_error_: - return result; + return N_OK; } -#endif + static sint8 spi_data_read(uint8 *b, uint16 sz,uint8 clockless) { sint16 retry, ix, nbytes; @@ -761,7 +395,7 @@ static sint8 spi_data_read(uint8 *b, uint16 sz,uint8 clockless) result = N_FAIL; break; } - if (((rsp >> 4) & 0xf) == 0xf) + if((rsp & 0xf0) == 0xf0) break; } while (retry--); @@ -805,16 +439,15 @@ static sint8 spi_data_read(uint8 *b, uint16 sz,uint8 clockless) static sint8 spi_data_write(uint8 *b, uint16 sz) { - sint16 ix; + sint16 ix = 0; uint16 nbytes; - sint8 result = 1; + sint8 result = N_OK; uint8 cmd, order, crc[2] = {0}; //uint8 rsp; /** Data **/ - ix = 0; do { if (sz <= DATA_PKT_SZ) nbytes = sz; @@ -883,7 +516,16 @@ static sint8 spi_data_write(uint8 *b, uint16 sz) ********************************************/ -static sint8 spi_write_reg(uint32 addr, uint32 u32data) +/** + * @fn nm_spi_write_reg + * @brief Write register + * @param[in] u32Addr + * Register address + * @param[in] u32Val + * Value to be written to the register + * @return @ref M2M_SUCCESS in case of success and @ref M2M_ERR_BUS_FAIL in case of failure + */ +sint8 nm_spi_write_reg(uint32 addr, uint32 u32data) { uint8 retry = SPI_RETRY_COUNT; sint8 result = N_OK; @@ -899,13 +541,7 @@ _RETRY_: cmd = CMD_INTERNAL_WRITE; clockless = 1; } - else - { - cmd = CMD_SINGLE_WRITE; - clockless = 0; - } -#if defined USE_OLD_SPI_SW result = spi_cmd(cmd, addr, u32data, 4, clockless); if (result != N_OK) { M2M_ERR("[nmi spi]: Failed cmd, write reg (%08x)...\n", (unsigned int)addr); @@ -917,23 +553,13 @@ _RETRY_: M2M_ERR("[nmi spi]: Failed cmd response, write reg (%08x)...\n", (unsigned int)addr); goto _FAIL_; } - -#else - - result = spi_cmd_complete(cmd, addr, (uint8*)&u32data, 4, clockless); - if (result != N_OK) { - M2M_ERR( "[nmi spi]: Failed cmd, write reg (%08x)...\n", addr); - goto _FAIL_; - } - -#endif _FAIL_: if(result != N_OK) { nm_bsp_sleep(1); spi_cmd(CMD_RESET, 0, 0, 0, 0); spi_cmd_rsp(CMD_RESET); - M2M_ERR("Reset and retry %d %lx %lx\n",retry,addr,u32data); + M2M_ERR("Reset and retry %d %x %x\n",retry,addr,u32data); nm_bsp_sleep(1); retry--; if(retry) goto _RETRY_; @@ -953,7 +579,6 @@ _RETRY_: /** Command **/ -#if defined USE_OLD_SPI_SW //Workaround hardware problem with single byte transfers over SPI bus if (size == 1) size = 2; @@ -969,13 +594,6 @@ _RETRY_: M2M_ERR("[nmi spi ]: Failed cmd response, write block (%08x)...\n", (unsigned int)addr); goto _FAIL_; } -#else - result = spi_cmd_complete(cmd, addr, NULL, size, 0); - if (result != N_OK) { - M2M_ERR( "[nmi spi]: Failed cmd, write block (%08x)...\n", addr); - goto _FAIL_; - } -#endif /** Data @@ -1000,7 +618,7 @@ _FAIL_: nm_bsp_sleep(1); spi_cmd(CMD_RESET, 0, 0, 0, 0); spi_cmd_rsp(CMD_RESET); - M2M_ERR("Reset and retry %d %lx %d\n",retry,addr,size); + M2M_ERR("Reset and retry %d %x %d\n",retry,addr,size); nm_bsp_sleep(1); retry--; if(retry) goto _RETRY_; @@ -1010,10 +628,19 @@ _FAIL_: return result; } -static sint8 spi_read_reg(uint32 addr, uint32 *u32data) +/** + * @fn nm_spi_read_reg_with_ret + * @brief Read register with error code return + * @param[in] u32Addr + * Register address + * @param[out] pu32RetVal + * Pointer to u32 variable used to return the read value + * @return @ref M2M_SUCCESS in case of success and M2M_ERR_BUS_FAIL in case of failure + */ +sint8 nm_spi_read_reg_with_ret(uint32 addr, uint32 *u32data) { uint8 retry = SPI_RETRY_COUNT; - sint8 result = N_OK; + volatile sint8 result = N_OK; uint8 cmd = CMD_SINGLE_READ; uint8 tmp[4]; uint8 clockless = 0; @@ -1028,13 +655,7 @@ _RETRY_: cmd = CMD_INTERNAL_READ; clockless = 1; } - else - { - cmd = CMD_SINGLE_READ; - clockless = 0; - } -#if defined USE_OLD_SPI_SW result = spi_cmd(cmd, addr, 0, 4, clockless); if (result != N_OK) { M2M_ERR("[nmi spi]: Failed cmd, read reg (%08x)...\n", (unsigned int)addr); @@ -1053,14 +674,6 @@ _RETRY_: M2M_ERR("[nmi spi]: Failed data read...\n"); goto _FAIL_; } -#else - result = spi_cmd_complete(cmd, addr, (uint8*)&tmp[0], 4, clockless); - if (result != N_OK) { - M2M_ERR( "[nmi spi]: Failed cmd, read reg (%08x)...\n", addr); - goto _FAIL_; - } - -#endif *u32data = tmp[0] | ((uint32)tmp[1] << 8) | @@ -1070,7 +683,6 @@ _RETRY_: _FAIL_: if(result != N_OK) { - nm_bsp_sleep(1); spi_cmd(CMD_RESET, 0, 0, 0, 0); spi_cmd_rsp(CMD_RESET); @@ -1088,17 +700,14 @@ static sint8 nm_spi_read(uint32 addr, uint8 *buf, uint16 size) uint8 cmd = CMD_DMA_EXT_READ; sint8 result; uint8 retry = SPI_RETRY_COUNT; -#if defined USE_OLD_SPI_SW uint8 tmp[2]; uint8 single_byte_workaround = 0; -#endif _RETRY_: /** Command **/ -#if defined USE_OLD_SPI_SW if (size == 1) { //Workaround hardware problem with single byte transfers over SPI bus @@ -1132,13 +741,6 @@ _RETRY_: M2M_ERR("[nmi spi]: Failed block data read...\n"); goto _FAIL_; } -#else - result = spi_cmd_complete(cmd, addr, buf, size, 0); - if (result != N_OK) { - M2M_ERR("[nmi spi]: Failed cmd, read block (%08x)...\n", addr); - goto _FAIL_; - } -#endif _FAIL_: if(result != N_OK) @@ -1170,31 +772,32 @@ static void spi_init_pkt_sz(void) val32 &= ~(0x7 << 4); switch(DATA_PKT_SZ) { - case 256: val32 |= (0 << 4); break; - case 512: val32 |= (1 << 4); break; - case 1024: val32 |= (2 << 4); break; - case 2048: val32 |= (3 << 4); break; - case 4096: val32 |= (4 << 4); break; - case 8192: val32 |= (5 << 4); break; - + case 256: + val32 |= (0 << 4); + break; + case 512: + val32 |= (1 << 4); + break; + case 1024: + val32 |= (2 << 4); + break; + case 2048: + val32 |= (3 << 4); + break; + case 4096: + val32 |= (4 << 4); + break; + case 8192: + val32 |= (5 << 4); + break; } nm_spi_write_reg(SPI_BASE+0x24, val32); } -sint8 nm_spi_reset(void) -{ - spi_cmd(CMD_RESET, 0, 0, 0, 0); - spi_cmd_rsp(CMD_RESET); - return M2M_SUCCESS; -} - -/* +/** * @fn nm_spi_init * @brief Initialize the SPI -* @return M2M_SUCCESS in case of success and M2M_ERR_BUS_FAIL in case of failure -* @author M. Abdelmawla -* @date 11 July 2012 -* @version 1.0 + * @return @ref M2M_SUCCESS in case of success and @ref M2M_ERR_BUS_FAIL in case of failure */ sint8 nm_spi_init(void) { @@ -1207,17 +810,15 @@ sint8 nm_spi_init(void) **/ gu8Crc_off = 0; - // TODO: We can remove the CRC trials if there is a definite way to reset - // the SPI to it's initial value. - if (!spi_read_reg(NMI_SPI_PROTOCOL_CONFIG, ®)) { + if(nm_spi_read_reg_with_ret(NMI_SPI_PROTOCOL_CONFIG, ®) != M2M_SUCCESS) { /* Read failed. Try with CRC off. This might happen when module is removed but chip isn't reset*/ gu8Crc_off = 1; M2M_ERR("[nmi spi]: Failed internal read protocol with CRC on, retrying with CRC off...\n"); - if (!spi_read_reg(NMI_SPI_PROTOCOL_CONFIG, ®)){ + if(nm_spi_read_reg_with_ret(NMI_SPI_PROTOCOL_CONFIG, ®) != M2M_SUCCESS) { // Read failed with both CRC on and off, something went bad M2M_ERR( "[nmi spi]: Failed internal read protocol...\n"); - return 0; + return M2M_ERR_BUS_FAIL; } } if(gu8Crc_off == 0) @@ -1225,9 +826,9 @@ sint8 nm_spi_init(void) reg &= ~0xc; /* disable crc checking */ reg &= ~0x70; reg |= (0x5 << 4); - if (!spi_write_reg(NMI_SPI_PROTOCOL_CONFIG, reg)) { + if(nm_spi_write_reg(NMI_SPI_PROTOCOL_CONFIG, reg) != M2M_SUCCESS) { M2M_ERR( "[nmi spi]: Failed internal write protocol reg...\n"); - return 0; + return M2M_ERR_BUS_FAIL; } gu8Crc_off = 1; } @@ -1235,7 +836,7 @@ sint8 nm_spi_init(void) /** make sure can read back chip id correctly **/ - if (!spi_read_reg(0x1000, &chipid)) { + if(nm_spi_read_reg_with_ret(0x1000, &chipid) != M2M_SUCCESS) { M2M_ERR("[nmi spi]: Fail cmd read chip id...\n"); return M2M_ERR_BUS_FAIL; } @@ -1247,13 +848,10 @@ sint8 nm_spi_init(void) return M2M_SUCCESS; } -/* +/** * @fn nm_spi_init * @brief DeInitialize the SPI -* @return M2M_SUCCESS in case of success and M2M_ERR_BUS_FAIL in case of failure -* @author Samer Sarhan -* @date 27 Feb 2015 -* @version 1.0 + * @return @ref M2M_SUCCESS in case of success and @ref M2M_ERR_BUS_FAIL in case of failure */ sint8 nm_spi_deinit(void) { @@ -1267,67 +865,16 @@ sint8 nm_spi_deinit(void) * @param [in] u32Addr * Register address * @return Register value -* @author M. Abdelmawla -* @date 11 July 2012 -* @version 1.0 */ uint32 nm_spi_read_reg(uint32 u32Addr) { uint32 u32Val; - spi_read_reg(u32Addr, &u32Val); + nm_spi_read_reg_with_ret(u32Addr, &u32Val); return u32Val; } -/* -* @fn nm_spi_read_reg_with_ret -* @brief Read register with error code return -* @param [in] u32Addr -* Register address -* @param [out] pu32RetVal -* Pointer to u32 variable used to return the read value -* @return M2M_SUCCESS in case of success and M2M_ERR_BUS_FAIL in case of failure -* @author M. Abdelmawla -* @date 11 July 2012 -* @version 1.0 -*/ -sint8 nm_spi_read_reg_with_ret(uint32 u32Addr, uint32* pu32RetVal) -{ - sint8 s8Ret; - - s8Ret = spi_read_reg(u32Addr,pu32RetVal); - - if(N_OK == s8Ret) s8Ret = M2M_SUCCESS; - else s8Ret = M2M_ERR_BUS_FAIL; - - return s8Ret; -} - -/* -* @fn nm_spi_write_reg -* @brief write register -* @param [in] u32Addr -* Register address -* @param [in] u32Val -* Value to be written to the register -* @return M2M_SUCCESS in case of success and M2M_ERR_BUS_FAIL in case of failure -* @author M. Abdelmawla -* @date 11 July 2012 -* @version 1.0 -*/ -sint8 nm_spi_write_reg(uint32 u32Addr, uint32 u32Val) -{ - sint8 s8Ret; - - s8Ret = spi_write_reg(u32Addr, u32Val); - - if(N_OK == s8Ret) s8Ret = M2M_SUCCESS; - else s8Ret = M2M_ERR_BUS_FAIL; - - return s8Ret; -} - /* * @fn nm_spi_read_block * @brief Read block of data diff --git a/src/drivers/winc1500/src/nmuart.c b/src/drivers/winc1500/src/nmuart.c index 308a8da5a..4af32234f 100644 --- a/src/drivers/winc1500/src/nmuart.c +++ b/src/drivers/winc1500/src/nmuart.c @@ -68,32 +68,32 @@ sint8 nm_uart_sync_cmd(void) /*read reg*/ while (retries--) { - b[0] = 0x12; - rsz = 1; - strUart.pu8Buf = b; - strUart.u16Sz = 1; + b[0] = 0x12; + rsz = 1; + strUart.pu8Buf = b; + strUart.u16Sz = 1; - if(M2M_SUCCESS == nm_bus_ioctl(NM_BUS_IOCTL_W, &strUart)) - { - strUart.u16Sz = rsz; + if (M2M_SUCCESS == nm_bus_ioctl(NM_BUS_IOCTL_W, &strUart)) + { + strUart.u16Sz = rsz; b[0] = 0; // Pull all chars from buffer, we only care about the last while (M2M_SUCCESS == nm_bus_ioctl(NM_BUS_IOCTL_R, &strUart)) - { + { strUart.u16Sz = rsz; - } + } - if (b[0] == 0x5a) - { + if (b[0] == 0x5a) + { s8Ret = 1; // found on-chip (no bridge) - M2M_INFO("Built-in WINC1500 UART Found\n"); - } - else if(b[0] == 0x5b) - { + M2M_INFO("Built-in WINC1500 UART Found\n"); + } + else if (b[0] == 0x5b) + { s8Ret = 0; // found off-chip (std serial bridge) - M2M_INFO("WINC1500 Serial Bridge Found\n"); - } + M2M_INFO("WINC1500 Serial Bridge Found\n"); + } else if (b[0] == 0x5c) { s8Ret = 0; // found of-chip (at cmd app serial bridge) diff --git a/src/drivers/winc1500/src/socket.c b/src/drivers/winc1500/src/socket.c index ae1df5cc0..33c8e875a 100644 --- a/src/drivers/winc1500/src/socket.c +++ b/src/drivers/winc1500/src/socket.c @@ -4,7 +4,7 @@ * * \brief BSD compatible socket interface. * - * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries. + * Copyright (c) 2016-2021 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * @@ -47,29 +47,30 @@ MACROS *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ -#define TLS_RECORD_HEADER_LENGTH (5) -#define ETHERNET_HEADER_OFFSET (34) -#define ETHERNET_HEADER_LENGTH (14) -#define TCP_IP_HEADER_LENGTH (40) -#define UDP_IP_HEADER_LENGTH (28) +#define TLS_RECORD_HEADER_LENGTH (5) +#define ETHERNET_HEADER_OFFSET (34) +#define ETHERNET_HEADER_LENGTH (14) +#define TCP_IP_HEADER_LENGTH (40) +#define UDP_IP_HEADER_LENGTH (28) -#define IP_PACKET_OFFSET (ETHERNET_HEADER_LENGTH + ETHERNET_HEADER_OFFSET - M2M_HIF_HDR_OFFSET) +#define IP_PACKET_OFFSET (ETHERNET_HEADER_LENGTH + ETHERNET_HEADER_OFFSET - M2M_HIF_HDR_OFFSET) -#define TCP_TX_PACKET_OFFSET (IP_PACKET_OFFSET + TCP_IP_HEADER_LENGTH) -#define UDP_TX_PACKET_OFFSET (IP_PACKET_OFFSET + UDP_IP_HEADER_LENGTH) -#define SSL_TX_PACKET_OFFSET (TCP_TX_PACKET_OFFSET + TLS_RECORD_HEADER_LENGTH) +#define TCP_TX_PACKET_OFFSET (IP_PACKET_OFFSET + TCP_IP_HEADER_LENGTH) +#define UDP_TX_PACKET_OFFSET (IP_PACKET_OFFSET + UDP_IP_HEADER_LENGTH) +#define SSL_TX_PACKET_OFFSET (TCP_TX_PACKET_OFFSET + TLS_RECORD_HEADER_LENGTH) -#define SOCKET_REQUEST(reqID, reqArgs, reqSize, reqPayload, reqPayloadSize, reqPayloadOffset) \ - hif_send(M2M_REQ_GROUP_IP, reqID, reqArgs, reqSize, reqPayload, reqPayloadSize, reqPayloadOffset) +#define SOCKET_REQUEST(reqID, reqArgs, reqSize, reqPayload, reqPayloadSize, reqPayloadOffset) \ + hif_send(M2M_REQ_GROUP_IP, reqID, reqArgs, reqSize, reqPayload, reqPayloadSize, reqPayloadOffset) -#define SSL_FLAGS_ACTIVE NBIT0 -#define SSL_FLAGS_BYPASS_X509 NBIT1 -#define SSL_FLAGS_2_RESERVD NBIT2 -#define SSL_FLAGS_3_RESERVD NBIT3 -#define SSL_FLAGS_CACHE_SESSION NBIT4 -#define SSL_FLAGS_NO_TX_COPY NBIT5 -#define SSL_FLAGS_CHECK_SNI NBIT6 +#define SSL_FLAGS_ACTIVE NBIT0 +#define SSL_FLAGS_BYPASS_X509 NBIT1 +#define SSL_FLAGS_2_RESERVD NBIT2 +#define SSL_FLAGS_3_RESERVD NBIT3 +#define SSL_FLAGS_CACHE_SESSION NBIT4 +#define SSL_FLAGS_NO_TX_COPY NBIT5 +#define SSL_FLAGS_CHECK_SNI NBIT6 +#define SSL_FLAGS_DELAY NBIT7 /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* PRIVATE DATA TYPES @@ -79,540 +80,562 @@ PRIVATE DATA TYPES /*! * @brief */ -typedef struct{ - SOCKET sock; - uint8 u8Dummy; - uint16 u16SessionID; -}tstrCloseCmd; +typedef struct { + SOCKET sock; + uint8 u8Dummy; + uint16 u16SessionID; +} tstrCloseCmd; /*! * @brief */ -typedef struct{ - uint8 *pu8UserBuffer; - uint16 u16UserBufferSize; - uint16 u16SessionID; - uint16 u16DataOffset; - uint8 bIsUsed; - uint8 u8SSLFlags; - uint8 bIsRecvPending; -}tstrSocket; +typedef struct { + uint8 *pu8UserBuffer; + uint16 u16UserBufferSize; + uint16 u16SessionID; + uint16 u16DataOffset; + uint8 bIsUsed; + uint8 u8SSLFlags; + uint8 bIsRecvPending; + uint8 u8AlpnStatus; + uint8 u8ErrSource; + uint8 u8ErrCode; +} tstrSocket; /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* GLOBALS *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ -volatile sint8 gsockerrno; -volatile tstrSocket gastrSockets[MAX_SOCKET]; -volatile uint8 gu8OpCode; -volatile uint16 gu16BufferSize; -volatile uint16 gu16SessionID = 0; +volatile sint8 gsockerrno; +volatile tstrSocket gastrSockets[MAX_SOCKET]; +volatile uint8 gu8OpCode; +volatile uint16 gu16BufferSize; +volatile uint16 gu16SessionID = 0; -volatile tpfAppSocketCb gpfAppSocketCb; -volatile tpfAppResolveCb gpfAppResolveCb; -volatile uint8 gbSocketInit = 0; -volatile tpfPingCb gfpPingCb; +volatile tpfAppSocketCb gpfAppSocketCb; +volatile tpfAppResolveCb gpfAppResolveCb; +volatile uint8 gbSocketInit = 0; + +static tpfPingCb gfpPingCb = NULL; +static uint32 gu32PingId = 0; /********************************************************************* Function - Socket_ReadSocketData + Socket_ReadSocketData Description - Callback function used by the NMC1500 driver to deliver messages - for socket layer. + Callback function used by the NMC1500 driver to deliver messages + for socket layer. Return - None. + None. Author - Ahmed Ezzat + Ahmed Ezzat Version - 1.0 + 1.0 Date - 17 July 2012 + 17 July 2012 *********************************************************************/ -NMI_API void Socket_ReadSocketData(SOCKET sock, tstrSocketRecvMsg *pstrRecv,uint8 u8SocketMsg, - uint32 u32StartAddress,uint16 u16ReadCount) +NMI_API void Socket_ReadSocketData(SOCKET sock, tstrSocketRecvMsg *pstrRecv, uint8 u8SocketMsg, + uint32 u32StartAddress, uint16 u16ReadCount) { - if((u16ReadCount > 0) && (gastrSockets[sock].pu8UserBuffer != NULL) && (gastrSockets[sock].u16UserBufferSize > 0) && (gastrSockets[sock].bIsUsed == 1)) - { - uint32 u32Address = u32StartAddress; - uint16 u16Read; - sint16 s16Diff; - uint8 u8SetRxDone; + uint32 u32Address = u32StartAddress; + uint16 u16Read; + sint16 s16Diff; - pstrRecv->u16RemainingSize = u16ReadCount; - do - { - u8SetRxDone = 1; - u16Read = u16ReadCount; - s16Diff = u16Read - gastrSockets[sock].u16UserBufferSize; - if(s16Diff > 0) - { - u8SetRxDone = 0; - u16Read = gastrSockets[sock].u16UserBufferSize; - } - - if(hif_receive(u32Address, gastrSockets[sock].pu8UserBuffer, u16Read, u8SetRxDone) == M2M_SUCCESS) - { - pstrRecv->pu8Buffer = gastrSockets[sock].pu8UserBuffer; - pstrRecv->s16BufferSize = u16Read; - pstrRecv->u16RemainingSize -= u16Read; + pstrRecv->u16RemainingSize = u16ReadCount; + if((u16ReadCount > 0) && (gastrSockets[sock].pu8UserBuffer != NULL) && (gastrSockets[sock].u16UserBufferSize > 0) && (gastrSockets[sock].bIsUsed == 1)) + { + u16Read = u16ReadCount; + s16Diff = u16Read - gastrSockets[sock].u16UserBufferSize; + if(s16Diff > 0) + { + /* We don't expect to be here. Firmware 19.6.4 and later only sends data to the driver according to the application's buffer size. + * But it is worth keeping this check, eg in case the application calls recv again with a smaller buffer size, or in case of HIF hacking. */ + u16Read = gastrSockets[sock].u16UserBufferSize; + } - if (gpfAppSocketCb) - gpfAppSocketCb(sock,u8SocketMsg, pstrRecv); + if(hif_receive(u32Address, gastrSockets[sock].pu8UserBuffer, u16Read, 1) == M2M_SUCCESS) + { + pstrRecv->pu8Buffer = gastrSockets[sock].pu8UserBuffer; + pstrRecv->s16BufferSize = u16Read; + pstrRecv->u16RemainingSize -= u16Read; - u16ReadCount -= u16Read; - u32Address += u16Read; + gastrSockets[sock].u16UserBufferSize = 0; + gastrSockets[sock].pu8UserBuffer = NULL; - if((!gastrSockets[sock].bIsUsed) && (u16ReadCount)) - { - M2M_DBG("Application Closed Socket While Rx Is not Complete\n"); - if(hif_receive(0, NULL, 0, 1) == M2M_SUCCESS) - M2M_DBG("hif_receive Success\n"); - else - M2M_DBG("hif_receive Fail\n"); - break; - } - } - else - { - M2M_INFO("(ERRR)Current <%d>\n", u16ReadCount); - break; - } - }while(u16ReadCount != 0); - } + if(gpfAppSocketCb) + gpfAppSocketCb(sock, u8SocketMsg, pstrRecv); + } + else + { + M2M_ERR("Current <%d>\n", u16ReadCount); + } + } } + /********************************************************************* Function - m2m_ip_cb + m2m_ip_cb Description - Callback function used by the NMC1000 driver to deliver messages - for socket layer. + Callback function used by the NMC1000 driver to deliver messages + for socket layer. Return - None. + None. Author - Ahmed Ezzat + Ahmed Ezzat Version - 1.0 + 1.0 Date - 17 July 2012 + 17 July 2012 *********************************************************************/ -static void m2m_ip_cb(uint8 u8OpCode, uint16 u16BufferSize,uint32 u32Address) -{ - if((u8OpCode == SOCKET_CMD_BIND) || (u8OpCode == SOCKET_CMD_SSL_BIND)) - { - tstrBindReply strBindReply; - tstrSocketBindMsg strBind; +static void m2m_ip_cb(uint8 u8OpCode, uint16 u16BufferSize, uint32 u32Address) +{ + if((u8OpCode == SOCKET_CMD_BIND) || (u8OpCode == SOCKET_CMD_SSL_BIND)) + { + tstrBindReply strBindReply; + tstrSocketBindMsg strBind; - if(hif_receive(u32Address, (uint8*)&strBindReply, sizeof(tstrBindReply), 0) == M2M_SUCCESS) - { - strBind.status = strBindReply.s8Status; - if(gpfAppSocketCb) - gpfAppSocketCb(strBindReply.sock,SOCKET_MSG_BIND,&strBind); - } - } - else if(u8OpCode == SOCKET_CMD_LISTEN) - { - tstrListenReply strListenReply; - tstrSocketListenMsg strListen; - if(hif_receive(u32Address, (uint8*)&strListenReply, sizeof(tstrListenReply), 0) == M2M_SUCCESS) - { - strListen.status = strListenReply.s8Status; - if(gpfAppSocketCb) - gpfAppSocketCb(strListenReply.sock,SOCKET_MSG_LISTEN, &strListen); - } - } - else if(u8OpCode == SOCKET_CMD_ACCEPT) - { - tstrAcceptReply strAcceptReply; - tstrSocketAcceptMsg strAccept; - if(hif_receive(u32Address, (uint8*)&strAcceptReply, sizeof(tstrAcceptReply), 0) == M2M_SUCCESS) - { - if(strAcceptReply.sConnectedSock >= 0) - { - gastrSockets[strAcceptReply.sConnectedSock].u8SSLFlags = gastrSockets[strAcceptReply.sListenSock].u8SSLFlags; - gastrSockets[strAcceptReply.sConnectedSock].bIsUsed = 1; - gastrSockets[strAcceptReply.sConnectedSock].u16DataOffset = strAcceptReply.u16AppDataOffset - M2M_HIF_HDR_OFFSET; + if(hif_receive(u32Address, (uint8 *)&strBindReply, sizeof(tstrBindReply), 0) == M2M_SUCCESS) + { + strBind.status = strBindReply.s8Status; + if(gpfAppSocketCb) + gpfAppSocketCb(strBindReply.sock, SOCKET_MSG_BIND, &strBind); + } + } + else if(u8OpCode == SOCKET_CMD_LISTEN) + { + tstrListenReply strListenReply; + tstrSocketListenMsg strListen; + if(hif_receive(u32Address, (uint8 *)&strListenReply, sizeof(tstrListenReply), 0) == M2M_SUCCESS) + { + strListen.status = strListenReply.s8Status; + if(gpfAppSocketCb) + gpfAppSocketCb(strListenReply.sock, SOCKET_MSG_LISTEN, &strListen); + } + } + else if(u8OpCode == SOCKET_CMD_ACCEPT) + { + tstrAcceptReply strAcceptReply; + tstrSocketAcceptMsg strAccept; + if(hif_receive(u32Address, (uint8 *)&strAcceptReply, sizeof(tstrAcceptReply), 0) == M2M_SUCCESS) + { + if((strAcceptReply.sConnectedSock >= 0) && (strAcceptReply.sConnectedSock < MAX_SOCKET)) + { + gastrSockets[strAcceptReply.sConnectedSock].u8SSLFlags = gastrSockets[strAcceptReply.sListenSock].u8SSLFlags; + gastrSockets[strAcceptReply.sConnectedSock].bIsUsed = 1; + gastrSockets[strAcceptReply.sConnectedSock].u16DataOffset = strAcceptReply.u16AppDataOffset - M2M_HIF_HDR_OFFSET; - /* The session ID is used to distinguish different socket connections - by comparing the assigned session ID to the one reported by the firmware*/ - ++gu16SessionID; - if(gu16SessionID == 0) - ++gu16SessionID; + /* The session ID is used to distinguish different socket connections + by comparing the assigned session ID to the one reported by the firmware*/ + ++gu16SessionID; + if(gu16SessionID == 0) + ++gu16SessionID; - gastrSockets[strAcceptReply.sConnectedSock].u16SessionID = gu16SessionID; - M2M_DBG("Socket %d session ID = %d\r\n",strAcceptReply.sConnectedSock , gu16SessionID ); - } - strAccept.sock = strAcceptReply.sConnectedSock; - strAccept.strAddr.sin_family = AF_INET; - strAccept.strAddr.sin_port = strAcceptReply.strAddr.u16Port; - strAccept.strAddr.sin_addr.s_addr = strAcceptReply.strAddr.u32IPAddr; - if(gpfAppSocketCb) - gpfAppSocketCb(strAcceptReply.sListenSock, SOCKET_MSG_ACCEPT, &strAccept); - } - } - else if((u8OpCode == SOCKET_CMD_CONNECT) || (u8OpCode == SOCKET_CMD_SSL_CONNECT)) - { - tstrConnectReply strConnectReply; - tstrSocketConnectMsg strConnMsg; - if(hif_receive(u32Address, (uint8*)&strConnectReply, sizeof(tstrConnectReply), 0) == M2M_SUCCESS) - { - strConnMsg.sock = strConnectReply.sock; - strConnMsg.s8Error = strConnectReply.s8Error; - if(strConnectReply.s8Error == SOCK_ERR_NO_ERROR) - { - gastrSockets[strConnectReply.sock].u16DataOffset = strConnectReply.u16AppDataOffset - M2M_HIF_HDR_OFFSET; - } - if(gpfAppSocketCb) - gpfAppSocketCb(strConnectReply.sock,SOCKET_MSG_CONNECT, &strConnMsg); - } - } - else if(u8OpCode == SOCKET_CMD_DNS_RESOLVE) - { - tstrDnsReply strDnsReply; - if(hif_receive(u32Address, (uint8*)&strDnsReply, sizeof(tstrDnsReply), 0) == M2M_SUCCESS) - { - if(gpfAppResolveCb) - gpfAppResolveCb((uint8*)strDnsReply.acHostName, strDnsReply.u32HostIP); - } - } - else if((u8OpCode == SOCKET_CMD_RECV) || (u8OpCode == SOCKET_CMD_RECVFROM) || (u8OpCode == SOCKET_CMD_SSL_RECV)) - { - SOCKET sock; - sint16 s16RecvStatus; - tstrRecvReply strRecvReply; - uint16 u16ReadSize; - tstrSocketRecvMsg strRecvMsg; - uint8 u8CallbackMsgID = SOCKET_MSG_RECV; - uint16 u16DataOffset; + gastrSockets[strAcceptReply.sConnectedSock].u16SessionID = gu16SessionID; + M2M_DBG("Socket %d session ID = %d\r\n", strAcceptReply.sConnectedSock, gu16SessionID); + } + strAccept.sock = strAcceptReply.sConnectedSock; + strAccept.strAddr.sin_family = AF_INET; + strAccept.strAddr.sin_port = strAcceptReply.strAddr.u16Port; + strAccept.strAddr.sin_addr.s_addr = strAcceptReply.strAddr.u32IPAddr; + if(gpfAppSocketCb) + gpfAppSocketCb(strAcceptReply.sListenSock, SOCKET_MSG_ACCEPT, &strAccept); + } + } + else if((u8OpCode == SOCKET_CMD_CONNECT) || (u8OpCode == SOCKET_CMD_SSL_CONNECT) || (u8OpCode == SOCKET_CMD_SSL_CONNECT_ALPN)) + { + /* Note that for successful connections the fw always sends SOCKET_CMD_CONNECT, even for SSL connections. */ + tstrConnectAlpnReply strConnectAlpnReply = {{0}}; + tstrSocketConnectMsg strConnMsg; + uint16 u16HifSz = sizeof(tstrConnectAlpnReply); + if(u8OpCode != SOCKET_CMD_SSL_CONNECT_ALPN) + u16HifSz = sizeof(tstrConnectReply); + if(hif_receive(u32Address, (uint8*)&strConnectAlpnReply, u16HifSz, 0) == M2M_SUCCESS) + { + if((strConnectAlpnReply.strConnReply.sock >= 0) && (strConnectAlpnReply.strConnReply.sock < MAX_SOCKET)) + { + uint8 u8Msg = SOCKET_MSG_CONNECT; - if(u8OpCode == SOCKET_CMD_RECVFROM) - u8CallbackMsgID = SOCKET_MSG_RECVFROM; + strConnMsg.sock = strConnectAlpnReply.strConnReply.sock; + strConnMsg.s8Error = strConnectAlpnReply.strConnReply.s8Error; - /* Read RECV REPLY data structure. - */ - u16ReadSize = sizeof(tstrRecvReply); - if(hif_receive(u32Address, (uint8*)&strRecvReply, u16ReadSize, 0) == M2M_SUCCESS) - { - uint16 u16SessionID = 0; + /* If the SOCKET_CMD_SSL_CONNECT op code is received and the socket was already connected, then the + callback corresponds to an attempt to make the socket secure. */ + if(0 != gastrSockets[strConnMsg.sock].u16DataOffset) + { + u8Msg = SOCKET_MSG_SECURE; + } + if(strConnectAlpnReply.strConnReply.s8Error == SOCK_ERR_NO_ERROR) + { + gastrSockets[strConnMsg.sock].u16DataOffset = strConnectAlpnReply.strConnReply.u16AppDataOffset - M2M_HIF_HDR_OFFSET; + gastrSockets[strConnMsg.sock].u8AlpnStatus = strConnectAlpnReply.u8AppProtocolIdx; + } + else + { + gastrSockets[strConnMsg.sock].u8ErrSource = strConnectAlpnReply.strConnReply.u8ErrSource; + gastrSockets[strConnMsg.sock].u8ErrCode = strConnectAlpnReply.strConnReply.u8ErrCode; + } + if(gpfAppSocketCb) + gpfAppSocketCb(strConnMsg.sock, u8Msg, &strConnMsg); + } + } + } + else if(u8OpCode == SOCKET_CMD_DNS_RESOLVE) + { + tstrDnsReply strDnsReply; + if(hif_receive(u32Address, (uint8 *)&strDnsReply, sizeof(tstrDnsReply), 0) == M2M_SUCCESS) + { + if(gpfAppResolveCb) + gpfAppResolveCb((uint8 *)strDnsReply.acHostName, strDnsReply.u32HostIP); + } + } + else if((u8OpCode == SOCKET_CMD_RECV) || (u8OpCode == SOCKET_CMD_RECVFROM) || (u8OpCode == SOCKET_CMD_SSL_RECV)) + { + SOCKET sock; + sint16 s16RecvStatus; + tstrRecvReply strRecvReply; + uint16 u16ReadSize; + tstrSocketRecvMsg strRecvMsg; + uint8 u8CallbackMsgID = SOCKET_MSG_RECV; + uint16 u16DataOffset; - sock = strRecvReply.sock; - u16SessionID = strRecvReply.u16SessionID; - M2M_DBG("recv callback session ID = %d\r\n",u16SessionID); - - /* Reset the Socket RX Pending Flag. - */ - gastrSockets[sock].bIsRecvPending = 0; + if(u8OpCode == SOCKET_CMD_RECVFROM) + u8CallbackMsgID = SOCKET_MSG_RECVFROM; - s16RecvStatus = NM_BSP_B_L_16(strRecvReply.s16RecvStatus); - u16DataOffset = NM_BSP_B_L_16(strRecvReply.u16DataOffset); - strRecvMsg.strRemoteAddr.sin_port = strRecvReply.strRemoteAddr.u16Port; - strRecvMsg.strRemoteAddr.sin_addr.s_addr = strRecvReply.strRemoteAddr.u32IPAddr; + /* Read RECV REPLY data structure. + */ + u16ReadSize = sizeof(tstrRecvReply); + if(hif_receive(u32Address, (uint8 *)&strRecvReply, u16ReadSize, 0) == M2M_SUCCESS) + { + if((strRecvReply.sock >= 0) && (strRecvReply.sock < MAX_SOCKET)) + { + uint16 u16SessionID = 0; - if(u16SessionID == gastrSockets[sock].u16SessionID) - { - if((s16RecvStatus > 0) && (s16RecvStatus < u16BufferSize)) - { - /* Skip incoming bytes until reaching the Start of Application Data. - */ - u32Address += u16DataOffset; + sock = strRecvReply.sock; + u16SessionID = strRecvReply.u16SessionID; + M2M_DBG("recv callback session ID = %d\r\n", u16SessionID); - /* Read the Application data and deliver it to the application callback in - the given application buffer. If the buffer is smaller than the received data, - the data is passed to the application in chunks according to its buffer size. - */ - u16ReadSize = (uint16)s16RecvStatus; - Socket_ReadSocketData(sock, &strRecvMsg, u8CallbackMsgID, u32Address, u16ReadSize); - } - else - { - /* Don't tidy up here. Application must call close(). - */ - strRecvMsg.s16BufferSize = s16RecvStatus; - strRecvMsg.pu8Buffer = NULL; - if(gpfAppSocketCb) - gpfAppSocketCb(sock,u8CallbackMsgID, &strRecvMsg); - } - } - else - { - M2M_DBG("Discard recv callback %d %d \r\n",u16SessionID , gastrSockets[sock].u16SessionID); - if(u16ReadSize < u16BufferSize) - { - if(hif_receive(0, NULL, 0, 1) == M2M_SUCCESS) - M2M_DBG("hif_receive Success\n"); - else - M2M_DBG("hif_receive Fail\n"); - } - } - } - } - else if((u8OpCode == SOCKET_CMD_SEND) || (u8OpCode == SOCKET_CMD_SENDTO) || (u8OpCode == SOCKET_CMD_SSL_SEND)) - { - SOCKET sock; - sint16 s16Rcvd; - tstrSendReply strReply; - uint8 u8CallbackMsgID = SOCKET_MSG_SEND; + /* Reset the Socket RX Pending Flag. + */ + gastrSockets[sock].bIsRecvPending = 0; - if(u8OpCode == SOCKET_CMD_SENDTO) - u8CallbackMsgID = SOCKET_MSG_SENDTO; + s16RecvStatus = NM_BSP_B_L_16(strRecvReply.s16RecvStatus); + u16DataOffset = NM_BSP_B_L_16(strRecvReply.u16DataOffset); + strRecvMsg.strRemoteAddr.sin_port = strRecvReply.strRemoteAddr.u16Port; + strRecvMsg.strRemoteAddr.sin_addr.s_addr = strRecvReply.strRemoteAddr.u32IPAddr; - if(hif_receive(u32Address, (uint8*)&strReply, sizeof(tstrSendReply), 0) == M2M_SUCCESS) - { - uint16 u16SessionID = 0; - - sock = strReply.sock; - u16SessionID = strReply.u16SessionID; - M2M_DBG("send callback session ID = %d\r\n",u16SessionID); - - s16Rcvd = NM_BSP_B_L_16(strReply.s16SentBytes); + if(u16SessionID == gastrSockets[sock].u16SessionID) + { + if((s16RecvStatus > 0) && (s16RecvStatus < u16BufferSize)) + { + /* Skip incoming bytes until reaching the Start of Application Data. + */ + u32Address += u16DataOffset; - if(u16SessionID == gastrSockets[sock].u16SessionID) - { - if(gpfAppSocketCb) - gpfAppSocketCb(sock,u8CallbackMsgID, &s16Rcvd); - } - else - { - M2M_DBG("Discard send callback %d %d \r\n",u16SessionID , gastrSockets[sock].u16SessionID); - } - } - } - else if(u8OpCode == SOCKET_CMD_PING) - { - tstrPingReply strPingReply; - if(hif_receive(u32Address, (uint8*)&strPingReply, sizeof(tstrPingReply), 1) == M2M_SUCCESS) - { - gfpPingCb = (void (*)(uint32 , uint32 , uint8))strPingReply.u32CmdPrivate; - if(gfpPingCb != NULL) - { - gfpPingCb(strPingReply.u32IPAddr, strPingReply.u32RTT, strPingReply.u8ErrorCode); - } - } - } + /* Read the Application data and deliver it to the application callback in + the given application buffer. Firmware since 19.6.4 only sends data up to + the size of the application buffer. For TCP, a new call to recv is needed + in order to retrieve any outstanding data from firmware. + */ + u16ReadSize = (uint16)s16RecvStatus; + Socket_ReadSocketData(sock, &strRecvMsg, u8CallbackMsgID, u32Address, u16ReadSize); + } + else + { + /* Don't tidy up here. Application must call close(). + */ + strRecvMsg.s16BufferSize = s16RecvStatus; + strRecvMsg.pu8Buffer = NULL; + if(gpfAppSocketCb) + gpfAppSocketCb(sock, u8CallbackMsgID, &strRecvMsg); + } + } + else + { + M2M_DBG("Discard recv callback %d %d \r\n", u16SessionID, gastrSockets[sock].u16SessionID); + if(u16ReadSize < u16BufferSize) + if(hif_receive(0, NULL, 0, 1) != M2M_SUCCESS) + M2M_ERR("hif rx done failed\n"); + } + } + } + } + else if((u8OpCode == SOCKET_CMD_SEND) || (u8OpCode == SOCKET_CMD_SENDTO) || (u8OpCode == SOCKET_CMD_SSL_SEND)) + { + SOCKET sock; + sint16 s16Rcvd; + tstrSendReply strReply; + uint8 u8CallbackMsgID = SOCKET_MSG_SEND; + + if(u8OpCode == SOCKET_CMD_SENDTO) + u8CallbackMsgID = SOCKET_MSG_SENDTO; + + if(hif_receive(u32Address, (uint8 *)&strReply, sizeof(tstrSendReply), 0) == M2M_SUCCESS) + { + if((strReply.sock >=0) && (strReply.sock < MAX_SOCKET)) + { + uint16 u16SessionID = 0; + + sock = strReply.sock; + u16SessionID = strReply.u16SessionID; + M2M_DBG("send callback session ID = %d\r\n", u16SessionID); + + s16Rcvd = NM_BSP_B_L_16(strReply.s16SentBytes); + + if(u16SessionID == gastrSockets[sock].u16SessionID) + { + if(gpfAppSocketCb) + gpfAppSocketCb(sock, u8CallbackMsgID, &s16Rcvd); + } + else + { + M2M_DBG("Discard send callback %d %d \r\n", u16SessionID, gastrSockets[sock].u16SessionID); + } + } + } + } + else if(u8OpCode == SOCKET_CMD_PING) + { + tstrPingReply strPingReply; + if(hif_receive(u32Address, (uint8 *)&strPingReply, sizeof(tstrPingReply), 1) == M2M_SUCCESS) + { + if((gu32PingId == strPingReply.u32CmdPrivate) && (gfpPingCb != NULL)) + { + gfpPingCb(strPingReply.u32IPAddr, strPingReply.u32RTT, strPingReply.u8ErrorCode); + } + } + } } + /********************************************************************* Function - socketInit + socketInit Description Return - None. + None. Author - Ahmed Ezzat + Ahmed Ezzat Version - 1.0 + 1.0 Date - 4 June 2012 + 4 June 2012 *********************************************************************/ void socketInit(void) { - if(gbSocketInit == 0) - { - m2m_memset((uint8*)gastrSockets, 0, MAX_SOCKET * sizeof(tstrSocket)); - hif_register_cb(M2M_REQ_GROUP_IP,m2m_ip_cb); - gbSocketInit = 1; - gu16SessionID = 0; - } + if(gbSocketInit == 0) + { + m2m_memset((uint8 *)gastrSockets, 0, MAX_SOCKET * sizeof(tstrSocket)); + hif_register_cb(M2M_REQ_GROUP_IP, m2m_ip_cb); + gbSocketInit = 1; + gu16SessionID = 0; + } } + /********************************************************************* Function - socketDeinit - -Description - -Return - None. - -Author - Samer Sarhan - -Version - 1.0 - -Date - 27 Feb 2015 -*********************************************************************/ -void socketDeinit(void) -{ - m2m_memset((uint8*)gastrSockets, 0, MAX_SOCKET * sizeof(tstrSocket)); - hif_register_cb(M2M_REQ_GROUP_IP, NULL); - gpfAppSocketCb = NULL; - gpfAppResolveCb = NULL; - gbSocketInit = 0; -} -/********************************************************************* -Function - registerSocketCallback + socketDeinit Description Return - None. + None. Author - Ahmed Ezzat + Samer Sarhan Version - 1.0 + 1.0 Date - 4 June 2012 + 27 Feb 2015 +*********************************************************************/ +void socketDeinit(void) +{ + m2m_memset((uint8 *)gastrSockets, 0, MAX_SOCKET * sizeof(tstrSocket)); + hif_register_cb(M2M_REQ_GROUP_IP, NULL); + gpfAppSocketCb = NULL; + gpfAppResolveCb = NULL; + gbSocketInit = 0; +} +/********************************************************************* +Function + registerSocketCallback + +Description + +Return + None. + +Author + Ahmed Ezzat + +Version + 1.0 + +Date + 4 June 2012 *********************************************************************/ void registerSocketCallback(tpfAppSocketCb pfAppSocketCb, tpfAppResolveCb pfAppResolveCb) { - gpfAppSocketCb = pfAppSocketCb; - gpfAppResolveCb = pfAppResolveCb; + gpfAppSocketCb = pfAppSocketCb; + gpfAppResolveCb = pfAppResolveCb; } /********************************************************************* Function - socket + socket Description - Creates a socket. + Creates a socket. Return - - Negative value for error. - - ZERO or positive value as a socket ID if successful. + - Negative value for error. + - ZERO or positive value as a socket ID if successful. Author - Ahmed Ezzat + Ahmed Ezzat Version - 1.0 + 1.0 Date - 4 June 2012 + 4 June 2012 *********************************************************************/ -SOCKET WINC1500_EXPORT(socket)(uint16 u16Domain, uint8 u8Type, uint8 u8Flags) +SOCKET WINC1500_EXPORT(socket)(uint16 u16Domain, uint8 u8Type, uint8 u8Config) { - SOCKET sock = -1; - uint8 u8SockID; - uint8 u8Count; - volatile tstrSocket *pstrSock; - static volatile uint8 u8NextTcpSock = 0; - static volatile uint8 u8NextUdpSock = 0; + SOCKET sock = -1; + uint8 u8SockID; + uint8 u8Count; + volatile tstrSocket *pstrSock; + static volatile uint8 u8NextTcpSock = 0; + static volatile uint8 u8NextUdpSock = 0; - /* The only supported family is the AF_INET for UDP and TCP transport layer protocols. */ - if(u16Domain == AF_INET) - { - if(u8Type == SOCK_STREAM) - { - for(u8Count = 0; u8Count < TCP_SOCK_MAX; u8Count ++) - { - u8SockID = u8NextTcpSock; - pstrSock = &gastrSockets[u8NextTcpSock]; - u8NextTcpSock = (u8NextTcpSock + 1) % TCP_SOCK_MAX; - if(!pstrSock->bIsUsed) - { - sock = (SOCKET)u8SockID; - break; - } - } - } - else if(u8Type == SOCK_DGRAM) - { - volatile tstrSocket *pastrUDPSockets = &gastrSockets[TCP_SOCK_MAX]; - for(u8Count = 0; u8Count < UDP_SOCK_MAX; u8Count ++) - { - u8SockID = u8NextUdpSock; - pstrSock = &pastrUDPSockets[u8NextUdpSock]; - u8NextUdpSock = (u8NextUdpSock + 1) % UDP_SOCK_MAX; - if(!pstrSock->bIsUsed) - { - sock = (SOCKET)(u8SockID + TCP_SOCK_MAX); - break; - } - } - } + /* The only supported family is the AF_INET for UDP and TCP transport layer protocols. */ + if(u16Domain == AF_INET) + { + if(u8Type == SOCK_STREAM) + { + for(u8Count = 0; u8Count < TCP_SOCK_MAX; u8Count ++) + { + u8SockID = u8NextTcpSock; + pstrSock = &gastrSockets[u8NextTcpSock]; + u8NextTcpSock = (u8NextTcpSock + 1) % TCP_SOCK_MAX; + if(!pstrSock->bIsUsed) + { + sock = (SOCKET)u8SockID; + break; + } + } + } + else if(u8Type == SOCK_DGRAM) + { + volatile tstrSocket *pastrUDPSockets = &gastrSockets[TCP_SOCK_MAX]; + for(u8Count = 0; u8Count < UDP_SOCK_MAX; u8Count ++) + { + u8SockID = u8NextUdpSock; + pstrSock = &pastrUDPSockets[u8NextUdpSock]; + u8NextUdpSock = (u8NextUdpSock + 1) % UDP_SOCK_MAX; + if(!pstrSock->bIsUsed) + { + sock = (SOCKET)(u8SockID + TCP_SOCK_MAX); + break; + } + } + } - if(sock >= 0) - { - m2m_memset((uint8*)pstrSock, 0, sizeof(tstrSocket)); - pstrSock->bIsUsed = 1; + if(sock >= 0) + { + m2m_memset((uint8 *)pstrSock, 0, sizeof(tstrSocket)); + pstrSock->bIsUsed = 1; - /* The session ID is used to distinguish different socket connections - by comparing the assigned session ID to the one reported by the firmware*/ - ++gu16SessionID; - if(gu16SessionID == 0) - ++gu16SessionID; - - pstrSock->u16SessionID = gu16SessionID; - M2M_INFO("Socket %d session ID = %d\r\n",sock, gu16SessionID ); + /* The session ID is used to distinguish different socket connections + by comparing the assigned session ID to the one reported by the firmware*/ + ++gu16SessionID; + if(gu16SessionID == 0) + ++gu16SessionID; - if(u8Flags & SOCKET_FLAGS_SSL) - { - tstrSSLSocketCreateCmd strSSLCreate; - strSSLCreate.sslSock = sock; - pstrSock->u8SSLFlags = SSL_FLAGS_ACTIVE | SSL_FLAGS_NO_TX_COPY; - SOCKET_REQUEST(SOCKET_CMD_SSL_CREATE, (uint8*)&strSSLCreate, sizeof(tstrSSLSocketCreateCmd), 0, 0, 0); - } - } - } - return sock; + pstrSock->u16SessionID = gu16SessionID; + M2M_INFO("Socket %d session ID = %d\r\n", sock, gu16SessionID); + + if((u8Type == SOCK_STREAM) && (u8Config != SOCKET_CONFIG_SSL_OFF)) + { + tstrSSLSocketCreateCmd strSSLCreate; + strSSLCreate.sslSock = sock; + SOCKET_REQUEST(SOCKET_CMD_SSL_CREATE, (uint8 *)&strSSLCreate, sizeof(tstrSSLSocketCreateCmd), 0, 0, 0); + + pstrSock->u8SSLFlags = SSL_FLAGS_ACTIVE | SSL_FLAGS_NO_TX_COPY; + if(u8Config == SOCKET_CONFIG_SSL_DELAY) + pstrSock->u8SSLFlags |= SSL_FLAGS_DELAY; + } + } + } + return sock; } + /********************************************************************* Function - bind + bind Description - Request to bind a socket on a local address. + Request to bind a socket on a local address. Return Author - Ahmed Ezzat + Ahmed Ezzat Version - 1.0 + 1.0 Date - 5 June 2012 + 5 June 2012 *********************************************************************/ sint8 WINC1500_EXPORT(bind)(SOCKET sock, struct sockaddr *pstrAddr, uint8 u8AddrLen) { - sint8 s8Ret = SOCK_ERR_INVALID_ARG; - if((pstrAddr != NULL) && (sock >= 0) && (gastrSockets[sock].bIsUsed == 1) && (u8AddrLen != 0)) - { - tstrBindCmd strBind; - uint8 u8CMD = SOCKET_CMD_BIND; - if(gastrSockets[sock].u8SSLFlags & SSL_FLAGS_ACTIVE) - { - u8CMD = SOCKET_CMD_SSL_BIND; - } + sint8 s8Ret = SOCK_ERR_INVALID_ARG; + if((pstrAddr != NULL) && (sock >= 0) && (sock < MAX_SOCKET) && (gastrSockets[sock].bIsUsed == 1) && (u8AddrLen != 0)) + { + tstrBindCmd strBind; + uint8 u8CMD = SOCKET_CMD_BIND; + if(gastrSockets[sock].u8SSLFlags & SSL_FLAGS_ACTIVE) + { + u8CMD = SOCKET_CMD_SSL_BIND; + } - /* Build the bind request. */ - strBind.sock = sock; - m2m_memcpy((uint8 *)&strBind.strAddr, (uint8 *)pstrAddr, sizeof(tstrSockAddr)); - strBind.u16SessionID = gastrSockets[sock].u16SessionID; - - /* Send the request. */ - s8Ret = SOCKET_REQUEST(u8CMD, (uint8*)&strBind,sizeof(tstrBindCmd) , NULL , 0, 0); - if(s8Ret != SOCK_ERR_NO_ERROR) - { - s8Ret = SOCK_ERR_INVALID; - } - } - return s8Ret; + /* Build the bind request. */ + strBind.sock = sock; + m2m_memcpy((uint8 *)&strBind.strAddr, (uint8 *)pstrAddr, sizeof(tstrSockAddr)); + strBind.u16SessionID = gastrSockets[sock].u16SessionID; + + /* Send the request. */ + s8Ret = SOCKET_REQUEST(u8CMD, (uint8 *)&strBind, sizeof(tstrBindCmd), NULL, 0, 0); + if(s8Ret != SOCK_ERR_NO_ERROR) + { + s8Ret = SOCK_ERR_INVALID; + } + } + return s8Ret; } + /********************************************************************* Function - listen + listen Description @@ -621,37 +644,37 @@ Return Author - Ahmed Ezzat + Ahmed Ezzat Version - 1.0 + 1.0 Date - 5 June 2012 + 5 June 2012 *********************************************************************/ sint8 WINC1500_EXPORT(listen)(SOCKET sock, uint8 backlog) { - sint8 s8Ret = SOCK_ERR_INVALID_ARG; - - if(sock >= 0 && (gastrSockets[sock].bIsUsed == 1)) - { - tstrListenCmd strListen; + sint8 s8Ret = SOCK_ERR_INVALID_ARG; - strListen.sock = sock; - strListen.u8BackLog = backlog; - strListen.u16SessionID = gastrSockets[sock].u16SessionID; + if((sock >= 0) && (sock < MAX_SOCKET) && (gastrSockets[sock].bIsUsed == 1)) + { + tstrListenCmd strListen; - s8Ret = SOCKET_REQUEST(SOCKET_CMD_LISTEN, (uint8*)&strListen, sizeof(tstrListenCmd), NULL, 0, 0); - if(s8Ret != SOCK_ERR_NO_ERROR) - { - s8Ret = SOCK_ERR_INVALID; - } - } - return s8Ret; + strListen.sock = sock; + strListen.u8BackLog = backlog; + strListen.u16SessionID = gastrSockets[sock].u16SessionID; + + s8Ret = SOCKET_REQUEST(SOCKET_CMD_LISTEN, (uint8 *)&strListen, sizeof(tstrListenCmd), NULL, 0, 0); + if(s8Ret != SOCK_ERR_NO_ERROR) + { + s8Ret = SOCK_ERR_INVALID; + } + } + return s8Ret; } /********************************************************************* Function - accept + accept Description @@ -659,175 +682,224 @@ Return Author - Ahmed Ezzat + Ahmed Ezzat Version - 1.0 + 1.0 Date - 5 June 2012 + 5 June 2012 *********************************************************************/ sint8 WINC1500_EXPORT(accept)(SOCKET sock, struct sockaddr *addr, uint8 *addrlen) { - sint8 s8Ret = SOCK_ERR_INVALID_ARG; - - if(sock >= 0 && (gastrSockets[sock].bIsUsed == 1) ) - { - s8Ret = SOCK_ERR_NO_ERROR; - } - return s8Ret; + sint8 s8Ret = SOCK_ERR_INVALID_ARG; + + if((sock >= 0) && (sock < MAX_SOCKET) && (gastrSockets[sock].bIsUsed == 1)) + { + s8Ret = SOCK_ERR_NO_ERROR; + } + return s8Ret; } /********************************************************************* Function - connect + connect Description - Connect to a remote TCP Server. + Connect to a remote TCP Server. Return Author - Ahmed Ezzat + Ahmed Ezzat Version - 1.0 + 1.0 Date - 5 June 2012 + 5 June 2012 *********************************************************************/ sint8 WINC1500_EXPORT(connect)(SOCKET sock, struct sockaddr *pstrAddr, uint8 u8AddrLen) { - sint8 s8Ret = SOCK_ERR_INVALID_ARG; - if((sock >= 0) && (pstrAddr != NULL) && (gastrSockets[sock].bIsUsed == 1) && (u8AddrLen != 0)) - { - tstrConnectCmd strConnect; - uint8 u8Cmd = SOCKET_CMD_CONNECT; - if((gastrSockets[sock].u8SSLFlags) & SSL_FLAGS_ACTIVE) - { - u8Cmd = SOCKET_CMD_SSL_CONNECT; - strConnect.u8SslFlags = gastrSockets[sock].u8SSLFlags; - } - strConnect.sock = sock; - m2m_memcpy((uint8 *)&strConnect.strAddr, (uint8 *)pstrAddr, sizeof(tstrSockAddr)); + sint8 s8Ret = SOCK_ERR_INVALID_ARG; + if((sock >= 0) && (sock < MAX_SOCKET) && (pstrAddr != NULL) && (gastrSockets[sock].bIsUsed == 1) && (u8AddrLen != 0)) + { + tstrConnectCmd strConnect; + uint8 u8Cmd = SOCKET_CMD_CONNECT; + if((gastrSockets[sock].u8SSLFlags) & SSL_FLAGS_ACTIVE) + { + u8Cmd = SOCKET_CMD_SSL_CONNECT; + strConnect.u8SslFlags = gastrSockets[sock].u8SSLFlags; + } + strConnect.sock = sock; + m2m_memcpy((uint8 *)&strConnect.strAddr, (uint8 *)pstrAddr, sizeof(tstrSockAddr)); - strConnect.u16SessionID = gastrSockets[sock].u16SessionID; - s8Ret = SOCKET_REQUEST(u8Cmd, (uint8*)&strConnect,sizeof(tstrConnectCmd), NULL, 0, 0); - if(s8Ret != SOCK_ERR_NO_ERROR) - { - s8Ret = SOCK_ERR_INVALID; - } - } - return s8Ret; + strConnect.u16SessionID = gastrSockets[sock].u16SessionID; + s8Ret = SOCKET_REQUEST(u8Cmd, (uint8 *)&strConnect, sizeof(tstrConnectCmd), NULL, 0, 0); + if(s8Ret != SOCK_ERR_NO_ERROR) + { + s8Ret = SOCK_ERR_INVALID; + } + } + return s8Ret; } /********************************************************************* Function - send + secure + +Description + Make secure (TLS) an open TCP client connection. + +Return + + +Author + Matthew Gunton + +Version + 1.0 + +Date + 7 November 2019 +*********************************************************************/ +sint8 WINC1500_EXPORT(secure)(SOCKET sock) +{ + sint8 s8Ret = SOCK_ERR_INVALID_ARG; + if((sock >= 0) && (sock < MAX_SOCKET) && (gastrSockets[sock].bIsUsed == 1)) + { + if( + (gastrSockets[sock].u8SSLFlags & SSL_FLAGS_ACTIVE) + && (gastrSockets[sock].u8SSLFlags & SSL_FLAGS_DELAY) + && (gastrSockets[sock].u16DataOffset != 0) + ) + { + tstrConnectCmd strConnect = {0}; + + gastrSockets[sock].u8SSLFlags &= ~SSL_FLAGS_DELAY; + strConnect.u8SslFlags = gastrSockets[sock].u8SSLFlags; + strConnect.sock = sock; + strConnect.u16SessionID = gastrSockets[sock].u16SessionID; + + s8Ret = SOCKET_REQUEST(SOCKET_CMD_SECURE, (uint8*)&strConnect, sizeof(tstrConnectCmd), NULL, 0, 0); + if(s8Ret != SOCK_ERR_NO_ERROR) + { + s8Ret = SOCK_ERR_INVALID; + } + } + } + return s8Ret; +} +/********************************************************************* +Function + send Description Return Author - Ahmed Ezzat + Ahmed Ezzat Version - 1.0 + 1.0 Date - 5 June 2012 + 5 June 2012 *********************************************************************/ sint16 WINC1500_EXPORT(send)(SOCKET sock, void *pvSendBuffer, uint16 u16SendLength, uint16 flags) { - sint16 s16Ret = SOCK_ERR_INVALID_ARG; - - if((sock >= 0) && (pvSendBuffer != NULL) && (u16SendLength <= SOCKET_BUFFER_MAX_LENGTH) && (gastrSockets[sock].bIsUsed == 1)) - { - uint16 u16DataOffset; - tstrSendCmd strSend; - uint8 u8Cmd; + sint16 s16Ret = SOCK_ERR_INVALID_ARG; - u8Cmd = SOCKET_CMD_SEND; - u16DataOffset = TCP_TX_PACKET_OFFSET; + if((sock >= 0) && (sock < MAX_SOCKET) && (pvSendBuffer != NULL) && (u16SendLength <= SOCKET_BUFFER_MAX_LENGTH) && (gastrSockets[sock].bIsUsed == 1)) + { + uint16 u16DataOffset; + tstrSendCmd strSend; + uint8 u8Cmd; - strSend.sock = sock; - strSend.u16DataSize = NM_BSP_B_L_16(u16SendLength); - strSend.u16SessionID = gastrSockets[sock].u16SessionID; + u8Cmd = SOCKET_CMD_SEND; + u16DataOffset = TCP_TX_PACKET_OFFSET; - if(sock >= TCP_SOCK_MAX) - { - u16DataOffset = UDP_TX_PACKET_OFFSET; - } - if(gastrSockets[sock].u8SSLFlags & SSL_FLAGS_ACTIVE) - { - u8Cmd = SOCKET_CMD_SSL_SEND; - u16DataOffset = gastrSockets[sock].u16DataOffset; - } + strSend.sock = sock; + strSend.u16DataSize = NM_BSP_B_L_16(u16SendLength); + strSend.u16SessionID = gastrSockets[sock].u16SessionID; - s16Ret = SOCKET_REQUEST(u8Cmd|M2M_REQ_DATA_PKT, (uint8*)&strSend, sizeof(tstrSendCmd), pvSendBuffer, u16SendLength, u16DataOffset); - if(s16Ret != SOCK_ERR_NO_ERROR) - { - s16Ret = SOCK_ERR_BUFFER_FULL; - } - } - return s16Ret; + if(sock >= TCP_SOCK_MAX) + { + u16DataOffset = UDP_TX_PACKET_OFFSET; + } + if( + (gastrSockets[sock].u8SSLFlags & SSL_FLAGS_ACTIVE) + && (!(gastrSockets[sock].u8SSLFlags & SSL_FLAGS_DELAY)) + ) + { + u8Cmd = SOCKET_CMD_SSL_SEND; + u16DataOffset = gastrSockets[sock].u16DataOffset; + } + + s16Ret = SOCKET_REQUEST(u8Cmd|M2M_REQ_DATA_PKT, (uint8 *)&strSend, sizeof(tstrSendCmd), pvSendBuffer, u16SendLength, u16DataOffset); + if(s16Ret != SOCK_ERR_NO_ERROR) + { + s16Ret = SOCK_ERR_BUFFER_FULL; + } + } + return s16Ret; } /********************************************************************* Function - sendto + sendto Description Return Author - Ahmed Ezzat + Ahmed Ezzat Version - 1.0 + 1.0 Date - 4 June 2012 + 4 June 2012 *********************************************************************/ sint16 WINC1500_EXPORT(sendto)(SOCKET sock, void *pvSendBuffer, uint16 u16SendLength, uint16 flags, struct sockaddr *pstrDestAddr, uint8 u8AddrLen) { - sint16 s16Ret = SOCK_ERR_INVALID_ARG; - - if((sock >= 0) && (pvSendBuffer != NULL) && (u16SendLength <= SOCKET_BUFFER_MAX_LENGTH) && (gastrSockets[sock].bIsUsed == 1)) - { - if(gastrSockets[sock].bIsUsed) - { - tstrSendCmd strSendTo; + sint16 s16Ret = SOCK_ERR_INVALID_ARG; - m2m_memset((uint8*)&strSendTo, 0, sizeof(tstrSendCmd)); + if((sock >= 0) && (sock < MAX_SOCKET) && (pvSendBuffer != NULL) && (u16SendLength <= SOCKET_BUFFER_MAX_LENGTH) && (gastrSockets[sock].bIsUsed == 1)) + { + if(gastrSockets[sock].bIsUsed) + { + tstrSendCmd strSendTo; - strSendTo.sock = sock; - strSendTo.u16DataSize = NM_BSP_B_L_16(u16SendLength); - strSendTo.u16SessionID = gastrSockets[sock].u16SessionID; - - if(pstrDestAddr != NULL) - { - struct sockaddr_in *pstrAddr; - pstrAddr = (void*)pstrDestAddr; + m2m_memset((uint8 *)&strSendTo, 0, sizeof(tstrSendCmd)); - strSendTo.strAddr.u16Family = pstrAddr->sin_family; - strSendTo.strAddr.u16Port = pstrAddr->sin_port; - strSendTo.strAddr.u32IPAddr = pstrAddr->sin_addr.s_addr; - } - s16Ret = SOCKET_REQUEST(SOCKET_CMD_SENDTO|M2M_REQ_DATA_PKT, (uint8*)&strSendTo, sizeof(tstrSendCmd), - pvSendBuffer, u16SendLength, UDP_TX_PACKET_OFFSET); + strSendTo.sock = sock; + strSendTo.u16DataSize = NM_BSP_B_L_16(u16SendLength); + strSendTo.u16SessionID = gastrSockets[sock].u16SessionID; - if(s16Ret != SOCK_ERR_NO_ERROR) - { - s16Ret = SOCK_ERR_BUFFER_FULL; - } - } - } - return s16Ret; + if(pstrDestAddr != NULL) + { + struct sockaddr_in *pstrAddr; + pstrAddr = (void *)pstrDestAddr; + + strSendTo.strAddr.u16Family = pstrAddr->sin_family; + strSendTo.strAddr.u16Port = pstrAddr->sin_port; + strSendTo.strAddr.u32IPAddr = pstrAddr->sin_addr.s_addr; + } + s16Ret = SOCKET_REQUEST(SOCKET_CMD_SENDTO|M2M_REQ_DATA_PKT, (uint8 *)&strSendTo, sizeof(tstrSendCmd), + pvSendBuffer, u16SendLength, UDP_TX_PACKET_OFFSET); + + if(s16Ret != SOCK_ERR_NO_ERROR) + { + s16Ret = SOCK_ERR_BUFFER_FULL; + } + } + } + return s16Ret; } /********************************************************************* Function - recv + recv Description @@ -835,98 +907,102 @@ Return Author - Ahmed Ezzat + Ahmed Ezzat Version - 1.0 - 2.0 9 April 2013 --> Add timeout for recv operation. + 1.0 + 2.0 9 April 2013 --> Add timeout for recv operation. Date - 5 June 2012 + 5 June 2012 *********************************************************************/ sint16 WINC1500_EXPORT(recv)(SOCKET sock, void *pvRecvBuf, uint16 u16BufLen, uint32 u32Timeoutmsec) { - sint16 s16Ret = SOCK_ERR_INVALID_ARG; - - if((sock >= 0) && (pvRecvBuf != NULL) && (u16BufLen != 0) && (gastrSockets[sock].bIsUsed == 1)) - { - s16Ret = SOCK_ERR_NO_ERROR; - gastrSockets[sock].pu8UserBuffer = (uint8*)pvRecvBuf; - gastrSockets[sock].u16UserBufferSize = u16BufLen; + sint16 s16Ret = SOCK_ERR_INVALID_ARG; - if(!gastrSockets[sock].bIsRecvPending) - { - tstrRecvCmd strRecv; - uint8 u8Cmd = SOCKET_CMD_RECV; + if((sock >= 0) && (sock < MAX_SOCKET) && (pvRecvBuf != NULL) && (u16BufLen != 0) && (gastrSockets[sock].bIsUsed == 1)) + { + s16Ret = SOCK_ERR_NO_ERROR; + gastrSockets[sock].pu8UserBuffer = (uint8 *)pvRecvBuf; + gastrSockets[sock].u16UserBufferSize = u16BufLen; - gastrSockets[sock].bIsRecvPending = 1; - if(gastrSockets[sock].u8SSLFlags & SSL_FLAGS_ACTIVE) - { - u8Cmd = SOCKET_CMD_SSL_RECV; - } + if(!gastrSockets[sock].bIsRecvPending) + { + tstrRecvCmd strRecv; + uint8 u8Cmd = SOCKET_CMD_RECV; - /* Check the timeout value. */ - if(u32Timeoutmsec == 0) - strRecv.u32Timeoutmsec = 0xFFFFFFFF; - else - strRecv.u32Timeoutmsec = NM_BSP_B_L_32(u32Timeoutmsec); - strRecv.sock = sock; - strRecv.u16SessionID = gastrSockets[sock].u16SessionID; + gastrSockets[sock].bIsRecvPending = 1; + if( + (gastrSockets[sock].u8SSLFlags & SSL_FLAGS_ACTIVE) + && (!(gastrSockets[sock].u8SSLFlags & SSL_FLAGS_DELAY)) + ) + { + u8Cmd = SOCKET_CMD_SSL_RECV; + } + + /* Check the timeout value. */ + if(u32Timeoutmsec == 0) + strRecv.u32Timeoutmsec = 0xFFFFFFFF; + else + strRecv.u32Timeoutmsec = NM_BSP_B_L_32(u32Timeoutmsec); + strRecv.sock = sock; + strRecv.u16SessionID = gastrSockets[sock].u16SessionID; + strRecv.u16BufLen = u16BufLen; s16Ret = SOCKET_REQUEST(u8Cmd, (uint8*)&strRecv, sizeof(tstrRecvCmd), NULL , 0, 0); - if(s16Ret != SOCK_ERR_NO_ERROR) - { - s16Ret = SOCK_ERR_BUFFER_FULL; - } - } - } - return s16Ret; + if(s16Ret != SOCK_ERR_NO_ERROR) + { + s16Ret = SOCK_ERR_BUFFER_FULL; + } + } + } + return s16Ret; } /********************************************************************* Function - close + close Description Return - None. + None. Author - Ahmed Ezzat + Ahmed Ezzat Version - 1.0 + 1.0 Date - 4 June 2012 + 4 June 2012 *********************************************************************/ sint8 WINC1500_EXPORT(close)(SOCKET sock) { - sint8 s8Ret = SOCK_ERR_INVALID_ARG; + sint8 s8Ret = SOCK_ERR_INVALID_ARG; M2M_INFO("Sock to delete <%d>\n", sock); - if(sock >= 0 && (gastrSockets[sock].bIsUsed == 1)) - { - uint8 u8Cmd = SOCKET_CMD_CLOSE; - tstrCloseCmd strclose; - strclose.sock = sock; - strclose.u16SessionID = gastrSockets[sock].u16SessionID; - - if(gastrSockets[sock].u8SSLFlags & SSL_FLAGS_ACTIVE) - { - u8Cmd = SOCKET_CMD_SSL_CLOSE; - } - s8Ret = SOCKET_REQUEST(u8Cmd, (uint8*)&strclose, sizeof(tstrCloseCmd), NULL,0, 0); - if(s8Ret != SOCK_ERR_NO_ERROR) - { - s8Ret = SOCK_ERR_INVALID; - } - m2m_memset((uint8*)&gastrSockets[sock], 0, sizeof(tstrSocket)); - } - return s8Ret; + if((sock >= 0) && (sock < MAX_SOCKET) && (gastrSockets[sock].bIsUsed == 1)) + { + uint8 u8Cmd = SOCKET_CMD_CLOSE; + tstrCloseCmd strclose; + strclose.sock = sock; + strclose.u16SessionID = gastrSockets[sock].u16SessionID; + + if(gastrSockets[sock].u8SSLFlags & SSL_FLAGS_ACTIVE) + { + u8Cmd = SOCKET_CMD_SSL_CLOSE; + } + s8Ret = SOCKET_REQUEST(u8Cmd, (uint8 *)&strclose, sizeof(tstrCloseCmd), NULL, 0, 0); + if(s8Ret != SOCK_ERR_NO_ERROR) + { + s8Ret = SOCK_ERR_INVALID; + } + m2m_memset((uint8 *)&gastrSockets[sock], 0, sizeof(tstrSocket)); + } + return s8Ret; } /********************************************************************* Function - recvfrom + recvfrom Description @@ -934,391 +1010,487 @@ Return Author - Ahmed Ezzat + Ahmed Ezzat Version - 1.0 - 2.0 9 April 2013 --> Add timeout for recv operation. + 1.0 + 2.0 9 April 2013 --> Add timeout for recv operation. Date - 5 June 2012 + 5 June 2012 *********************************************************************/ sint16 WINC1500_EXPORT(recvfrom)(SOCKET sock, void *pvRecvBuf, uint16 u16BufLen, uint32 u32Timeoutmsec) { - sint16 s16Ret = SOCK_ERR_NO_ERROR; - if((sock >= 0) && (pvRecvBuf != NULL) && (u16BufLen != 0) && (gastrSockets[sock].bIsUsed == 1)) - { - if(gastrSockets[sock].bIsUsed) - { - s16Ret = SOCK_ERR_NO_ERROR; - gastrSockets[sock].pu8UserBuffer = (uint8*)pvRecvBuf; - gastrSockets[sock].u16UserBufferSize = u16BufLen; + sint16 s16Ret = SOCK_ERR_NO_ERROR; + if((sock >= 0) && (sock < MAX_SOCKET) && (pvRecvBuf != NULL) && (u16BufLen != 0) && (gastrSockets[sock].bIsUsed == 1)) + { + if(gastrSockets[sock].bIsUsed) + { + s16Ret = SOCK_ERR_NO_ERROR; + gastrSockets[sock].pu8UserBuffer = (uint8 *)pvRecvBuf; + gastrSockets[sock].u16UserBufferSize = u16BufLen; - if(!gastrSockets[sock].bIsRecvPending) - { - tstrRecvCmd strRecv; + if(!gastrSockets[sock].bIsRecvPending) + { + tstrRecvCmd strRecv; + gastrSockets[sock].bIsRecvPending = 1; - gastrSockets[sock].bIsRecvPending = 1; - - /* Check the timeout value. */ - if(u32Timeoutmsec == 0) - strRecv.u32Timeoutmsec = 0xFFFFFFFF; - else - strRecv.u32Timeoutmsec = NM_BSP_B_L_32(u32Timeoutmsec); - strRecv.sock = sock; - strRecv.u16SessionID = gastrSockets[sock].u16SessionID; + /* Check the timeout value. */ + if(u32Timeoutmsec == 0) + strRecv.u32Timeoutmsec = 0xFFFFFFFF; + else + strRecv.u32Timeoutmsec = NM_BSP_B_L_32(u32Timeoutmsec); + strRecv.sock = sock; + strRecv.u16SessionID = gastrSockets[sock].u16SessionID; + strRecv.u16BufLen = u16BufLen; s16Ret = SOCKET_REQUEST(SOCKET_CMD_RECVFROM, (uint8*)&strRecv, sizeof(tstrRecvCmd), NULL , 0, 0); if(s16Ret != SOCK_ERR_NO_ERROR) - { - s16Ret = SOCK_ERR_BUFFER_FULL; - } - } - } - } - else - { - s16Ret = SOCK_ERR_INVALID_ARG; - } - return s16Ret; + { + s16Ret = SOCK_ERR_BUFFER_FULL; + } + } + } + } + else + { + s16Ret = SOCK_ERR_INVALID_ARG; + } + return s16Ret; } /********************************************************************* Function - nmi_inet_addr + nmi_inet_addr Description Return - Unsigned 32-bit integer representing the IP address in Network - byte order. + Unsigned 32-bit integer representing the IP address in Network + byte order. Author - Ahmed Ezzat + Ahmed Ezzat Version - 1.0 + 1.0 Date - 4 June 2012 + 4 June 2012 *********************************************************************/ uint32 nmi_inet_addr(char *pcIpAddr) { - uint8 tmp; - uint32 u32IP = 0; - uint8 au8IP[4]; - uint8 c; - uint8 i, j; + uint8 tmp = 0; + uint32 u32IP = 0; + uint8 au8IP[4]; + uint8 c; + uint8 i, j; - tmp = 0; - - for(i = 0; i < 4; ++i) - { - j = 0; - do - { - c = *pcIpAddr; - ++j; - if(j > 4) - { - return 0; - } - if(c == '.' || c == 0) - { - au8IP[i] = tmp; - tmp = 0; - } - else if(c >= '0' && c <= '9') - { - tmp = (tmp * 10) + (c - '0'); - } - else - { - return 0; - } - ++pcIpAddr; - } while(c != '.' && c != 0); - } - m2m_memcpy((uint8*)&u32IP, au8IP, 4); - return u32IP; + for(i = 0; i < 4; ++i) + { + j = 0; + do + { + c = *pcIpAddr; + ++j; + if(j > 4) + { + return 0; + } + if(c == '.' || c == 0) + { + au8IP[i] = tmp; + tmp = 0; + } + else if(c >= '0' && c <= '9') + { + tmp = (tmp * 10) + (c - '0'); + } + else + { + return 0; + } + ++pcIpAddr; + } while(c != '.' && c != 0); + } + m2m_memcpy((uint8 *)&u32IP, au8IP, 4); + return u32IP; } + /********************************************************************* Function - gethostbyname + gethostbyname Description Return - None. + None. Author - Ahmed Ezzat + Ahmed Ezzat Version - 1.0 + 1.0 Date - 4 June 2012 + 4 June 2012 *********************************************************************/ -sint8 WINC1500_EXPORT(gethostbyname)(uint8 * pcHostName) +sint8 WINC1500_EXPORT(gethostbyname)(uint8 *pcHostName) { - sint8 s8Err = SOCK_ERR_INVALID_ARG; - uint8 u8HostNameSize = (uint8)m2m_strlen(pcHostName); - if(u8HostNameSize <= HOSTNAME_MAX_SIZE) - { - s8Err = SOCKET_REQUEST(SOCKET_CMD_DNS_RESOLVE, (uint8*)pcHostName, u8HostNameSize + 1, NULL,0, 0); - } - return s8Err; + sint8 s8Err = SOCK_ERR_INVALID_ARG; + uint8 u8HostNameSize = (uint8)m2m_strlen(pcHostName); + if(u8HostNameSize <= HOSTNAME_MAX_SIZE) + { + s8Err = SOCKET_REQUEST(SOCKET_CMD_DNS_RESOLVE, (uint8 *)pcHostName, u8HostNameSize + 1, NULL, 0, 0); + } + return s8Err; } + /********************************************************************* Function - setsockopt + setsockopt Description Return - None. + None. Author - Abdelrahman Diab + Abdelrahman Diab Version - 1.0 + 1.0 Date - 9 September 2014 + 9 September 2014 *********************************************************************/ static sint8 sslSetSockOpt(SOCKET sock, uint8 u8Opt, const void *pvOptVal, uint16 u16OptLen) { - sint8 s8Ret = SOCK_ERR_INVALID_ARG; - if(sock < TCP_SOCK_MAX) - { - if(gastrSockets[sock].u8SSLFlags & SSL_FLAGS_ACTIVE) - { - if(u8Opt == SO_SSL_BYPASS_X509_VERIF) - { - int optVal = *((int*)pvOptVal); - if(optVal) - { - gastrSockets[sock].u8SSLFlags |= SSL_FLAGS_BYPASS_X509; - } - else - { - gastrSockets[sock].u8SSLFlags &= ~SSL_FLAGS_BYPASS_X509; - } - s8Ret = SOCK_ERR_NO_ERROR; - } - else if(u8Opt == SO_SSL_ENABLE_SESSION_CACHING) - { - int optVal = *((int*)pvOptVal); - if(optVal) - { - gastrSockets[sock].u8SSLFlags |= SSL_FLAGS_CACHE_SESSION; - } - else - { - gastrSockets[sock].u8SSLFlags &= ~SSL_FLAGS_CACHE_SESSION; - } - s8Ret = SOCK_ERR_NO_ERROR; - } - else if(u8Opt == SO_SSL_ENABLE_SNI_VALIDATION) - { - int optVal = *((int*)pvOptVal); - if(optVal) - { - gastrSockets[sock].u8SSLFlags |= SSL_FLAGS_CHECK_SNI; - } - else - { - gastrSockets[sock].u8SSLFlags &= ~SSL_FLAGS_CHECK_SNI; - } - s8Ret = SOCK_ERR_NO_ERROR; - } - else if(u8Opt == SO_SSL_SNI) - { - if(u16OptLen < HOSTNAME_MAX_SIZE) - { - uint8 *pu8SNI = (uint8*)pvOptVal; - tstrSSLSetSockOptCmd strCmd; + sint8 s8Ret = SOCK_ERR_INVALID_ARG; + if(sock < TCP_SOCK_MAX) + { + if(gastrSockets[sock].u8SSLFlags & SSL_FLAGS_ACTIVE) + { + uint8 sslFlag = 0; - strCmd.sock = sock; - strCmd.u16SessionID = gastrSockets[sock].u16SessionID; - strCmd.u8Option = u8Opt; - strCmd.u32OptLen = u16OptLen; - m2m_memcpy(strCmd.au8OptVal, pu8SNI, HOSTNAME_MAX_SIZE); - - if(SOCKET_REQUEST(SOCKET_CMD_SSL_SET_SOCK_OPT, (uint8*)&strCmd, sizeof(tstrSSLSetSockOptCmd), - 0, 0, 0) == M2M_ERR_MEM_ALLOC) - { - s8Ret = SOCKET_REQUEST(SOCKET_CMD_SSL_SET_SOCK_OPT | M2M_REQ_DATA_PKT, - (uint8*)&strCmd, sizeof(tstrSSLSetSockOptCmd), 0, 0, 0); - } - s8Ret = SOCK_ERR_NO_ERROR; - } - else - { - M2M_ERR("SNI Exceeds Max Length\n"); - } - } - else - { - M2M_ERR("Unknown SSL Socket Option %d\n",u8Opt); - } - } - else - { - M2M_ERR("Not SSL Socket\n"); - } - } - return s8Ret; + s8Ret = SOCK_ERR_NO_ERROR; + if(u16OptLen == sizeof(int)) + { + if(u8Opt == SO_SSL_BYPASS_X509_VERIF) + { + sslFlag = SSL_FLAGS_BYPASS_X509; + } + else if(u8Opt == SO_SSL_ENABLE_SESSION_CACHING) + { + sslFlag = SSL_FLAGS_CACHE_SESSION; + } + else if(u8Opt == SO_SSL_ENABLE_SNI_VALIDATION) + { + sslFlag = SSL_FLAGS_CHECK_SNI; + } + } + if(sslFlag) + { + int optVal = *((int*)pvOptVal); + if(optVal) + { + gastrSockets[sock].u8SSLFlags |= sslFlag; + } + else + { + gastrSockets[sock].u8SSLFlags &= ~sslFlag; + } + } + else if( + ((u8Opt == SO_SSL_SNI) && (u16OptLen < HOSTNAME_MAX_SIZE)) + || ((u8Opt == SO_SSL_ALPN) && (u16OptLen <= ALPN_LIST_MAX_SIZE)) + ) + { + tstrSSLSetSockOptCmd strCmd = {0}; + + strCmd.sock = sock; + strCmd.u16SessionID = gastrSockets[sock].u16SessionID; + strCmd.u8Option = u8Opt; + strCmd.u32OptLen = u16OptLen; + m2m_memcpy(strCmd.au8OptVal, (uint8*)pvOptVal, u16OptLen); + + s8Ret = SOCKET_REQUEST(SOCKET_CMD_SSL_SET_SOCK_OPT, (uint8*)&strCmd, sizeof(tstrSSLSetSockOptCmd), 0, 0, 0); + if(s8Ret == M2M_ERR_MEM_ALLOC) + { + s8Ret = SOCKET_REQUEST(SOCKET_CMD_SSL_SET_SOCK_OPT | M2M_REQ_DATA_PKT, + (uint8*)&strCmd, sizeof(tstrSSLSetSockOptCmd), 0, 0, 0); + } + } + else + { + M2M_ERR("Unknown SSL Socket Option %d\n", u8Opt); + s8Ret = SOCK_ERR_INVALID_ARG; + } + } + else + { + M2M_ERR("Not SSL Socket\n"); + } + } + return s8Ret; } + /********************************************************************* Function - setsockopt + setsockopt Description Return - None. + None. Author - Abdelrahman Diab + Abdelrahman Diab Version - 1.0 + 1.0 Date - 9 September 2014 + 9 September 2014 *********************************************************************/ sint8 WINC1500_EXPORT(setsockopt)(SOCKET sock, uint8 u8Level, uint8 option_name, - const void *option_value, uint16 u16OptionLen) + const void *option_value, uint16 u16OptionLen) { - sint8 s8Ret = SOCK_ERR_INVALID_ARG; - if((sock >= 0) && (option_value != NULL) && (gastrSockets[sock].bIsUsed == 1)) - { - if(u8Level == SOL_SSL_SOCKET) - { - s8Ret = sslSetSockOpt(sock, option_name, option_value, u16OptionLen); - } - else - { - uint8 u8Cmd = SOCKET_CMD_SET_SOCKET_OPTION; - tstrSetSocketOptCmd strSetSockOpt; - strSetSockOpt.u8Option=option_name; - strSetSockOpt.sock = sock; - strSetSockOpt.u32OptionValue = *(uint32*)option_value; - strSetSockOpt.u16SessionID = gastrSockets[sock].u16SessionID; + sint8 s8Ret = SOCK_ERR_INVALID_ARG; + if((sock >= 0) && (sock < MAX_SOCKET) && (option_value != NULL) && (gastrSockets[sock].bIsUsed == 1)) + { + if(u8Level == SOL_SSL_SOCKET) + { + s8Ret = sslSetSockOpt(sock, option_name, option_value, u16OptionLen); + } + else if(u8Level == SOL_SOCKET) + { + if(u16OptionLen == sizeof(uint32)) + { + uint8 u8Cmd = SOCKET_CMD_SET_SOCKET_OPTION; + tstrSetSocketOptCmd strSetSockOpt; + strSetSockOpt.u8Option=option_name; + strSetSockOpt.sock = sock; + strSetSockOpt.u32OptionValue = *(uint32*)option_value; + strSetSockOpt.u16SessionID = gastrSockets[sock].u16SessionID; - s8Ret = SOCKET_REQUEST(u8Cmd, (uint8*)&strSetSockOpt, sizeof(tstrSetSocketOptCmd), NULL,0, 0); - if(s8Ret != SOCK_ERR_NO_ERROR) - { - s8Ret = SOCK_ERR_INVALID; - } - } - } - return s8Ret; + s8Ret = SOCKET_REQUEST(u8Cmd, (uint8*)&strSetSockOpt, sizeof(tstrSetSocketOptCmd), NULL, 0, 0); + if(s8Ret != SOCK_ERR_NO_ERROR) + { + s8Ret = SOCK_ERR_INVALID; + } + } + } + } + return s8Ret; } + /********************************************************************* Function - getsockopt + getsockopt Description Return - None. + None. Author - Ahmed Ezzat + Ahmed Ezzat Version - 1.0 + 1.0 Date - 24 August 2014 + 24 August 2014 *********************************************************************/ -sint8 WINC1500_EXPORT(getsockopt)(SOCKET sock, uint8 u8Level, uint8 u8OptName, const void *pvOptValue, uint8* pu8OptLen) +sint8 WINC1500_EXPORT(getsockopt)(SOCKET sock, uint8 u8Level, uint8 u8OptName, const void *pvOptValue, uint8 *pu8OptLen) { - /* TBD */ - return M2M_SUCCESS; + // This is not implemented so return a value that will cause failure should this be used. + return SOCK_ERR_INVALID_ARG; } + /********************************************************************* Function - m2m_ping_req + m2m_ping_req Description - Send Ping request. + Send Ping request. Return - + Author - Ahmed Ezzat + Ahmed Ezzat Version - 1.0 + 1.0 Date - 4 June 2015 + 4 June 2015 *********************************************************************/ sint8 m2m_ping_req(uint32 u32DstIP, uint8 u8TTL, tpfPingCb fpPingCb) { - sint8 s8Ret = M2M_ERR_INVALID_ARG; + sint8 s8Ret = M2M_ERR_INVALID_ARG; - if((u32DstIP != 0) && (fpPingCb != NULL)) - { - tstrPingCmd strPingCmd; + if((u32DstIP != 0) && (fpPingCb != NULL)) + { + tstrPingCmd strPingCmd; + strPingCmd.u16PingCount = 1; + strPingCmd.u32DestIPAddr = u32DstIP; + strPingCmd.u32CmdPrivate = ++gu32PingId; + strPingCmd.u8TTL = u8TTL; - strPingCmd.u16PingCount = 1; - strPingCmd.u32DestIPAddr = u32DstIP; - strPingCmd.u32CmdPrivate = (uint32)fpPingCb; - strPingCmd.u8TTL = u8TTL; - - s8Ret = SOCKET_REQUEST(SOCKET_CMD_PING, (uint8*)&strPingCmd, sizeof(tstrPingCmd), NULL, 0, 0); - } - return s8Ret; + gfpPingCb = fpPingCb; + s8Ret = SOCKET_REQUEST(SOCKET_CMD_PING, (uint8 *)&strPingCmd, sizeof(tstrPingCmd), NULL, 0, 0); + } + return s8Ret; } + /********************************************************************* Function - sslEnableCertExpirationCheck + set_alpn_protocol_list Description - Enable/Disable TLS Certificate Expiration Check. + This function sets the protocol list used for application-layer protocol negotiation (ALPN). + If used, it must be called after creating a SSL socket (using @ref socket) and before + connecting/binding (using @ref connect or @ref bind). Return - -Author - Ahmed Ezzat - -Version - 1.0 - -Date - + The function returns @ref M2M_SUCCESS for successful operations and a negative value otherwise. *********************************************************************/ -sint8 WINC1500_EXPORT(sslEnableCertExpirationCheck)(tenuSslCertExpSettings enuValidationSetting) +sint8 set_alpn_list(SOCKET sock, const char *pcProtocolList) { - tstrSslCertExpSettings strSettings; - strSettings.u32CertExpValidationOpt = (uint32)enuValidationSetting; - return SOCKET_REQUEST(SOCKET_CMD_SSL_EXP_CHECK, (uint8*)&strSettings, sizeof(tstrSslCertExpSettings), NULL, 0, 0); + sint8 s8Ret = SOCK_ERR_INVALID_ARG; + + if((sock >= 0) && (sock < TCP_SOCK_MAX) && (pcProtocolList != NULL)) + { + uint8 u8Length = m2m_strlen((uint8 *)pcProtocolList); + if((u8Length > 0) && (u8Length < ALPN_LIST_MAX_APP_LENGTH)) + { + /* + ALPN socket option requires Alpn list in this format: + 0 1 2 3 ... (bytes) + +-------+-------+-------+ ... +-------+ ... +-------+ ... + | Length L (BE) | len1 | name1... | len2 | name2... | len3 | name3... + +-------+-------+-------+ ... +-------+ ... +-------+ ... + Length fields do not include themselves. + */ + uint8 au8AlpnList[ALPN_LIST_MAX_SIZE] = {0}; + uint8 *pu8Ptr = &au8AlpnList[3] + u8Length; + uint8 u8Len = 0; + + m2m_memcpy(&au8AlpnList[3], (uint8 *)pcProtocolList, u8Length); + u8Length++; + au8AlpnList[1] = u8Length; + au8AlpnList[2] = ' '; + + /* Convert space characters into length fields. */ + while(u8Length--) + { + if(*--pu8Ptr == ' ') + { + if(u8Len == 0) goto ERR; + *pu8Ptr = u8Len; + u8Len = 0; + } + else u8Len++; + } + s8Ret = WINC1500_EXPORT(setsockopt)(sock, SOL_SSL_SOCKET, SO_SSL_ALPN, au8AlpnList, sizeof(au8AlpnList)); + } + } +ERR: + return s8Ret; +} +/********************************************************************* +Function + get_alpn_protocol_index + +Description + This function gets the protocol list used for application-layer protocol negotiation (ALPN). + If used, it must be called after creating a SSL socket (using @ref socket) and before + connecting/binding (using @ref connect or @ref bind). + +Return + The function returns the index of the selected application-layer protocol. + Special values: + 0: no negotiation has occurred. + <0: error. +*********************************************************************/ +sint8 get_alpn_index(SOCKET sock) +{ + if(sock >= TCP_SOCK_MAX || sock < 0) + return SOCK_ERR_INVALID_ARG; + if(!(gastrSockets[sock].u8SSLFlags & SSL_FLAGS_ACTIVE) || !gastrSockets[sock].bIsUsed) + return SOCK_ERR_INVALID_ARG; + return gastrSockets[sock].u8AlpnStatus; } /********************************************************************* Function - IsSocketReady + sslEnableCertExpirationCheck -Description +Description + Enable/Disable TLS Certificate Expiration Check. Return - None. + +Author + Ahmed Ezzat + +Version + 1.0 + +Date + +*********************************************************************/ +sint8 sslEnableCertExpirationCheck(tenuSslCertExpSettings enuValidationSetting) +{ + tstrSslCertExpSettings strSettings; + strSettings.u32CertExpValidationOpt = (uint32)enuValidationSetting; + return SOCKET_REQUEST(SOCKET_CMD_SSL_EXP_CHECK, (uint8 *)&strSettings, sizeof(tstrSslCertExpSettings), NULL, 0, 0); +} + +/********************************************************************* +Function + IsSocketReady + +Description + +Return + None. Author Version - 1.0 + 1.0 Date - 24 Apr 2018 + 24 Apr 2018 *********************************************************************/ uint8 IsSocketReady(void) { return gbSocketInit; } +/********************************************************************* +Function + get_error_detail + +Description + This function gets detail about a socket failure. + The application can call this when notified of a socket failure via + @ref SOCKET_MSG_CONNECT or @ref SOCKET_MSG_RECV. + If used, it must be called before @ref close. + +Return + The function returns @ref SOCK_ERR_NO_ERROR if the request is successful + and a negative value otherwise. +*********************************************************************/ +sint8 get_error_detail(SOCKET sock, tstrSockErr *pstrErr) +{ + if((sock >= TCP_SOCK_MAX) || (sock < 0) || (pstrErr == NULL)) + return SOCK_ERR_INVALID_ARG; + if(!gastrSockets[sock].bIsUsed) + return SOCK_ERR_INVALID_ARG; + pstrErr->enuErrSource = gastrSockets[sock].u8ErrSource; + pstrErr->u8ErrCode = gastrSockets[sock].u8ErrCode; + return SOCK_ERR_NO_ERROR; +} diff --git a/src/drivers/winc1500/src/spi_flash.c b/src/drivers/winc1500/src/spi_flash.c index 9b317eae1..4b4108744 100644 --- a/src/drivers/winc1500/src/spi_flash.c +++ b/src/drivers/winc1500/src/spi_flash.c @@ -4,7 +4,7 @@ * * \brief WINC1500 SPI Flash. * - * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries. + * Copyright (c) 2016-2021 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * @@ -539,17 +539,16 @@ sint8 spi_flash_enable(uint8 enable) } /* GPIO15/16/17/18 */ u32Val &= ~((0x7777ul) << 12); - u32Val |= ((0x1111ul) << 12); - nm_write_reg(0x1410, u32Val); if(enable) { + u32Val |= ((0x1111ul) << 12); + nm_write_reg(0x1410, u32Val); spi_flash_leave_low_power_mode(); } else { spi_flash_enter_low_power_mode(); + /* Disable pinmux to SPI flash to minimize leakage. */ + u32Val |= ((0x0010ul) << 12); + nm_write_reg(0x1410, u32Val); } - /* Disable pinmux to SPI flash to minimize leakage. */ - u32Val &= ~((0x7777ul) << 12); - u32Val |= ((0x0010ul) << 12); - nm_write_reg(0x1410, u32Val); } ERR1: return s8Ret; @@ -724,8 +723,8 @@ uint32 spi_flash_get_size(void) if(!gu32InternalFlashSize) { - u32FlashId = spi_flash_rdid();//spi_flash_probe(); - if(u32FlashId != 0xffffffff) + u32FlashId = spi_flash_rdid(); + if((u32FlashId != 0xffffffff) && (u32FlashId !=0)) { /*flash size is the third byte from the FLASH RDID*/ u32FlashPwr = ((u32FlashId>>16)&0xff) - 0x11; /*2MBIT is the min*/