OSAL(协议栈操作系统)
comdef.h
#ifndef COMDEF_H
#define COMDEF_H
#ifdef __cplusplus
extern "C"
{
#endif
/* HAL */
#include "hal_types.h"
#include "hal_defs.h"
#define VOID (void)
#define NULL_OK
#define INP
#define OUTP
#define UNUSED
#define ONLY
#define READONLY
#define SHARED
#define KEEP
#define RELAX
#ifndef false
#define false 0
#endif
#ifndef true
#define true 1
#endif
#ifndef CONST
#define CONST const
#endif
#ifndef GENERIC
#define GENERIC
#endif
/*** Generic Status Return Values ***/
#define SUCCESS 0x00
#define FAILURE 0x01
#define INVALIDPARAMETER 0x02
#define INVALID_TASK 0x03
#define MSG_BUFFER_NOT_AVAIL 0x04
#define INVALID_MSG_POINTER 0x05
#define INVALID_EVENT_ID 0x06
#define INVALID_INTERRUPT_ID 0x07
#define NO_TIMER_AVAIL 0x08
#define NV_ITEM_UNINIT 0x09
#define NV_OPER_FAILED 0x0A
#define INVALID_MEM_SIZE 0x0B
#define NV_BAD_ITEM_LEN 0x0C
// Generic Status return
typedef uint8 Status_t;
// Data types
typedef int32 int24;
typedef uint32 uint24;
#define SYS_EVENT_MSG 0x8000 // A message is waiting event
#define KEY_CHANGE 0xC0 // Key Events
#ifdef __cplusplus
}
#endif
#endif
OSAL.c
#include <string.h>
#include "comdef.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
// Message Pool Definitions
osal_msg_q_t osal_qHead;
// Index of active task
static uint8 activeTaskID = TASK_NO_TASK;
/* very ugly stub so Keil can compile */
#ifdef __KEIL__
char * itoa ( int value, char * buffer, int radix )
{
return(buffer);
}
#endif
int osal_strlen( char *pString )
{
return (int)( strlen( pString ) );
}
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 );
}
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 );
}
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 );
}
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;
}
void *osal_memset( void *dest, uint8 value, int len )
{
return memset( dest, value, len );
}
uint16 osal_build_uint16( uint8 *swapped )
{
return ( BUILD_UINT16( swapped[0], swapped[1] ) );
}
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)
unsigned char * _ltoa(unsigned long l, unsigned char *buf, unsigned char radix)
{
#if 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)
uint16 osal_rand( void )
{
return ( Onboard_rand() );
}
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 );
}
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 );
}
uint8 osal_msg_send( uint8 destination_task, uint8 *msg_ptr )
{
if ( msg_ptr == NULL )
return ( INVALID_MSG_POINTER );
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;
// queue 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 );
}
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 );
}
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;
}
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);
}
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;
}
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);
}
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);
}
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;
}
uint8 osal_set_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; // Stuff the event bit(s)
HAL_EXIT_CRITICAL_SECTION(intState); // Release interrupts
return ( SUCCESS );
}
else
{
return ( 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 );
}
}
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 );
}
uint8 osal_int_enable( uint8 interrupt_id )
{
if ( interrupt_id == INTS_ALL )
{
HAL_ENABLE_INTERRUPTS();
return ( SUCCESS );
}
else
{
return ( 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 );
}
}
uint8 osal_init_system( void )
{
// Initialize the Memory Allocation System
osal_mem_init();
// Initialize the message queue
osal_qHead = NULL;
// Initialize the timers
osalTimerInit();
// Initialize the Power Management System
osal_pwrmgr_init();
// Initialize the system tasks.
osalInitTasks();
// Setup efficient search for the first free block of heap.
osal_mem_kick();
return ( SUCCESS );
}
void osal_start_system( void )
{
#if !defined ( ZBIT ) && !defined ( UBIT )
for(;;) // Forever Loop
#endif
{
osal_run_system();
}
}
void osal_run_system( void )
{
uint8 idx = 0;
osalTimeUpdate();
Hal_ProcessPoll();
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 )
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
}
/
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;
}
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;
}
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 );
}
uint8 osal_self( void )
{
return ( activeTaskID );
}
OSAL.h
#ifndef OSAL_H
#define OSAL_H
#ifdef __cplusplus
extern "C"
{
#endif
#include "comdef.h"
#include "OSAL_Memory.h"
#include "OSAL_Timers.h"
#if ( UINT_MAX == 65535 ) /* 8-bit and 16-bit devices */
#define osal_offsetof(type, member) ((uint16) &(((type *) 0)->member))
#else /* 32-bit devices */
#define osal_offsetof(type, member) ((uint32) &(((type *) 0)->member))
#endif
#define OSAL_MSG_NEXT(msg_ptr) ((osal_msg_hdr_t *) (msg_ptr) - 1)->next
#define OSAL_MSG_Q_INIT(q_ptr) *(q_ptr) = NULL
#define OSAL_MSG_Q_EMPTY(q_ptr) (*(q_ptr) == NULL)
#define OSAL_MSG_Q_HEAD(q_ptr) (*(q_ptr))
#define OSAL_MSG_LEN(msg_ptr) ((osal_msg_hdr_t *) (msg_ptr) - 1)->len
#define OSAL_MSG_ID(msg_ptr) ((osal_msg_hdr_t *) (msg_ptr) - 1)->dest_id
/*** Interrupts ***/
#define INTS_ALL 0xFF
typedef struct
{
void *next;
uint16 len;
uint8 dest_id;
} osal_msg_hdr_t;
typedef struct
{
uint8 event;
uint8 status;
} osal_event_hdr_t;
typedef void * osal_msg_q_t;
extern uint8 * osal_msg_allocate(uint16 len );
extern uint8 osal_msg_deallocate( uint8 *msg_ptr );
extern uint8 osal_msg_send( uint8 destination_task, uint8 *msg_ptr );
extern uint8 *osal_msg_receive( uint8 task_id );
extern osal_event_hdr_t *osal_msg_find(uint8 task_id, uint8 event);
extern void osal_msg_enqueue( osal_msg_q_t *q_ptr, void *msg_ptr );
extern uint8 osal_msg_enqueue_max( osal_msg_q_t *q_ptr, void *msg_ptr, uint8 max );
extern void *osal_msg_dequeue( osal_msg_q_t *q_ptr );
extern void osal_msg_push( osal_msg_q_t *q_ptr, void *msg_ptr );
extern void osal_msg_extract( osal_msg_q_t *q_ptr, void *msg_ptr, void *prev_ptr );
extern uint8 osal_set_event( uint8 task_id, uint16 event_flag );
extern uint8 osal_clear_event( uint8 task_id, uint16 event_flag );
extern uint8 osal_isr_register( uint8 interrupt_id, void (*isr_ptr)( uint8* ) );
extern uint8 osal_int_enable( uint8 interrupt_id );
extern uint8 osal_int_disable( uint8 interrupt_id );
extern uint8 osal_init_system( void );
#if defined (ZBIT)
extern __declspec(dllexport) void osal_start_system( void );
#else
extern void osal_start_system( void );
#endif
extern void osal_run_system( void );
extern uint8 osal_self( void );
extern int osal_strlen( char *pString );
extern void *osal_memcpy( void*, const void GENERIC *, unsigned int );
extern void *osal_memdup( const void GENERIC *src, unsigned int len );
extern void *osal_revmemcpy( void*, const void GENERIC *, unsigned int );
extern uint8 osal_memcmp( const void GENERIC *src1, const void GENERIC *src2, unsigned int len );
extern void *osal_memset( void *dest, uint8 value, int len );
extern uint16 osal_build_uint16( uint8 *swapped );
extern uint32 osal_build_uint32( uint8 *swapped, uint8 len );
#if !defined ( ZBIT ) && !defined ( ZBIT2 ) && !defined (UBIT)
extern uint8 *_ltoa( uint32 l, uint8 * buf, uint8 radix );
#endif
extern uint16 osal_rand( void );
extern uint8* osal_buffer_uint32( uint8 *buf, uint32 val );
extern uint8* osal_buffer_uint24( uint8 *buf, uint24 val );
extern uint8 osal_isbufset( uint8 *buf, uint8 val, uint8 len );
#ifdef __cplusplus
}
#endif
#endif
OSAL_Clock.c
#include "comdef.h"
#include "OnBoard.h"
#include "OSAL.h"
#include "OSAL_Clock.h"
#define YearLength(yr) ((uint16)(IsLeapYear(yr) ? 366 : 365))
#define BEGYEAR 2000 // UTC started at 00:00:00 January 1, 2000
#define DAY 86400UL // 24 hours * 60 minutes * 60 seconds
extern uint32 macMcuPrecisionCount(void);
#if (defined HAL_MCU_CC2430) || (defined HAL_MCU_CC2530) || (defined HAL_MCU_CC2533)
extern __near_func uint32 osalMcuDivide31By16To16( uint32 dividend, uint16 divisor );
#define CONVERT_320US_TO_MS_ELAPSED_REMAINDER( x, y, z ) st( \
\
/* The 16 bit quotient is in MSW and */ \
/* the 16 bit remainder is in LSW. */ \
x = osalMcuDivide31By16To16( x, 25 ); \
\
/* Add quotient to y */ \
y += (x >> 16); \
\
/* Copy remainder to z */ \
z = (uint16)(x & 0x0FFFF); \
)
#else /* (defined HAL_MCU_CC2430) || (defined HAL_MCU_CC2530) || (defined HAL_MCU_CC2533) */
#define CONVERT_320US_TO_MS_ELAPSED_REMAINDER( x, y, z ) st( \
y += x / 25; \
z = x % 25; \
)
#endif /* (defined HAL_MCU_CC2430) || (defined HAL_MCU_CC2530) || (defined HAL_MCU_CC2533) */
static uint32 previousMacTimerTick = 0;
static uint16 remUsTicks = 0;
static uint16 timeMSec = 0;
// number of seconds since 0 hrs, 0 minutes, 0 seconds, on the
// 1st of January 2000 UTC
UTCTime OSAL_timeSeconds = 0;
static uint8 monthLength( uint8 lpyr, uint8 mon );
static void osalClockUpdate( uint16 elapsedMSec );
void osalTimeUpdate( void )
{
halIntState_t intState;
uint32 tmp;
uint32 ticks320us;
uint16 elapsedMSec = 0;
HAL_ENTER_CRITICAL_SECTION(intState);
// Get the free-running count of 320us timer ticks
tmp = macMcuPrecisionCount();
HAL_EXIT_CRITICAL_SECTION(intState);
if ( tmp != previousMacTimerTick )
{
// Calculate the elapsed ticks of the free-running timer.
ticks320us = (tmp - previousMacTimerTick) & 0xffffffffu;
// Store the MAC Timer tick count for the next time through this function.
previousMacTimerTick = tmp;
// update converted number with remaining ticks from loop and the
// accumulated remainder from loop
tmp = (ticks320us * 8) + remUsTicks;
// Convert the 320 us ticks into milliseconds and a remainder
CONVERT_320US_TO_MS_ELAPSED_REMAINDER( tmp, elapsedMSec, remUsTicks );
// Update OSAL Clock and Timers
if ( elapsedMSec )
{
osalClockUpdate( elapsedMSec );
osalTimerUpdate( elapsedMSec );
}
}
}
static void osalClockUpdate( uint16 elapsedMSec )
{
// Add elapsed milliseconds to the saved millisecond portion of time
timeMSec += elapsedMSec;
// Roll up milliseconds to the number of seconds
if ( timeMSec >= 1000 )
{
OSAL_timeSeconds += timeMSec / 1000;
timeMSec = timeMSec % 1000;
}
}
void osal_setClock( UTCTime newTime )
{
OSAL_timeSeconds = newTime;
}
UTCTime osal_getClock( void )
{
return ( OSAL_timeSeconds );
}
void osal_ConvertUTCTime( UTCTimeStruct *tm, UTCTime secTime )
{
// calculate the time less than a day - hours, minutes, seconds
{
uint32 day = secTime % DAY;
tm->seconds = day % 60UL;
tm->minutes = (day % 3600UL) / 60UL;
tm->hour = day / 3600UL;
}
// Fill in the calendar - day, month, year
{
uint16 numDays = secTime / DAY;
tm->year = BEGYEAR;
while ( numDays >= YearLength( tm->year ) )
{
numDays -= YearLength( tm->year );
tm->year++;
}
tm->month = 0;
while ( numDays >= monthLength( IsLeapYear( tm->year ), tm->month ) )
{
numDays -= monthLength( IsLeapYear( tm->year ), tm->month );
tm->month++;
}
tm->day = numDays;
}
}
static uint8 monthLength( uint8 lpyr, uint8 mon )
{
uint8 days = 31;
if ( mon == 1 ) // feb
{
days = ( 28 + lpyr );
}
else
{
if ( mon > 6 ) // aug-dec
{
mon--;
}
if ( mon & 1 )
{
days = 30;
}
}
return ( days );
}
UTCTime osal_ConvertUTCSecs( UTCTimeStruct *tm )
{
uint32 seconds;
/* Seconds for the partial day */
seconds = (((tm->hour * 60UL) + tm->minutes) * 60UL) + tm->seconds;
/* Account for previous complete days */
{
/* Start with complete days in current month */
uint16 days = tm->day;
/* Next, complete months in current year */
{
int8 month = tm->month;
while ( --month >= 0 )
{
days += monthLength( IsLeapYear( tm->year ), month );
}
}
/* Next, complete years before current year */
{
uint16 year = tm->year;
while ( --year >= BEGYEAR )
{
days += YearLength( year );
}
}
/* Add total seconds before partial day */
seconds += (days * DAY);
}
return ( seconds );
}
OSAL_Clock.h
#ifndef OSAL_CLOCK_H
#define OSAL_CLOCK_H
#ifdef __cplusplus
extern "C"
{
#endif
#define IsLeapYear(yr) (!((yr) % 400) || (((yr) % 100) && !((yr) % 4)))
// number of seconds since 0 hrs, 0 minutes, 0 seconds, on the
// 1st of January 2000 UTC
typedef uint32 UTCTime;
// To be used with
typedef struct
{
uint8 seconds; // 0-59
uint8 minutes; // 0-59
uint8 hour; // 0-23
uint8 day; // 0-30
uint8 month; // 0-11
uint16 year; // 2000+
} UTCTimeStruct;
extern void osalTimeUpdate( void );
extern void osal_setClock( UTCTime newTime );
extern UTCTime osal_getClock( void );
extern void osal_ConvertUTCTime( UTCTimeStruct *tm, UTCTime secTime );
extern UTCTime osal_ConvertUTCSecs( UTCTimeStruct *tm );
#ifdef __cplusplus
}
#endif
#endif /* OSAL_CLOCK_H */
OSAL_Math.s51
MODULE OSAL_Math.s51
PUBLIC osalMcuDivide31By16To16
FUNCTION osalMcuDivide31By16To16,0201H
EXTERN ?XSP
RSEG DATA_Z:DATA:NOROOT(0)
savedR6:
DS 1
savedR7:
DS 1
savedRemHi:
DS 1
savedRemLow:
DS 1
savedDPH:
DS 1
savedDPL:
DS 1
RSEG NEAR_CODE:CODE:NOROOT(2)
osalMcuDivide31By16To16:
#ifdef DEBUG_GPIO
//SETB P0.1
#endif
// initialize loop counter
MOV B,#16
// save preserved registers R6 and R7
MOV savedR6,R6
MOV savedR7,R7
// save DPTR (which ever one is currently selected)
MOV savedDPL,DPL
MOV savedDPH,DPH
// get divisor from the XDATA stack
MOV DPL,?XSP
MOV DPH,?XSP+1
MOVX A,@DPTR
XCH A,R6
INC DPTR
MOVX A,@DPTR
XCH A,R7
// restore DPTR
MOV DPL,savedDPL
MOV DPH,savedDPH
// move lower dividend R3:R2 to R1:R0
MOV A,R3
XCH A,R1
MOV A,R2
XCH A,R0
// move upper dividend R5:R4 to what will be the remainder R3:R2
MOV A,R5
XCH A,R3
MOV A,R4
XCH A,R2
TopOfLoop:
// Step 1: Shift dividend and remainder left, feeding dividend into remainder.
CLR C
MOV A,R0
RLC A
XCH A,R0
MOV A,R1
RLC A
XCH A,R1
MOV A,R2
RLC A
XCH A,R2
MOV A,R3
RLC A
XCH A,R3
// Step 2: Subtract divisor from remainder. If there is a borrow, then restore
// the remainder.
CLR C
MOV A,R2
SUBB A,R6
MOV savedRemLow,A ; save remainder in case it needs to be restored
MOV A,R3
SUBB A,R7
MOV savedRemHi,A ; save remainder in case it needs to be restored
// Step 3: Shift quotient left, feeding complement of borrow bit
// Note: Remainder is left unchanged if there was a borrow.
CPL C
JNC ShiftQuotient
//
MOV R3,savedRemHi
MOV R2,savedRemLow
ShiftQuotient:
MOV A,R4
RLC A
XCH A,R4
MOV A,R5
RLC A
XCH A,R5
// Step 4: Decrement count and loop if not zero.
DJNZ B,TopOfLoop
// restore R6 and R7
MOV R6,savedR6
MOV R7,savedR7
#ifdef DEBUG_GPIO
//CLR P0.1
#endif
RET
END
OSAL_Memory.c
#include "comdef.h"
#include "OSAL.h"
#include "OSAL_Memory.h"
#include "OnBoard.h"
#include "hal_assert.h"
#define OSALMEM_IN_USE 0x8000
#if (MAXMEMHEAP & OSALMEM_IN_USE)
#error MAXMEMHEAP is too big to manage!
#endif
#define OSALMEM_HDRSZ sizeof(osalMemHdr_t)
// Round a value up to the ceiling of OSALMEM_HDRSZ for critical dependencies on even multiples.
#define OSALMEM_ROUND(X) ((((X) + OSALMEM_HDRSZ - 1) / OSALMEM_HDRSZ) * OSALMEM_HDRSZ)
/* Minimum wasted bytes to justify splitting a block before allocation.
* Adjust accordingly to attempt to balance the tradeoff of wasted space and runtime throughput
* spent splitting blocks into sizes that may not be practically usable when sandwiched between
* two blocks in use (and thereby not able to be coalesced.)
* Ensure that this size is an even multiple of OSALMEM_HDRSZ.
*/
#if !defined OSALMEM_MIN_BLKSZ
#define OSALMEM_MIN_BLKSZ (OSALMEM_ROUND((OSALMEM_HDRSZ * 2)))
#endif
#if !defined OSALMEM_LL_BLKSZ
#if defined NONWK
#define OSALMEM_LL_BLKSZ (OSALMEM_ROUND(6) + (1 * OSALMEM_HDRSZ))
#else
/*
* Profiling the sample apps with default settings shows the following long-lived allocations
* which should live at the bottom of the small-block bucket so that they are never iterated over
* by osal_mem_alloc/free(), nor ever considered for coalescing, etc. This saves significant
* run-time throughput (on 8051 SOC if not also MSP). This is dynamic "dead space" and is not
* available to the small-block bucket heap.
*
* Adjust this size accordingly to accomodate application-specific changes including changing the
* size of long-lived objects profiled by sample apps and long-lived objects added by application.
*/
#if defined ZCL_KEY_ESTABLISH // Attempt to capture worst-case for SE sample apps.
#define OSALMEM_LL_BLKSZ (OSALMEM_ROUND(526) + (32 * OSALMEM_HDRSZ))
#elif defined TC_LINKKEY_JOIN
#define OSALMEM_LL_BLKSZ (OSALMEM_ROUND(454) + (21 * OSALMEM_HDRSZ))
#elif ((defined SECURE) && (SECURE != 0))
#define OSALMEM_LL_BLKSZ (OSALMEM_ROUND(418) + (19 * OSALMEM_HDRSZ))
#else
#define OSALMEM_LL_BLKSZ (OSALMEM_ROUND(417) + (19 * OSALMEM_HDRSZ))
#endif
#endif
#endif
/* Adjust accordingly to attempt to accomodate the block sizes of the vast majority of
* very high frequency allocations/frees by profiling the system runtime.
* This default of 16 accomodates the OSAL timers block, osalTimerRec_t, and many others.
* Ensure that this size is an even multiple of OSALMEM_MIN_BLKSZ for run-time efficiency.
*/
#if !defined OSALMEM_SMALL_BLKSZ
#define OSALMEM_SMALL_BLKSZ (OSALMEM_ROUND(16))
#endif
#if !defined OSALMEM_SMALL_BLKCNT
#define OSALMEM_SMALL_BLKCNT 8
#endif
/*
* These numbers setup the size of the small-block bucket which is reserved at the front of the
* heap for allocations of OSALMEM_SMALL_BLKSZ or smaller.
*/
// Size of the heap bucket reserved for small block-sized allocations.
// Adjust accordingly to attempt to accomodate the vast majority of very high frequency operations.
#define OSALMEM_SMALLBLK_BUCKET ((OSALMEM_SMALL_BLKSZ * OSALMEM_SMALL_BLKCNT) + OSALMEM_LL_BLKSZ)
// Index of the first available osalMemHdr_t after the small-block heap which will be set in-use in
// order to prevent the small-block bucket from being coalesced with the wilderness.
#define OSALMEM_SMALLBLK_HDRCNT (OSALMEM_SMALLBLK_BUCKET / OSALMEM_HDRSZ)
// Index of the first available osalMemHdr_t after the small-block heap which will be set in-use in
#define OSALMEM_BIGBLK_IDX (OSALMEM_SMALLBLK_HDRCNT + 1)
// The size of the wilderness after losing the small-block heap, the wasted header to block the
// small-block heap from being coalesced, and the wasted header to mark the end of the heap.
#define OSALMEM_BIGBLK_SZ (MAXMEMHEAP - OSALMEM_SMALLBLK_BUCKET - OSALMEM_HDRSZ*2)
// Index of the last available osalMemHdr_t at the end of the heap which will be set to zero for
// fast comparisons with zero to determine the end of the heap.
#define OSALMEM_LASTBLK_IDX ((MAXMEMHEAP / OSALMEM_HDRSZ) - 1)
// For information about memory profiling, refer to SWRA204 "Heap Memory Management", section 1.5.
#if !defined OSALMEM_PROFILER
#define OSALMEM_PROFILER FALSE // Enable/disable the memory usage profiling buckets.
#endif
#if !defined OSALMEM_PROFILER_LL
#define OSALMEM_PROFILER_LL FALSE // Special profiling of the Long-Lived bucket.
#endif
#if OSALMEM_PROFILER
#define OSALMEM_INIT 'X'
#define OSALMEM_ALOC 'A'
#define OSALMEM_REIN 'F'
#endif
typedef struct {
// The 15 LSB's of 'val' indicate the total item size, including the header, in 8-bit bytes.
unsigned len : 15;
// The 1 MSB of 'val' is used as a boolean to indicate in-use or freed.
unsigned inUse : 1;
} osalMemHdrHdr_t;
typedef union {
/* Dummy variable so compiler forces structure to alignment of largest element while not wasting
* space on targets when the halDataAlign_t is smaller than a UINT16.
*/
halDataAlign_t alignDummy;
uint16 val;
osalMemHdrHdr_t hdr;
} osalMemHdr_t;
static __no_init osalMemHdr_t theHeap[MAXMEMHEAP / OSALMEM_HDRSZ];
static __no_init osalMemHdr_t *ff1; // First free block in the small-block bucket.
static uint8 osalMemStat; // Discrete status flags: 0x01 = kicked.
#if OSALMEM_METRICS
static uint16 blkMax; // Max cnt of all blocks ever seen at once.
static uint16 blkCnt; // Current cnt of all blocks.
static uint16 blkFree; // Current cnt of free blocks.
static uint16 memAlo; // Current total memory allocated.
static uint16 memMax; // Max total memory ever allocated at once.
#endif
#if OSALMEM_PROFILER
#define OSALMEM_PROMAX 8
/* The profiling buckets must differ by at least OSALMEM_MIN_BLKSZ; the
* last bucket must equal the max alloc size. Set the bucket sizes to
* whatever sizes necessary to show how your application is using memory.
*/
static uint16 proCnt[OSALMEM_PROMAX] = {
OSALMEM_SMALL_BLKSZ, 48, 112, 176, 192, 224, 256, 65535 };
static uint16 proCur[OSALMEM_PROMAX] = { 0 };
static uint16 proMax[OSALMEM_PROMAX] = { 0 };
static uint16 proTot[OSALMEM_PROMAX] = { 0 };
static uint16 proSmallBlkMiss;
#endif
#ifdef DPRINTF_HEAPTRACE
extern int dprintf(const char *fmt, ...);
#endif /* DPRINTF_HEAPTRACE */
void osal_mem_init(void)
{
HAL_ASSERT(((OSALMEM_MIN_BLKSZ % OSALMEM_HDRSZ) == 0));
HAL_ASSERT(((OSALMEM_LL_BLKSZ % OSALMEM_HDRSZ) == 0));
HAL_ASSERT(((OSALMEM_SMALL_BLKSZ % OSALMEM_HDRSZ) == 0));
#if OSALMEM_PROFILER
(void)osal_memset(theHeap, OSALMEM_INIT, MAXMEMHEAP);
#endif
// Setup a NULL block at the end of the heap for fast comparisons with zero.
theHeap[OSALMEM_LASTBLK_IDX].val = 0;
// Setup the small-block bucket.
ff1 = theHeap;
ff1->val = OSALMEM_SMALLBLK_BUCKET; // Set 'len' & clear 'inUse' field.
// Set 'len' & 'inUse' fields - this is a 'zero data bytes' lifetime allocation to block the
// small-block bucket from ever being coalesced with the wilderness.
theHeap[OSALMEM_SMALLBLK_HDRCNT].val = (OSALMEM_HDRSZ | OSALMEM_IN_USE);
// Setup the wilderness.
theHeap[OSALMEM_BIGBLK_IDX].val = OSALMEM_BIGBLK_SZ; // Set 'len' & clear 'inUse' field.
#if ( OSALMEM_METRICS )
/* Start with the small-block bucket and the wilderness - don't count the
* end-of-heap NULL block nor the end-of-small-block NULL block.
*/
blkCnt = blkFree = 2;
#endif
}
void osal_mem_kick(void)
{
halIntState_t intState;
osalMemHdr_t *tmp = osal_mem_alloc(1);
HAL_ASSERT((tmp != NULL));
HAL_ENTER_CRITICAL_SECTION(intState); // Hold off interrupts.
/* All long-lived allocations have filled the LL block reserved in the small-block bucket.
* Set 'osalMemStat' so searching for memory in this bucket from here onward will only be done
* for sizes meeting the OSALMEM_SMALL_BLKSZ criteria.
*/
ff1 = tmp - 1; // Set 'ff1' to point to the first available memory after the LL block.
osal_mem_free(tmp);
osalMemStat = 0x01; // Set 'osalMemStat' after the free because it enables memory profiling.
HAL_EXIT_CRITICAL_SECTION(intState); // Re-enable interrupts.
}
#ifdef DPRINTF_OSALHEAPTRACE
void *osal_mem_alloc_dbg( uint16 size, const char *fname, unsigned lnum )
#else /* DPRINTF_OSALHEAPTRACE */
void *osal_mem_alloc( uint16 size )
#endif /* DPRINTF_OSALHEAPTRACE */
{
osalMemHdr_t *prev = NULL;
osalMemHdr_t *hdr;
halIntState_t intState;
uint8 coal = 0;
size += OSALMEM_HDRSZ;
// Calculate required bytes to add to 'size' to align to halDataAlign_t.
if ( sizeof( halDataAlign_t ) == 2 )
{
size += (size & 0x01);
}
else if ( sizeof( halDataAlign_t ) != 1 )
{
const uint8 mod = size % sizeof( halDataAlign_t );
if ( mod != 0 )
{
size += (sizeof( halDataAlign_t ) - mod);
}
}
HAL_ENTER_CRITICAL_SECTION( intState ); // Hold off interrupts.
// Smaller allocations are first attempted in the small-block bucket, and all long-lived
// allocations are channeled into the LL block reserved within this bucket.
if ((osalMemStat == 0) || (size <= OSALMEM_SMALL_BLKSZ))
{
hdr = ff1;
}
else
{
hdr = (theHeap + OSALMEM_BIGBLK_IDX);
}
do
{
if ( hdr->hdr.inUse )
{
coal = 0;
}
else
{
if ( coal != 0 )
{
#if ( OSALMEM_METRICS )
blkCnt--;
blkFree--;
#endif
prev->hdr.len += hdr->hdr.len;
if ( prev->hdr.len >= size )
{
hdr = prev;
break;
}
}
else
{
if ( hdr->hdr.len >= size )
{
break;
}
coal = 1;
prev = hdr;
}
}
hdr = (osalMemHdr_t *)((uint8 *)hdr + hdr->hdr.len);
if ( hdr->val == 0 )
{
hdr = NULL;
break;
}
} while (1);
if ( hdr != NULL )
{
uint16 tmp = hdr->hdr.len - size;
// Determine whether the threshold for splitting is met.
if ( tmp >= OSALMEM_MIN_BLKSZ )
{
// Split the block before allocating it.
osalMemHdr_t *next = (osalMemHdr_t *)((uint8 *)hdr + size);
next->val = tmp; // Set 'len' & clear 'inUse' field.
hdr->val = (size | OSALMEM_IN_USE); // Set 'len' & 'inUse' field.
#if ( OSALMEM_METRICS )
blkCnt++;
if ( blkMax < blkCnt )
{
blkMax = blkCnt;
}
memAlo += size;
#endif
}
else
{
#if ( OSALMEM_METRICS )
memAlo += hdr->hdr.len;
blkFree--;
#endif
hdr->hdr.inUse = TRUE;
}
#if ( OSALMEM_METRICS )
if ( memMax < memAlo )
{
memMax = memAlo;
}
#endif
#if ( OSALMEM_PROFILER )
#if !OSALMEM_PROFILER_LL
if (osalMemStat != 0) // Don't profile until after the LL block is filled.
#endif
{
uint8 idx;
for ( idx = 0; idx < OSALMEM_PROMAX; idx++ )
{
if ( hdr->hdr.len <= proCnt[idx] )
{
break;
}
}
proCur[idx]++;
if ( proMax[idx] < proCur[idx] )
{
proMax[idx] = proCur[idx];
}
proTot[idx]++;
/* A small-block could not be allocated in the small-block bucket.
* When this occurs significantly frequently, increase the size of the
* bucket in order to restore better worst case run times. Set the first
* profiling bucket size in proCnt[] to the small-block bucket size and
* divide proSmallBlkMiss by the corresponding proTot[] size to get % miss.
* Best worst case time on TrasmitApp was achieved at a 0-15% miss rate
* during steady state Tx load, 0% during idle and steady state Rx load.
*/
if ((hdr->hdr.len <= OSALMEM_SMALL_BLKSZ) && (hdr >= (theHeap + OSALMEM_BIGBLK_IDX)))
{
proSmallBlkMiss++;
}
}
(void)osal_memset((uint8 *)(hdr+1), OSALMEM_ALOC, (hdr->hdr.len - OSALMEM_HDRSZ));
#endif
if ((osalMemStat != 0) && (ff1 == hdr))
{
ff1 = (osalMemHdr_t *)((uint8 *)hdr + hdr->hdr.len);
}
hdr++;
}
HAL_EXIT_CRITICAL_SECTION( intState ); // Re-enable interrupts.
#pragma diag_suppress=Pe767
HAL_ASSERT(((halDataAlign_t)hdr % sizeof(halDataAlign_t)) == 0);
#pragma diag_default=Pe767
#ifdef DPRINTF_OSALHEAPTRACE
dprintf("osal_mem_alloc(%u)->%lx:%s:%u\n", size, (unsigned) hdr, fname, lnum);
#endif /* DPRINTF_OSALHEAPTRACE */
return (void *)hdr;
}
#ifdef DPRINTF_OSALHEAPTRACE
void osal_mem_free_dbg(void *ptr, const char *fname, unsigned lnum)
#else /* DPRINTF_OSALHEAPTRACE */
void osal_mem_free(void *ptr)
#endif /* DPRINTF_OSALHEAPTRACE */
{
osalMemHdr_t *hdr = (osalMemHdr_t *)ptr - 1;
halIntState_t intState;
#ifdef DPRINTF_OSALHEAPTRACE
dprintf("osal_mem_free(%lx):%s:%u\n", (unsigned) ptr, fname, lnum);
#endif /* DPRINTF_OSALHEAPTRACE */
HAL_ASSERT(((uint8 *)ptr >= (uint8 *)theHeap) && ((uint8 *)ptr < (uint8 *)theHeap+MAXMEMHEAP));
HAL_ASSERT(hdr->hdr.inUse);
HAL_ENTER_CRITICAL_SECTION( intState ); // Hold off interrupts.
hdr->hdr.inUse = FALSE;
if (ff1 > hdr)
{
ff1 = hdr;
}
#if OSALMEM_PROFILER
#if !OSALMEM_PROFILER_LL
if (osalMemStat != 0) // Don't profile until after the LL block is filled.
#endif
{
uint8 idx;
for (idx = 0; idx < OSALMEM_PROMAX; idx++)
{
if (hdr->hdr.len <= proCnt[idx])
{
break;
}
}
proCur[idx]--;
}
(void)osal_memset((uint8 *)(hdr+1), OSALMEM_REIN, (hdr->hdr.len - OSALMEM_HDRSZ) );
#endif
#if OSALMEM_METRICS
memAlo -= hdr->hdr.len;
blkFree++;
#endif
HAL_EXIT_CRITICAL_SECTION( intState ); // Re-enable interrupts.
}
#if OSALMEM_METRICS
uint16 osal_heap_block_max( void )
{
return blkMax;
}
uint16 osal_heap_block_cnt( void )
{
return blkCnt;
}
uint16 osal_heap_block_free( void )
{
return blkFree;
}
uint16 osal_heap_mem_used( void )
{
return memAlo;
}
#endif
#if defined (ZTOOL_P1) || defined (ZTOOL_P2)
uint16 osal_heap_high_water( void )
{
#if ( OSALMEM_METRICS )
return memMax;
#else
return MAXMEMHEAP;
#endif
}
#endif
OSAL_Memory.h
#ifndef OSAL_MEMORY_H
#define OSAL_MEMORY_H
#ifdef __cplusplus
extern "C"
{
#endif
#include "comdef.h"
#if !defined ( OSALMEM_METRICS )
#define OSALMEM_METRICS FALSE
#endif
#define osal_stack_used() OnBoard_stack_used()
void osal_mem_init( void );
void osal_mem_kick( void );
#ifdef DPRINTF_OSALHEAPTRACE
void *osal_mem_alloc_dbg( uint16 size, const char *fname, unsigned lnum );
#define osal_mem_alloc(_size ) osal_mem_alloc_dbg(_size, __FILE__, __LINE__)
#else /* DPRINTF_OSALHEAPTRACE */
void *osal_mem_alloc( uint16 size );
#endif /* DPRINTF_OSALHEAPTRACE */
/*
* Free a block of memory.
*/
#ifdef DPRINTF_OSALHEAPTRACE
void osal_mem_free_dbg( void *ptr, const char *fname, unsigned lnum );
#define osal_mem_free(_ptr ) osal_mem_free_dbg(_ptr, __FILE__, __LINE__)
#else /* DPRINTF_OSALHEAPTRACE */
void osal_mem_free( void *ptr );
#endif /* DPRINTF_OSALHEAPTRACE */
#if ( OSALMEM_METRICS )
/*
* Return the maximum number of blocks ever allocated at once.
*/
uint16 osal_heap_block_max( void );
/*
* Return the current number of blocks now allocated.
*/
uint16 osal_heap_block_cnt( void );
/*
* Return the current number of free blocks.
*/
uint16 osal_heap_block_free( void );
/*
* Return the current number of bytes allocated.
*/
uint16 osal_heap_mem_used( void );
#endif
#if defined (ZTOOL_P1) || defined (ZTOOL_P2)
/*
* Return the highest number of bytes ever used in the heap.
*/
uint16 osal_heap_high_water( void );
#endif
#ifdef __cplusplus
}
#endif
#endif /* #ifndef OSAL_MEMORY_H */
OSAL_Nv.c
#include "hal_adc.h"
#include "hal_flash.h"
#include "hal_types.h"
#include "OSAL_Nv.h"
#include "ZComDef.h"
#define OSAL_NV_PAGE_SIZE HAL_FLASH_PAGE_SIZE
#define OSAL_NV_PAGES_USED HAL_NV_PAGE_CNT
#define OSAL_NV_PAGE_BEG HAL_NV_PAGE_BEG
#define OSAL_NV_PAGE_END (OSAL_NV_PAGE_BEG + OSAL_NV_PAGES_USED - 1)
#define OSAL_NV_ACTIVE 0x00
#define OSAL_NV_ERASED 0xFF
#define OSAL_NV_ERASED_ID 0xFFFF
#define OSAL_NV_ZEROED_ID 0x0000
// Reserve MSB of Id to signal a search for the "old" source copy (new write interrupted/failed.)
#define OSAL_NV_SOURCE_ID 0x8000
// In case pages 0-1 are ever used, define a null page value.
#define OSAL_NV_PAGE_NULL 0
// In case item Id 0 is ever used, define a null item value.
#define OSAL_NV_ITEM_NULL 0
#define OSAL_NV_WORD_SIZE HAL_FLASH_WORD_SIZE
#define OSAL_NV_PAGE_HDR_OFFSET 0
#define OSAL_NV_MAX_HOT 3
static const uint16 hotIds[OSAL_NV_MAX_HOT] = {
ZCD_NV_NWKKEY,
ZCD_NV_NWK_ACTIVE_KEY_INFO,
ZCD_NV_NWK_ALTERN_KEY_INFO,
};
#define OSAL_NV_CHECK_BUS_VOLTAGE HalAdcCheckVdd(VDD_MIN_NV)
#define OSAL_NV_DATA_SIZE( LEN ) \
(((LEN) >= ((uint16)(65536UL - OSAL_NV_WORD_SIZE))) ? \
((uint16)(65536UL - OSAL_NV_WORD_SIZE)) : \
((((LEN) + OSAL_NV_WORD_SIZE - 1) / OSAL_NV_WORD_SIZE) * OSAL_NV_WORD_SIZE))
#define OSAL_NV_ITEM_SIZE( LEN ) \
(((LEN) >= ((uint16)(65536UL - OSAL_NV_WORD_SIZE - OSAL_NV_HDR_SIZE))) ? \
((uint16)(65536UL - OSAL_NV_WORD_SIZE)) : \
(((((LEN) + OSAL_NV_WORD_SIZE - 1) / OSAL_NV_WORD_SIZE) * OSAL_NV_WORD_SIZE) + OSAL_NV_HDR_SIZE))
#define COMPACT_PAGE_CLEANUP( COM_PG ) st ( \
/* In order to recover from a page compaction that is interrupted,\
* the logic in osal_nv_init() depends upon the following order:\
* 1. State of the target of compaction is changed to ePgInUse.\
* 2. Compacted page is erased.\
*/\
setPageUse( pgRes, TRUE ); /* Mark the reserve page as being in use. */\
erasePage( (COM_PG) ); \
\
pgRes = (COM_PG); /* Set the reserve page to be the newly erased page. */\
)
typedef struct
{
uint16 id;
uint16 len; // Enforce Flash-WORD size on len.
uint16 chk; // Byte-wise checksum of the 'len' data bytes of the item.
uint16 stat; // Item status.
} osalNvHdr_t;
// Struct member offsets.
#define OSAL_NV_HDR_ID 0
#define OSAL_NV_HDR_LEN 2
#define OSAL_NV_HDR_CHK 4
#define OSAL_NV_HDR_STAT 6
#define OSAL_NV_HDR_ITEM 2 // Length of any item of a header struct.
#define OSAL_NV_HDR_SIZE 8
#define OSAL_NV_HDR_HALF (OSAL_NV_HDR_SIZE / 2)
typedef struct
{
uint16 active;
uint16 inUse;
uint16 xfer;
uint16 spare;
} osalNvPgHdr_t;
// Struct member offsets.
#define OSAL_NV_PG_ACTIVE 0
#define OSAL_NV_PG_INUSE 2
#define OSAL_NV_PG_XFER 4
#define OSAL_NV_PG_SPARE 6
#define OSAL_NV_PAGE_HDR_SIZE 8
#define OSAL_NV_PAGE_HDR_HALF (OSAL_NV_PAGE_HDR_SIZE / 2)
typedef enum
{
eNvXfer,
eNvZero
} eNvHdrEnum;
typedef enum
{
ePgActive,
ePgInUse,
ePgXfer,
ePgSpare
} ePgHdrEnum;
#ifndef OAD_KEEP_NV_PAGES
// When NV pages are to remain intact during OAD download,
// the image itself should not include NV pages.
#pragma location="ZIGNV_ADDRESS_SPACE"
__no_init uint8 _nvBuf[OSAL_NV_PAGES_USED * OSAL_NV_PAGE_SIZE];
#pragma required=_nvBuf
#endif // OAD_KEEP_NV_PAGES
// Offset into the page of the first available erased space.
static uint16 pgOff[OSAL_NV_PAGES_USED];
// Count of the bytes lost for the zeroed-out items.
static uint16 pgLost[OSAL_NV_PAGES_USED];
static uint8 pgRes; // Page reserved for item compacting transfer.
// Saving ~100 code bytes to move a uint8* parameter/return value from findItem() to a global.
static uint8 findPg;
// NV page and offsets for hot items.
static uint8 hotPg[OSAL_NV_MAX_HOT];
static uint16 hotOff[OSAL_NV_MAX_HOT];
static uint8 initNV( void );
static void setPageUse( uint8 pg, uint8 inUse );
static uint16 initPage( uint8 pg, uint16 id, uint8 findDups );
static void erasePage( uint8 pg );
static uint8 compactPage( uint8 srcPg, uint16 skipId );
static uint16 findItem( uint16 id );
static uint8 initItem( uint8 flag, uint16 id, uint16 len, void *buf );
static void setItem( uint8 pg, uint16 offset, eNvHdrEnum stat );
static uint16 setChk( uint8 pg, uint16 offset, uint16 chk );
static uint16 calcChkB( uint16 len, uint8 *buf );
static uint16 calcChkF( uint8 pg, uint16 offset, uint16 len );
static void writeWord( uint8 pg, uint16 offset, uint8 *buf );
static void writeWordH( uint8 pg, uint16 offset, uint8 *buf );
static void writeWordM( uint8 pg, uint16 offset, uint8 *buf, uint16 cnt );
static void writeBuf( uint8 pg, uint16 offset, uint16 len, uint8 *buf );
static void xferBuf( uint8 srcPg, uint16 srcOff, uint8 dstPg, uint16 dstOff, uint16 len );
static uint8 writeItem( uint8 pg, uint16 id, uint16 len, void *buf, uint8 flag );
static uint8 hotItem(uint16 id);
static void hotItemUpdate(uint8 pg, uint16 off, uint16 id);
static uint8 initNV( void )
{
osalNvPgHdr_t pgHdr;
uint8 oldPg = OSAL_NV_PAGE_NULL;
uint8 findDups = FALSE;
uint8 pg;
pgRes = OSAL_NV_PAGE_NULL;
for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ )
{
HalFlashRead(pg, OSAL_NV_PAGE_HDR_OFFSET, (uint8 *)(&pgHdr), OSAL_NV_HDR_SIZE);
if ( pgHdr.active == OSAL_NV_ERASED_ID )
{
if ( pgRes == OSAL_NV_PAGE_NULL )
{
pgRes = pg;
}
else
{
setPageUse( pg, TRUE );
}
}
// An Xfer from this page was in progress.
else if ( pgHdr.xfer != OSAL_NV_ERASED_ID )
{
oldPg = pg;
}
}
// If a page compaction was interrupted before the old page was erased.
if ( oldPg != OSAL_NV_PAGE_NULL )
{
/* Interrupted compaction before the target of compaction was put in use;
* so erase the target of compaction and start again.
*/
if ( pgRes != OSAL_NV_PAGE_NULL )
{
erasePage( pgRes );
(void)compactPage( oldPg, OSAL_NV_ITEM_NULL );
}
/* Interrupted compaction after the target of compaction was put in use,
* but before the old page was erased; so erase it now and create a new reserve page.
*/
else
{
erasePage( oldPg );
pgRes = oldPg;
}
}
else if ( pgRes != OSAL_NV_PAGE_NULL )
{
erasePage( pgRes ); // The last page erase could have been interrupted by a power-cycle.
}
/* else if there is no reserve page, COMPACT_PAGE_CLEANUP() must have succeeded to put the old
* reserve page (i.e. the target of the compacted items) into use but got interrupted by a reset
* while trying to erase the page to be compacted. Such a page should only contain duplicate items
* (i.e. all items will be marked 'Xfer') and thus should have the lost count equal to the page
* size less the page header.
*/
for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ )
{
// Calculate page offset and lost bytes - any "old" item triggers an N^2 re-scan from start.
if ( initPage( pg, OSAL_NV_ITEM_NULL, findDups ) != OSAL_NV_ITEM_NULL )
{
findDups = TRUE;
pg = (OSAL_NV_PAGE_BEG - 1); // Pre-decrement so that loop increment will start over at zero.
continue;
}
}
if (findDups)
{
// Final pass to calculate page lost after invalidating duplicate items.
for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ )
{
(void)initPage( pg, OSAL_NV_ITEM_NULL, FALSE );
}
}
if ( pgRes == OSAL_NV_PAGE_NULL )
{
uint8 idx, mostLost = 0;
for ( idx = 0; idx < OSAL_NV_PAGES_USED; idx++ )
{
// Is this the page that was compacted?
if (pgLost[idx] == (OSAL_NV_PAGE_SIZE - OSAL_NV_PAGE_HDR_SIZE))
{
mostLost = idx;
break;
}
/* This check is not expected to be necessary because the above test should always succeed
* with an early loop exit.
*/
else if (pgLost[idx] > pgLost[mostLost])
{
mostLost = idx;
}
}
pgRes = mostLost + OSAL_NV_PAGE_BEG;
erasePage( pgRes ); // The last page erase had been interrupted by a power-cycle.
}
return TRUE;
}
static void setPageUse( uint8 pg, uint8 inUse )
{
osalNvPgHdr_t pgHdr;
pgHdr.active = OSAL_NV_ZEROED_ID;
if ( inUse )
{
pgHdr.inUse = OSAL_NV_ZEROED_ID;
}
else
{
pgHdr.inUse = OSAL_NV_ERASED_ID;
}
writeWord( pg, OSAL_NV_PAGE_HDR_OFFSET, (uint8*)(&pgHdr) );
}
static uint16 initPage( uint8 pg, uint16 id, uint8 findDups )
{
uint16 offset = OSAL_NV_PAGE_HDR_SIZE;
uint16 sz, lost = 0;
osalNvHdr_t hdr;
do
{
HalFlashRead(pg, offset, (uint8 *)(&hdr), OSAL_NV_HDR_SIZE);
if ( hdr.id == OSAL_NV_ERASED_ID )
{
break;
}
// Get the actual size in bytes which is the ceiling(hdr.len)
sz = OSAL_NV_DATA_SIZE( hdr.len );
// A bad 'len' write has blown away the rest of the page.
if (sz > (OSAL_NV_PAGE_SIZE - OSAL_NV_HDR_SIZE - offset))
{
lost += (OSAL_NV_PAGE_SIZE - offset);
offset = OSAL_NV_PAGE_SIZE;
break;
}
offset += OSAL_NV_HDR_SIZE;
if ( hdr.id != OSAL_NV_ZEROED_ID )
{
/* This trick allows function to do double duty for findItem() without
* compromising its essential functionality at powerup initialization.
*/
if ( id != OSAL_NV_ITEM_NULL )
{
/* This trick allows asking to find the old/transferred item in case
* of a successful new item write that gets interrupted before the
* old item can be zeroed out.
*/
if ( (id & 0x7fff) == hdr.id )
{
if ( (((id & OSAL_NV_SOURCE_ID) == 0) && (hdr.stat == OSAL_NV_ERASED_ID)) ||
(((id & OSAL_NV_SOURCE_ID) != 0) && (hdr.stat != OSAL_NV_ERASED_ID)) )
{
return offset;
}
}
}
// When invoked from the osal_nv_init(), verify checksums and find & zero any duplicates.
else
{
if ( hdr.chk == calcChkF( pg, offset, hdr.len ) )
{
if ( findDups )
{
if ( hdr.stat == OSAL_NV_ERASED_ID )
{
/* The trick of setting the MSB of the item Id causes the logic
* immediately above to return a valid page only if the header 'stat'
* indicates that it was the older item being transferred.
*/
uint16 off = findItem( (hdr.id | OSAL_NV_SOURCE_ID) );
if ( off != OSAL_NV_ITEM_NULL )
{
setItem( findPg, off, eNvZero ); // Mark old duplicate as invalid.
}
}
}
// Any "old" item immediately exits and triggers the N^2 exhaustive initialization.
else if ( hdr.stat != OSAL_NV_ERASED_ID )
{
return OSAL_NV_ERASED_ID;
}
}
else
{
setItem( pg, offset, eNvZero ); // Mark bad checksum as invalid.
lost += (OSAL_NV_HDR_SIZE + sz);
}
}
}
else
{
lost += (OSAL_NV_HDR_SIZE + sz);
}
offset += sz;
} while (offset < (OSAL_NV_PAGE_SIZE - OSAL_NV_HDR_SIZE));
pgOff[pg - OSAL_NV_PAGE_BEG] = offset;
pgLost[pg - OSAL_NV_PAGE_BEG] = lost;
return OSAL_NV_ITEM_NULL;
}
static void erasePage( uint8 pg )
{
HalFlashErase(pg);
pgOff[pg - OSAL_NV_PAGE_BEG] = OSAL_NV_PAGE_HDR_SIZE;
pgLost[pg - OSAL_NV_PAGE_BEG] = 0;
}
static uint8 compactPage( uint8 srcPg, uint16 skipId )
{
uint16 srcOff;
uint8 rtrn;
// To minimize code size, only check for a clean page here where it's absolutely required.
for (srcOff = 0; srcOff < OSAL_NV_PAGE_SIZE; srcOff++)
{
HalFlashRead(pgRes, srcOff, &rtrn, 1);
if (rtrn != OSAL_NV_ERASED)
{
erasePage(pgRes);
return FALSE;
}
}
srcOff = OSAL_NV_PAGE_HDR_SIZE;
rtrn = TRUE;
while ( srcOff < (OSAL_NV_PAGE_SIZE - OSAL_NV_HDR_SIZE ) )
{
osalNvHdr_t hdr;
uint16 sz, dstOff = pgOff[pgRes-OSAL_NV_PAGE_BEG];
HalFlashRead(srcPg, srcOff, (uint8 *)(&hdr), OSAL_NV_HDR_SIZE);
if ( hdr.id == OSAL_NV_ERASED_ID )
{
break;
}
// Get the actual size in bytes which is the ceiling(hdr.len)
sz = OSAL_NV_DATA_SIZE( hdr.len );
if ( sz > (OSAL_NV_PAGE_SIZE - OSAL_NV_HDR_SIZE - srcOff) )
{
break;
}
if ( sz > (OSAL_NV_PAGE_SIZE - OSAL_NV_HDR_SIZE - dstOff) )
{
rtrn = FALSE;
break;
}
srcOff += OSAL_NV_HDR_SIZE;
if ( (hdr.id != OSAL_NV_ZEROED_ID) && (hdr.id != skipId) )
{
if ( hdr.chk == calcChkF( srcPg, srcOff, hdr.len ) )
{
/* Prevent excessive re-writes to item header caused by numerous, rapid, & successive
* OSAL_Nv interruptions caused by resets.
*/
if ( hdr.stat == OSAL_NV_ERASED_ID )
{
setItem( srcPg, srcOff, eNvXfer );
}
if ( writeItem( pgRes, hdr.id, hdr.len, NULL, FALSE ) )
{
dstOff += OSAL_NV_HDR_SIZE;
xferBuf( srcPg, srcOff, pgRes, dstOff, sz );
// Calculate and write the new checksum.
if (hdr.chk == calcChkF(pgRes, dstOff, hdr.len))
{
if ( hdr.chk != setChk( pgRes, dstOff, hdr.chk ) )
{
rtrn = FALSE;
break;
}
else
{
hotItemUpdate(pgRes, dstOff, hdr.id);
}
}
else
{
rtrn = FALSE;
break;
}
}
else
{
rtrn = FALSE;
break;
}
}
}
srcOff += sz;
}
if (rtrn == FALSE)
{
erasePage(pgRes);
}
else if (skipId == OSAL_NV_ITEM_NULL)
{
COMPACT_PAGE_CLEANUP(srcPg);
}
// else invoking function must cleanup.
return rtrn;
}
static uint16 findItem( uint16 id )
{
uint16 off;
uint8 pg;
for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ )
{
if ( (off = initPage( pg, id, FALSE )) != OSAL_NV_ITEM_NULL )
{
findPg = pg;
return off;
}
}
// Now attempt to find the item as the "old" item of a failed/interrupted NV write.
if ( (id & OSAL_NV_SOURCE_ID) == 0 )
{
return findItem( id | OSAL_NV_SOURCE_ID );
}
else
{
findPg = OSAL_NV_PAGE_NULL;
return OSAL_NV_ITEM_NULL;
}
}
static uint8 initItem( uint8 flag, uint16 id, uint16 len, void *buf )
{
uint16 sz = OSAL_NV_ITEM_SIZE( len );
uint8 rtrn = OSAL_NV_PAGE_NULL;
uint8 cnt = OSAL_NV_PAGES_USED;
uint8 pg = pgRes+1; // Set to 1 after the reserve page to even wear across all available pages.
do {
if (pg >= OSAL_NV_PAGE_BEG+OSAL_NV_PAGES_USED)
{
pg = OSAL_NV_PAGE_BEG;
}
if ( pg != pgRes )
{
uint8 idx = pg - OSAL_NV_PAGE_BEG;
if ( sz <= (OSAL_NV_PAGE_SIZE - pgOff[idx] + pgLost[idx]) )
{
break;
}
}
pg++;
} while (--cnt);
if (cnt)
{
// Item fits if an old page is compacted.
if ( sz > (OSAL_NV_PAGE_SIZE - pgOff[pg - OSAL_NV_PAGE_BEG]) )
{
osalNvPgHdr_t pgHdr;
/* Prevent excessive re-writes to page header caused by numerous, rapid, & successive
* OSAL_Nv interruptions caused by resets.
*/
HalFlashRead(pg, OSAL_NV_PAGE_HDR_OFFSET, (uint8 *)(&pgHdr), OSAL_NV_PAGE_HDR_SIZE);
if ( pgHdr.xfer == OSAL_NV_ERASED_ID )
{
// Mark the old page as being in process of compaction.
sz = OSAL_NV_ZEROED_ID;
writeWordH( pg, OSAL_NV_PG_XFER, (uint8*)(&sz) );
}
/* First the old page is compacted, then the new item will be the last one written to what
* had been the reserved page.
*/
if (compactPage( pg, id ))
{
if ( writeItem( pgRes, id, len, buf, flag ) )
{
rtrn = pgRes;
}
if ( flag == FALSE )
{
/* Overload 'buf' as an OUT parameter to pass back to the calling function
* the old page to be cleaned up.
*/
*(uint8 *)buf = pg;
}
else
{
/* Safe to do the compacted page cleanup even if writeItem() above failed because the
* item does not yet exist since this call with flag==TRUE is from osal_nv_item_init().
*/
COMPACT_PAGE_CLEANUP( pg );
}
}
}
else
{
if ( writeItem( pg, id, len, buf, flag ) )
{
rtrn = pg;
}
}
}
return rtrn;
}
static void setItem( uint8 pg, uint16 offset, eNvHdrEnum stat )
{
osalNvHdr_t hdr;
offset -= OSAL_NV_HDR_SIZE;
HalFlashRead(pg, offset, (uint8 *)(&hdr), OSAL_NV_HDR_SIZE);
if ( stat == eNvXfer )
{
hdr.stat = OSAL_NV_ACTIVE;
writeWord( pg, offset+OSAL_NV_HDR_CHK, (uint8*)(&(hdr.chk)) );
}
else // if ( stat == eNvZero )
{
uint16 sz = ((hdr.len + (OSAL_NV_WORD_SIZE-1)) / OSAL_NV_WORD_SIZE) * OSAL_NV_WORD_SIZE +
OSAL_NV_HDR_SIZE;
hdr.id = 0;
writeWord( pg, offset, (uint8 *)(&hdr) );
pgLost[pg-OSAL_NV_PAGE_BEG] += sz;
}
}
static uint16 setChk( uint8 pg, uint16 offset, uint16 chk )
{
offset -= OSAL_NV_WORD_SIZE;
writeWordH( pg, offset, (uint8 *)&chk );
HalFlashRead( pg, offset, (uint8 *)(&chk), sizeof( chk ) );
return chk;
}
static uint16 calcChkB( uint16 len, uint8 *buf )
{
uint8 fill = len % OSAL_NV_WORD_SIZE;
uint16 chk;
if ( !buf )
{
chk = len * OSAL_NV_ERASED;
}
else
{
chk = 0;
while ( len-- )
{
chk += *buf++;
}
}
// calcChkF() will calculate over OSAL_NV_WORD_SIZE alignment.
if ( fill )
{
chk += (OSAL_NV_WORD_SIZE - fill) * OSAL_NV_ERASED;
}
return chk;
}
static uint16 calcChkF( uint8 pg, uint16 offset, uint16 len )
{
uint16 chk = 0;
len = (len + (OSAL_NV_WORD_SIZE-1)) / OSAL_NV_WORD_SIZE;
while ( len-- )
{
uint8 cnt, tmp[OSAL_NV_WORD_SIZE];
HalFlashRead(pg, offset, tmp, OSAL_NV_WORD_SIZE);
offset += OSAL_NV_WORD_SIZE;
for ( cnt = 0; cnt < OSAL_NV_WORD_SIZE; cnt++ )
{
chk += tmp[cnt];
}
}
return chk;
}
static void writeWord( uint8 pg, uint16 offset, uint8 *buf )
{
offset = (offset / HAL_FLASH_WORD_SIZE) +
((uint16)pg * (HAL_FLASH_PAGE_SIZE / HAL_FLASH_WORD_SIZE));
HalFlashWrite(offset, buf, 1);
}
static void writeWordM( uint8 pg, uint16 offset, uint8 *buf, uint16 cnt )
{
offset = (offset / HAL_FLASH_WORD_SIZE) +
((uint16)pg * (HAL_FLASH_PAGE_SIZE / HAL_FLASH_WORD_SIZE));
HalFlashWrite(offset, buf, cnt);
}
static void writeWordH( uint8 pg, uint16 offset, uint8 *buf )
{
uint8 tmp[4];
tmp[0] = buf[0];
tmp[1] = buf[1];
tmp[2] = OSAL_NV_ERASED;
tmp[3] = OSAL_NV_ERASED;
writeWord( pg, offset, tmp );
}
static void writeBuf( uint8 dstPg, uint16 dstOff, uint16 len, uint8 *buf )
{
uint8 rem = dstOff % OSAL_NV_WORD_SIZE;
uint8 tmp[OSAL_NV_WORD_SIZE];
if ( rem )
{
dstOff = (dstOff / OSAL_NV_WORD_SIZE) * OSAL_NV_WORD_SIZE;
HalFlashRead(dstPg, dstOff, tmp, OSAL_NV_WORD_SIZE);
while ( (rem < OSAL_NV_WORD_SIZE) && len )
{
tmp[rem++] = *buf++;
len--;
}
writeWord( dstPg, dstOff, tmp );
dstOff += OSAL_NV_WORD_SIZE;
}
rem = len % OSAL_NV_WORD_SIZE;
len /= OSAL_NV_WORD_SIZE;
if ( len )
{
writeWordM( dstPg, dstOff, buf, len );
dstOff += OSAL_NV_WORD_SIZE * len;
buf += OSAL_NV_WORD_SIZE * len;
}
if ( rem )
{
uint8 idx = 0;
HalFlashRead(dstPg, dstOff, tmp, OSAL_NV_WORD_SIZE);
while ( rem-- )
{
tmp[idx++] = *buf++;
}
writeWord( dstPg, dstOff, tmp );
}
}
static void xferBuf( uint8 srcPg, uint16 srcOff, uint8 dstPg, uint16 dstOff, uint16 len )
{
uint8 rem = dstOff % OSAL_NV_WORD_SIZE;
uint8 tmp[OSAL_NV_WORD_SIZE];
if ( rem )
{
dstOff -= rem;
HalFlashRead(dstPg, dstOff, tmp, OSAL_NV_WORD_SIZE);
while ( (rem < OSAL_NV_WORD_SIZE) && len )
{
HalFlashRead(srcPg, srcOff, tmp+rem, 1);
srcOff++;
rem++;
len--;
}
writeWord( dstPg, dstOff, tmp );
dstOff += OSAL_NV_WORD_SIZE;
}
rem = len % OSAL_NV_WORD_SIZE;
len /= OSAL_NV_WORD_SIZE;
while ( len-- )
{
HalFlashRead(srcPg, srcOff, tmp, OSAL_NV_WORD_SIZE);
srcOff += OSAL_NV_WORD_SIZE;
writeWord( dstPg, dstOff, tmp );
dstOff += OSAL_NV_WORD_SIZE;
}
if ( rem )
{
uint8 idx = 0;
HalFlashRead(dstPg, dstOff, tmp, OSAL_NV_WORD_SIZE);
while ( rem-- )
{
HalFlashRead(srcPg, srcOff, tmp+idx, 1);
srcOff++;
idx++;
}
writeWord( dstPg, dstOff, tmp );
}
}
static uint8 writeItem( uint8 pg, uint16 id, uint16 len, void *buf, uint8 flag )
{
uint16 offset = pgOff[pg-OSAL_NV_PAGE_BEG];
uint8 rtrn = FALSE;
osalNvHdr_t hdr;
hdr.id = id;
hdr.len = len;
writeWord( pg, offset, (uint8 *)&hdr );
HalFlashRead(pg, offset, (uint8 *)(&hdr), OSAL_NV_HDR_SIZE);
if ( (hdr.id == id) && (hdr.len == len) )
{
if ( flag )
{
hdr.chk = calcChkB( len, buf );
offset += OSAL_NV_HDR_SIZE;
if ( buf != NULL )
{
writeBuf( pg, offset, len, buf );
}
if ( hdr.chk == calcChkF( pg, offset, len ) )
{
if ( hdr.chk == setChk( pg, offset, hdr.chk ) )
{
hotItemUpdate(pg, offset, hdr.id);
rtrn = TRUE;
}
}
}
else
{
rtrn = TRUE;
}
len = OSAL_NV_ITEM_SIZE( hdr.len );
}
else
{
len = OSAL_NV_ITEM_SIZE( hdr.len );
if (len > (OSAL_NV_PAGE_SIZE - pgOff[pg - OSAL_NV_PAGE_BEG]))
{
len = (OSAL_NV_PAGE_SIZE - pgOff[pg - OSAL_NV_PAGE_BEG]);
}
pgLost[pg - OSAL_NV_PAGE_BEG] += len;
}
pgOff[pg - OSAL_NV_PAGE_BEG] += len;
return rtrn;
}
static uint8 hotItem(uint16 id)
{
uint8 hotIdx;
for (hotIdx = 0; hotIdx < OSAL_NV_MAX_HOT; hotIdx++)
{
if (hotIds[hotIdx] == id)
{
break;
}
}
return hotIdx;
}
static void hotItemUpdate(uint8 pg, uint16 off, uint16 id)
{
uint8 hotIdx = hotItem(id);
if (hotIdx < OSAL_NV_MAX_HOT)
{
{
hotPg[hotIdx] = pg;
hotOff[hotIdx] = off;
}
}
}
void osal_nv_init( void *p )
{
(void)p; // Suppress Lint warning.
(void)initNV(); // Always returns TRUE after pages have been erased.
}
uint8 osal_nv_item_init( uint16 id, uint16 len, void *buf )
{
uint16 offset;
if ( !OSAL_NV_CHECK_BUS_VOLTAGE )
{
return NV_OPER_FAILED;
}
else if ((offset = findItem(id)) != OSAL_NV_ITEM_NULL)
{
// Re-populate the NV hot item data if the corresponding items are already established.
hotItemUpdate(findPg, offset, id);
return SUCCESS;
}
else if ( initItem( TRUE, id, len, buf ) != OSAL_NV_PAGE_NULL )
{
return NV_ITEM_UNINIT;
}
else
{
return NV_OPER_FAILED;
}
}
uint16 osal_nv_item_len( uint16 id )
{
osalNvHdr_t hdr;
uint16 offset;
uint8 hotIdx;
if ((hotIdx = hotItem(id)) < OSAL_NV_MAX_HOT)
{
findPg = hotPg[hotIdx];
offset = hotOff[hotIdx];
}
else if ((offset = findItem(id)) == OSAL_NV_ITEM_NULL)
{
return 0;
}
HalFlashRead(findPg, (offset - OSAL_NV_HDR_SIZE), (uint8 *)(&hdr), OSAL_NV_HDR_SIZE);
return hdr.len;
}
uint8 osal_nv_write( uint16 id, uint16 ndx, uint16 len, void *buf )
{
uint8 rtrn = SUCCESS;
if ( !OSAL_NV_CHECK_BUS_VOLTAGE )
{
return NV_OPER_FAILED;
}
else if ( len != 0 )
{
osalNvHdr_t hdr;
uint16 origOff, srcOff;
uint16 cnt, chk;
uint8 *ptr, srcPg;
origOff = srcOff = findItem( id );
srcPg = findPg;
if ( srcOff == OSAL_NV_ITEM_NULL )
{
return NV_ITEM_UNINIT;
}
HalFlashRead(srcPg, (srcOff - OSAL_NV_HDR_SIZE), (uint8 *)(&hdr), OSAL_NV_HDR_SIZE);
if ( hdr.len < (ndx + len) )
{
return NV_OPER_FAILED;
}
srcOff += ndx;
ptr = buf;
cnt = len;
chk = 0;
while ( cnt-- )
{
uint8 tmp;
HalFlashRead(srcPg, srcOff, &tmp, 1);
if ( tmp != *ptr )
{
chk = 1; // Mark that at least one byte is different.
// Calculate expected checksum after transferring old data and writing new data.
hdr.chk -= tmp;
hdr.chk += *ptr;
}
srcOff++;
ptr++;
}
if ( chk != 0 ) // If the buffer to write is different in one or more bytes.
{
uint8 comPg = OSAL_NV_PAGE_NULL;
uint8 dstPg = initItem( FALSE, id, hdr.len, &comPg );
if ( dstPg != OSAL_NV_PAGE_NULL )
{
uint16 tmp = OSAL_NV_DATA_SIZE( hdr.len );
uint16 dstOff = pgOff[dstPg-OSAL_NV_PAGE_BEG] - tmp;
srcOff = origOff;
/* Prevent excessive re-writes to item header caused by numerous, rapid, & successive
* OSAL_Nv interruptions caused by resets.
*/
if ( hdr.stat == OSAL_NV_ERASED_ID )
{
setItem( srcPg, srcOff, eNvXfer );
}
xferBuf( srcPg, srcOff, dstPg, dstOff, ndx );
srcOff += ndx;
dstOff += ndx;
writeBuf( dstPg, dstOff, len, buf );
srcOff += len;
dstOff += len;
xferBuf( srcPg, srcOff, dstPg, dstOff, (hdr.len-ndx-len) );
// Calculate and write the new checksum.
dstOff = pgOff[dstPg-OSAL_NV_PAGE_BEG] - tmp;
if ( hdr.chk == calcChkF( dstPg, dstOff, hdr.len ) )
{
if ( hdr.chk != setChk( dstPg, dstOff, hdr.chk ) )
{
rtrn = NV_OPER_FAILED;
}
else
{
hotItemUpdate(dstPg, dstOff, hdr.id);
}
}
else
{
rtrn = NV_OPER_FAILED;
}
}
else
{
rtrn = NV_OPER_FAILED;
}
if ( comPg != OSAL_NV_PAGE_NULL )
{
/* Even though the page compaction succeeded, if the new item is coming from the compacted
* page and writing the new value failed, then the compaction must be aborted.
*/
if ( (srcPg == comPg) && (rtrn == NV_OPER_FAILED) )
{
erasePage( pgRes );
}
else
{
COMPACT_PAGE_CLEANUP( comPg );
}
}
/* Zero of the old item must wait until after compact page cleanup has finished - if the item
* is zeroed before and cleanup is interrupted by a power-cycle, the new item can be lost.
*/
if ( (srcPg != comPg) && (rtrn != NV_OPER_FAILED) )
{
setItem( srcPg, origOff, eNvZero );
}
}
}
return rtrn;
}
uint8 osal_nv_read( uint16 id, uint16 ndx, uint16 len, void *buf )
{
uint16 offset;
uint8 hotIdx;
if ((hotIdx = hotItem(id)) < OSAL_NV_MAX_HOT)
{
HalFlashRead(hotPg[hotIdx], hotOff[hotIdx]+ndx, buf, len);
return SUCCESS;
}
if ((offset = findItem(id)) == OSAL_NV_ITEM_NULL)
{
return NV_OPER_FAILED;
}
else
{
HalFlashRead(findPg, offset+ndx, buf, len);
return SUCCESS;
}
}
uint8 osal_nv_delete( uint16 id, uint16 len )
{
uint16 length;
uint16 offset;
offset = findItem( id );
if ( offset == OSAL_NV_ITEM_NULL )
{
// NV item does not exist
return NV_ITEM_UNINIT;
}
length = osal_nv_item_len( id );
if ( length != len )
{
// NV item has different length
return NV_BAD_ITEM_LEN;
}
// Set item header ID to zero to 'delete' the item
setItem( findPg, offset, eNvZero );
// Verify that item has been removed
offset = findItem( id );
if ( offset != OSAL_NV_ITEM_NULL )
{
// Still there
return NV_OPER_FAILED;
}
else
{
// Yes, it's gone
return SUCCESS;
}
}
OSAL_Nv.h
#ifndef OSAL_NV_H
#define OSAL_NV_H
#ifdef __cplusplus
extern "C"
{
#endif
#include "hal_types.h"
extern void osal_nv_init( void *p );
extern uint8 osal_nv_item_init( uint16 id, uint16 len, void *buf );
extern uint8 osal_nv_read( uint16 id, uint16 offset, uint16 len, void *buf );
extern uint8 osal_nv_write( uint16 id, uint16 offset, uint16 len, void *buf );
extern uint16 osal_nv_item_len( uint16 id );
extern uint8 osal_nv_delete( uint16 id, uint16 len );
#ifdef __cplusplus
}
#endif
#endif /* OSAL_NV.H */
OSAL_PwrMgr.c
#include "comdef.h"
#include "OnBoard.h"
#include "OSAL.h"
#include "OSAL_Tasks.h"
#include "OSAL_Timers.h"
#include "OSAL_PwrMgr.h"
pwrmgr_attribute_t pwrmgr_attribute;
void osal_pwrmgr_init( void )
{
pwrmgr_attribute.pwrmgr_device = PWRMGR_ALWAYS_ON; // Default to no power conservation.
pwrmgr_attribute.pwrmgr_task_state = 0; // Cleared. All set to conserve
}
void osal_pwrmgr_device( uint8 pwrmgr_device )
{
pwrmgr_attribute.pwrmgr_device = pwrmgr_device;
}
uint8 osal_pwrmgr_task_state( uint8 task_id, uint8 state )
{
if ( task_id >= tasksCnt )
return ( INVALID_TASK );
if ( state == PWRMGR_CONSERVE )
{
// Clear the task state flag
pwrmgr_attribute.pwrmgr_task_state &= ~(1 << task_id );
}
else
{
// Set the task state flag
pwrmgr_attribute.pwrmgr_task_state |= (1 << task_id);
}
return ( SUCCESS );
}
#if defined( POWER_SAVING )
void osal_pwrmgr_powerconserve( void )
{
uint16 next;
halIntState_t intState;
// Should we even look into power conservation
if ( pwrmgr_attribute.pwrmgr_device != PWRMGR_ALWAYS_ON )
{
// Are all tasks in agreement to conserve
if ( pwrmgr_attribute.pwrmgr_task_state == 0 )
{
// Hold off interrupts.
HAL_ENTER_CRITICAL_SECTION( intState );
// Get next time-out
next = osal_next_timeout();
// Re-enable interrupts.
HAL_EXIT_CRITICAL_SECTION( intState );
// Put the processor into sleep mode
OSAL_SET_CPU_INTO_SLEEP( next );
}
}
}
#endif /* POWER_SAVING */
OSAL_PwrMgr.h
#ifndef OSAL_PWRMGR_H
#define OSAL_PWRMGR_H
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct
{
uint16 pwrmgr_task_state;
uint16 pwrmgr_next_timeout;
uint16 accumulated_sleep_time;
uint8 pwrmgr_device;
} pwrmgr_attribute_t;
/* With PWRMGR_ALWAYS_ON selection, there is no power savings and the
* device is most likely on mains power. The PWRMGR_BATTERY selection allows
* the HAL sleep manager to enter SLEEP LITE state or SLEEP DEEP state.
*/
#define PWRMGR_ALWAYS_ON 0
#define PWRMGR_BATTERY 1
#define PWRMGR_CONSERVE 0
#define PWRMGR_HOLD 1
extern pwrmgr_attribute_t pwrmgr_attribute;
extern void osal_pwrmgr_init( void );
extern uint8 osal_pwrmgr_task_state( uint8 task_id, uint8 state );
extern void osal_pwrmgr_device( uint8 pwrmgr_device );
extern void osal_pwrmgr_powerconserve( void );
#ifdef __cplusplus
}
#endif
#endif /* OSAL_PWRMGR_H */
OSAL_Tasks.h
#ifndef OSAL_TASKS_H
#define OSAL_TASKS_H
#ifdef __cplusplus
extern "C"
{
#endif
#define TASK_NO_TASK 0xFF
typedef unsigned short (*pTaskEventHandlerFn)( unsigned char task_id, unsigned short event );
extern const pTaskEventHandlerFn tasksArr[];
extern const uint8 tasksCnt;
extern uint16 *tasksEvents;
extern void osalInitTasks( void );
#ifdef __cplusplus
}
#endif
#endif /* OSAL_TASKS_H */
OSAL_Timers.c
#include "comdef.h"
#include "OnBoard.h"
#include "OSAL.h"
#include "OSAL_Timers.h"
#include "hal_timer.h"
typedef struct
{
void *next;
uint16 timeout;
uint16 event_flag;
uint8 task_id;
uint16 reloadTimeout;
} osalTimerRec_t;
osalTimerRec_t *timerHead;
// Milliseconds since last reboot
static uint32 osal_systemClock;
osalTimerRec_t *osalAddTimer( uint8 task_id, uint16 event_flag, uint16 timeout );
osalTimerRec_t *osalFindTimer( uint8 task_id, uint16 event_flag );
void osalDeleteTimer( osalTimerRec_t *rmTimer );
void osalTimerInit( void )
{
osal_systemClock = 0;
}
osalTimerRec_t * osalAddTimer( uint8 task_id, uint16 event_flag, uint16 timeout )
{
osalTimerRec_t *newTimer;
osalTimerRec_t *srchTimer;
// Look for an existing timer first
newTimer = osalFindTimer( task_id, event_flag );
if ( newTimer )
{
// Timer is found - update it.
newTimer->timeout = timeout;
return ( newTimer );
}
else
{
// New Timer
newTimer = osal_mem_alloc( sizeof( osalTimerRec_t ) );
if ( newTimer )
{
// Fill in new timer
newTimer->task_id = task_id;
newTimer->event_flag = event_flag;
newTimer->timeout = timeout;
newTimer->next = (void *)NULL;
newTimer->reloadTimeout = 0;
// Does the timer list already exist
if ( timerHead == NULL )
{
// Start task list
timerHead = newTimer;
}
else
{
// Add it to the end of the timer list
srchTimer = timerHead;
// Stop at the last record
while ( srchTimer->next )
srchTimer = srchTimer->next;
// Add to the list
srchTimer->next = newTimer;
}
return ( newTimer );
}
else
return ( (osalTimerRec_t *)NULL );
}
}
osalTimerRec_t *osalFindTimer( uint8 task_id, uint16 event_flag )
{
osalTimerRec_t *srchTimer;
// Head of the timer list
srchTimer = timerHead;
// Stop when found or at the end
while ( srchTimer )
{
if ( srchTimer->event_flag == event_flag &&
srchTimer->task_id == task_id )
break;
// Not this one, check another
srchTimer = srchTimer->next;
}
return ( srchTimer );
}
void osalDeleteTimer( osalTimerRec_t *rmTimer )
{
// Does the timer list really exist
if ( rmTimer )
{
// Clear the event flag and osalTimerUpdate() will delete
// the timer from the list.
rmTimer->event_flag = 0;
}
}
uint8 osal_start_timerEx( uint8 taskID, uint16 event_id, uint16 timeout_value )
{
halIntState_t intState;
osalTimerRec_t *newTimer;
HAL_ENTER_CRITICAL_SECTION( intState ); // Hold off interrupts.
// Add timer
newTimer = osalAddTimer( taskID, event_id, timeout_value );
HAL_EXIT_CRITICAL_SECTION( intState ); // Re-enable interrupts.
return ( (newTimer != NULL) ? SUCCESS : NO_TIMER_AVAIL );
}
uint8 osal_start_reload_timer( uint8 taskID, uint16 event_id, uint16 timeout_value )
{
halIntState_t intState;
osalTimerRec_t *newTimer;
HAL_ENTER_CRITICAL_SECTION( intState ); // Hold off interrupts.
// Add timer
newTimer = osalAddTimer( taskID, event_id, timeout_value );
if ( newTimer )
{
// Load the reload timeout value
newTimer->reloadTimeout = timeout_value;
}
HAL_EXIT_CRITICAL_SECTION( intState ); // Re-enable interrupts.
return ( (newTimer != NULL) ? SUCCESS : NO_TIMER_AVAIL );
}
uint8 osal_stop_timerEx( uint8 task_id, uint16 event_id )
{
halIntState_t intState;
osalTimerRec_t *foundTimer;
HAL_ENTER_CRITICAL_SECTION( intState ); // Hold off interrupts.
// Find the timer to stop
foundTimer = osalFindTimer( task_id, event_id );
if ( foundTimer )
{
osalDeleteTimer( foundTimer );
}
HAL_EXIT_CRITICAL_SECTION( intState ); // Re-enable interrupts.
return ( (foundTimer != NULL) ? SUCCESS : INVALID_EVENT_ID );
}
uint16 osal_get_timeoutEx( uint8 task_id, uint16 event_id )
{
halIntState_t intState;
uint16 rtrn = 0;
osalTimerRec_t *tmr;
HAL_ENTER_CRITICAL_SECTION( intState ); // Hold off interrupts.
tmr = osalFindTimer( task_id, event_id );
if ( tmr )
{
rtrn = tmr->timeout;
}
HAL_EXIT_CRITICAL_SECTION( intState ); // Re-enable interrupts.
return rtrn;
}
uint8 osal_timer_num_active( void )
{
halIntState_t intState;
uint8 num_timers = 0;
osalTimerRec_t *srchTimer;
HAL_ENTER_CRITICAL_SECTION( intState ); // Hold off interrupts.
// Head of the timer list
srchTimer = timerHead;
// Count timers in the list
while ( srchTimer != NULL )
{
num_timers++;
srchTimer = srchTimer->next;
}
HAL_EXIT_CRITICAL_SECTION( intState ); // Re-enable interrupts.
return num_timers;
}
void osalTimerUpdate( uint16 updateTime )
{
halIntState_t intState;
osalTimerRec_t *srchTimer;
osalTimerRec_t *prevTimer;
HAL_ENTER_CRITICAL_SECTION( intState ); // Hold off interrupts.
// Update the system time
osal_systemClock += updateTime;
HAL_EXIT_CRITICAL_SECTION( intState ); // Re-enable interrupts.
// Look for open timer slot
if ( timerHead != NULL )
{
// Add it to the end of the timer list
srchTimer = timerHead;
prevTimer = (void *)NULL;
// Look for open timer slot
while ( srchTimer )
{
osalTimerRec_t *freeTimer = NULL;
HAL_ENTER_CRITICAL_SECTION( intState ); // Hold off interrupts.
if (srchTimer->timeout <= updateTime)
{
srchTimer->timeout = 0;
}
else
{
srchTimer->timeout = srchTimer->timeout - updateTime;
}
// Check for reloading
if ( (srchTimer->timeout == 0) && (srchTimer->reloadTimeout) && (srchTimer->event_flag) )
{
// Notify the task of a timeout
osal_set_event( srchTimer->task_id, srchTimer->event_flag );
// Reload the timer timeout value
srchTimer->timeout = srchTimer->reloadTimeout;
}
// When timeout or delete (event_flag == 0)
if ( srchTimer->timeout == 0 || srchTimer->event_flag == 0 )
{
// Take out of list
if ( prevTimer == NULL )
timerHead = srchTimer->next;
else
prevTimer->next = srchTimer->next;
// Setup to free memory
freeTimer = srchTimer;
// Next
srchTimer = srchTimer->next;
}
else
{
// Get next
prevTimer = srchTimer;
srchTimer = srchTimer->next;
}
HAL_EXIT_CRITICAL_SECTION( intState ); // Re-enable interrupts.
if ( freeTimer )
{
if ( freeTimer->timeout == 0 )
{
osal_set_event( freeTimer->task_id, freeTimer->event_flag );
}
osal_mem_free( freeTimer );
}
}
}
}
#ifdef POWER_SAVING
void osal_adjust_timers( void )
{
uint16 eTime;
if ( timerHead != NULL )
{
// Compute elapsed time (msec)
eTime = TimerElapsed() / TICK_COUNT;
if ( eTime )
osalTimerUpdate( eTime );
}
}
uint16 osal_next_timeout( void )
{
uint16 nextTimeout;
osalTimerRec_t *srchTimer;
if ( timerHead != NULL )
{
// Head of the timer list
srchTimer = timerHead;
nextTimeout = OSAL_TIMERS_MAX_TIMEOUT;
// Look for the next timeout timer
while ( srchTimer != NULL )
{
if (srchTimer->timeout < nextTimeout)
{
nextTimeout = srchTimer->timeout;
}
// Check next timer
srchTimer = srchTimer->next;
}
}
else
{
// No timers
nextTimeout = 0;
}
return ( nextTimeout );
}
#endif // POWER_SAVING
uint32 osal_GetSystemClock( void )
{
return ( osal_systemClock );
}
OSAL_Timers.h
#ifndef OSAL_TIMERS_H
#define OSAL_TIMERS_H
#ifdef __cplusplus
extern "C"
{
#endif
#define OSAL_TIMERS_MAX_TIMEOUT 0xFFFF
extern void osalTimerInit( void );
extern uint8 osal_start_timerEx( uint8 task_id, uint16 event_id, uint16 timeout_value );
extern uint8 osal_start_reload_timer( uint8 taskID, uint16 event_id, uint16 timeout_value );
extern uint8 osal_stop_timerEx( uint8 task_id, uint16 event_id );
extern uint16 osal_get_timeoutEx( uint8 task_id, uint16 event_id );
extern void osal_timer_ISR( void );
extern void osal_adjust_timers( void );
extern void osalTimerUpdate( uint16 updateTime );
extern uint8 osal_timer_num_active( void );
extern void osal_sleep_timers( void );
extern void osal_unsleep_timers( void );
extern uint32 osal_GetSystemClock( void );
extern uint16 osal_next_timeout( void );
#ifdef __cplusplus
}
#endif
#endif /* OSAL_TIMERS_H */
ZComDef.h
#ifndef ZCOMDEF_H
#define ZCOMDEF_H
#ifdef __cplusplus
extern "C"
{
#endif
#include "comdef.h"
#include "saddr.h"
#define osal_cpyExtAddr(a, b) sAddrExtCpy((a), (const uint8 *)(b))
#define osal_ExtAddrEqual(a, b) sAddrExtCmp((const uint8 *)(a), (const uint8 *)(b))
#define osal_copyAddress(a, b) sAddrCpy( (sAddr_t *)(a), (const sAddr_t *)(b) )
// Build Device Types - Used during compilation
// These are the types of devices to build
// Bit masked into ZSTACK_DEVICE_BUILD
#define DEVICE_BUILD_COORDINATOR 0x01
#define DEVICE_BUILD_ROUTER 0x02
#define DEVICE_BUILD_ENDDEVICE 0x04
/*** Return Values ***/
#define ZSUCCESS SUCCESS
/*** Component IDs ***/
#define COMPID_OSAL 0
#define COMPID_MTEL 1
#define COMPID_MTSPCI 2
#define COMPID_NWK 3
#define COMPID_NWKIF 4
#define COMPID_MACCB 5
#define COMPID_MAC 6
#define COMPID_APP 7
#define COMPID_TEST 8
#define COMPID_RTG 9
#define COMPID_DATA 11
/* Temp CompIDs for testing */
#define COMPID_TEST_NWK_STARTUP 20
#define COMPID_TEST_SCAN_CONFIRM 21
#define COMPID_TEST_ASSOC_CONFIRM 22
#define COMPID_TEST_REMOTE_DATA_CONFIRM 23
// OSAL NV item IDs
#define ZCD_NV_EXTADDR 0x0001
#define ZCD_NV_BOOTCOUNTER 0x0002
#define ZCD_NV_STARTUP_OPTION 0x0003
#define ZCD_NV_START_DELAY 0x0004
// NWK Layer NV item IDs
#define ZCD_NV_NIB 0x0021
#define ZCD_NV_DEVICE_LIST 0x0022
#define ZCD_NV_ADDRMGR 0x0023
#define ZCD_NV_POLL_RATE 0x0024
#define ZCD_NV_QUEUED_POLL_RATE 0x0025
#define ZCD_NV_RESPONSE_POLL_RATE 0x0026
#define ZCD_NV_REJOIN_POLL_RATE 0x0027
#define ZCD_NV_DATA_RETRIES 0x0028
#define ZCD_NV_POLL_FAILURE_RETRIES 0x0029
#define ZCD_NV_STACK_PROFILE 0x002A
#define ZCD_NV_INDIRECT_MSG_TIMEOUT 0x002B
#define ZCD_NV_ROUTE_EXPIRY_TIME 0x002C
#define ZCD_NV_EXTENDED_PAN_ID 0x002D
#define ZCD_NV_BCAST_RETRIES 0x002E
#define ZCD_NV_PASSIVE_ACK_TIMEOUT 0x002F
#define ZCD_NV_BCAST_DELIVERY_TIME 0x0030
#define ZCD_NV_NWK_MODE 0x0031
#define ZCD_NV_CONCENTRATOR_ENABLE 0x0032
#define ZCD_NV_CONCENTRATOR_DISCOVERY 0x0033
#define ZCD_NV_CONCENTRATOR_RADIUS 0x0034
#define ZCD_NV_CONCENTRATOR_RC 0x0036
#define ZCD_NV_NWK_MGR_MODE 0x0037
#define ZCD_NV_SRC_RTG_EXPIRY_TIME 0x0038
#define ZCD_NV_ROUTE_DISCOVERY_TIME 0x0039
#define ZCD_NV_NWK_ACTIVE_KEY_INFO 0x003A
#define ZCD_NV_NWK_ALTERN_KEY_INFO 0x003B
#define ZCD_NV_ROUTER_OFF_ASSOC_CLEANUP 0x003C
// APS Layer NV item IDs
#define ZCD_NV_BINDING_TABLE 0x0041
#define ZCD_NV_GROUP_TABLE 0x0042
#define ZCD_NV_APS_FRAME_RETRIES 0x0043
#define ZCD_NV_APS_ACK_WAIT_DURATION 0x0044
#define ZCD_NV_APS_ACK_WAIT_MULTIPLIER 0x0045
#define ZCD_NV_BINDING_TIME 0x0046
#define ZCD_NV_APS_USE_EXT_PANID 0x0047
#define ZCD_NV_APS_USE_INSECURE_JOIN 0x0048
#define ZCD_NV_APS_NONMEMBER_RADIUS 0x004B // Multicast non_member radius
#define ZCD_NV_APS_LINK_KEY_TABLE 0x004C
// Security NV Item IDs
#define ZCD_NV_SECURITY_LEVEL 0x0061
#define ZCD_NV_PRECFGKEY 0x0062
#define ZCD_NV_PRECFGKEYS_ENABLE 0x0063
#define ZCD_NV_SECURITY_MODE 0x0064
#define ZCD_NV_SECURE_PERMIT_JOIN 0x0065
#define ZCD_NV_IMPLICIT_CERTIFICATE 0x0069
#define ZCD_NV_DEVICE_PRIVATE_KEY 0x006A
#define ZCD_NV_CA_PUBLIC_KEY 0x006B
#define ZCD_NV_USE_DEFAULT_TCLK 0x006D
#define ZCD_NV_TRUSTCENTER_ADDR 0x006E
#define ZCD_NV_RNG_COUNTER 0x006F
#define ZCD_NV_RANDOM_SEED 0x0070
// ZDO NV Item IDs
#define ZCD_NV_USERDESC 0x0081
#define ZCD_NV_NWKKEY 0x0082
#define ZCD_NV_PANID 0x0083
#define ZCD_NV_CHANLIST 0x0084
#define ZCD_NV_LEAVE_CTRL 0x0085
#define ZCD_NV_SCAN_DURATION 0x0086
#define ZCD_NV_LOGICAL_TYPE 0x0087
#define ZCD_NV_NWKMGR_MIN_TX 0x0088
#define ZCD_NV_NWKMGR_ADDR 0x0089
#define ZCD_NV_ZDO_DIRECT_CB 0x008F
// ZCL NV item IDs
#define ZCD_NV_SCENE_TABLE 0x0091
// Non-standard NV item IDs
#define ZCD_NV_SAPI_ENDPOINT 0x00A1
// NV Items Reserved for Commissioning Cluster Startup Attribute Set (SAS):
// 0x00B1 - 0x00BF: Parameters related to APS and NWK layers
// 0x00C1 - 0x00CF: Parameters related to Security
// 0x00D1 - 0x00DF: Current key parameters
#define ZCD_NV_SAS_SHORT_ADDR 0x00B1
#define ZCD_NV_SAS_EXT_PANID 0x00B2
#define ZCD_NV_SAS_PANID 0x00B3
#define ZCD_NV_SAS_CHANNEL_MASK 0x00B4
#define ZCD_NV_SAS_PROTOCOL_VER 0x00B5
#define ZCD_NV_SAS_STACK_PROFILE 0x00B6
#define ZCD_NV_SAS_STARTUP_CTRL 0x00B7
#define ZCD_NV_SAS_TC_ADDR 0x00C1
#define ZCD_NV_SAS_TC_MASTER_KEY 0x00C2
#define ZCD_NV_SAS_NWK_KEY 0x00C3
#define ZCD_NV_SAS_USE_INSEC_JOIN 0x00C4
#define ZCD_NV_SAS_PRECFG_LINK_KEY 0x00C5
#define ZCD_NV_SAS_NWK_KEY_SEQ_NUM 0x00C6
#define ZCD_NV_SAS_NWK_KEY_TYPE 0x00C7
#define ZCD_NV_SAS_NWK_MGR_ADDR 0x00C8
#define ZCD_NV_SAS_CURR_TC_MASTER_KEY 0x00D1
#define ZCD_NV_SAS_CURR_NWK_KEY 0x00D2
#define ZCD_NV_SAS_CURR_PRECFG_LINK_KEY 0x00D3
// NV Items Reserved for Trust Center Link Key Table entries
// 0x0101 - 0x01FF
#define ZCD_NV_TCLK_TABLE_START 0x0101
#define ZCD_NV_TCLK_TABLE_END 0x01FF
// NV Items Reserved for APS Link Key Table entries
// 0x0201 - 0x02FF
#define ZCD_NV_APS_LINK_KEY_DATA_START 0x0201 // APS key data
#define ZCD_NV_APS_LINK_KEY_DATA_END 0x02FF
// NV Items Reserved for Master Key Table entries
// 0x0301 - 0x03FF
#define ZCD_NV_MASTER_KEY_DATA_START 0x0301 // Master key data
#define ZCD_NV_MASTER_KEY_DATA_END 0x03FF
// NV Items Reserved for applications (user applications)
// 0x0401 ?0x0FFF
// ZCD_NV_STARTUP_OPTION values
// These are bit weighted - you can OR these together.
// Setting one of these bits will set their associated NV items
// to code initialized values.
#define ZCD_STARTOPT_DEFAULT_CONFIG_STATE 0x01
#define ZCD_STARTOPT_DEFAULT_NETWORK_STATE 0x02
#define ZCD_STARTOPT_AUTO_START 0x04
#define ZCD_STARTOPT_CLEAR_CONFIG ZCD_STARTOPT_DEFAULT_CONFIG_STATE
#define ZCD_STARTOPT_CLEAR_STATE ZCD_STARTOPT_DEFAULT_NETWORK_STATE
#define ZCL_KE_IMPLICIT_CERTIFICATE_LEN 48
#define ZCL_KE_CA_PUBLIC_KEY_LEN 22
#define ZCL_KE_DEVICE_PRIVATE_KEY_LEN 21
/*** Data Types ***/
typedef uint8 byte;
typedef uint16 UINT16;
typedef int16 INT16;
enum
{
AddrNotPresent = 0,
AddrGroup = 1,
Addr16Bit = 2,
Addr64Bit = 3,
AddrBroadcast = 15
};
#define Z_EXTADDR_LEN 8
typedef byte ZLongAddr_t[Z_EXTADDR_LEN];
typedef struct
{
union
{
uint16 shortAddr;
ZLongAddr_t extAddr;
} addr;
byte addrMode;
} zAddrType_t;
// Redefined Generic Status Return Values for code backwards compatibility
#define ZSuccess SUCCESS
#define ZFailure FAILURE
#define ZInvalidParameter INVALIDPARAMETER
// ZStack status values must start at 0x10, after the generic status values (defined in comdef.h)
#define ZMemError 0x10
#define ZBufferFull 0x11
#define ZUnsupportedMode 0x12
#define ZMacMemError 0x13
#define ZSapiInProgress 0x20
#define ZSapiTimeout 0x21
#define ZSapiInit 0x22
#define ZNotAuthorized 0x7E
#define ZMalformedCmd 0x80
#define ZUnsupClusterCmd 0x81
// OTA Status values
#define ZOtaAbort 0x95
#define ZOtaImageInvalid 0x96
#define ZOtaWaitForData 0x97
#define ZOtaNoImageAvailable 0x98
#define ZOtaRequireMoreImage 0x99
// APS status values
#define ZApsFail 0xb1
#define ZApsTableFull 0xb2
#define ZApsIllegalRequest 0xb3
#define ZApsInvalidBinding 0xb4
#define ZApsUnsupportedAttrib 0xb5
#define ZApsNotSupported 0xb6
#define ZApsNoAck 0xb7
#define ZApsDuplicateEntry 0xb8
#define ZApsNoBoundDevice 0xb9
#define ZApsNotAllowed 0xba
#define ZApsNotAuthenticated 0xbb
// Security status values
#define ZSecNoKey 0xa1
#define ZSecOldFrmCount 0xa2
#define ZSecMaxFrmCount 0xa3
#define ZSecCcmFail 0xa4
// NWK status values
#define ZNwkInvalidParam 0xc1
#define ZNwkInvalidRequest 0xc2
#define ZNwkNotPermitted 0xc3
#define ZNwkStartupFailure 0xc4
#define ZNwkAlreadyPresent 0xc5
#define ZNwkSyncFailure 0xc6
#define ZNwkTableFull 0xc7
#define ZNwkUnknownDevice 0xc8
#define ZNwkUnsupportedAttribute 0xc9
#define ZNwkNoNetworks 0xca
#define ZNwkLeaveUnconfirmed 0xcb
#define ZNwkNoAck 0xcc // not in spec
#define ZNwkNoRoute 0xcd
// MAC status values
#define ZMacSuccess 0x00
#define ZMacBeaconLoss 0xe0
#define ZMacChannelAccessFailure 0xe1
#define ZMacDenied 0xe2
#define ZMacDisableTrxFailure 0xe3
#define ZMacFailedSecurityCheck 0xe4
#define ZMacFrameTooLong 0xe5
#define ZMacInvalidGTS 0xe6
#define ZMacInvalidHandle 0xe7
#define ZMacInvalidParameter 0xe8
#define ZMacNoACK 0xe9
#define ZMacNoBeacon 0xea
#define ZMacNoData 0xeb
#define ZMacNoShortAddr 0xec
#define ZMacOutOfCap 0xed
#define ZMacPANIDConflict 0xee
#define ZMacRealignment 0xef
#define ZMacTransactionExpired 0xf0
#define ZMacTransactionOverFlow 0xf1
#define ZMacTxActive 0xf2
#define ZMacUnAvailableKey 0xf3
#define ZMacUnsupportedAttribute 0xf4
#define ZMacUnsupported 0xf5
#define ZMacSrcMatchInvalidIndex 0xff
typedef Status_t ZStatus_t;
typedef struct
{
uint8 txCounter; // Counter of transmission success/failures
uint8 txCost; // Average of sending rssi values if link staus is enabled
// i.e. NWK_LINK_STATUS_PERIOD is defined as non zero
uint8 rxLqi; // average of received rssi values
// needs to be converted to link cost (1-7) before used
uint8 inKeySeqNum; // security key sequence number
uint32 inFrmCntr; // security frame counter..
uint16 txFailure; // higher values indicate more failures
} linkInfo_t;
#define SPI_INCOMING_ZTOOL_PORT 0x21 // Raw data from ZTool Port (not implemented)
#define SPI_INCOMING_ZAPP_DATA 0x22 // Raw data from the ZAPP port (see serialApp.c)
#define MT_SYS_APP_MSG 0x23 // Raw data from an MT Sys message
#define MT_SYS_APP_RSP_MSG 0x24 // Raw data output for an MT Sys message
#define MT_SYS_OTA_MSG 0x25 // Raw data output for an MT OTA Rsp
#define AF_DATA_CONFIRM_CMD 0xFD // Data confirmation
#define AF_INCOMING_MSG_CMD 0x1A // Incoming MSG type message
#define AF_INCOMING_KVP_CMD 0x1B // Incoming KVP type message
#define AF_INCOMING_GRP_KVP_CMD 0x1C // Incoming Group KVP type message
//#define KEY_CHANGE 0xC0 // Key Events
#define ZDO_NEW_DSTADDR 0xD0 // ZDO has received a new DstAddr for this app
#define ZDO_STATE_CHANGE 0xD1 // ZDO has changed the device's network state
#define ZDO_MATCH_DESC_RSP_SENT 0xD2 // ZDO match descriptor response was sent
#define ZDO_CB_MSG 0xD3 // ZDO incoming message callback
#define ZDO_NETWORK_REPORT 0xD4 // ZDO received a Network Report message
#define ZDO_NETWORK_UPDATE 0xD5 // ZDO received a Network Update message
#define ZDO_ADDR_CHANGE_IND 0xD6 // ZDO was informed of device address change
#define NM_CHANNEL_INTERFERE 0x31 // NwkMgr received a Channel Interference message
#define NM_ED_SCAN_CONFIRM 0x32 // NwkMgr received an ED Scan Confirm message
#define SAPS_CHANNEL_CHANGE 0x33 // Stub APS has changed the device's channel
#define ZCL_INCOMING_MSG 0x34 // Incoming ZCL foundation message
#define ZCL_KEY_ESTABLISH_IND 0x35 // ZCL Key Establishment Completion Indication
#define ZCL_OTA_CALLBACK_IND 0x36 // ZCL OTA Completion Indication
#ifdef __cplusplus
}
#endif
#endif /* ZCOMDEF_H */
Profile(应用框架层)
AF.c
#include "OSAL.h"
#include "AF.h"
#include "nwk_globals.h"
#include "nwk_util.h"
#include "aps_groups.h"
#include "ZDProfile.h"
#include "aps_frag.h"
#include "rtg.h"
#if defined ( MT_AF_CB_FUNC )
#include "MT_AF.h"
#endif
#if defined ( INTER_PAN )
#include "stub_aps.h"
#endif
#define afSend( dstAddr, srcEP, cID, len, buf, transID, options, radius ) \
AF_DataRequest( (dstAddr), afFindEndPointDesc( (srcEP) ), \
(cID), (len), (buf), (transID), (options), (radius) )
epList_t *epList;
static void afBuildMSGIncoming( aps_FrameFormat_t *aff, endPointDesc_t *epDesc,
zAddrType_t *SrcAddress, uint16 SrcPanId, NLDE_Signal_t *sig,
uint8 nwkSeqNum, uint8 SecurityUse, uint32 timestamp );
static epList_t *afFindEndPointDescList( uint8 EndPoint );
static pDescCB afGetDescCB( endPointDesc_t *epDesc );
epList_t *afRegisterExtended( endPointDesc_t *epDesc, pDescCB descFn )
{
epList_t *ep = osal_mem_alloc(sizeof(epList_t));
if (ep != NULL)
{
ep->nextDesc = epList;
epList = ep;
ep->epDesc = epDesc;
ep->pfnDescCB = descFn;
ep->apsfCfg.frameDelay = APSF_DEFAULT_INTERFRAME_DELAY;
ep->apsfCfg.windowSize = APSF_DEFAULT_WINDOW_SIZE;
ep->flags = eEP_AllowMatch; // Default to allow Match Descriptor.
}
return ep;
}
afStatus_t afRegister( endPointDesc_t *epDesc )
{
if (afFindEndPointDescList(epDesc->endPoint)) // Look for duplicate endpoint.
{
return afStatus_INVALID_PARAMETER;
}
return ((NULL == afRegisterExtended(epDesc, NULL)) ? afStatus_MEM_FAIL : afStatus_SUCCESS);
}
afStatus_t afDelete( uint8 EndPoint )
{
epList_t *epCurrent;
epList_t *epPrevious;
if (epList != NULL)
{
epPrevious = epCurrent = epList;
// first element of the list matches
if (epCurrent->epDesc->endPoint == EndPoint)
{
epList = epCurrent->nextDesc;
osal_mem_free(epCurrent);
return (afStatus_SUCCESS);
}
else
{
// search the list
for (epCurrent = epPrevious->nextDesc; epCurrent != NULL; epPrevious = epCurrent)
{
if (epCurrent->epDesc->endPoint == EndPoint)
{
epPrevious->nextDesc = epCurrent->nextDesc;
osal_mem_free(epCurrent);
// delete the entry and free the memory
return (afStatus_SUCCESS);
}
}
}
// no endpoint found
return (afStatus_INVALID_PARAMETER);
}
else
{
// epList is empty
return (afStatus_FAILED);
}
}
void afDataConfirm( uint8 endPoint, uint8 transID, ZStatus_t status )
{
endPointDesc_t *epDesc;
afDataConfirm_t *msgPtr;
// Find the endpoint description
epDesc = afFindEndPointDesc( endPoint );
if ( epDesc == NULL )
return;
// Determine the incoming command type
msgPtr = (afDataConfirm_t *)osal_msg_allocate( sizeof(afDataConfirm_t) );
if ( msgPtr )
{
// Build the Data Confirm message
msgPtr->hdr.event = AF_DATA_CONFIRM_CMD;
msgPtr->hdr.status = status;
msgPtr->endpoint = endPoint;
msgPtr->transID = transID;
#if defined ( MT_AF_CB_FUNC )
/* If MT has subscribed for this callback, don't send as a message. */
if ( AFCB_CHECK(CB_ID_AF_DATA_CNF,*(epDesc->task_id)) )
{
/* Send callback if it's subscribed */
MT_AfDataConfirm ((void *)msgPtr);
/* Release the memory. */
osal_msg_deallocate( (void *)msgPtr );
}
else
#endif
{
/* send message through task message */
osal_msg_send( *(epDesc->task_id), (uint8 *)msgPtr );
}
}
}
void afIncomingData( aps_FrameFormat_t *aff, zAddrType_t *SrcAddress, uint16 SrcPanId,
NLDE_Signal_t *sig, uint8 nwkSeqNum, uint8 SecurityUse, uint32 timestamp )
{
endPointDesc_t *epDesc = NULL;
epList_t *pList = epList;
#if !defined ( APS_NO_GROUPS )
uint8 grpEp = APS_GROUPS_EP_NOT_FOUND;
#endif
if ( ((aff->FrmCtrl & APS_DELIVERYMODE_MASK) == APS_FC_DM_GROUP) )
{
#if !defined ( APS_NO_GROUPS )
// Find the first endpoint for this group
grpEp = aps_FindGroupForEndpoint( aff->GroupID, APS_GROUPS_FIND_FIRST );
if ( grpEp == APS_GROUPS_EP_NOT_FOUND )
return; // No endpoint found
epDesc = afFindEndPointDesc( grpEp );
if ( epDesc == NULL )
return; // Endpoint descriptor not found
pList = afFindEndPointDescList( epDesc->endPoint );
#else
return; // Not supported
#endif
}
else if ( aff->DstEndPoint == AF_BROADCAST_ENDPOINT )
{
// Set the list
if ( pList != NULL )
{
epDesc = pList->epDesc;
}
}
else if ( (epDesc = afFindEndPointDesc( aff->DstEndPoint )) )
{
pList = afFindEndPointDescList( epDesc->endPoint );
}
while ( epDesc )
{
uint16 epProfileID = 0xFFFF; // Invalid Profile ID
if ( pList->pfnDescCB )
{
uint16 *pID = (uint16 *)(pList->pfnDescCB(
AF_DESCRIPTOR_PROFILE_ID, epDesc->endPoint ));
if ( pID )
{
epProfileID = *pID;
osal_mem_free( pID );
}
}
else if ( epDesc->simpleDesc )
{
epProfileID = epDesc->simpleDesc->AppProfId;
}
if ( (aff->ProfileID == epProfileID) ||
((epDesc->endPoint == ZDO_EP) && (aff->ProfileID == ZDO_PROFILE_ID)) )
{
{
// Save original endpoint
uint8 endpoint = aff->DstEndPoint;
// overwrite with descriptor's endpoint
aff->DstEndPoint = epDesc->endPoint;
afBuildMSGIncoming( aff, epDesc, SrcAddress, SrcPanId, sig,
nwkSeqNum, SecurityUse, timestamp );
// Restore with original endpoint
aff->DstEndPoint = endpoint;
}
}
if ( ((aff->FrmCtrl & APS_DELIVERYMODE_MASK) == APS_FC_DM_GROUP) )
{
#if !defined ( APS_NO_GROUPS )
// Find the next endpoint for this group
grpEp = aps_FindGroupForEndpoint( aff->GroupID, grpEp );
if ( grpEp == APS_GROUPS_EP_NOT_FOUND )
return; // No endpoint found
epDesc = afFindEndPointDesc( grpEp );
if ( epDesc == NULL )
return; // Endpoint descriptor not found
pList = afFindEndPointDescList( epDesc->endPoint );
#else
return;
#endif
}
else if ( aff->DstEndPoint == AF_BROADCAST_ENDPOINT )
{
pList = pList->nextDesc;
if ( pList )
epDesc = pList->epDesc;
else
epDesc = NULL;
}
else
epDesc = NULL;
}
}
static void afBuildMSGIncoming( aps_FrameFormat_t *aff, endPointDesc_t *epDesc,
zAddrType_t *SrcAddress, uint16 SrcPanId, NLDE_Signal_t *sig,
uint8 nwkSeqNum, uint8 SecurityUse, uint32 timestamp )
{
afIncomingMSGPacket_t *MSGpkt;
const uint8 len = sizeof( afIncomingMSGPacket_t ) + aff->asduLength;
uint8 *asdu = aff->asdu;
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_allocate( len );
if ( MSGpkt == NULL )
{
return;
}
MSGpkt->hdr.event = AF_INCOMING_MSG_CMD;
MSGpkt->groupId = aff->GroupID;
MSGpkt->clusterId = aff->ClusterID;
afCopyAddress( &MSGpkt->srcAddr, SrcAddress );
MSGpkt->srcAddr.endPoint = aff->SrcEndPoint;
MSGpkt->endPoint = epDesc->endPoint;
MSGpkt->wasBroadcast = aff->wasBroadcast;
MSGpkt->LinkQuality = sig->LinkQuality;
MSGpkt->correlation = sig->correlation;
MSGpkt->rssi = sig->rssi;
MSGpkt->SecurityUse = SecurityUse;
MSGpkt->timestamp = timestamp;
MSGpkt->nwkSeqNum = nwkSeqNum;
MSGpkt->macDestAddr = aff->macDestAddr;
MSGpkt->srcAddr.panId = SrcPanId;
MSGpkt->cmd.TransSeqNumber = 0;
MSGpkt->cmd.DataLength = aff->asduLength;
if ( MSGpkt->cmd.DataLength )
{
MSGpkt->cmd.Data = (uint8 *)(MSGpkt + 1);
osal_memcpy( MSGpkt->cmd.Data, asdu, MSGpkt->cmd.DataLength );
}
else
{
MSGpkt->cmd.Data = NULL;
}
#if defined ( MT_AF_CB_FUNC )
// If ZDO or SAPI have registered for this endpoint, dont intercept it here
if (AFCB_CHECK(CB_ID_AF_DATA_IND, *(epDesc->task_id)))
{
MT_AfIncomingMsg( (void *)MSGpkt );
// Release the memory.
osal_msg_deallocate( (void *)MSGpkt );
}
else
#endif
{
// Send message through task message.
osal_msg_send( *(epDesc->task_id), (uint8 *)MSGpkt );
}
}
uint8 AF_DataRequestDiscoverRoute = TRUE;
afStatus_t AF_DataRequest( afAddrType_t *dstAddr, endPointDesc_t *srcEP,
uint16 cID, uint16 len, uint8 *buf, uint8 *transID,
uint8 options, uint8 radius )
{
pDescCB pfnDescCB;
ZStatus_t stat;
APSDE_DataReq_t req;
afDataReqMTU_t mtu;
// Verify source end point
if ( srcEP == NULL )
{
return afStatus_INVALID_PARAMETER;
}
#if !defined( REFLECTOR )
if ( dstAddr->addrMode == afAddrNotPresent )
{
return afStatus_INVALID_PARAMETER;
}
#endif
// Check if route is available before sending data
if ( options & AF_LIMIT_CONCENTRATOR )
{
if ( dstAddr->addrMode != afAddr16Bit )
{
return ( afStatus_INVALID_PARAMETER );
}
// First, make sure the destination is not its self, then check for an existing route.
if ( (dstAddr->addr.shortAddr != NLME_GetShortAddr())
&& (RTG_CheckRtStatus( dstAddr->addr.shortAddr, RT_ACTIVE, (MTO_ROUTE | NO_ROUTE_CACHE) ) != RTG_SUCCESS) )
{
// A valid route to a concentrator wasn't found
return ( afStatus_NO_ROUTE );
}
}
// Validate broadcasting
if ( ( dstAddr->addrMode == afAddr16Bit ) ||
( dstAddr->addrMode == afAddrBroadcast ) )
{
// Check for valid broadcast values
if( ADDR_NOT_BCAST != NLME_IsAddressBroadcast( dstAddr->addr.shortAddr ) )
{
// Force mode to broadcast
dstAddr->addrMode = afAddrBroadcast;
}
else
{
// Address is not a valid broadcast type
if ( dstAddr->addrMode == afAddrBroadcast )
{
return afStatus_INVALID_PARAMETER;
}
}
}
else if ( dstAddr->addrMode != afAddr64Bit &&
dstAddr->addrMode != afAddrGroup &&
dstAddr->addrMode != afAddrNotPresent )
{
return afStatus_INVALID_PARAMETER;
}
// Set destination address
req.dstAddr.addrMode = dstAddr->addrMode;
if ( dstAddr->addrMode == afAddr64Bit )
osal_cpyExtAddr( req.dstAddr.addr.extAddr, dstAddr->addr.extAddr );
else
req.dstAddr.addr.shortAddr = dstAddr->addr.shortAddr;
req.profileID = ZDO_PROFILE_ID;
if ( (pfnDescCB = afGetDescCB( srcEP )) )
{
uint16 *pID = (uint16 *)(pfnDescCB(
AF_DESCRIPTOR_PROFILE_ID, srcEP->endPoint ));
if ( pID )
{
req.profileID = *pID;
osal_mem_free( pID );
}
}
else if ( srcEP->simpleDesc )
{
req.profileID = srcEP->simpleDesc->AppProfId;
}
req.txOptions = 0;
if ( ( options & AF_ACK_REQUEST ) &&
( req.dstAddr.addrMode != AddrBroadcast ) &&
( req.dstAddr.addrMode != AddrGroup ) )
{
req.txOptions |= APS_TX_OPTIONS_ACK;
}
if ( options & AF_SKIP_ROUTING )
{
req.txOptions |= APS_TX_OPTIONS_SKIP_ROUTING;
}
if ( options & AF_EN_SECURITY )
{
req.txOptions |= APS_TX_OPTIONS_SECURITY_ENABLE;
mtu.aps.secure = TRUE;
}
else
{
mtu.aps.secure = FALSE;
}
if ( options & AF_PREPROCESS )
{
req.txOptions |= APS_TX_OPTIONS_PREPROCESS;
}
mtu.kvp = FALSE;
req.transID = *transID;
req.srcEP = srcEP->endPoint;
req.dstEP = dstAddr->endPoint;
req.clusterID = cID;
req.asduLen = len;
req.asdu = buf;
req.discoverRoute = AF_DataRequestDiscoverRoute;//(uint8)((options & AF_DISCV_ROUTE) ? 1 : 0);
req.radiusCounter = radius;
#if defined ( INTER_PAN )
req.dstPanId = dstAddr->panId;
if ( StubAPS_InterPan( dstAddr->panId, dstAddr->endPoint ) )
{
if ( len > INTERP_DataReqMTU() )
{
stat = afStatus_INVALID_PARAMETER;
}
else
{
stat = INTERP_DataReq( &req );
}
}
else
#endif // INTER_PAN
{
if (len > afDataReqMTU( &mtu ) )
{
if (apsfSendFragmented)
{
stat = (*apsfSendFragmented)( &req );
}
else
{
stat = afStatus_INVALID_PARAMETER;
}
}
else
{
stat = APSDE_DataReq( &req );
}
}
if ( (req.dstAddr.addrMode == Addr16Bit) &&
(req.dstAddr.addr.shortAddr == NLME_GetShortAddr()) )
{
afDataConfirm( srcEP->endPoint, *transID, stat );
}
if ( stat == afStatus_SUCCESS )
{
(*transID)++;
}
return (afStatus_t)stat;
}
#if defined ( ZIGBEE_SOURCE_ROUTING )
afStatus_t AF_DataRequestSrcRtg( afAddrType_t *dstAddr, endPointDesc_t *srcEP,
uint16 cID, uint16 len, uint8 *buf, uint8 *transID,
uint8 options, uint8 radius, uint8 relayCnt, uint16* pRelayList )
{
uint8 status;
/* Add the source route to the source routing table */
status = RTG_AddSrcRtgEntry_Guaranteed( dstAddr->addr.shortAddr, relayCnt,
pRelayList );
if( status == RTG_SUCCESS)
{
/* Call AF_DataRequest to send the data */
status = AF_DataRequest( dstAddr, srcEP, cID, len, buf, transID, options, radius );
}
else if( status == RTG_INVALID_PATH )
{
/* The source route relay count is exceeding the network limit */
status = afStatus_INVALID_PARAMETER;
}
else
{
/* The guaranteed adding entry fails due to memory failure */
status = afStatus_MEM_FAIL;
}
return status;
}
#endif
static epList_t *afFindEndPointDescList( uint8 EndPoint )
{
epList_t *epSearch;
for (epSearch = epList; epSearch != NULL; epSearch = epSearch->nextDesc)
{
if (epSearch->epDesc->endPoint == EndPoint)
{
break;
}
}
return epSearch;
}
endPointDesc_t *afFindEndPointDesc( uint8 EndPoint )
{
epList_t *epSearch;
// Look for the endpoint
epSearch = afFindEndPointDescList( EndPoint );
if ( epSearch )
return ( epSearch->epDesc );
else
return ( (endPointDesc_t *)NULL );
}
uint8 afFindSimpleDesc( SimpleDescriptionFormat_t **ppDesc, uint8 EP )
{
epList_t *epItem = afFindEndPointDescList( EP );
uint8 rtrn = FALSE;
if ( epItem )
{
if ( epItem->pfnDescCB )
{
*ppDesc = epItem->pfnDescCB( AF_DESCRIPTOR_SIMPLE, EP );
rtrn = TRUE;
}
else
{
*ppDesc = epItem->epDesc->simpleDesc;
}
}
else
{
*ppDesc = NULL;
}
return rtrn;
}
static pDescCB afGetDescCB( endPointDesc_t *epDesc )
{
epList_t *epSearch;
// Start at the beginning
epSearch = epList;
// Look through the list until the end
while ( epSearch )
{
// Is there a match?
if ( epSearch->epDesc == epDesc )
{
return ( epSearch->pfnDescCB );
}
else
epSearch = epSearch->nextDesc; // Next entry
}
return ( (pDescCB)NULL );
}
uint8 afDataReqMTU( afDataReqMTU_t* fields )
{
uint8 len;
uint8 hdr;
if ( fields->kvp == TRUE )
{
hdr = AF_HDR_KVP_MAX_LEN;
}
else
{
hdr = AF_HDR_V1_1_MAX_LEN;
}
len = (uint8)(APSDE_DataReqMTU(&fields->aps) - hdr);
return len;
}
uint8 afGetMatch( uint8 ep )
{
epList_t *epSearch;
// Look for the endpoint
epSearch = afFindEndPointDescList( ep );
if ( epSearch )
{
if ( epSearch->flags & eEP_AllowMatch )
return ( TRUE );
else
return ( FALSE );
}
else
return ( FALSE );
}
uint8 afSetMatch( uint8 ep, uint8 action )
{
epList_t *epSearch;
// Look for the endpoint
epSearch = afFindEndPointDescList( ep );
if ( epSearch )
{
if ( action )
{
epSearch->flags |= eEP_AllowMatch;
}
else
{
epSearch->flags &= (eEP_AllowMatch ^ 0xFFFF);
}
return ( TRUE );
}
else
return ( FALSE );
}
uint8 afNumEndPoints( void )
{
epList_t *epSearch;
uint8 endpoints;
// Start at the beginning
epSearch = epList;
endpoints = 0;
while ( epSearch )
{
endpoints++;
epSearch = epSearch->nextDesc;
}
return ( endpoints );
}
void afEndPoints( uint8 *epBuf, uint8 skipZDO )
{
epList_t *epSearch;
uint8 endPoint;
// Start at the beginning
epSearch = epList;
while ( epSearch )
{
endPoint = epSearch->epDesc->endPoint;
if ( !skipZDO || endPoint != 0 )
*epBuf++ = endPoint;
epSearch = epSearch->nextDesc;
}
}
void afCopyAddress( afAddrType_t *afAddr, zAddrType_t *zAddr )
{
afAddr->addrMode = (afAddrMode_t)zAddr->addrMode;
if ( zAddr->addrMode == Addr64Bit )
{
(void)osal_cpyExtAddr( afAddr->addr.extAddr, zAddr->addr.extAddr );
}
else
{
afAddr->addr.shortAddr = zAddr->addr.shortAddr;
}
// Since zAddrType_t has no INTER-PAN information, set the panId member to zero.
afAddr->panId = 0;
}
void afAPSF_ConfigGet(uint8 endPoint, afAPSF_Config_t *pCfg)
{
epList_t *pList = afFindEndPointDescList(endPoint);
if (pList == NULL)
{
pCfg->frameDelay = APSF_DEFAULT_INTERFRAME_DELAY;
pCfg->windowSize = APSF_DEFAULT_WINDOW_SIZE;
}
else
{
(void)osal_memcpy(pCfg, &pList->apsfCfg, sizeof(afAPSF_Config_t));
}
}
afStatus_t afAPSF_ConfigSet(uint8 endPoint, afAPSF_Config_t *pCfg)
{
epList_t *pList = afFindEndPointDescList(endPoint);
if (pList == NULL)
{
return afStatus_INVALID_PARAMETER;
}
(void)osal_memcpy(&pList->apsfCfg, pCfg, sizeof(afAPSF_Config_t));
return afStatus_SUCCESS;
}
AF.h
#ifndef AF_H
#define AF_H
#ifdef __cplusplus
extern "C"
{
#endif
#include "ZComDef.h"
#include "nwk.h"
#include "APSMEDE.h"
#define AF_BROADCAST_ENDPOINT 0xFF
#define AF_PREPROCESS 0x04 // Will force APS to callback to preprocess before calling NWK layer
#define AF_LIMIT_CONCENTRATOR 0x08
#define AF_ACK_REQUEST 0x10
#define AF_DISCV_ROUTE 0x20 // This option is no longer used, and will be taken out later
#define AF_EN_SECURITY 0x40
#define AF_SKIP_ROUTING 0x80
// Backwards support for afAddOrSendMessage / afFillAndSendMessage.
#define AF_TX_OPTIONS_NONE 0
#define AF_MSG_ACK_REQUEST AF_ACK_REQUEST
// Default Radius Count value
#define AF_DEFAULT_RADIUS DEF_NWK_RADIUS
#define AF_MAX_USER_DESCRIPTOR_LEN 16
#define AF_USER_DESCRIPTOR_FILL 0x20
typedef struct
{
uint8 len; // Length of string descriptor
uint8 desc[AF_MAX_USER_DESCRIPTOR_LEN];
} UserDescriptorFormat_t;
// Node Logical Types
#define NODETYPE_COORDINATOR 0x00
#define NODETYPE_ROUTER 0x01
#define NODETYPE_DEVICE 0x02
// Node Frequency Band - bit map
#define NODEFREQ_800 0x01 // 868 - 868.6 MHz
#define NODEFREQ_900 0x04 // 902 - 928 MHz
#define NODEFREQ_2400 0x08 // 2400 - 2483.5 MHz
// Node MAC Capabilities - bit map
// Use CAPINFO_ALTPANCOORD, CAPINFO_DEVICETYPE_FFD,
// CAPINFO_DEVICETYPE_RFD, CAPINFO_POWER_AC,
// and CAPINFO_RCVR_ON_IDLE from NLMEDE.h
// Node Descriptor format structure
typedef struct
{
uint8 LogicalType:3;
uint8 ComplexDescAvail:1; /* AF_V1_SUPPORT - reserved bit. */
uint8 UserDescAvail:1; /* AF_V1_SUPPORT - reserved bit. */
uint8 Reserved:3;
uint8 APSFlags:3;
uint8 FrequencyBand:5;
uint8 CapabilityFlags;
uint8 ManufacturerCode[2];
uint8 MaxBufferSize;
uint8 MaxInTransferSize[2];
uint16 ServerMask;
uint8 MaxOutTransferSize[2];
uint8 DescriptorCapability;
} NodeDescriptorFormat_t;
// Bit masks for the ServerMask.
#define PRIM_TRUST_CENTER 0x01
#define BKUP_TRUST_CENTER 0x02
#define PRIM_BIND_TABLE 0x04
#define BKUP_BIND_TABLE 0x08
#define PRIM_DISC_TABLE 0x10
#define BKUP_DISC_TABLE 0x20
#define NETWORK_MANAGER 0x40
// Node Current Power Modes (CURPWR)
// Receiver permanently on or sync with coordinator beacon.
#define NODECURPWR_RCVR_ALWAYS_ON 0x00
// Receiver automatically comes on periodically as defined by the
// Node Power Descriptor.
#define NODECURPWR_RCVR_AUTO 0x01
// Receiver comes on when simulated, eg by a user pressing a button.
#define NODECURPWR_RCVR_STIM 0x02
// Node Available Power Sources (AVAILPWR) - bit map
// Can be used for AvailablePowerSources or CurrentPowerSource
#define NODEAVAILPWR_MAINS 0x01 // Constant (Mains) power
#define NODEAVAILPWR_RECHARGE 0x02 // Rechargeable Battery
#define NODEAVAILPWR_DISPOSE 0x04 // Disposable Battery
// Power Level
#define NODEPOWER_LEVEL_CRITICAL 0x00 // Critical
#define NODEPOWER_LEVEL_33 0x04 // 33%
#define NODEPOWER_LEVEL_66 0x08 // 66%
#define NODEPOWER_LEVEL_100 0x0C // 100%
// Node Power Descriptor format structure
typedef struct
{
unsigned int PowerMode:4;
unsigned int AvailablePowerSources:4;
unsigned int CurrentPowerSource:4;
unsigned int CurrentPowerSourceLevel:4;
} NodePowerDescriptorFormat_t;
// AppDevVer values
#define APPDEVVER_1 0x01
// AF_V1_SUPPORT AppFlags - bit map
#define APPFLAG_NONE 0x00 // Backwards compatibility to AF_V1.
// AF-AppFlags - bit map
#define AF_APPFLAG_NONE 0x00
#define AF_APPFLAG_COMPLEXDESC 0x01 // Complex Descriptor Available
#define AF_APPFLAG_USERDESC 0x02 // User Descriptor Available
typedef uint16 cId_t;
// Simple Description Format Structure
typedef struct
{
uint8 EndPoint;
uint16 AppProfId;
uint16 AppDeviceId;
uint8 AppDevVer:4;
uint8 Reserved:4; // AF_V1_SUPPORT uses for AppFlags:4.
uint8 AppNumInClusters;
cId_t *pAppInClusterList;
uint8 AppNumOutClusters;
cId_t *pAppOutClusterList;
} SimpleDescriptionFormat_t;
// Frame Types
#define FRAMETYPE_KVP 0x01 // 0001
#define FRAMETYPE_MSG 0x02 // 0010
#define ERRORCODE_SUCCESS 0x00
#define AF_HDR_KVP_MAX_LEN 0x08 // Max possible AF KVP header.
#define AF_HDR_V1_0_MAX_LEN 0x03 // Max possible AF Ver 1.0 header.
#define AF_HDR_V1_1_MAX_LEN 0x00 // Max possible AF Ver 1.1 header.
// Generalized MSG Command Format
typedef struct
{
uint8 TransSeqNumber;
uint16 DataLength; // Number of bytes in TransData
uint8 *Data;
} afMSGCommandFormat_t;
typedef enum
{
noLatencyReqs,
fastBeacons,
slowBeacons
} afNetworkLatencyReq_t;
typedef enum
{
afAddrNotPresent = AddrNotPresent,
afAddr16Bit = Addr16Bit,
afAddr64Bit = Addr64Bit,
afAddrGroup = AddrGroup,
afAddrBroadcast = AddrBroadcast
} afAddrMode_t;
typedef struct
{
union
{
uint16 shortAddr;
ZLongAddr_t extAddr;
} addr;
afAddrMode_t addrMode;
uint8 endPoint;
uint16 panId; // used for the INTER_PAN feature
} afAddrType_t;
typedef struct
{
osal_event_hdr_t hdr; /* OSAL Message header */
uint16 groupId; /* Message's group ID - 0 if not set */
uint16 clusterId; /* Message's cluster ID */
afAddrType_t srcAddr; /* Source Address, if endpoint is STUBAPS_INTER_PAN_EP,
it's an InterPAN message */
uint16 macDestAddr; /* MAC header destination short address */
uint8 endPoint; /* destination endpoint */
uint8 wasBroadcast; /* TRUE if network destination was a broadcast address */
uint8 LinkQuality; /* The link quality of the received data frame */
uint8 correlation; /* The raw correlation value of the received data frame */
int8 rssi; /* The received RF power in units dBm */
uint8 SecurityUse; /* deprecated */
uint32 timestamp; /* receipt timestamp from MAC */
uint8 nwkSeqNum; /* network header frame sequence number */
afMSGCommandFormat_t cmd; /* Application Data */
} afIncomingMSGPacket_t;
typedef struct
{
osal_event_hdr_t hdr;
uint8 endpoint;
uint8 transID;
} afDataConfirm_t;
// Endpoint Table - this table is the device description
// or application registration.
// There will be one entry in this table for every
// endpoint defined.
typedef struct
{
uint8 endPoint;
uint8 *task_id; // Pointer to location of the Application task ID.
SimpleDescriptionFormat_t *simpleDesc;
afNetworkLatencyReq_t latencyReq;
} endPointDesc_t;
// Typedef for callback function to retrieve an endpoints
// descriptors, contained in the endpoint descriptor.
// This will allow an application to dynamically change
// the descriptor and not use the RAM/ROM.
typedef void *(*pDescCB)( uint8 type, uint8 endpoint );
// Descriptor types used in the above callback
#define AF_DESCRIPTOR_SIMPLE 1
#define AF_DESCRIPTOR_PROFILE_ID 2
// Bit definitions for epList_t flags.
typedef enum
{
eEP_AllowMatch = 1,
eEP_NotUsed
} eEP_Flags;
typedef struct {
uint8 frameDelay;
uint8 windowSize;
} afAPSF_Config_t;
typedef struct _epList_t {
struct _epList_t *nextDesc;
endPointDesc_t *epDesc;
pDescCB pfnDescCB; // Don't use if this function pointer is NULL.
afAPSF_Config_t apsfCfg;
eEP_Flags flags;
} epList_t;
#define afStatus_SUCCESS ZSuccess /* 0x00 */
#define afStatus_FAILED ZFailure /* 0x01 */
#define afStatus_INVALID_PARAMETER ZInvalidParameter /* 0x02 */
#define afStatus_MEM_FAIL ZMemError /* 0x10 */
#define afStatus_NO_ROUTE ZNwkNoRoute /* 0xCD */
typedef ZStatus_t afStatus_t;
typedef struct
{
uint8 kvp;
APSDE_DataReqMTU_t aps;
} afDataReqMTU_t;
extern epList_t *epList;
//extern void afInit( void );
#define afInit() // No work to do for now.
extern epList_t *afRegisterExtended( endPointDesc_t *epDesc, pDescCB descFn );
extern afStatus_t afRegister( endPointDesc_t *epDesc );
extern afStatus_t afDelete( uint8 EndPoint );
extern void afDataConfirm( uint8 endPoint, uint8 transID, ZStatus_t status );
extern void afIncomingData( aps_FrameFormat_t *aff, zAddrType_t *SrcAddress, uint16 SrcPanId,
NLDE_Signal_t *sig, uint8 nwkSeqNum, uint8 SecurityUse, uint32 timestamp );
afStatus_t AF_DataRequest( afAddrType_t *dstAddr, endPointDesc_t *srcEP,
uint16 cID, uint16 len, uint8 *buf, uint8 *transID,
uint8 options, uint8 radius );
afStatus_t AF_DataRequestSrcRtg( afAddrType_t *dstAddr, endPointDesc_t *srcEP,
uint16 cID, uint16 len, uint8 *buf, uint8 *transID,
uint8 options, uint8 radius, uint8 relayCnt,
uint16* pRelayList );
extern endPointDesc_t *afFindEndPointDesc( uint8 endPoint );
extern uint8 afFindSimpleDesc( SimpleDescriptionFormat_t **ppDesc, uint8 EP );
extern uint8 afDataReqMTU( afDataReqMTU_t* fields );
extern uint8 afGetMatch( uint8 ep );
extern uint8 afSetMatch( uint8 ep, uint8 action );
extern uint8 afNumEndPoints( void );
extern void afEndPoints( uint8 *epBuf, uint8 skipZDO );
extern void afCopyAddress (afAddrType_t *afAddr, zAddrType_t *zAddr);
void afAPSF_ConfigGet(uint8 endPoint, afAPSF_Config_t *pCfg);
afStatus_t afAPSF_ConfigSet(uint8 endPoint, afAPSF_Config_t *pCfg);
#ifdef __cplusplus
}
#endif
#endif
Security(安全层)
ssp.h
#ifndef SSP_H
#define SSP_H
#ifdef __cplusplus
extern "C"
{
#endif
#include "ZComDef.h"
#define SSP_APPLY 0
#define SSP_REMOVE 1
// Auxiliary header field lengths
#define FRAME_COUNTER_LEN 4
#define SEC_KEY_LEN 16 // 128/8 octets (128-bit key is standard for ZigBee)
// Security Key Indentifiers
#define SEC_KEYID_LINK 0x00
#define SEC_KEYID_NWK 0x01
#define SEC_KEYID_TRANSPORT 0x02
#define SEC_KEYID_LOAD 0x03
// Security Levels
#define SEC_MASK 0x07
#define SEC_NONE 0x00
#define SEC_MIC_32 0x01
#define SEC_MIC_64 0x02
#define SEC_MIC_128 0x03
#define SEC_ENC 0x04
#define SEC_ENC_MIC_32 0x05
#define SEC_ENC_MIC_64 0x06
#define SEC_ENC_MIC_128 0x07
// Key types
#define KEY_TYPE_TC_MASTER 0 // Trust Center Master Key
#define KEY_TYPE_NWK 1 // Standard Network Key
#define KEY_TYPE_APP_MASTER 2 // Application Master Key
#define KEY_TYPE_APP_LINK 3 // Application Link Key
#define KEY_TYPE_TC_LINK 4 // Trust Center Link Key
#define KEY_TYPE_NWK_HIGH 5 // High Security Network Key
#define SSP_AUXHDR_CTRL 0
#define SSP_AUXHDR_FRAMECNTR 1
#define SSP_AUXHDR_KEYID_MASK 0x03
#define SSP_AUXHDR_KEYID_SHIFT 3
#define SSP_AUXHDR_EXTNONCE_SHIFT 5
#define SSP_AUXHDR_EXTNONCE_BIT 0x01
#define SSP_AUXHDR_LEVEL_MASK 0x07
#define SSP_AUXHDR_MIN_LEN 5
#define SSP_AUXHDR_SEQNUM_LEN 1
#define SSP_AUXHDR_EXT_LEN ( SSP_AUXHDR_MIN_LEN + Z_EXTADDR_LEN )
#define SSP_AUXHDR_NWK_LEN ( SSP_AUXHDR_EXT_LEN + SSP_AUXHDR_SEQNUM_LEN )
#define SSP_MIC_LEN_MAX 16
#define SSP_NONCE_LEN 13
#define SSP_TEXT_LEN 4
// SSP_MacTagData_t::type
#define SSP_MAC_TAGS_SKKE 0
#define SSP_MAC_TAGS_EA 1
// Error value used when security key NV ID is not available
#define SEC_NO_KEY_NV_ID 0
typedef struct
{
uint8 keySeqNum;
uint8 key[SEC_KEY_LEN];
} nwkKeyDesc;
typedef struct
{
nwkKeyDesc active;
uint32 frameCounter;
} nwkActiveKeyItems;
typedef struct
{
uint32 inFrmCntr;
uint32 outFrmCntr;
uint8 masterKey[SEC_KEY_LEN]; // optional!!
uint8 linkKey[SEC_KEY_LEN];
uint8 partnerDevice[Z_EXTADDR_LEN];
} linkKeyDesc;
typedef struct
{
uint8 hdrLen;
uint8 auxLen;
uint8 msgLen;
uint8 secLevel;
uint8 keyId;
uint32 frameCtr;
uint8 *key;
} ssp_ctx;
typedef struct
{
uint8* initExtAddr;
uint8* rspExtAddr;
uint8* key;
uint8* qeu;
uint8* qev;
uint8* text1;
uint8* text2;
uint8* tag1;
uint8* tag2;
uint8* linkKey;
uint8 type;
} SSP_MacTagData_t;
typedef struct
{
uint8 dir;
uint8 secLevel;
uint8 hdrLen;
uint8 sduLen; //service data unit length
uint8* pdu; //protocol data unit
uint8 extAddr[Z_EXTADDR_LEN];
uint8 keyID;
uint16 keyNvId; // NV ID of key: NWK, TCLK or APS
uint8 keySeqNum;
uint32 frmCntr;
uint8 auxLen;
uint8 micLen;
} SSP_Info_t;
extern uint32 nwkFrameCounter;
extern void SSP_Init( void );
extern void SSP_ParseAuxHdr( SSP_Info_t* si );
extern ZStatus_t SSP_Process( SSP_Info_t* si );
extern ZStatus_t SSP_GetMacTags( SSP_MacTagData_t* data );
extern void SSP_GetTrueRand( uint8 len, uint8 *rand );
extern ZStatus_t SSP_GetTrueRandAES( uint8 len, uint8 *rand );
extern void SSP_StoreRandomSeedNV( uint8 *pSeed );
extern void SSP_ReadNwkActiveKey( nwkActiveKeyItems *items );
extern uint16 SSP_GetNwkKey( uint8 seqNum );
extern ZStatus_t SSP_NwkSecurity(uint8 ed_flag, uint8 *msg, uint8 hdrLen, uint8 nsduLen);
extern void SSP_UpdateNwkKey( uint8 *key, uint8 keySeqNum );
extern void SSP_SwitchNwkKey( uint8 seqNum );
extern void SSP_BuildNonce( uint8 *addr, uint32 frameCntr, uint8 secCtrl, uint8 *nonce );
extern uint8 SSP_GetMicLen( uint8 securityLevel );
#ifdef __cplusplus
}
#endif
#endif /* SSP_H */
ssp.hash.h
#ifndef SSP_HASH_H
#define SSP_HASH_H
#ifdef __cplusplus
extern "C"
{
#endif
#include "ssp.h"
//void SSP_KeyedHash (uint8 *, uint16, uint8 *, uint8 *);
void sspMMOHash (uint8 *, uint8, uint8 *, uint16, uint8 *);
void SSP_KeyedHash (uint8 *M, uint16 bitlen, uint8 *AesKey, uint8 *Cstate);
#ifdef __cplusplus
}
#endif
#endif /* SSP_HASH_H */
Serives(地址处理函)
saddr.c
#include "hal_types.h"
#include "OSAL.h"
#include "saddr.h"
bool sAddrCmp(const sAddr_t *pAddr1, const sAddr_t *pAddr2)
{
if (pAddr1->addrMode != pAddr2->addrMode)
{
return FALSE;
}
else if (pAddr1->addrMode == SADDR_MODE_NONE)
{
return FALSE;
}
else if (pAddr1->addrMode == SADDR_MODE_SHORT)
{
return (bool) (pAddr1->addr.shortAddr == pAddr2->addr.shortAddr);
}
else if (pAddr1->addrMode == SADDR_MODE_EXT)
{
return (sAddrExtCmp(pAddr1->addr.extAddr, pAddr2->addr.extAddr));
}
else
{
return FALSE;
}
}
bool sAddrIden(const sAddr_t *pAddr1, const sAddr_t *pAddr2)
{
// first check if the address modes are the same
if (pAddr1->addrMode != pAddr2->addrMode)
{
// no, so no point in comparing any further
return FALSE;
}
// the address modes are the same; check if there is no address
else if (pAddr1->addrMode == SADDR_MODE_NONE)
{
// no address, so no need to compare any further as both addresses have the
// same address mode but no address, so they are identical
return TRUE;
}
// there's an address; check if it is short
else if (pAddr1->addrMode == SADDR_MODE_SHORT)
{
// compare short addresses
return (bool) (pAddr1->addr.shortAddr == pAddr2->addr.shortAddr);
}
// there's an address; check if it is extended
else if (pAddr1->addrMode == SADDR_MODE_EXT)
{
// compare extended addresses
return (sAddrExtCmp(pAddr1->addr.extAddr, pAddr2->addr.extAddr));
}
else // unknown error
{
return FALSE;
}
}
void sAddrCpy(sAddr_t *pDest, const sAddr_t *pSrc)
{
pDest->addrMode = pSrc->addrMode;
if (pDest->addrMode == SADDR_MODE_EXT)
{
sAddrExtCpy(pDest->addr.extAddr, pSrc->addr.extAddr);
}
else
{
pDest->addr.shortAddr = pSrc->addr.shortAddr;
}
}
bool sAddrExtCmp(const uint8 * pAddr1, const uint8 * pAddr2)
{
uint8 i;
for (i = SADDR_EXT_LEN; i != 0; i--)
{
if (*pAddr1++ != *pAddr2++)
{
return FALSE;
}
}
return TRUE;
}
void *sAddrExtCpy(uint8 * pDest, const uint8 * pSrc)
{
return osal_memcpy(pDest, pSrc, SADDR_EXT_LEN);
}
saddr.h
#ifndef SADDR_H
#define SADDR_H
#ifdef __cplusplus
extern "C" {
#endif
/* Extended address length */
#define SADDR_EXT_LEN 8
/* Address modes */
#define SADDR_MODE_NONE 0 /* Address not present */
#define SADDR_MODE_SHORT 2 /* Short address */
#define SADDR_MODE_EXT 3 /* Extended address */
/* Extended address */
typedef uint8 sAddrExt_t[SADDR_EXT_LEN];
/* Combined short/extended device address */
typedef struct
{
union
{
uint16 shortAddr; /* Short address */
sAddrExt_t extAddr; /* Extended address */
} addr;
uint8 addrMode; /* Address mode */
} sAddr_t;
extern bool sAddrCmp(const sAddr_t *pAddr1, const sAddr_t *pAddr2);
extern bool sAddrIden(const sAddr_t *pAddr1, const sAddr_t *pAddr2);
extern void sAddrCpy(sAddr_t *pDest, const sAddr_t *pSrc);
extern bool sAddrExtCmp(const uint8 * pAddr1, const uint8 * pAddr2);
void *sAddrExtCpy(uint8 * pDest, const uint8 * pSrc);
#ifdef __cplusplus
}
#endif
#endif /* SADDR_H */
Tools(工程配置)
f8w2530.xcl
另外说明
f8wConfig.cfg
-DZIGBEEPRO
/* Set to 0 for no security, otherwise non-0 */
-DSECURE=0
-DZG_SECURE_DYNAMIC=0
/* Enable the Reflector */
-DREFLECTOR
/* Default channel is Channel 11 - 0x0B */
// Channels are defined in the following:
// 0 : 868 MHz 0x00000001
// 1 - 10 : 915 MHz 0x000007FE
// 11 - 26 : 2.4 GHz 0x07FFF800
//
//-DMAX_CHANNELS_868MHZ 0x00000001
//-DMAX_CHANNELS_915MHZ 0x000007FE
//-DMAX_CHANNELS_24GHZ 0x07FFF800
//-DDEFAULT_CHANLIST=0x04000000 // 26 - 0x1A
//-DDEFAULT_CHANLIST=0x02000000 // 25 - 0x19
//-DDEFAULT_CHANLIST=0x01000000 // 24 - 0x18
//-DDEFAULT_CHANLIST=0x00800000 // 23 - 0x17
//-DDEFAULT_CHANLIST=0x00400000 // 22 - 0x16
//-DDEFAULT_CHANLIST=0x00200000 // 21 - 0x15
//-DDEFAULT_CHANLIST=0x00100000 // 20 - 0x14
//-DDEFAULT_CHANLIST=0x00080000 // 19 - 0x13
//-DDEFAULT_CHANLIST=0x00040000 // 18 - 0x12
//-DDEFAULT_CHANLIST=0x00020000 // 17 - 0x11
//-DDEFAULT_CHANLIST=0x00010000 // 16 - 0x10
//-DDEFAULT_CHANLIST=0x00008000 // 15 - 0x0F
//-DDEFAULT_CHANLIST=0x00004000 // 14 - 0x0E
//-DDEFAULT_CHANLIST=0x00002000 // 13 - 0x0D
//-DDEFAULT_CHANLIST=0x00001000 // 12 - 0x0C
-DDEFAULT_CHANLIST=0x00000800 // 11 - 0x0B
/* Define the default PAN ID.
*
* Setting this to a value other than 0xFFFF causes
* ZDO_COORD to use this value as its PAN ID and
* Routers and end devices to join PAN with this ID
*/
-DZDAPP_CONFIG_PAN_ID=0xFFF1
/* Minimum number of milliseconds to hold off the start of the device
* in the network and the minimum delay between joining cycles.
*/
-DNWK_START_DELAY=100
/* Mask for the random joining delay. This value is masked with
* the return from osal_rand() to get a random delay time for
* each joining cycle. This random value is added to NWK_START_DELAY.
* For example, a value of 0x007F will be a joining delay of 0 to 127
* milliseconds.
*/
-DEXTENDED_JOINING_RANDOM_MASK=0x007F
/* Minimum number of milliseconds to delay between each beacon request
* in a joining cycle.
*/
-DBEACON_REQUEST_DELAY=100
-DBEACON_REQ_DELAY_MASK=0x00FF
-DLINK_STATUS_JITTER_MASK=0x007F
/* in seconds; set to 0 to turn off route expiry */
-DROUTE_EXPIRY_TIME=30
-DAPSC_ACK_WAIT_DURATION_POLLED=3000
/* Default indirect message holding timeout value:
* 1-65535 (0 -> 65536) X CNT_RTG_TIMER X RTG_TIMER_INTERVAL
*/
-DNWK_INDIRECT_MSG_TIMEOUT=7
/* The number of simultaneous route discoveries in network */
-DMAX_RREQ_ENTRIES=8
/* The maximum number of retries allowed after a transmission failure */
-DAPSC_MAX_FRAME_RETRIES=3
/* Max number of times retry looking for the next hop address of a message */
-DNWK_MAX_DATA_RETRIES=2
-DMAX_POLL_FAILURE_RETRIES=2
/* The number of items in the broadcast table */
-DMAX_BCAST=9
/* The maximum number of groups in the groups table */
-DAPS_MAX_GROUPS=16
/* Number of entries in the regular routing table plus additional
* entries for route repair
*/
-DMAX_RTG_ENTRIES=40
/* Maximum number of entries in the Binding table. */
-DNWK_MAX_BINDING_ENTRIES=4
/* Maximum number of cluster IDs for each binding table entry.
* Note that any value other than the default value may cause a
* compilation warning but Device Binding will function correctly.
*/
-DMAX_BINDING_CLUSTER_IDS=4
/* Default security key. */
-DDEFAULT_KEY="{0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0D}"
/* Reset when ASSERT occurs, otherwise flash LEDs */
//-DASSERT_RESET
/* Set the MAC MAX Frame Size (802.15.4 default is 102) */
-DMAC_MAX_FRAME_SIZE=116
/* Minimum transmissions attempted for Channel Interference detection,
* Frequency Agility can be disabled by setting this parameter to zero.
*/
-DZDNWKMGR_MIN_TRANSMISSIONS=20
/* Compiler keywords */
-DCONST="const __code"
-DGENERIC=__generic
-DRFD_RCVC_ALWAYS_ON=FALSE
/* The number of milliseconds to wait between data request polls to the coordinator. */
-DPOLL_RATE=1000
-DQUEUED_POLL_RATE=100
-DRESPONSE_POLL_RATE=100
-DREJOIN_POLL_RATE=440
f8wCoord.cfg
-DCPU32MHZ // CC2530s Run at 32MHz
-DROOT=__near_func // MAC/ZMAC code in NEAR
/* MAC Settings */
-DMAC_CFG_APP_PENDING_QUEUE=TRUE
-DMAC_CFG_TX_DATA_MAX=5
-DMAC_CFG_TX_MAX=8
-DMAC_CFG_RX_MAX=5
/* Coordinator Settings */
-DZDO_COORDINATOR // Coordinator Functions
-DRTR_NWK // Router Functions
f8wEndev.cfg
-DCPU32MHZ // CC2530s Run at 32MHz
-DROOT=__near_func // MAC/ZMAC code in NEAR
/* MAC Settings */
-DMAC_CFG_TX_DATA_MAX=3
-DMAC_CFG_TX_MAX=6
-DMAC_CFG_RX_MAX=3
f8wRounter.cfg
-DCPU32MHZ // CC2530s Run at 32MHz
-DROOT=__near_func // MAC/ZMAC code in NEAR
/* MAC Settings */
-DMAC_CFG_APP_PENDING_QUEUE=TRUE
-DMAC_CFG_TX_DATA_MAX=5
-DMAC_CFG_TX_MAX=8
-DMAC_CFG_RX_MAX=5
/* Router Settings */
-DRTR_NWK // Router Functions