/* * Amazon FreeRTOS CELLULAR Preview Release * 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 */ #include #include "cellular_platform.h" #ifdef CONFIG_CDB_PLATFORM #include "qurt_error.h" #endif // #ifdef CONFIG_CDB_PLATFORM /*-----------------------------------------------------------*/ typedef QueueHandle_t SemaphoreHandle_t; typedef struct threadInfo { void * pArgument; /**< @brief Argument to `threadRoutine`. */ void ( *threadRoutine )( void * ); /**< @brief Thread function to run. */ //#ifdef CONFIG_CDB_PLATFORM // qurt_thread_attr_t thread_attr; // qurt_thread_t thread_hndl; //#endif //#ifdef CONFIG_CDB_PLATFORM } threadInfo_t; /*-----------------------------------------------------------*/ /** * @brief Sends provided buffer to network using transport send. * * @param[in] pArgument Argument passed to threadRoutine function. * */ static void prvThreadRoutineWrapper( void * pArgument ); /** * @brief Lock mutex with timeout. * * @param[in] pMutex Mutex to lock. * @param[in] timeout Timeout value to lock mutex. * * @return ture if mutex is locked successfully. Otherwise false. */ static bool prIotMutexTimedLock( PlatformMutex_t * pMutex, TickType_t timeout ); /*-----------------------------------------------------------*/ static void prvThreadRoutineWrapper( void * pArgument ) { threadInfo_t * pThreadInfo = ( threadInfo_t * ) pArgument; print_heap_status( __FILE__, __LINE__, __func__ ); /* Run the thread routine. */ pThreadInfo->threadRoutine( pThreadInfo->pArgument ); print_heap_status( __FILE__, __LINE__, __func__ ); configPRINTF( ( "\x1B[45mFREE-%p,size=%lu;%s()-%lu\x1B[0m\r\n", pThreadInfo, sizeof( threadInfo_t ), __func__, __LINE__ ) ); Platform_Free( pThreadInfo ); print_heap_status( __FILE__, __LINE__, __func__ ); #ifdef CONFIG_CDB_PLATFORM qurt_thread_stop(); #else vTaskDelete( NULL ); #endif /* CONFIG_CDB_PLATFORM */ print_heap_status( __FILE__, __LINE__, __func__ ); } /*-----------------------------------------------------------*/ static bool prIotMutexTimedLock( PlatformMutex_t * pMutex, TickType_t timeout ) { BaseType_t lockResult = pdTRUE; // QCLI_Printf( qcli_bg95_demo_handle, "%s:%u-%s() === PROGRESS pMutex=%p,mutex=%p\n", __FILE__, __LINE__, __func__, pMutex, pMutex->xMutex ); configASSERT( pMutex != NULL ); // QCLI_Printf( qcli_bg95_demo_handle, "%s:%u-%s() === PROGRESS\n", __FILE__, __LINE__, __func__ ); CellularLogDebug( "Locking mutex %p.", pMutex ); /* Call the correct FreeRTOS mutex take function based on mutex type. */ if( pMutex->recursive == pdTRUE ) { // QCLI_Printf( qcli_bg95_demo_handle, "%s:%u-%s() === PROGRESS\n", __FILE__, __LINE__, __func__ ); lockResult = xSemaphoreTakeRecursive( ( SemaphoreHandle_t ) pMutex->xMutex, timeout ); // QCLI_Printf( qcli_bg95_demo_handle, "%s:%u-%s() === PROGRESS\n", __FILE__, __LINE__, __func__ ); } else { // QCLI_Printf( qcli_bg95_demo_handle, "%s:%u-%s() === PROGRESS pMutex=%lx,mutex=%lx,address=%lx/%lx\n", __FILE__, __LINE__, __func__, pMutex, pMutex->xMutex, ( SemaphoreHandle_t ) &pMutex->xMutex, ( SemaphoreHandle_t ) &(pMutex->xMutex) ); lockResult = xSemaphoreTake( ( SemaphoreHandle_t ) pMutex->xMutex, timeout ); // QCLI_Printf( qcli_bg95_demo_handle, "%s:%u-%s() === PROGRESS\n", __FILE__, __LINE__, __func__ ); } // QCLI_Printf( qcli_bg95_demo_handle, "%s:%u-%s() === PROGRESS\n", __FILE__, __LINE__, __func__ ); return( lockResult == pdTRUE ); } /*-----------------------------------------------------------*/ bool Platform_CreateDetachedThread( void ( *threadRoutine )( void * ), void * pArgument, int32_t priority, size_t stackSize ) { bool status = true; threadInfo_t * pThreadInfo = NULL; configASSERT( threadRoutine != NULL ); CellularLogDebug( "Creating new thread." ); //#ifdef CONFIG_CDB_PLATFORM // print_heap_status( __FILE__, __LINE__, __func__ ); // qurt_thread_attr_t thread_attr; // qurt_thread_t thread_hndl; // qurt_thread_attr_init( &thread_attr ); // qurt_thread_attr_set_name( &thread_attr, "PlatformThread" ); // qurt_thread_attr_set_priority( &thread_attr, priority); /* Task priority, must be between 0 and configMAX_PRIORITIES - 1. */ // qurt_thread_attr_set_stack_size( &thread_attr, stackSize); /* Size of stack (in words, not bytes) to allocate for the task. */ // int result = qurt_thread_create( &thread_hndl // , &thread_attr // , threadRoutine /* Function that implements the task. */ // , NULL /* Used to pass out a handle to the created task - not used in this case. */ // ); // if( QURT_EOK != result ) // { // /* Task creation failed. */ // CellularLogError( "Failed to create thread." ); // status = false; // } // // print_heap_status( __FILE__, __LINE__, __func__ ); //#else // print_heap_status( __FILE__, __LINE__, __func__ ); pThreadInfo = Platform_Malloc( sizeof( threadInfo_t ) ); configPRINTF( ( "\x1B[35mMALL-%p,size=%lu;%s()-%lu\x1B[0m\r\n", pThreadInfo, sizeof( threadInfo_t ), __func__, __LINE__ ) ); print_heap_status( __FILE__, __LINE__, __func__ ); if( pThreadInfo == NULL ) { CellularLogError( "\x1B[31mUnable to allocate memory for threadRoutine %p.\x1B[0m", threadRoutine ); status = false; } /* Create the FreeRTOS task that will run the thread. */ if( status == true ) { pThreadInfo->threadRoutine = threadRoutine; pThreadInfo->pArgument = pArgument; print_heap_status( __FILE__, __LINE__, __func__ ); #ifdef CONFIG_CDB_PLATFORM // Use Qualcomm thread routine qurt_thread_attr_t thread_attr; qurt_thread_t thread_hndl; qurt_thread_attr_init( &thread_attr ); qurt_thread_attr_set_name( &thread_attr, "PlatformThread" ); qurt_thread_attr_set_priority( &thread_attr, priority); /* Task priority, must be between 0 and configMAX_PRIORITIES - 1. */ qurt_thread_attr_set_stack_size( &thread_attr, stackSize); /* Size of stack (in words, not bytes) to allocate for the task. */ int result = qurt_thread_create( &thread_hndl , &thread_attr , prvThreadRoutineWrapper /* Function that implements the task. */ , pThreadInfo /* Used to pass out a handle to the created task. */ ); if( QURT_EOK != result ) { /* Task creation failed. */ CellularLogError( "\x1B[31mFailed to create thread.\x1B[0m" ); configPRINTF( ( "\x1B[45mFREE-%p,size=%lu;%s()-%lu\x1B[0m\r\n", pThreadInfo, sizeof( threadInfo_t ), __func__, __LINE__ ) ); Platform_Free( pThreadInfo ); status = false; } else { CellularLogDebug( "\x1B[32mNew thread created.\x1B[0m" ); } #else if( xTaskCreate( prvThreadRoutineWrapper, "Cellular_Thread", ( configSTACK_DEPTH_TYPE ) stackSize, pThreadInfo, priority, NULL ) != pdPASS ) { /* Task creation failed. */ CellularLogError( "\x1B[31mFailed to create thread.\x1B[0m" ); configPRINTF( ( "\x1B[45mFREE-%p,size=%lu;%s()-%lu\x1B[0m\r\n", pThreadInfo, sizeof( threadInfo_t ), __func__, __LINE__ ) ); Platform_Free( pThreadInfo ); status = false; } else { CellularLogDebug( "\x1B[32mNew thread created.\x1B[0m" ); } #endif /* CONFIG_CDB_PLATFORM */ print_heap_status( __FILE__, __LINE__, __func__ ); } //#endif //#ifdef CONFIG_CDB_PLATFORM return status; } /*-----------------------------------------------------------*/ bool PlatformMutex_Create( PlatformMutex_t * pNewMutex, bool recursive ) { bool retMutexCreate = false; configASSERT( pNewMutex != NULL ); CellularLogDebug( "Creating new mutex %p.", pNewMutex ); #ifdef CONFIG_CDB_PLATFORM if( recursive == true ) { pNewMutex->xMutex = xSemaphoreCreateRecursiveMutex(); configPRINTF( ( "\x1B[35mMALL-%p,size=%lu;%s()-%lu\x1B[0m\r\n", pNewMutex->xMutex, sizeof( *pNewMutex->xMutex ), __func__, __LINE__ ) ); // NOTE: FreeRTOS requires semaphore to be given at creation - initializes xSemaphoreGiveRecursive( pNewMutex->xMutex ); } else { pNewMutex->xMutex = xSemaphoreCreateMutex(); configPRINTF( ( "\x1B[35mMALL-%p,size=%lu;%s()-%lu\x1B[0m\r\n", pNewMutex->xMutex, sizeof( *pNewMutex->xMutex ), __func__, __LINE__ ) ); // NOTE: FreeRTOS requires semaphore to be given at creation - initializes xSemaphoreGive( pNewMutex->xMutex ); } /* Remember the type of mutex. */ if( recursive == true ) { pNewMutex->recursive = pdTRUE; } else { pNewMutex->recursive = pdFALSE; } /* Check the handle value returned by the mutex create function. */ if( pNewMutex->xMutex == NULL ) { retMutexCreate = false; } else { retMutexCreate = true; } #else SemaphoreHandle_t xSemaphore; if( recursive == true ) { xSemaphore = xSemaphoreCreateRecursiveMutexStatic( &pNewMutex->xMutex ); xSemaphoreGiveRecursive( pNewMutex->xMutex ); } else { xSemaphore = xSemaphoreCreateMutexStatic( &pNewMutex->xMutex ); xSemaphoreGive( pNewMutex->xMutex ); } /* Remember the type of mutex. */ if( recursive == true ) { pNewMutex->recursive = pdTRUE; } else { pNewMutex->recursive = pdFALSE; } /* Check the handle value returned by the mutex create function. */ if( xSemaphore == NULL ) { retMutexCreate = false; } else { retMutexCreate = true; } #endif /* CONFIG_CDB_PLATFORM */ if( retMutexCreate ) { CellularLogDebug( "\x1B[32mmutex create result=%u\x1B[0m", retMutexCreate ); } else { CellularLogError( "\x1B[31mmutex create result=%u\x1B[0m", retMutexCreate ); } return retMutexCreate; } /*-----------------------------------------------------------*/ void PlatformMutex_Destroy( PlatformMutex_t * pMutex ) { configASSERT( pMutex != NULL ); configPRINTF( ( "\x1B[45mFREE-%p,size=%lu;%s()-%lu\x1B[0m\r\n", pMutex->xMutex, sizeof( *pMutex->xMutex ), __func__, __LINE__ ) ); vSemaphoreDelete( ( SemaphoreHandle_t ) pMutex->xMutex ); } /*-----------------------------------------------------------*/ void PlatformMutex_Lock( PlatformMutex_t * pMutex ) { prIotMutexTimedLock( pMutex, portMAX_DELAY ); } /*-----------------------------------------------------------*/ bool PlatformMutex_TryLock( PlatformMutex_t * pMutex ) { return prIotMutexTimedLock( pMutex, 0 ); } /*-----------------------------------------------------------*/ void PlatformMutex_Unlock( PlatformMutex_t * pMutex ) { configASSERT( pMutex != NULL ); CellularLogDebug( "Unlocking mutex %p.", pMutex ); /* Call the correct FreeRTOS mutex unlock function based on mutex type. */ if( pMutex->recursive == pdTRUE ) { ( void ) xSemaphoreGiveRecursive( ( SemaphoreHandle_t ) pMutex->xMutex ); } else { ( void ) xSemaphoreGive( ( SemaphoreHandle_t ) pMutex->xMutex ); } } #ifdef CONFIG_CDB_PLATFORM #ifdef DEBUG_CHECK_MEMORY_LEAKS static uint32_t platformFreeCount = 0; static uint32_t platformMallocCount = 0; /** * Saved list of pointers */ #define NUMBER_OF_POINTERS_IN_LIST 2000 struct debugPointerEntry_t { char type; uint32_t tickCount; uint32_t mfCount; size_t sizeRequested; uint32_t *address; }; #define GET_TIME_MS (*((volatile uint32_t*)0X5074207C)) //uint32_t time_now = (GET_TIME_MS*31)/1000; static uint32_t pointerListIdx = 0; struct debugPointerEntry_t pointerTable[ NUMBER_OF_POINTERS_IN_LIST ]; void prvStoreDebugPointer( bool isMalloc, void *address, size_t sizeRequested ); void prvStoreDebugPointer( bool isMalloc, void *address, size_t sizeRequested ) { if( pointerListIdx >= (sizeof pointerTable / sizeof pointerTable[0] ) ) return; pointerTable[ pointerListIdx ].type = ( isMalloc ) ? 'm' : 'f' ; pointerTable[ pointerListIdx ].mfCount = ( isMalloc ) ? platformMallocCount: platformFreeCount; pointerTable[ pointerListIdx ].sizeRequested = sizeRequested; pointerTable[ pointerListIdx ].tickCount = (GET_TIME_MS*31)/1000; pointerTable[ pointerListIdx ].address = address; pointerListIdx++; } void printPointerTable( void ); void printPointerTable( void ) { // Printing in sequence for( uint32_t loopIdx = 0; loopIdx < pointerListIdx; loopIdx++ ) { if( 'm' == pointerTable[ loopIdx ].type ) { configPRINTF( ("\x1B[35mMALL-%p,size=%lu,tick=%lu\x1B[0m\r\n", pointerTable[ loopIdx ].address, pointerTable[ loopIdx ].sizeRequested, pointerTable[ loopIdx ].tickCount ) ); } else { configPRINTF( ( "\x1B[45mFREE-%p,size=%lu,tick=%lu\x1B[0m\r\n", pointerTable[ loopIdx ].address, pointerTable[ loopIdx ].sizeRequested, pointerTable[ loopIdx ].tickCount ) ); } } } #endif /* DEBUG_CHECK_MEMORY_LEAKS */ #endif /* CONFIG_CDB_PLATFORM */ /*-----------------------------------------------------------*/ /** * Platform dependent abstraction helper layer * With simple setup no extra abstraction is required, can rely on macro definition in header file */ #ifdef CONFIG_CDB_PLATFORM #ifdef DEBUG_CHECK_MEMORY_LEAKS extern QCLI_Group_Handle_t qcli_bg95_demo_handle; /* Handle for BG95 demo Command Group. */ // Implemented as a debug abstraction layer function void * Platform_Malloc( size_t xWantedSize ) { void *retPtr = NULL; retPtr = malloc( xWantedSize ); prvStoreDebugPointer( true, retPtr, xWantedSize ); // CellularLogDebug( "\x1B[32m%s ptr=%p, size=%lu\x1B[0m", __func__, retPtr, xWantedSize ); // configPRINTF( ("\x1B[32m%s ptr=%p, size=%lu\x1B[0m\r\n", __func__, retPtr, xWantedSize) ); // QCLI_Printf( qcli_bg95_demo_handle, "\x1B[32m%s ptr=%p, size=%lu\x1B[0m", __func__, retPtr, xWantedSize ); platformMallocCount++; return retPtr; } void Platform_Free( void * pv ) { // CellularLogDebug( "\x1B[32m%s ptr=%p\x1B[0m", __func__, pv ); // configPRINTF( ("\x1B[32m%s ptr=%p\x1B[0m\r\n", __func__, pv) ); // QCLI_Printf( qcli_bg95_demo_handle, "\x1B[32m%s ptr=%p\x1B[0m", __func__, pv ); prvStoreDebugPointer( false, pv, 0 ); free( pv ); platformFreeCount++; } #else // deliberately empty as macro defines this case #endif /* DEBUG_CHECK_MEMORY_LEAKS */ #else // deliberately empty as macro defines this case #endif /* CONFIG_CDB_PLATFORM */ /*-----------------------------------------------------------*/