/* * FreeRTOS BLE V2.2.0 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * http://aws.amazon.com/freertos * http://www.FreeRTOS.org */ /** * @file aws_ble_wifi_provisioning.c * @brief BLE Gatt service for WiFi provisioning */ #include <string.h> #include "FreeRTOS.h" #include "queue.h" #include "semphr.h" #include "iot_config.h" #include "iot_ble_config.h" #include "iot_ble_data_transfer.h" #include "iot_ble_wifi_provisioning.h" /* Configure logs for the functions in this file. */ #ifdef IOT_LOG_LEVEL_GLOBAL #define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_GLOBAL #else #define LIBRARY_LOG_LEVEL IOT_LOG_NONE #endif #define LIBRARY_LOG_NAME ( "BLE_WIFI_PROV" ) #include "iot_logging_setup.h" #define IOT_BLE_WIFI_PROVISIONING_COMMAND_TIMEOUT_MS ( 5000 ) /** * @brief Macro to check if provided network index for an operation is within range. */ #define IS_INDEX_WITHIN_RANGE( index ) ( ( index >= 0 ) && ( index < wifiProvisioning.numNetworks ) ) #define STORAGE_INDEX( priority ) ( wifiProvisioning.numNetworks - priority - 1 ) #define INVALID_INDEX ( -1 ) /*---------------------------------------------------------------------------------------------------------*/ /** * @ingroup ble_datatypes_structs * @brief Structure used for internal bookkeeping of WiFi provisioning service. */ typedef struct IotBleWifiProvService { TaskHandle_t taskHandle; /**< Handle for the task used for WiFi provisioing. */ IotBleDataTransferChannel_t * pChannel; /**< A pointer to the ble connection object. */ uint16_t numNetworks; /**< Keeps track of total number of networks stored. */ int16_t connectedIdx; /**< Keeps track of the flash index of the network that is connected. */ QueueHandle_t messageQueue; /**< Message queue use by the provisioning task to process incoming requests. */ SemaphoreHandle_t mutex; /**< Mutex used to synchronize between application task the provsioning task .*/ IotBleWifiProvSerializer_t * pSerializer; /**< Serializer interface used to pack/unpack messages over BLE. */ } IotBleWifiProvService_t; /** * @brief Structure is used to hold each element in the message queue of WiFi provisioing service. */ typedef struct { IotBleWiFiProvRequest_t type; union { IotBleWifiProvListNetworksRequest_t listNetworkRequest; IotBleWifiProvAddNetworkRequest_t addNetworkRequest; IotBleWifiProvEditNetworkRequest_t editNetworkRequest; IotBleWifiProvDeleteNetworkRequest_t deleteNetworkRequest; } m; } IotBleWifiProvMessage_t; static IotBleWifiProvService_t wifiProvisioning = { 0 }; /*---------------------------------------------------------------------------------------------------------*/ static WIFIScanResult_t scanNetworks[ IOT_BLE_WIFI_PROVISIONIG_MAX_SCAN_NETWORKS ] = { 0 }; /* * @brief Callback registered for BLE write and read events received for each characteristic. */ static void prvTransportReceiveCallback( IotBleDataTransferChannelEvent_t event, IotBleDataTransferChannel_t * pChannel, void * pContext ); /* * @brief Parses List Network request params and creates task to list networks. */ static BaseType_t prvHandleListNetworkRequest( const uint8_t * pData, size_t length ); /* * @brief Parses Save Network request params and creates task to save the new network. */ static BaseType_t prvHandleAddNetworkRequest( const uint8_t * pData, size_t length ); /* * @brief Parses Edit Network request params and creates task to edit network priority. */ static BaseType_t prvHandleEditNetworkRequest( const uint8_t * pData, size_t length ); /* * @brief Parses Delete Network request params and creates task to delete a WiFi networ. */ static BaseType_t prvHandleDeleteNetworkRequest( const uint8_t * pData, size_t length ); /* * @brief Sends a status response for the request. */ static void prvSendStatusResponse( IotBleWiFiProvRequest_t responseType, WIFIReturnCode_t status ); WIFIReturnCode_t _appendNetwork( WIFINetworkProfile_t * pProfile ); WIFIReturnCode_t _insertNetwork( uint16_t index, WIFINetworkProfile_t * pProfile ); WIFIReturnCode_t _popNetwork( uint16_t index, WIFINetworkProfile_t * pProfile ); WIFIReturnCode_t _moveNetwork( uint16_t currentIndex, uint16_t newIndex ); WIFIReturnCode_t _getSavedNetwork( uint16_t index, WIFINetworkProfile_t * pProfile ); WIFIReturnCode_t _connectNetwork( WIFINetworkProfile_t * pProfile ); WIFIReturnCode_t _connectSavedNetwork( uint16_t index ); WIFIReturnCode_t _addNewNetwork( WIFINetworkProfile_t * pProfile, bool connect ); /* * @brief Gets the number of saved networks from flash. */ static uint32_t _getNumSavedNetworks( void ); /*-----------------------------------------------------------*/ static void prvTransportReceiveCallback( IotBleDataTransferChannelEvent_t event, IotBleDataTransferChannel_t * pChannel, void * pContext ) { IotBleWiFiProvRequest_t request; const uint8_t * pBuffer; size_t length; if( event == IOT_BLE_DATA_TRANSFER_CHANNEL_DATA_RECEIVED ) { IotBleDataTransfer_PeekReceiveBuffer( pChannel, &pBuffer, &length ); if( wifiProvisioning.pSerializer->getRequestType( pBuffer, length, &request ) == true ) { configPRINTF( ( "Received message type: %d, length %d.\r\n", request, length ) ); switch( request ) { case IotBleWiFiProvRequestListNetwork: prvHandleListNetworkRequest( pBuffer, length ); break; case IotBleWiFiProvRequestAddNetwork: prvHandleAddNetworkRequest( pBuffer, length ); break; case IotBleWiFiProvRequestEditNetwork: prvHandleEditNetworkRequest( pBuffer, length ); break; case IotBleWiFiProvRequestDeleteNetwork: prvHandleDeleteNetworkRequest( pBuffer, length ); break; default: IotLogWarn( "Invalid request type ( %d ) received, discarding the message", request ); break; } } /* Do an empty read to flush the buffer. */ IotBleDataTransfer_Receive( pChannel, NULL, length ); } } /*-----------------------------------------------------------*/ static BaseType_t prvHandleListNetworkRequest( const uint8_t * pData, size_t length ) { BaseType_t status; IotBleWifiProvMessage_t message = { 0 }; message.type = IotBleWiFiProvRequestListNetwork; status = wifiProvisioning.pSerializer->deserializeListNetworkRequest( pData, length, &message.m.listNetworkRequest ); if( status == pdTRUE ) { if( ( message.m.listNetworkRequest.scanSize <= 0 ) || ( message.m.listNetworkRequest.scanSize > IOT_BLE_WIFI_PROVISIONIG_MAX_SCAN_NETWORKS ) ) { IotLogInfo( "Networks %d exceeds configured value, truncating to %d max network\n", ( uint16_t ) message.m.listNetworkRequest.scanSize, IOT_BLE_WIFI_PROVISIONIG_MAX_SCAN_NETWORKS ); message.m.listNetworkRequest.scanSize = IOT_BLE_WIFI_PROVISIONIG_MAX_SCAN_NETWORKS; } IotLogDebug( "List network request parameters: max networks = %d, timeout = %d", message.m.listNetworkRequest.scanSize, message.m.listNetworkRequest.scanTimeoutMS ); status = xQueueSend( wifiProvisioning.messageQueue, &message, 0 ); if( status != pdTRUE ) { IotLogError( "Failed to enqueue list network request, queue is full." ); } } else { IotLogError( "Failed to deserialize list network request." ); } return status; } /*-----------------------------------------------------------*/ static BaseType_t prvHandleAddNetworkRequest( const uint8_t * pData, size_t length ) { BaseType_t status; IotBleWifiProvMessage_t message = { 0 }; message.type = IotBleWiFiProvRequestAddNetwork; status = wifiProvisioning.pSerializer->deserializeAddNetworkRequest( pData, length, &message.m.addNetworkRequest ); if( status == pdTRUE ) { IotLogDebug( "Add network request parameters, SSID: %.*s, isProvisioned: %d, index: %d, connect: %d", message.m.addNetworkRequest.info.network.ucSSIDLength, ( char * ) message.m.addNetworkRequest.info.network.ucSSID, message.m.addNetworkRequest.isProvisioned, message.m.addNetworkRequest.info.index, message.m.addNetworkRequest.shouldConnect ); status = xQueueSend( wifiProvisioning.messageQueue, &message, 0 ); if( status != pdTRUE ) { IotLogError( "Failed to enqueue add network request, queue is full." ); } } else { IotLogError( "Failed to deserialize add network request." ); } return status; } /*-----------------------------------------------------------*/ static BaseType_t prvHandleEditNetworkRequest( const uint8_t * pData, size_t length ) { BaseType_t status; IotBleWifiProvMessage_t message = { 0 }; message.type = IotBleWiFiProvRequestEditNetwork; status = wifiProvisioning.pSerializer->deserializeEditNetworkRequest( pData, length, &message.m.editNetworkRequest ); if( status == pdTRUE ) { IotLogDebug( "Edit network request parameters, Current index: %d, new index: %d", message.m.editNetworkRequest.currentPriorityIndex, message.m.editNetworkRequest.newPriorityIndex ); status = xQueueSend( wifiProvisioning.messageQueue, &message, 0 ); if( status != pdTRUE ) { IotLogError( "Failed to enqueue edit network request, queue is full." ); } } else { IotLogError( "Failed to deserialize edit network request." ); } return status; } /*-----------------------------------------------------------*/ static BaseType_t prvHandleDeleteNetworkRequest( const uint8_t * pData, size_t length ) { BaseType_t status; IotBleWifiProvMessage_t message = { 0 }; message.type = IotBleWiFiProvRequestDeleteNetwork; status = wifiProvisioning.pSerializer->deserializeDeleteNetworkRequest( pData, length, &message.m.deleteNetworkRequest ); if( status == pdTRUE ) { IotLogDebug( "Delete network request parameters, index: %d", message.m.deleteNetworkRequest.priorityIndex ); status = xQueueSend( wifiProvisioning.messageQueue, &message, 0 ); if( status != pdTRUE ) { IotLogError( "Failed to enqueue delete network request, queue is full." ); } } else { IotLogError( "Failed to deserialize delete network request." ); } return status; } /*-----------------------------------------------------------*/ static void prvSendStatusResponse( IotBleWiFiProvRequest_t request, WIFIReturnCode_t status ) { uint8_t * pBuffer = NULL; size_t mesgLen = 0; bool ret; IotBleWifiProvResponse_t response = { 0 }; response.requestType = request; response.status = status; response.statusOnly = true; ret = wifiProvisioning.pSerializer->getSerializedSizeForResponse( &response, &mesgLen ); if( ret == true ) { IotLogDebug( "Respone for request type %d, serialized message length %d.", request, mesgLen ); pBuffer = pvPortMalloc( mesgLen ); if( pBuffer == NULL ) { IotLogError( "Failed to allocate message for serializing response for request type %d.", request ); ret = false; } } else { IotLogError( "Failed to get serialized size of status response." ); } if( ret == true ) { ret = wifiProvisioning.pSerializer->serializeResponse( &response, pBuffer, mesgLen ); if( ret == false ) { IotLogError( "Failed to serialize status response." ); } } if( ret == true ) { if( IotBleDataTransfer_Send( wifiProvisioning.pChannel, pBuffer, mesgLen ) != mesgLen ) { IotLogError( "Failed to send response for request type %d through ble connection.", request ); } } if( pBuffer != NULL ) { vPortFree( pBuffer ); } } /*-----------------------------------------------------------*/ static void prvSendListNetworkResponse( IotBleWifiProvResponse_t * pResponse ) { uint8_t * message = NULL; size_t messageLen = 0; bool status; status = wifiProvisioning.pSerializer->getSerializedSizeForResponse( pResponse, &messageLen ); if( status == true ) { message = pvPortMalloc( messageLen ); if( message != NULL ) { status = wifiProvisioning.pSerializer->serializeResponse( pResponse, message, messageLen ); } else { IotLogError( "Not enough memory to serialize response." ); status = false; } } else { IotLogError( "Failed to serialize list network response." ); } if( status == true ) { if( IotBleDataTransfer_Send( wifiProvisioning.pChannel, message, messageLen ) != messageLen ) { IotLogError( "Failed to send list network response over BLE." ); } } if( message != NULL ) { vPortFree( message ); } } /*-----------------------------------------------------------*/ static void prvSendSavedNetwork( WIFINetworkProfile_t * pNetwork, uint16_t priority, bool isLast ) { IotBleWifiProvResponse_t response = { 0 }; response.requestType = IotBleWiFiProvRequestListNetwork; response.networkInfo.isSavedNetwork = true; response.networkInfo.isHidden = false; response.networkInfo.isLast = isLast; response.networkInfo.isConnected = ( wifiProvisioning.connectedIdx == priority ); response.networkInfo.info.pSavedNetwork = pNetwork; response.networkInfo.index = priority; prvSendListNetworkResponse( &response ); } /*-----------------------------------------------------------*/ static void prvSendScanNetwork( WIFIScanResult_t * pNetwork, bool isLast ) { IotBleWifiProvResponse_t response = { 0 }; response.requestType = IotBleWiFiProvRequestListNetwork; response.networkInfo.isSavedNetwork = false; response.networkInfo.isHidden = false; response.networkInfo.isConnected = false; response.networkInfo.isLast = isLast; response.networkInfo.info.pScannedNetwork = pNetwork; prvSendListNetworkResponse( &response ); } /*-----------------------------------------------------------*/ WIFIReturnCode_t _appendNetwork( WIFINetworkProfile_t * pProfile ) { WIFIReturnCode_t ret; uint16_t idx; ret = WIFI_NetworkAdd( pProfile, &idx ); if( ret == eWiFiSuccess ) { wifiProvisioning.numNetworks++; wifiProvisioning.connectedIdx++; } return ret; } /*-----------------------------------------------------------*/ WIFIReturnCode_t _popNetwork( uint16_t index, WIFINetworkProfile_t * pProfile ) { WIFIReturnCode_t ret = eWiFiSuccess; if( pProfile != NULL ) { ret = WIFI_NetworkGet( pProfile, STORAGE_INDEX( index ) ); } if( ret == eWiFiSuccess ) { ret = WIFI_NetworkDelete( STORAGE_INDEX( index ) ); } if( ( ret == eWiFiSuccess ) && ( wifiProvisioning.numNetworks > 0 ) ) { wifiProvisioning.numNetworks--; /* Shift the priority for connected network */ if( index < wifiProvisioning.connectedIdx ) { wifiProvisioning.connectedIdx--; } else if( index == wifiProvisioning.connectedIdx ) { wifiProvisioning.connectedIdx = INVALID_INDEX; } } return ret; } /*-----------------------------------------------------------*/ WIFIReturnCode_t _moveNetwork( uint16_t currentIndex, uint16_t newIndex ) { WIFIReturnCode_t ret = eWiFiSuccess; WIFINetworkProfile_t profile; if( currentIndex != newIndex ) { ret = _popNetwork( currentIndex, &profile ); if( ret == eWiFiSuccess ) { ret = _insertNetwork( newIndex, &profile ); } } return ret; } /*-----------------------------------------------------------*/ WIFIReturnCode_t _getSavedNetwork( uint16_t index, WIFINetworkProfile_t * pProfile ) { return WIFI_NetworkGet( pProfile, STORAGE_INDEX( index ) ); } /*-----------------------------------------------------------*/ WIFIReturnCode_t _connectNetwork( WIFINetworkProfile_t * pProfile ) { WIFIReturnCode_t ret = eWiFiFailure; WIFINetworkParams_t networkParams = { 0 }; memcpy( networkParams.ucSSID, pProfile->ucSSID, pProfile->ucSSIDLength ); networkParams.ucSSIDLength = pProfile->ucSSIDLength; networkParams.xSecurity = pProfile->xSecurity; if( ( networkParams.xSecurity == eWiFiSecurityWPA2 ) || ( networkParams.xSecurity == eWiFiSecurityWPA ) ) { memcpy( networkParams.xPassword.xWPA.cPassphrase, pProfile->cPassword, pProfile->ucPasswordLength ); networkParams.xPassword.xWPA.ucLength = pProfile->ucPasswordLength; } ret = WIFI_ConnectAP( &networkParams ); return ret; } WIFIReturnCode_t _addNewNetwork( WIFINetworkProfile_t * pProfile, bool connect ) { WIFIReturnCode_t ret = eWiFiSuccess; if( connect == true ) { ret = _connectNetwork( pProfile ); } if( ret == eWiFiSuccess ) { ret = _appendNetwork( pProfile ); if( ( ret == eWiFiSuccess ) && ( connect == true ) ) { wifiProvisioning.connectedIdx = 0; } } return ret; } WIFIReturnCode_t _connectSavedNetwork( uint16_t index ) { WIFIReturnCode_t ret; WIFINetworkProfile_t profile; ret = _getSavedNetwork( index, &profile ); if( ret == eWiFiSuccess ) { ret = _connectNetwork( &profile ); if( ret == eWiFiSuccess ) { wifiProvisioning.connectedIdx = index; } } return ret; } /*-----------------------------------------------------------*/ WIFIReturnCode_t _insertNetwork( uint16_t index, WIFINetworkProfile_t * pProfile ) { WIFIReturnCode_t ret; WIFINetworkProfile_t profile; uint16_t numElementsToShift, x; /* All higher priority elements needs to be shifted */ numElementsToShift = index; ret = _appendNetwork( pProfile ); if( ret == eWiFiSuccess ) { for( x = 0; x < numElementsToShift; x++ ) { ret = _popNetwork( index, &profile ); if( ret == eWiFiSuccess ) { ret = _appendNetwork( &profile ); } if( ret != eWiFiSuccess ) { IotLogError( "Failed to insert network at index %d", index ); break; } } } return ret; } /*-----------------------------------------------------------*/ static void prvProcessListNetworkRequest( IotBleWifiProvListNetworksRequest_t * pRequest ) { WIFINetworkProfile_t profile; uint16_t idx, numNetworks; WIFIReturnCode_t status; bool isLast = false; ( void ) pRequest->scanTimeoutMS; for( idx = 0; idx < wifiProvisioning.numNetworks; idx++ ) { status = _getSavedNetwork( idx, &profile ); if( status == eWiFiSuccess ) { if( idx == wifiProvisioning.numNetworks - 1 ) { isLast = true; } else { isLast = false; } prvSendSavedNetwork( &profile, idx, isLast ); } } memset( scanNetworks, 0x00, sizeof( WIFIScanResult_t ) * IOT_BLE_WIFI_PROVISIONIG_MAX_SCAN_NETWORKS ); status = WIFI_Scan( scanNetworks, pRequest->scanSize ); if( status == eWiFiSuccess ) { for( numNetworks = 0; numNetworks < pRequest->scanSize; numNetworks++ ) { if( scanNetworks[ numNetworks ].ucSSIDLength == 0 ) { break; } } if( numNetworks == 0 ) { prvSendStatusResponse( IotBleWiFiProvRequestListNetwork, status ); } else { for( idx = 0; idx < numNetworks; idx++ ) { if( idx == numNetworks - 1 ) { isLast = true; } else { isLast = false; } prvSendScanNetwork( &scanNetworks[ idx ], isLast ); } } } else { prvSendStatusResponse( IotBleWiFiProvRequestListNetwork, status ); } } /*-----------------------------------------------------------*/ static void prvProcessAddNetworkRequest( IotBleWifiProvAddNetworkRequest_t * pRequest ) { WIFIReturnCode_t status = eWiFiFailure; if( pRequest->isProvisioned == true ) { if( IS_INDEX_WITHIN_RANGE( pRequest->info.index ) ) { status = _connectSavedNetwork( pRequest->info.index ); } } else { if( wifiProvisioning.numNetworks < IOT_BLE_WIFI_PROVISIONING_MAX_SAVED_NETWORKS ) { status = _addNewNetwork( &pRequest->info.network, pRequest->shouldConnect ); if( status != eWiFiSuccess ) { IotLogError( "Failed to provision new network with SSID: %.*s, password: %.*s", pRequest->info.network.ucSSIDLength, ( char * ) pRequest->info.network.ucSSID, pRequest->info.network.ucPasswordLength, pRequest->info.network.cPassword ); } } else { IotLogError( "Failed to add a new network, max networks limit (%d) reached", IOT_BLE_WIFI_PROVISIONING_MAX_SAVED_NETWORKS ); status = eWiFiFailure; } } prvSendStatusResponse( IotBleWiFiProvRequestAddNetwork, status ); } /*-----------------------------------------------------------*/ static void prvProcessDeleteNetworkRequest( IotBleWifiProvDeleteNetworkRequest_t * pRequest ) { WIFIReturnCode_t status = eWiFiFailure; if( IS_INDEX_WITHIN_RANGE( pRequest->priorityIndex ) ) { status = _popNetwork( pRequest->priorityIndex, NULL ); } if( status == eWiFiSuccess ) { if( wifiProvisioning.connectedIdx == INVALID_INDEX ) { ( void ) WIFI_Disconnect(); } } prvSendStatusResponse( IotBleWiFiProvRequestDeleteNetwork, status ); } /*-----------------------------------------------------------*/ static void prvProcessEditNetworkRequest( IotBleWifiProvEditNetworkRequest_t * pRequest ) { WIFIReturnCode_t status = eWiFiFailure; if( IS_INDEX_WITHIN_RANGE( pRequest->currentPriorityIndex ) && IS_INDEX_WITHIN_RANGE( pRequest->newPriorityIndex ) ) { status = _moveNetwork( pRequest->currentPriorityIndex, pRequest->newPriorityIndex ); } prvSendStatusResponse( IotBleWiFiProvRequestEditNetwork, status ); } /*-----------------------------------------------------------*/ static uint32_t _getNumSavedNetworks( void ) { uint32_t idx; WIFIReturnCode_t WifiRet; WIFINetworkProfile_t profile; for( idx = 0; idx < IOT_BLE_WIFI_PROVISIONING_MAX_SAVED_NETWORKS; idx++ ) { WifiRet = WIFI_NetworkGet( &profile, idx ); if( WifiRet != eWiFiSuccess ) { break; } } return idx; } /*-----------------------------------------------------------*/ bool IotBleWifiProv_Init( void ) { bool ret = true; wifiProvisioning.mutex = xSemaphoreCreateMutex(); if( wifiProvisioning.mutex == NULL ) { IotLogError( "Failed to create mutex for WiFi provisioning task." ); ret = false; } if( ret == true ) { wifiProvisioning.messageQueue = xQueueCreate( 2U, sizeof( IotBleWifiProvMessage_t ) ); if( wifiProvisioning.messageQueue == NULL ) { IotLogError( "Failed to create queue for WiFi provisioning task." ); ret = false; } } if( ret == true ) { wifiProvisioning.pSerializer = IotBleWifiProv_GetSerializer(); if( wifiProvisioning.pSerializer == NULL ) { ret = false; } } if( ret == true ) { wifiProvisioning.connectedIdx = INVALID_INDEX; wifiProvisioning.numNetworks = _getNumSavedNetworks(); } return ret; } bool IotBleWifiProv_RunProcessLoop( void ) { IotBleWifiProvMessage_t message = { 0 }; bool processLoopStatus = false; configPRINTF( ( "Entering provisonining loop function.\r\n" ) ); wifiProvisioning.pChannel = IotBleDataTransfer_Open( IOT_BLE_DATA_TRANSFER_SERVICE_TYPE_WIFI_PROVISIONING ); if( wifiProvisioning.pChannel != NULL ) { processLoopStatus = IotBleDataTransfer_SetCallback( wifiProvisioning.pChannel, prvTransportReceiveCallback, NULL ); } else { IotLogError( "Failed to open BLE transport channel for WiFi provisioning." ); processLoopStatus = false; } if( processLoopStatus == true ) { configPRINTF( ( "Entering loop.\r\n" ) ); for( ; ; ) { memset( &message, 0x00, sizeof( IotBleWifiProvMessage_t ) ); ( void ) xQueueReceive( wifiProvisioning.messageQueue, &message, portMAX_DELAY ); if( message.type == IotBleWiFiProvRequestStop ) { configPRINTF( ( "Received stop.\r\n" ) ); xQueueReset( wifiProvisioning.messageQueue ); break; } else { configPRINTF( ( "Received message.\r\n" ) ); xSemaphoreTake( wifiProvisioning.mutex, portMAX_DELAY ); switch( message.type ) { case IotBleWiFiProvRequestListNetwork: prvProcessListNetworkRequest( &message.m.listNetworkRequest ); break; case IotBleWiFiProvRequestAddNetwork: prvProcessAddNetworkRequest( &message.m.addNetworkRequest ); break; case IotBleWiFiProvRequestEditNetwork: prvProcessEditNetworkRequest( &message.m.editNetworkRequest ); break; case IotBleWiFiProvRequestDeleteNetwork: prvProcessDeleteNetworkRequest( &message.m.deleteNetworkRequest ); break; default: break; } xSemaphoreGive( wifiProvisioning.mutex ); } } } configPRINTF( ( "Exit loop.\r\n" ) ); if( wifiProvisioning.pChannel != NULL ) { IotBleDataTransfer_Close( wifiProvisioning.pChannel ); IotBleDataTransfer_Reset( wifiProvisioning.pChannel ); wifiProvisioning.pChannel = NULL; } return processLoopStatus; } /*--------------------------------------------------------------------------------*/ uint32_t IotBleWifiProv_GetNumNetworks( void ) { return _getNumSavedNetworks(); } bool IotBleWifiProv_Connect( uint32_t networkIndex ) { WIFIReturnCode_t WifiRet; bool ret = false; xSemaphoreTake( wifiProvisioning.mutex, portMAX_DELAY ); WifiRet = _connectSavedNetwork( networkIndex ); if( WifiRet == eWiFiSuccess ) { ret = true; } xSemaphoreGive( wifiProvisioning.mutex ); return ret; } bool IotBleWifiProv_EraseAllNetworks( void ) { bool ret = true; WIFIReturnCode_t WiFiRet; xSemaphoreTake( wifiProvisioning.mutex, portMAX_DELAY ); while( wifiProvisioning.numNetworks > 0 ) { WiFiRet = _popNetwork( 0, NULL ); if( WiFiRet != eWiFiSuccess ) { IotLogError( "Failed to delete WIFI network, error = %d", WiFiRet ); ret = false; break; } } xSemaphoreGive( wifiProvisioning.mutex ); return ret; } /*-----------------------------------------------------------*/ bool IotBleWifiProv_Stop( void ) { BaseType_t status; IotBleWifiProvMessage_t message = { .type = IotBleWiFiProvRequestStop }; status = xQueueSend( wifiProvisioning.messageQueue, &message, pdMS_TO_TICKS( IOT_BLE_WIFI_PROVISIONING_COMMAND_TIMEOUT_MS ) ); return( status == pdTRUE ); } /*-----------------------------------------------------------*/ void IotBleWifiProv_Deinit( void ) { /*Delete queue and semaphore. */ vQueueDelete( wifiProvisioning.messageQueue ); vSemaphoreDelete( wifiProvisioning.mutex ); memset( &wifiProvisioning, 0x00, sizeof( IotBleWifiProvService_t ) ); } /* Provide access to private members for testing. */ #ifdef FREERTOS_ENABLE_UNIT_TESTS #include "iot_ble_wifi_prov_test_access_define.h" #endif