Files
Jose d7f91ac9bb new file: include/OSAL.c
new file:   include/OSAL.h
	new file:   include/ZComDef.h
	new file:   include/i2c.h
2025-02-22 22:20:57 +01:00

1822 lines
47 KiB
C

/**************************************************************************************************
Filename: OSAL.c
Revised: $Date: 2014-11-04 15:36:27 -0800 (Tue, 04 Nov 2014) $
Revision: $Revision: 40989 $
Description: This API allows the software components in the Z-stack to be written
independently of the specifics of the operating system, kernel or tasking
environment (including control loops or connect-to-interrupt systems).
Copyright 2004-2014 Texas Instruments Incorporated. All rights reserved.
IMPORTANT: Your use of this Software is limited to those specific rights
granted under the terms of a software license agreement between the user
who downloaded the software, his/her employer (which must be your employer)
and Texas Instruments Incorporated (the "License"). You may not use this
Software unless you agree to abide by the terms of the License. The License
limits your use, and you acknowledge, that the Software may not be modified,
copied or distributed unless embedded on a Texas Instruments microcontroller
or used solely and exclusively in conjunction with a Texas Instruments radio
frequency transceiver, which is integrated into your product. Other than for
the foregoing purpose, you may not use, reproduce, copy, prepare derivative
works of, modify, distribute, perform, display or sell this Software and/or
its documentation for any purpose.
YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE
PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE,
NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL
TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT,
NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER
LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE
OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT
OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
(INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.
Should you have any questions regarding your right to use this Software,
contact Texas Instruments Incorporated at www.TI.com.
**************************************************************************************************/
/*********************************************************************
* INCLUDES
*/
#include <string.h>
#include "comdef.h"
#include "hal_board.h"
#include "OSAL.h"
#include "OSAL_Tasks.h"
#include "OSAL_Memory.h"
#include "OSAL_PwrMgr.h"
#include "OSAL_Clock.h"
#include "OnBoard.h"
/* HAL */
#include "hal_drivers.h"
#ifdef IAR_ARMCM3_LM
#include "FreeRTOSConfig.h"
#include "osal_task.h"
#endif
#ifdef USE_ICALL
#include <ICall.h>
#endif /* USE_ICALL */
/*********************************************************************
* MACROS
*/
/*********************************************************************
* CONSTANTS
*/
#ifdef USE_ICALL
// A bit mask to use to indicate a proxy OSAL task ID.
#define OSAL_PROXY_ID_FLAG 0x80
#endif // USE_ICALL
/*********************************************************************
* TYPEDEFS
*/
/*********************************************************************
* GLOBAL VARIABLES
*/
// Message Pool Definitions
osal_msg_q_t osal_qHead;
#ifdef USE_ICALL
// OSAL event loop hook function pointer
void (*osal_eventloop_hook)(void) = NULL;
#endif /* USE_ICALL */
/*********************************************************************
* EXTERNAL VARIABLES
*/
/*********************************************************************
* EXTERNAL FUNCTIONS
*/
/*********************************************************************
* LOCAL VARIABLES
*/
// Index of active task
static uint8 activeTaskID = TASK_NO_TASK;
#ifdef USE_ICALL
// Maximum number of proxy tasks
#ifndef OSAL_MAX_NUM_PROXY_TASKS
#define OSAL_MAX_NUM_PROXY_TASKS 2
#endif // OSAL_MAX_NUM_PROXY_TASKS
// ICall entity ID value used to indicate invalid value
#define OSAL_INVALID_DISPATCH_ID 0xffu
// Semaphore associated with OSAL RTOS thread receive queue
ICall_Semaphore osal_semaphore;
// Entity ID that OSAL RTOS thread has registered with
ICall_EntityID osal_entity;
// Last read tick count value reflected into the OSAL timer
uint_least32_t osal_last_timestamp;
// RTOS tick period in microseconds
uint_least32_t osal_tickperiod;
// Maximum timeout value in milliseconds that can be used with an RTOS timer
uint_least32_t osal_max_msecs;
// Timer ID for RTOS timer as backend engine for OSAL timer
static ICall_TimerID osal_timerid_msec_timer;
// Timer callback sequence tracking counter to handle race condition
static unsigned osal_msec_timer_seq = 0;
// proxy task ID map
static uint8 osal_proxy_tasks[OSAL_MAX_NUM_PROXY_TASKS];
// service dispatcher entity IDs corresponding to OSAL tasks
static uint8 *osal_dispatch_entities;
static uint8 osal_notask_entity;
#endif // USE_ICALL
/*********************************************************************
* LOCAL FUNCTION PROTOTYPES
*/
static uint8 osal_msg_enqueue_push( uint8 destination_task, uint8 *msg_ptr, uint8 urgent );
#ifdef USE_ICALL
static uint8 osal_alien2proxy(ICall_EntityID entity);
static ICall_EntityID osal_proxy2alien(uint8 proxyid);
static uint8 osal_dispatch2id(ICall_EntityID entity);
static void osal_msec_timer_cback(void *arg);
#endif // USE_ICALL
/*********************************************************************
* HELPER FUNCTIONS
*/
/* very ugly stub so Keil can compile */
#ifdef __KEIL__
char * itoa ( int value, char * buffer, int radix )
{
return(buffer);
}
#endif
/*********************************************************************
* @fn osal_strlen
*
* @brief
*
* Calculates the length of a string. The string must be null
* terminated.
*
* @param char *pString - pointer to text string
*
* @return int - number of characters
*/
int osal_strlen( char *pString )
{
return (int)( strlen( pString ) );
}
/*********************************************************************
* @fn osal_memcpy
*
* @brief
*
* Generic memory copy.
*
* Note: This function differs from the standard memcpy(), since
* it returns the pointer to the next destination uint8. The
* standard memcpy() returns the original destination address.
*
* @param dst - destination address
* @param src - source address
* @param len - number of bytes to copy
*
* @return pointer to end of destination buffer
*/
void *osal_memcpy( void *dst, const void GENERIC *src, unsigned int len )
{
uint8 *pDst;
const uint8 GENERIC *pSrc;
pSrc = src;
pDst = dst;
while ( len-- )
*pDst++ = *pSrc++;
return ( pDst );
}
/*********************************************************************
* @fn osal_revmemcpy
*
* @brief Generic reverse memory copy. Starts at the end of the
* source buffer, by taking the source address pointer and moving
* pointer ahead "len" bytes, then decrementing the pointer.
*
* Note: This function differs from the standard memcpy(), since
* it returns the pointer to the next destination uint8. The
* standard memcpy() returns the original destination address.
*
* @param dst - destination address
* @param src - source address
* @param len - number of bytes to copy
*
* @return pointer to end of destination buffer
*/
void *osal_revmemcpy( void *dst, const void GENERIC *src, unsigned int len )
{
uint8 *pDst;
const uint8 GENERIC *pSrc;
pSrc = src;
pSrc += (len-1);
pDst = dst;
while ( len-- )
*pDst++ = *pSrc--;
return ( pDst );
}
/*********************************************************************
* @fn osal_memdup
*
* @brief Allocates a buffer [with osal_mem_alloc()] and copies
* the src buffer into the newly allocated space.
*
* @param src - source address
* @param len - number of bytes to copy
*
* @return pointer to the new allocated buffer, or NULL if
* allocation problem.
*/
void *osal_memdup( const void GENERIC *src, unsigned int len )
{
uint8 *pDst;
pDst = osal_mem_alloc( len );
if ( pDst )
{
VOID osal_memcpy( pDst, src, len );
}
return ( (void *)pDst );
}
/*********************************************************************
* @fn osal_memcmp
*
* @brief
*
* Generic memory compare.
*
* @param src1 - source 1 addrexx
* @param src2 - source 2 address
* @param len - number of bytes to compare
*
* @return TRUE - same, FALSE - different
*/
uint8 osal_memcmp( const void GENERIC *src1, const void GENERIC *src2, unsigned int len )
{
const uint8 GENERIC *pSrc1;
const uint8 GENERIC *pSrc2;
pSrc1 = src1;
pSrc2 = src2;
while ( len-- )
{
if( *pSrc1++ != *pSrc2++ )
return FALSE;
}
return TRUE;
}
/*********************************************************************
* @fn osal_memset
*
* @brief
*
* Set memory buffer to value.
*
* @param dest - pointer to buffer
* @param value - what to set each uint8 of the message
* @param size - how big
*
* @return pointer to destination buffer
*/
void *osal_memset( void *dest, uint8 value, int len )
{
return memset( dest, value, len );
}
/*********************************************************************
* @fn osal_build_uint16
*
* @brief
*
* Build a uint16 out of 2 bytes (0 then 1).
*
* @param swapped - 0 then 1
*
* @return uint16
*/
uint16 osal_build_uint16( uint8 *swapped )
{
return ( BUILD_UINT16( swapped[0], swapped[1] ) );
}
/*********************************************************************
* @fn osal_build_uint32
*
* @brief
*
* Build a uint32 out of sequential bytes.
*
* @param swapped - sequential bytes
* @param len - number of bytes in the uint8 array
*
* @return uint32
*/
uint32 osal_build_uint32( uint8 *swapped, uint8 len )
{
if ( len == 2 )
return ( BUILD_UINT32( swapped[0], swapped[1], 0L, 0L ) );
else if ( len == 3 )
return ( BUILD_UINT32( swapped[0], swapped[1], swapped[2], 0L ) );
else if ( len == 4 )
return ( BUILD_UINT32( swapped[0], swapped[1], swapped[2], swapped[3] ) );
else
return ( (uint32)swapped[0] );
}
#if !defined ( ZBIT ) && !defined ( ZBIT2 ) && !defined (UBIT)
/*********************************************************************
* @fn _ltoa
*
* @brief
*
* convert a long unsigned int to a string.
*
* @param l - long to convert
* @param buf - buffer to convert to
* @param radix - 10 dec, 16 hex
*
* @return pointer to buffer
*/
unsigned char * _ltoa(unsigned long l, unsigned char *buf, unsigned char radix)
{
#if defined (__TI_COMPILER_VERSION)
return ( (unsigned char*)ltoa( l, (char *)buf ) );
#elif defined( __GNUC__ )
return ( (char*)ltoa( l, buf, radix ) );
#else
unsigned char tmp1[10] = "", tmp2[10] = "", tmp3[10] = "";
unsigned short num1, num2, num3;
unsigned char i;
buf[0] = '\0';
if ( radix == 10 )
{
num1 = l % 10000;
num2 = (l / 10000) % 10000;
num3 = (unsigned short)(l / 100000000);
if (num3) _itoa(num3, tmp3, 10);
if (num2) _itoa(num2, tmp2, 10);
if (num1) _itoa(num1, tmp1, 10);
if (num3)
{
strcpy((char*)buf, (char const*)tmp3);
for (i = 0; i < 4 - strlen((char const*)tmp2); i++)
strcat((char*)buf, "0");
}
strcat((char*)buf, (char const*)tmp2);
if (num3 || num2)
{
for (i = 0; i < 4 - strlen((char const*)tmp1); i++)
strcat((char*)buf, "0");
}
strcat((char*)buf, (char const*)tmp1);
if (!num3 && !num2 && !num1)
strcpy((char*)buf, "0");
}
else if ( radix == 16 )
{
num1 = l & 0x0000FFFF;
num2 = l >> 16;
if (num2) _itoa(num2, tmp2, 16);
if (num1) _itoa(num1, tmp1, 16);
if (num2)
{
strcpy((char*)buf,(char const*)tmp2);
for (i = 0; i < 4 - strlen((char const*)tmp1); i++)
strcat((char*)buf, "0");
}
strcat((char*)buf, (char const*)tmp1);
if (!num2 && !num1)
strcpy((char*)buf, "0");
}
else
return NULL;
return buf;
#endif
}
#endif // !defined(ZBIT) && !defined(ZBIT2)
/*********************************************************************
* @fn osal_rand
*
* @brief Random number generator
*
* @param none
*
* @return uint16 - new random number
*/
uint16 osal_rand( void )
{
return ( Onboard_rand() );
}
/*********************************************************************
* API FUNCTIONS
*********************************************************************/
#ifdef USE_ICALL
/*********************************************************************
* @fn osal_prepare_svc_enroll
*
* @brief Initialize data structures that map OSAL task ids to
* ICall entity ids.
*
* @param none
*
* @return none
*/
static void osal_prepare_svc_enroll(void)
{
osal_dispatch_entities = (uint8 *) osal_mem_alloc(tasksCnt * 2);
osal_memset(osal_dispatch_entities, OSAL_INVALID_DISPATCH_ID, tasksCnt * 2);
osal_memset(osal_proxy_tasks, OSAL_INVALID_DISPATCH_ID,
OSAL_MAX_NUM_PROXY_TASKS);
}
/*********************************************************************
* @fn osal_enroll_dispatchid
*
* @brief Map a task id to an ICall entity id for messaging in
* both directions (sending and receiving).
*
* @param taskid OSAL task id
* @param dispatchid ICall entity id
*
* @return none
*/
void osal_enroll_dispatchid(uint8 taskid, ICall_EntityID dispatchid)
{
osal_dispatch_entities[taskid] = dispatchid;
osal_dispatch_entities[tasksCnt + taskid] = dispatchid;
}
/*********************************************************************
* @fn osal_enroll_senderid
*
* @brief Map a task id to an ICall entity id, which shall be used
* just for sending a message from an OSAL task.
* Note that osal_enroll_dispatchid() must never be called
* with the same OSAL task id used in this function call.
* However, it is OK to call osal_enroll_dispatchid()
* with the same ICall entity id and a different OSAL task id.
*
* @param taskid OSAL task id
* @param dispatchid ICall entity id
*
* @return none
*/
void osal_enroll_senderid(uint8 taskid, ICall_EntityID dispatchid)
{
osal_dispatch_entities[tasksCnt + taskid] = dispatchid;
}
/*********************************************************************
* @fn osal_enroll_notasksender
*
* @brief Map no task id to an ICall entity id, which shall be used
* just for sending a message from non-OSAL task.
*
* @param dispatchid ICall entity id
*
* @return none
*/
void osal_enroll_notasksender(ICall_EntityID dispatchid)
{
osal_notask_entity = dispatchid;
}
#endif /* USE_ICALL */
/*********************************************************************
* @fn osal_msg_allocate
*
* @brief
*
* This function is called by a task to allocate a message buffer
* into which the task will encode the particular message it wishes
* to send. This common buffer scheme is used to strictly limit the
* creation of message buffers within the system due to RAM size
* limitations on the microprocessor. Note that all message buffers
* are a fixed size (at least initially). The parameter len is kept
* in case a message pool with varying fixed message sizes is later
* created (for example, a pool of message buffers of size LARGE,
* MEDIUM and SMALL could be maintained and allocated based on request
* from the tasks).
*
*
* @param uint8 len - wanted buffer length
*
*
* @return pointer to allocated buffer or NULL if allocation failed.
*/
uint8 * osal_msg_allocate( uint16 len )
{
osal_msg_hdr_t *hdr;
if ( len == 0 )
return ( NULL );
hdr = (osal_msg_hdr_t *) osal_mem_alloc( (short)(len + sizeof( osal_msg_hdr_t )) );
if ( hdr )
{
hdr->next = NULL;
hdr->len = len;
hdr->dest_id = TASK_NO_TASK;
return ( (uint8 *) (hdr + 1) );
}
else
return ( NULL );
}
/*********************************************************************
* @fn osal_msg_deallocate
*
* @brief
*
* This function is used to deallocate a message buffer. This function
* is called by a task (or processing element) after it has finished
* processing a received message.
*
*
* @param uint8 *msg_ptr - pointer to new message buffer
*
* @return SUCCESS, INVALID_MSG_POINTER
*/
uint8 osal_msg_deallocate( uint8 *msg_ptr )
{
uint8 *x;
if ( msg_ptr == NULL )
return ( INVALID_MSG_POINTER );
// don't deallocate queued buffer
if ( OSAL_MSG_ID( msg_ptr ) != TASK_NO_TASK )
return ( MSG_BUFFER_NOT_AVAIL );
x = (uint8 *)((uint8 *)msg_ptr - sizeof( osal_msg_hdr_t ));
osal_mem_free( (void *)x );
return ( SUCCESS );
}
/*********************************************************************
* @fn osal_msg_send
*
* @brief
*
* This function is called by a task to send a command message to
* another task or processing element. The sending_task field must
* refer to a valid task, since the task ID will be used
* for the response message. This function will also set a message
* ready event in the destination tasks event list.
*
*
* @param uint8 destination_task - Send msg to Task ID
* @param uint8 *msg_ptr - pointer to new message buffer
*
* @return SUCCESS, INVALID_TASK, INVALID_MSG_POINTER
*/
uint8 osal_msg_send( uint8 destination_task, uint8 *msg_ptr )
{
#ifdef USE_ICALL
if (destination_task & OSAL_PROXY_ID_FLAG)
{
/* Destination is a proxy task */
osal_msg_hdr_t *hdr = (osal_msg_hdr_t *)msg_ptr - 1;
ICall_EntityID src, dst;
uint8 taskid = osal_self();
if (taskid == TASK_NO_TASK)
{
/* Call must have been made from either an ISR or a user-thread */
src = osal_notask_entity;
}
else
{
src = (ICall_EntityID) osal_dispatch_entities[taskid + tasksCnt];
}
if (src == OSAL_INVALID_DISPATCH_ID)
{
/* The source entity is not registered */
/* abort */
ICall_abort();
return FAILURE;
}
dst = osal_proxy2alien(destination_task);
hdr->dest_id = TASK_NO_TASK;
if (ICall_send(src, dst, ICALL_MSG_FORMAT_KEEP, msg_ptr) ==
ICALL_ERRNO_SUCCESS)
{
return SUCCESS;
}
osal_msg_deallocate(msg_ptr);
return FAILURE;
}
#endif /* USE_ICALL */
return ( osal_msg_enqueue_push( destination_task, msg_ptr, FALSE ) );
}
/*********************************************************************
* @fn osal_msg_push_front
*
* @brief
*
* This function is called by a task to push a command message
* to the head of the OSAL queue. The destination_task field
* must refer to a valid task, since the task ID will be used to
* send the message to. This function will also set a message
* ready event in the destination task's event list.
*
* @param uint8 destination_task - Send msg to Task ID
* @param uint8 *msg_ptr - pointer to message buffer
*
* @return SUCCESS, INVALID_TASK, INVALID_MSG_POINTER
*/
uint8 osal_msg_push_front( uint8 destination_task, uint8 *msg_ptr )
{
return ( osal_msg_enqueue_push( destination_task, msg_ptr, TRUE ) );
}
/*********************************************************************
* @fn osal_msg_enqueue_push
*
* @brief
*
* This function is called by a task to either enqueue (append to
* queue) or push (prepend to queue) a command message to the OSAL
* queue. The destination_task field must refer to a valid task,
* since the task ID will be used to send the message to. This
* function will also set a message ready event in the destination
* task's event list.
*
* @param uint8 destination_task - Send msg to Task ID
* @param uint8 *msg_ptr - pointer to message buffer
* @param uint8 push - TRUE to push, otherwise enqueue
*
* @return SUCCESS, INVALID_TASK, INVALID_MSG_POINTER
*/
static uint8 osal_msg_enqueue_push( uint8 destination_task, uint8 *msg_ptr, uint8 push )
{
if ( msg_ptr == NULL )
{
return ( INVALID_MSG_POINTER );
}
#ifdef USE_ICALL
if (destination_task & OSAL_PROXY_ID_FLAG)
{
ICall_abort();
}
#endif /* USE_ICALL */
if ( destination_task >= tasksCnt )
{
osal_msg_deallocate( msg_ptr );
return ( INVALID_TASK );
}
// Check the message header
if ( OSAL_MSG_NEXT( msg_ptr ) != NULL ||
OSAL_MSG_ID( msg_ptr ) != TASK_NO_TASK )
{
osal_msg_deallocate( msg_ptr );
return ( INVALID_MSG_POINTER );
}
OSAL_MSG_ID( msg_ptr ) = destination_task;
if ( push == TRUE )
{
// prepend the message
osal_msg_push( &osal_qHead, msg_ptr );
}
else
{
// append the message
osal_msg_enqueue( &osal_qHead, msg_ptr );
}
// Signal the task that a message is waiting
osal_set_event( destination_task, SYS_EVENT_MSG );
return ( SUCCESS );
}
/*********************************************************************
* @fn osal_msg_receive
*
* @brief
*
* This function is called by a task to retrieve a received command
* message. The calling task must deallocate the message buffer after
* processing the message using the osal_msg_deallocate() call.
*
* @param uint8 task_id - receiving tasks ID
*
* @return *uint8 - message information or NULL if no message
*/
uint8 *osal_msg_receive( uint8 task_id )
{
osal_msg_hdr_t *listHdr;
osal_msg_hdr_t *prevHdr = NULL;
osal_msg_hdr_t *foundHdr = NULL;
halIntState_t intState;
// Hold off interrupts
HAL_ENTER_CRITICAL_SECTION(intState);
// Point to the top of the queue
listHdr = osal_qHead;
// Look through the queue for a message that belongs to the asking task
while ( listHdr != NULL )
{
if ( (listHdr - 1)->dest_id == task_id )
{
if ( foundHdr == NULL )
{
// Save the first one
foundHdr = listHdr;
}
else
{
// Second msg found, stop looking
break;
}
}
if ( foundHdr == NULL )
{
prevHdr = listHdr;
}
listHdr = OSAL_MSG_NEXT( listHdr );
}
// Is there more than one?
if ( listHdr != NULL )
{
// Yes, Signal the task that a message is waiting
osal_set_event( task_id, SYS_EVENT_MSG );
}
else
{
// No more
osal_clear_event( task_id, SYS_EVENT_MSG );
}
// Did we find a message?
if ( foundHdr != NULL )
{
// Take out of the link list
osal_msg_extract( &osal_qHead, foundHdr, prevHdr );
}
// Release interrupts
HAL_EXIT_CRITICAL_SECTION(intState);
return ( (uint8*) foundHdr );
}
/**************************************************************************************************
* @fn osal_msg_find
*
* @brief This function finds in place an OSAL message matching the task_id and event
* parameters.
*
* input parameters
*
* @param task_id - The OSAL task id that the enqueued OSAL message must match.
* @param event - The OSAL event id that the enqueued OSAL message must match.
*
* output parameters
*
* None.
*
* @return NULL if no match, otherwise an in place pointer to the matching OSAL message.
**************************************************************************************************
*/
osal_event_hdr_t *osal_msg_find(uint8 task_id, uint8 event)
{
osal_msg_hdr_t *pHdr;
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState); // Hold off interrupts.
pHdr = osal_qHead; // Point to the top of the queue.
// Look through the queue for a message that matches the task_id and event parameters.
while (pHdr != NULL)
{
if (((pHdr-1)->dest_id == task_id) && (((osal_event_hdr_t *)pHdr)->event == event))
{
break;
}
pHdr = OSAL_MSG_NEXT(pHdr);
}
HAL_EXIT_CRITICAL_SECTION(intState); // Release interrupts.
return (osal_event_hdr_t *)pHdr;
}
/**************************************************************************************************
* @fn osal_msg_count
*
* @brief This function counts the number of messages, in the OSAL message queue with a
* a given task ID and message event type.
*
* input parameters
*
* @param task_id - The OSAL task id that the enqueued OSAL message must match.
* @param event - The OSAL event id that the enqueued OSAL message must match. 0xFF for
* all events.
*
* output parameters
*
* None.
*
* @return The number of OSAL messages that match the task ID and Event.
**************************************************************************************************
*/
uint8 osal_msg_count( uint8 task_id, uint8 event )
{
uint8 count = 0;
osal_msg_hdr_t *pHdr;
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState); // Hold off interrupts.
pHdr = osal_qHead; // Point to the top of the queue.
// Look through the queue for a message that matches the task_id and event parameters.
while (pHdr != NULL)
{
if ( ((pHdr-1)->dest_id == task_id)
&& ((event == 0xFF) || (((osal_event_hdr_t *)pHdr)->event == event)) )
{
count++;
}
pHdr = OSAL_MSG_NEXT(pHdr);
}
HAL_EXIT_CRITICAL_SECTION(intState); // Release interrupts.
return ( count );
}
/*********************************************************************
* @fn osal_msg_enqueue
*
* @brief
*
* This function enqueues an OSAL message into an OSAL queue.
*
* @param osal_msg_q_t *q_ptr - OSAL queue
* @param void *msg_ptr - OSAL message
*
* @return none
*/
void osal_msg_enqueue( osal_msg_q_t *q_ptr, void *msg_ptr )
{
void *list;
halIntState_t intState;
// Hold off interrupts
HAL_ENTER_CRITICAL_SECTION(intState);
OSAL_MSG_NEXT( msg_ptr ) = NULL;
// If first message in queue
if ( *q_ptr == NULL )
{
*q_ptr = msg_ptr;
}
else
{
// Find end of queue
for ( list = *q_ptr; OSAL_MSG_NEXT( list ) != NULL; list = OSAL_MSG_NEXT( list ) );
// Add message to end of queue
OSAL_MSG_NEXT( list ) = msg_ptr;
}
// Re-enable interrupts
HAL_EXIT_CRITICAL_SECTION(intState);
}
/*********************************************************************
* @fn osal_msg_dequeue
*
* @brief
*
* This function dequeues an OSAL message from an OSAL queue.
*
* @param osal_msg_q_t *q_ptr - OSAL queue
*
* @return void * - pointer to OSAL message or NULL of queue is empty.
*/
void *osal_msg_dequeue( osal_msg_q_t *q_ptr )
{
void *msg_ptr = NULL;
halIntState_t intState;
// Hold off interrupts
HAL_ENTER_CRITICAL_SECTION(intState);
if ( *q_ptr != NULL )
{
// Dequeue message
msg_ptr = *q_ptr;
*q_ptr = OSAL_MSG_NEXT( msg_ptr );
OSAL_MSG_NEXT( msg_ptr ) = NULL;
OSAL_MSG_ID( msg_ptr ) = TASK_NO_TASK;
}
// Re-enable interrupts
HAL_EXIT_CRITICAL_SECTION(intState);
return msg_ptr;
}
/*********************************************************************
* @fn osal_msg_push
*
* @brief
*
* This function pushes an OSAL message to the head of an OSAL
* queue.
*
* @param osal_msg_q_t *q_ptr - OSAL queue
* @param void *msg_ptr - OSAL message
*
* @return none
*/
void osal_msg_push( osal_msg_q_t *q_ptr, void *msg_ptr )
{
halIntState_t intState;
// Hold off interrupts
HAL_ENTER_CRITICAL_SECTION(intState);
// Push message to head of queue
OSAL_MSG_NEXT( msg_ptr ) = *q_ptr;
*q_ptr = msg_ptr;
// Re-enable interrupts
HAL_EXIT_CRITICAL_SECTION(intState);
}
/*********************************************************************
* @fn osal_msg_extract
*
* @brief
*
* This function extracts and removes an OSAL message from the
* middle of an OSAL queue.
*
* @param osal_msg_q_t *q_ptr - OSAL queue
* @param void *msg_ptr - OSAL message to be extracted
* @param void *prev_ptr - OSAL message before msg_ptr in queue
*
* @return none
*/
void osal_msg_extract( osal_msg_q_t *q_ptr, void *msg_ptr, void *prev_ptr )
{
halIntState_t intState;
// Hold off interrupts
HAL_ENTER_CRITICAL_SECTION(intState);
if ( msg_ptr == *q_ptr )
{
// remove from first
*q_ptr = OSAL_MSG_NEXT( msg_ptr );
}
else
{
// remove from middle
OSAL_MSG_NEXT( prev_ptr ) = OSAL_MSG_NEXT( msg_ptr );
}
OSAL_MSG_NEXT( msg_ptr ) = NULL;
OSAL_MSG_ID( msg_ptr ) = TASK_NO_TASK;
// Re-enable interrupts
HAL_EXIT_CRITICAL_SECTION(intState);
}
/*********************************************************************
* @fn osal_msg_enqueue_max
*
* @brief
*
* This function enqueues an OSAL message into an OSAL queue if
* the length of the queue is less than max.
*
* @param osal_msg_q_t *q_ptr - OSAL queue
* @param void *msg_ptr - OSAL message
* @param uint8 max - maximum length of queue
*
* @return TRUE if message was enqueued, FALSE otherwise
*/
uint8 osal_msg_enqueue_max( osal_msg_q_t *q_ptr, void *msg_ptr, uint8 max )
{
void *list;
uint8 ret = FALSE;
halIntState_t intState;
// Hold off interrupts
HAL_ENTER_CRITICAL_SECTION(intState);
// If first message in queue
if ( *q_ptr == NULL )
{
*q_ptr = msg_ptr;
ret = TRUE;
}
else
{
// Find end of queue or max
list = *q_ptr;
max--;
while ( (OSAL_MSG_NEXT( list ) != NULL) && (max > 0) )
{
list = OSAL_MSG_NEXT( list );
max--;
}
// Add message to end of queue if max not reached
if ( max != 0 )
{
OSAL_MSG_NEXT( list ) = msg_ptr;
ret = TRUE;
}
}
// Re-enable interrupts
HAL_EXIT_CRITICAL_SECTION(intState);
return ret;
}
/*********************************************************************
* @fn osal_set_event
*
* @brief
*
* This function is called to set the event flags for a task. The
* event passed in is OR'd into the task's event variable.
*
* @param uint8 task_id - receiving tasks ID
* @param uint8 event_flag - what event to set
*
* @return SUCCESS, MSG_BUFFER_NOT_AVAIL, FAILURE, INVALID_TASK
*/
#ifdef OSAL_PORT2TIRTOS
uint8 osal_set_event_raw( uint8 task_id, uint16 event_flag )
#else /* OSAL_PORT2TIRTOS */
uint8 osal_set_event( uint8 task_id, uint16 event_flag )
#endif /* OSAL_PORT2TIRTOS */
{
#ifdef USE_ICALL
if (task_id & OSAL_PROXY_ID_FLAG)
{
/* Destination is a proxy task */
osal_msg_hdr_t *hdr;
ICall_EntityID src, dst;
uint8 taskid;
struct _osal_event_msg_t
{
uint16 signature;
uint16 event_flag;
} *msg_ptr = (struct _osal_event_msg_t *)
osal_msg_allocate(sizeof(*msg_ptr));
if (!msg_ptr)
{
return MSG_BUFFER_NOT_AVAIL;
}
msg_ptr->signature = 0xffffu;
msg_ptr->event_flag = event_flag;
hdr = (osal_msg_hdr_t *)msg_ptr - 1;
taskid = osal_self();
if (taskid == TASK_NO_TASK)
{
/* Call must have been made from either an ISR or a user-thread */
src = osal_notask_entity;
}
else
{
src = (ICall_EntityID) osal_dispatch_entities[taskid + tasksCnt];
}
if (src == OSAL_INVALID_DISPATCH_ID)
{
/* The source entity is not registered */
osal_msg_deallocate((uint8 *) msg_ptr);
ICall_abort();
return FAILURE;
}
dst = osal_proxy2alien(task_id);
hdr->dest_id = TASK_NO_TASK;
if (ICall_send(src, dst,
ICALL_MSG_FORMAT_KEEP, msg_ptr) ==
ICALL_ERRNO_SUCCESS)
{
return SUCCESS;
}
osal_msg_deallocate((uint8 *) msg_ptr);
return FAILURE;
}
#endif /* USE_ICALL */
if ( task_id < tasksCnt )
{
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState); // Hold off interrupts
tasksEvents[task_id] |= event_flag; // Stuff the event bit(s)
HAL_EXIT_CRITICAL_SECTION(intState); // Release interrupts
#ifdef USE_ICALL
ICall_signal(osal_semaphore);
#endif /* USE_ICALL */
return ( SUCCESS );
}
else
{
return ( INVALID_TASK );
}
}
/*********************************************************************
* @fn osal_clear_event
*
* @brief
*
* This function is called to clear the event flags for a task. The
* event passed in is masked out of the task's event variable.
*
* @param uint8 task_id - receiving tasks ID
* @param uint8 event_flag - what event to clear
*
* @return SUCCESS, INVALID_TASK
*/
uint8 osal_clear_event( uint8 task_id, uint16 event_flag )
{
if ( task_id < tasksCnt )
{
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState); // Hold off interrupts
tasksEvents[task_id] &= ~(event_flag); // Clear the event bit(s)
HAL_EXIT_CRITICAL_SECTION(intState); // Release interrupts
return ( SUCCESS );
}
else
{
return ( INVALID_TASK );
}
}
/*********************************************************************
* @fn osal_isr_register
*
* @brief
*
* This function is called to register a service routine with an
* interrupt. When the interrupt occurs, this service routine is called.
*
* @param uint8 interrupt_id - Interrupt number
* @param void (*isr_ptr)( uint8* ) - function pointer to ISR
*
* @return SUCCESS, INVALID_INTERRUPT_ID,
*/
uint8 osal_isr_register( uint8 interrupt_id, void (*isr_ptr)( uint8* ) )
{
// Remove these statements when functionality is complete
(void)interrupt_id;
(void)isr_ptr;
return ( SUCCESS );
}
/*********************************************************************
* @fn osal_int_enable
*
* @brief
*
* This function is called to enable an interrupt. Once enabled,
* occurrence of the interrupt causes the service routine associated
* with that interrupt to be called.
*
* If INTS_ALL is the interrupt_id, interrupts (in general) are enabled.
* If a single interrupt is passed in, then interrupts still have
* to be enabled with another call to INTS_ALL.
*
* @param uint8 interrupt_id - Interrupt number
*
* @return SUCCESS or INVALID_INTERRUPT_ID
*/
uint8 osal_int_enable( uint8 interrupt_id )
{
if ( interrupt_id == INTS_ALL )
{
HAL_ENABLE_INTERRUPTS();
return ( SUCCESS );
}
else
{
return ( INVALID_INTERRUPT_ID );
}
}
/*********************************************************************
* @fn osal_int_disable
*
* @brief
*
* This function is called to disable an interrupt. When a disabled
* interrupt occurs, the service routine associated with that
* interrupt is not called.
*
* If INTS_ALL is the interrupt_id, interrupts (in general) are disabled.
* If a single interrupt is passed in, then just that interrupt is disabled.
*
* @param uint8 interrupt_id - Interrupt number
*
* @return SUCCESS or INVALID_INTERRUPT_ID
*/
uint8 osal_int_disable( uint8 interrupt_id )
{
if ( interrupt_id == INTS_ALL )
{
HAL_DISABLE_INTERRUPTS();
return ( SUCCESS );
}
else
{
return ( INVALID_INTERRUPT_ID );
}
}
/*********************************************************************
* @fn osal_init_system
*
* @brief
*
* This function initializes the "task" system by creating the
* tasks defined in the task table (OSAL_Tasks.h).
*
* @param void
*
* @return SUCCESS
*/
uint8 osal_init_system( void )
{
#if !defined USE_ICALL && !defined OSAL_PORT2TIRTOS
// Initialize the Memory Allocation System
osal_mem_init();
#endif /* !defined USE_ICALL && !defined OSAL_PORT2TIRTOS */
// Initialize the message queue
osal_qHead = NULL;
// Initialize the timers
osalTimerInit();
// Initialize the Power Management System
osal_pwrmgr_init();
#ifdef USE_ICALL
/* Prepare memory space for service enrollment */
osal_prepare_svc_enroll();
#endif /* USE_ICALL */
// Initialize the system tasks.
osalInitTasks();
#if !defined USE_ICALL && !defined OSAL_PORT2TIRTOS
// Setup efficient search for the first free block of heap.
osal_mem_kick();
#endif /* !defined USE_ICALL && !defined OSAL_PORT2TIRTOS */
#ifdef USE_ICALL
// Initialize variables used to track timing and provide OSAL timer service
osal_last_timestamp = (uint_least32_t) ICall_getTicks();
osal_tickperiod = (uint_least32_t) ICall_getTickPeriod();
osal_max_msecs = (uint_least32_t) ICall_getMaxMSecs();
/* Reduce ceiling considering potential latency */
osal_max_msecs -= 2;
#endif /* USE_ICALL */
return ( SUCCESS );
}
/*********************************************************************
* @fn osal_start_system
*
* @brief
*
* This function is the main loop function of the task system (if
* ZBIT and UBIT are not defined). This Function doesn't return.
*
* @param void
*
* @return none
*/
void osal_start_system( void )
{
#ifdef USE_ICALL
/* Kick off timer service in order to allocate resources upfront.
* The first timeout is required to schedule next OSAL timer event
* as well. */
ICall_Errno errno = ICall_setTimer(1, osal_msec_timer_cback,
(void *) osal_msec_timer_seq,
&osal_timerid_msec_timer);
if (errno != ICALL_ERRNO_SUCCESS)
{
ICall_abort();
}
#endif /* USE_ICALL */
#if !defined ( ZBIT ) && !defined ( UBIT )
for(;;) // Forever Loop
#endif
{
osal_run_system();
#ifdef USE_ICALL
ICall_wait(ICALL_TIMEOUT_FOREVER);
#endif /* USE_ICALL */
}
}
#ifdef USE_ICALL
/*********************************************************************
* @fn osal_alien2proxy
*
* @brief
*
* Assign or retrieve a proxy OSAL task id for an external ICall entity.
*
* @param origid ICall entity id
*
* @return proxy OSAL task id
*/
static uint8 osal_alien2proxy(ICall_EntityID origid)
{
size_t i;
for (i = 0; i < OSAL_MAX_NUM_PROXY_TASKS; i++)
{
if (osal_proxy_tasks[i] == OSAL_INVALID_DISPATCH_ID)
{
/* proxy not found. Create a new one */
osal_proxy_tasks[i] = (uint8) origid;
return (OSAL_PROXY_ID_FLAG | i);
}
else if ((ICall_EntityID) osal_proxy_tasks[i] == origid)
{
return (OSAL_PROXY_ID_FLAG | i);
}
}
/* abort */
ICall_abort();
return TASK_NO_TASK;
}
/*********************************************************************
* @fn osal_proxy2alien
*
* @brief
*
* Retrieve the ICall entity id for a proxy OSAL task id
*
* @param proxyid Proxy OSAL task id
*
* @return ICall entity id
*/
static ICall_EntityID osal_proxy2alien(uint8 proxyid)
{
proxyid ^= OSAL_PROXY_ID_FLAG;
if (proxyid >= OSAL_MAX_NUM_PROXY_TASKS)
{
/* abort */
ICall_abort();
}
return (ICall_EntityID) osal_proxy_tasks[proxyid];
}
/*********************************************************************
* @fn osal_dispatch2id
*
* @brief
*
* Retrieve OSAL task id mapped to a designated ICall entity id
*
* @param entity ICall entity id
*
* @return OSAL task id
*/
static uint8 osal_dispatch2id(ICall_EntityID entity)
{
size_t i;
for (i = 0; i < tasksCnt; i++)
{
if ((ICall_EntityID) osal_dispatch_entities[i] == entity)
{
return i;
}
}
return TASK_NO_TASK;
}
/*********************************************************************
* @fn osal_msec_timer_cback
*
* @brief
*
* This function is a callback function for ICall_setTimer() service
* used to implement OSAL timer
*
* @param arg In this case, the timer sequence number is passed.
*
* @return None
*/
static void osal_msec_timer_cback(void *arg)
{
unsigned seq = (unsigned) arg;
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState);
if (seq == osal_msec_timer_seq)
{
ICall_signal(osal_semaphore);
}
HAL_EXIT_CRITICAL_SECTION(intState);
}
/*********************************************************************
* @fn osal_service_entry
*
* @brief
*
* This function is service function for messaging service
*
* @param args arguments.
*
* @return ICall error code
*/
ICall_Errno osal_service_entry(ICall_FuncArgsHdr *args)
{
if (args->func == ICALL_MSG_FUNC_GET_LOCAL_MSG_ENTITY_ID)
{
/* Get proxy ID */
((ICall_GetLocalMsgEntityIdArgs *)args)->localId =
osal_alien2proxy(((ICall_GetLocalMsgEntityIdArgs *)args)->entity);
if (((ICall_GetLocalMsgEntityIdArgs *)args)->localId == TASK_NO_TASK)
{
return ICALL_ERRNO_NO_RESOURCE;
}
}
else
{
return ICALL_ERRNO_INVALID_FUNCTION;
}
return ICALL_ERRNO_SUCCESS;
}
#endif /* USE_ICALL */
/*********************************************************************
* @fn osal_run_system
*
* @brief
*
* This function will make one pass through the OSAL taskEvents table
* and call the task_event_processor() function for the first task that
* is found with at least one event pending. If there are no pending
* events (all tasks), this function puts the processor into Sleep.
*
* @param void
*
* @return none
*/
void osal_run_system( void )
{
uint8 idx = 0;
#ifdef USE_ICALL
uint32 next_timeout_prior = osal_next_timeout();
#else /* USE_ICALL */
#ifndef HAL_BOARD_CC2538
osalTimeUpdate();
#endif
Hal_ProcessPoll();
#endif /* USE_ICALL */
#ifdef USE_ICALL
{
/* Update osal timers to the latest before running any OSAL processes
* regardless of wakeup callback from ICall because OSAL timers are added
* relative to the current time. */
unsigned long newtimestamp = ICall_getTicks();
uint32 milliseconds;
if (osal_tickperiod == 1000)
{
milliseconds = newtimestamp - osal_last_timestamp;
osal_last_timestamp = newtimestamp;
}
else
{
unsigned long long delta = (unsigned long long)
((newtimestamp - osal_last_timestamp) & 0xfffffffful);
delta *= osal_tickperiod;
delta /= 1000;
milliseconds = (uint32) delta;
osal_last_timestamp += (uint32) (delta * 1000 / osal_tickperiod);
}
osalAdjustTimer(milliseconds);
/* Set a value that will never match osal_next_timeout()
* return value so that the next time can be scheduled.
*/
next_timeout_prior = 0xfffffffful;
}
if (osal_eventloop_hook)
{
osal_eventloop_hook();
}
for (;;)
{
void *msg;
ICall_EntityID src, dst;
osal_msg_hdr_t *hdr;
uint8 dest_id;
if (ICall_fetchMsg(&src, &dst, &msg) != ICALL_ERRNO_SUCCESS)
{
break;
}
hdr = (osal_msg_hdr_t *) msg - 1;
dest_id = osal_dispatch2id(dst);
if (dest_id == TASK_NO_TASK)
{
/* Something wrong */
ICall_abort();
}
else
{
/* Message towards one of the tasks */
/* Create a proxy task ID if necessary and
* queue the message to the OSAL internal queue.
*/
uint8 proxyid = osal_alien2proxy(hdr->srcentity);
if (hdr->format == ICALL_MSG_FORMAT_1ST_CHAR_TASK_ID)
{
uint8 *bytes = msg;
*bytes = proxyid;
}
else if (hdr->format == ICALL_MSG_FORMAT_3RD_CHAR_TASK_ID)
{
uint8 *bytes = msg;
bytes[2] = proxyid;
}
/* now queue the message to the OSAL queue */
osal_msg_send(dest_id, msg);
}
}
#endif /* USE_ICALL */
do {
if (tasksEvents[idx]) // Task is highest priority that is ready.
{
break;
}
} while (++idx < tasksCnt);
if (idx < tasksCnt)
{
uint16 events;
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState);
events = tasksEvents[idx];
tasksEvents[idx] = 0; // Clear the Events for this task.
HAL_EXIT_CRITICAL_SECTION(intState);
activeTaskID = idx;
events = (tasksArr[idx])( idx, events );
activeTaskID = TASK_NO_TASK;
HAL_ENTER_CRITICAL_SECTION(intState);
tasksEvents[idx] |= events; // Add back unprocessed events to the current task.
HAL_EXIT_CRITICAL_SECTION(intState);
}
#if defined( POWER_SAVING ) && !defined(USE_ICALL)
else // Complete pass through all task events with no activity?
{
osal_pwrmgr_powerconserve(); // Put the processor/system into sleep
}
#endif
/* Yield in case cooperative scheduling is being used. */
#if defined (configUSE_PREEMPTION) && (configUSE_PREEMPTION == 0)
{
osal_task_yield();
}
#endif
#if defined USE_ICALL
/* Note that scheduling wakeup at this point instead of
* scheduling it upon ever OSAL start timer request,
* would only work if OSAL start timer call is made
* from OSAL tasks, but not from either ISR or
* non-OSAL application thread.
* In case, OSAL start timer is called from non-OSAL
* task, the scheduling should be part of OSAL_Timers
* module.
* Such a change to OSAL_Timers module was not made
* in order not to diverge the OSAL implementations
* too drastically between pure OSAL solution vs.
* OSAL upon service dispatcher (RTOS).
* TODO: reconsider the above statement.
*/
{
halIntState_t intState;
uint32 next_timeout_post = osal_next_timeout();
if (next_timeout_post != next_timeout_prior)
{
/* Next wakeup time has to be scheduled */
if (next_timeout_post == 0)
{
/* No timer. Set time to the max */
next_timeout_post = OSAL_TIMERS_MAX_TIMEOUT;
}
if (next_timeout_post > osal_max_msecs)
{
next_timeout_post = osal_max_msecs;
}
/* Restart timer */
HAL_ENTER_CRITICAL_SECTION(intState);
ICall_stopTimer(osal_timerid_msec_timer);
ICall_setTimerMSecs(next_timeout_post, osal_msec_timer_cback,
(void *) (++osal_msec_timer_seq),
&osal_timerid_msec_timer);
HAL_EXIT_CRITICAL_SECTION(intState);
}
}
#endif /* USE_ICALL */
}
/*********************************************************************
* @fn osal_buffer_uint32
*
* @brief
*
* Buffer an uint32 value - LSB first.
*
* @param buf - buffer
* @param val - uint32 value
*
* @return pointer to end of destination buffer
*/
uint8* osal_buffer_uint32( uint8 *buf, uint32 val )
{
*buf++ = BREAK_UINT32( val, 0 );
*buf++ = BREAK_UINT32( val, 1 );
*buf++ = BREAK_UINT32( val, 2 );
*buf++ = BREAK_UINT32( val, 3 );
return buf;
}
/*********************************************************************
* @fn osal_buffer_uint24
*
* @brief
*
* Buffer an uint24 value - LSB first. Note that type uint24 is
* typedef to uint32 in comdef.h
*
* @param buf - buffer
* @param val - uint24 value
*
* @return pointer to end of destination buffer
*/
uint8* osal_buffer_uint24( uint8 *buf, uint24 val )
{
*buf++ = BREAK_UINT32( val, 0 );
*buf++ = BREAK_UINT32( val, 1 );
*buf++ = BREAK_UINT32( val, 2 );
return buf;
}
/*********************************************************************
* @fn osal_isbufset
*
* @brief
*
* Is all of the array elements set to a value?
*
* @param buf - buffer to check
* @param val - value to check each array element for
* @param len - length to check
*
* @return TRUE if all "val"
* FALSE otherwise
*/
uint8 osal_isbufset( uint8 *buf, uint8 val, uint8 len )
{
uint8 x;
if ( buf == NULL )
{
return ( FALSE );
}
for ( x = 0; x < len; x++ )
{
// Check for non-initialized value
if ( buf[x] != val )
{
return ( FALSE );
}
}
return ( TRUE );
}
/*********************************************************************
* @fn osal_self
*
* @brief
*
* This function returns the task ID of the current (active) task.
*
* @param void
*
* @return active task ID or TASK_NO_TASK if no task is active
*/
uint8 osal_self( void )
{
return ( activeTaskID );
}
/*********************************************************************
*/