/*
*********************************************************************************************************
*                                              uC/TCP-IP
*                                      The Embedded TCP/IP Suite
*
*                          (c) Copyright 2003-2007; Micrium, Inc.; Weston, FL
*
*               All rights reserved.  Protected by international copyright laws.
*
*               uC/TCP-IP is provided in source form for FREE evaluation, for educational
*               use or peaceful research.  If you plan on using uC/TCP-IP in a commercial
*               product you need to contact Micrium to properly license its use in your
*               product.  We provide ALL the source code for your convenience and to help
*               you experience uC/TCP-IP.  The fact that the source code is provided does
*               NOT mean that you can use it without paying a licensing fee.
*
*               Knowledge of the source code may NOT be used to develop a similar product.
*
*               Please help us continue to provide the Embedded community with the finest
*               software available.  Your honesty is greatly appreciated.
*********************************************************************************************************
*/

/*
*********************************************************************************************************
*
*                                          NETWORK ARP LAYER
*                                    (ADDRESS RESOLUTION PROTOCOL)
*
* Filename      : net_arp.c
* Version       : V1.89
* Programmer(s) : ITJ
*********************************************************************************************************
* Note(s)       : (1) Address Resolution Protocol ONLY required for network interfaces that require
*                     network-address-to-hardware-address bindings (see RFC #826 'Abstract').
*
*                 (2) Supports Address Resolution Protocol as described in RFC #826 with the following
*                     restrictions/constraints :
*
*                     (a) ONLY supports the following hardware types :
*                         (1) 48-bit Ethernet
*
*                     (b) ONLY supports the following protocol types :
*                         (1) 32-bit IP
*********************************************************************************************************
*/

/*
*********************************************************************************************************
*                                            INCLUDE FILES
*********************************************************************************************************
*/

#define    NET_ARP_MODULE
#include  <net.h>


/*
*********************************************************************************************************
*                                               MODULE
*
* Note(s) : (1) See 'net_arp.h  MODULE'.
*********************************************************************************************************
*/

#ifdef  NET_ARP_MODULE_PRESENT


/*$PAGE*/
/*
*********************************************************************************************************
*                                            LOCAL DEFINES
*********************************************************************************************************
*/


/*
*********************************************************************************************************
*                                           LOCAL CONSTANTS
*********************************************************************************************************
*/


/*
*********************************************************************************************************
*                                          LOCAL DATA TYPES
*********************************************************************************************************
*/


/*
*********************************************************************************************************
*                                            LOCAL TABLES
*********************************************************************************************************
*/


/*
*********************************************************************************************************
*                                       LOCAL GLOBAL VARIABLES
*********************************************************************************************************
*/


/*$PAGE*/
/*
*********************************************************************************************************
*                                      LOCAL FUNCTION PROTOTYPES
*********************************************************************************************************
*/

                                                                            /* --------------- RX FNCTS --------------- */

#if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)
static  void            NetARP_RxPktValidateBuf     (NET_BUF_HDR    *pbuf_hdr,
                                                     NET_ERR        *perr);
#endif

static  void            NetARP_RxPktValidate        (NET_BUF_HDR    *pbuf_hdr,
                                                     NET_ARP_HDR    *parp_hdr,
                                                     NET_ERR        *perr);


static  void            NetARP_RxPktCacheUpdate     (NET_ARP_HDR    *parp_hdr,
                                                     NET_ERR        *perr);


static  void            NetARP_RxPktReply           (NET_ARP_HDR    *parp_hdr,
                                                     NET_ERR        *perr);


static  void            NetARP_RxPktIsTargetThisHost(NET_ARP_HDR    *parp_hdr,
                                                     NET_ERR        *perr);


static  void            NetARP_RxPktFree            (NET_BUF        *pbuf);

static  void            NetARP_RxPktDiscard         (NET_BUF        *pbuf,
                                                     NET_ERR        *perr);
                                                   

                                                                            /* --------------- TX FNCTS --------------- */

static  void            NetARP_Tx                   (CPU_INT08U     *paddr_hw_sender,
                                                     CPU_INT08U     *paddr_hw_target,
                                                     CPU_INT08U     *paddr_protocol_sender,
                                                     CPU_INT08U     *paddr_protocol_target,
                                                     CPU_INT16U      op_code,
                                                     NET_ERR        *perr);


static  void            NetARP_TxReq                (NET_ARP_CACHE  *pcache);

static  void            NetARP_TxReply              (NET_ARP_HDR    *parp_hdr);


static  void            NetARP_TxPktPrepareHdr      (NET_BUF        *pbuf,
                                                     CPU_INT16U      msg_ix,
                                                     CPU_INT08U     *paddr_hw_sender,
                                                     CPU_INT08U     *paddr_hw_target,
                                                     CPU_INT08U     *paddr_protocol_sender,
                                                     CPU_INT08U     *paddr_protocol_target,
                                                     CPU_INT16U      op_code,
                                                     NET_ERR        *perr);


static  void            NetARP_TxPktFree            (NET_BUF        *pbuf);

static  void            NetARP_TxPktDiscard         (NET_BUF        *pbuf,
                                                     NET_ERR        *perr);


/*$PAGE*/
                                                                            /* ----------- ARP CACHE FNCTS ------------ */

static  void            NetARP_CacheTxPktHandler    (NET_BUF        *pbuf_q,
                                                     CPU_INT08U     *paddr_hw);


static  NET_ARP_CACHE  *NetARP_CacheSrch            (CPU_INT08U     *paddr_protocol);


static  void            NetARP_CacheAddPend         (NET_BUF        *pbuf,
                                                     NET_BUF_HDR    *pbuf_hdr,
                                                     CPU_INT08U     *paddr_protocol,
                                                     NET_ERR        *perr);

static  void            NetARP_CacheAddResolved     (CPU_INT08U     *paddr_hw,
                                                     CPU_INT08U     *paddr_protocol,
                                                     NET_ERR        *perr);


static  void            NetARP_CacheRemove          (NET_ARP_CACHE  *pcache,
                                                     CPU_BOOLEAN     tmr_free);

static  void            NetARP_CacheInsert          (NET_ARP_CACHE  *pcache);

static  void            NetARP_CacheUnlink          (NET_ARP_CACHE  *pcache);


static  void            NetARP_CacheUnlinkBuf       (NET_BUF        *pbuf);


static  void            NetARP_CacheReqTimeout      (void           *pcache_timeout);

static  void            NetARP_CacheTimeout         (void           *pcache_timeout);


static  NET_ARP_CACHE  *NetARP_CacheCfg             (CPU_INT08U     *paddr_hw,
                                                     CPU_INT08U     *paddr_protocol,
                                                     CPU_FNCT_PTR    timeout_fnct,
                                                     NET_TMR_TICK    timeout_tick,
                                                     NET_ERR        *perr);

static  NET_ARP_CACHE  *NetARP_CacheGet             (NET_ERR        *perr);

static  void            NetARP_CacheFree            (NET_ARP_CACHE  *pcache,
                                                     CPU_BOOLEAN     tmr_free);

static  void            NetARP_CacheClr             (NET_ARP_CACHE  *pcache);

#if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)
static  CPU_BOOLEAN     NetARP_CacheIsUsed          (NET_ARP_CACHE  *pcache);

static  void            NetARP_CacheDiscard         (NET_ARP_CACHE  *pcache);
#endif


/*
*********************************************************************************************************
*                                     LOCAL CONFIGURATION ERRORS
*********************************************************************************************************
*/


/*$PAGE*/
/*
*********************************************************************************************************
*                                            NetARP_Init()
*
* Description : (1) Initialize Address Resolution Protocol :
*
*                   (a) Initialize ARP cache pool
*                   (b) Initialize ARP cache table
*                   (c) Initialize ARP cache list pointers
*                   (d) Initialize ARP host address pointers
*                   (e) Initialize ARP statistics & error counters
*
*
* Argument(s) : none.
*
* Return(s)   : none.
*
* Caller(s)   : NetIF_Init().
*
*               This function is a network protocol suite to network interface (IF) function & SHOULD be 
*               called only by appropriate network interface initialization function(s).
*
* Note(s)     : (2) ARP cache pool MUST be initialized PRIOR to initializing the pool with pointers to
*                   ARP caches.
*********************************************************************************************************
*/

void  NetARP_Init (void)
{
    NET_ARP_CACHE      *pcache;
    NET_ARP_CACHE_QTY   i;
    NET_ERR             stat_err;


                                                                /* ------------ INIT ARP CACHE POOL/STATS ------------- */
    NetARP_CachePoolPtr = (NET_ARP_CACHE *)0;                   /* Init-clr ARP cache pool (see Note #2).               */

    NetStat_PoolInit((NET_STAT_POOL   *)&NetARP_CachePoolStat,
                     (NET_STAT_POOL_QTY) NET_ARP_CFG_NBR_CACHE,
                     (NET_ERR         *)&stat_err);

#if (NET_CTR_CFG_STAT_EN    == DEF_ENABLED)
    NetARP_CacheSrchCtr_lo  =  0;
    NetARP_CacheSrchCtr_hi  =  0;
    NetARP_CacheFoundCtr_lo =  0;
    NetARP_CacheFoundCtr_hi =  0;
    NetARP_CacheFoundPct    =  0;
#endif

                                                                /* ---------------- INIT ARP CACHE TBL ---------------- */
    pcache = &NetARP_CacheTbl[0];
    for (i = 0; i < NET_ARP_CFG_NBR_CACHE; i++) {
                                                                /* Init each ARP cache type/addr len--NEVER modify.     */
        pcache->Type            =  NET_ARP_TYPE_CACHE;
        pcache->HW_Type         =  NET_ARP_CFG_HW_TYPE;
        pcache->HW_AddrLen      =  NET_ARP_CFG_HW_ADDR_LEN;
        pcache->ProtocolType    =  NET_ARP_CFG_PROTOCOL_TYPE;
        pcache->ProtocolAddrLen =  NET_ARP_CFG_PROTOCOL_ADDR_LEN;

        pcache->State           =  NET_ARP_CACHE_STATE_FREE;    /* Init each ARP cache as free/NOT used.                */
        pcache->Flags           =  NET_ARP_FLAG_NONE;

#if (NET_DBG_CFG_MEM_CLR_EN == DEF_ENABLED)
        NetARP_CacheClr(pcache);
#endif
                                                                /* Free each ARP cache to cache pool (see Note #2).     */
        pcache->NextPtr     = (void          *)NetARP_CachePoolPtr;
        NetARP_CachePoolPtr = (NET_ARP_CACHE *)pcache;

        pcache++;
    }

                                                                /* ------------- INIT ARP CACHE LIST PTRS ------------- */
    NetARP_CacheListHead       = (NET_ARP_CACHE *)0;
    NetARP_CacheListTail       = (NET_ARP_CACHE *)0;


                                                                /* ------------- INIT ARP HOST  ADDR PTRS ------------- */
    NetARP_HostAddrPtrHW       =  NetARP_GetHostAddrPtrHW();
    NetARP_HostAddrPtrProtocol =  NetARP_GetHostAddrPtrProtocol();


/*$PAGE*/
                                                                /* ------------- INIT ARP STAT & ERR CTRS ------------- */
#if (NET_CTR_CFG_STAT_EN                == DEF_ENABLED)
    NetARP_StatRxPktCtr                 =  0;
    NetARP_StatRxMsgProcessedCtr        =  0;
    NetARP_StatRxMsgReqProcessedCtr     =  0;
    NetARP_StatRxMsgReplyProcessedCtr   =  0;

    NetARP_StatTxMsgCtr                 =  0;
    NetARP_StatTxMsgReqCtr              =  0;
    NetARP_StatTxMsgReplyCtr            =  0;
#endif


#if (NET_CTR_CFG_ERR_EN                 == DEF_ENABLED)
    NetARP_ErrNoneAvailCtr              =  0;


    NetARP_ErrRxHdrHW_TypeCtr           =  0;
    NetARP_ErrRxHdrHW_AddrLenCtr        =  0;
    NetARP_ErrRxHdrHW_AddrCtr           =  0;
    NetARP_ErrRxHdrProtocolTypeCtr      =  0;
    NetARP_ErrRxHdrProtocolAddrLenCtr   =  0;
    NetARP_ErrRxHdrProtocolAddrCtr      =  0;
    NetARP_ErrRxHdrOpCodeCtr            =  0;
    NetARP_ErrRxHdrOpAddrCtr            =  0;
    NetARP_ErrRxHdrMsgLenCtr            =  0;

    NetARP_ErrRxPktTargetReplyCtr       =  0;
    NetARP_ErrRxPktTargetNotThisHostCtr =  0;
    NetARP_ErrRxPktDiscardedCtr         =  0;


    NetARP_ErrTxPktDiscardedCtr         =  0;


#if ((NET_ERR_CFG_ARG_CHK_EXT_EN        == DEF_ENABLED) || \
     (NET_ERR_CFG_ARG_CHK_DBG_EN        == DEF_ENABLED))
    NetARP_ErrNullPtrCtr                =  0;
#endif

#if  (NET_ERR_CFG_ARG_CHK_EXT_EN        == DEF_ENABLED)
    NetARP_ErrInvalidAddrLenHW_Ctr      =  0;
    NetARP_ErrInvalidAddrLenProtocolCtr =  0;
#endif

#if  (NET_ERR_CFG_ARG_CHK_DBG_EN        == DEF_ENABLED)
    NetARP_ErrNotUsedCtr                =  0;

    NetARP_ErrInvalidTypeCtr            =  0;


    NetARP_ErrRxInvalidBufIxCtr         =  0;

    NetARP_ErrTxHdrOpCodeCtr            =  0;
#endif
#endif
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                      NetARP_CfgCacheTimeout()
*
* Description : Configure ARP cache timeout from ARP Cache List.
*
* Argument(s) : timeout_sec     Desired value for ARP cache timeout (in seconds).
*
* Return(s)   : DEF_OK, ARP cache timeout configured.
*
* Caller(s)   : Net_InitDflt(),
*               Application.
*
*               This function is a network protocol suite application interface (API) function & MAY be 
*               called by application function(s).
*
* Note(s)     : (1) RFC #1122, Section 2.3.2.1 states that "an implementation of the Address Resolution
*                   Protocol (ARP) ... MUST provide a mechanism to flush out-of-date cache entries.  If
*                   this mechanism involves a timeout, it SHOULD be possible to configure the timeout
*                   value".
*
*               (2) Timeout in seconds converted to 'NET_TMR_TICK' ticks in order to pre-compute initial 
*                   timeout value in 'NET_TMR_TICK' ticks.
*********************************************************************************************************
*/

CPU_BOOLEAN  NetARP_CfgCacheTimeout (CPU_INT16U  timeout_sec)
{
#if (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)
    CPU_SR        cpu_sr;
#endif
    NET_TMR_TICK  tick;


#if (NET_ARP_CACHE_TIMEOUT_MIN_SEC > DEF_INT_16U_MIN_VAL)
    if (timeout_sec < NET_ARP_CACHE_TIMEOUT_MIN_SEC) {
        timeout_sec = NET_ARP_CACHE_TIMEOUT_MIN_SEC;
    }
#endif
#if (NET_ARP_CACHE_TIMEOUT_MAX_SEC < DEF_INT_16U_MAX_VAL)
    if (timeout_sec > NET_ARP_CACHE_TIMEOUT_MAX_SEC) {
        timeout_sec = NET_ARP_CACHE_TIMEOUT_MAX_SEC;
    }
#endif

    tick                     = (NET_TMR_TICK)timeout_sec * NET_TMR_TIME_TICK_PER_SEC;
    CPU_CRITICAL_ENTER();
    NetARP_CacheTimeout_sec  =  timeout_sec;
    NetARP_CacheTimeout_tick =  tick;
    CPU_CRITICAL_EXIT();

    return (DEF_OK);
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                      NetARP_CfgCacheAccessedTh()
*
* Description : Configure ARP cache accessed-promotion threshold.
*
* Argument(s) : nbr_access      Desired number of ARP cache accesses before ARP cache is promoted.
*
* Return(s)   : DEF_OK, ARP cache promotion threshold configured.
*
* Caller(s)   : Net_InitDflt(),
*               Application.
*
*               This function is a network protocol suite application interface (API) function & MAY be 
*               called by application function(s).
*
* Note(s)     : none.
*********************************************************************************************************
*/

CPU_BOOLEAN  NetARP_CfgCacheAccessedTh (CPU_INT16U  nbr_access)
{
#if (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)
    CPU_SR  cpu_sr;
#endif


#if (NET_ARP_CACHE_ACCESSED_TH_MIN > DEF_INT_16U_MIN_VAL)
    if (nbr_access < NET_ARP_CACHE_ACCESSED_TH_MIN) {
        nbr_access = NET_ARP_CACHE_ACCESSED_TH_MIN;
    }
#endif
#if (NET_ARP_CACHE_ACCESSED_TH_MAX < DEF_INT_16U_MAX_VAL)
    if (nbr_access > NET_ARP_CACHE_ACCESSED_TH_MAX) {
        nbr_access = NET_ARP_CACHE_ACCESSED_TH_MAX;
    }
#endif

    CPU_CRITICAL_ENTER();
    NetARP_CacheAccessedTh_nbr = nbr_access;
    CPU_CRITICAL_EXIT();

    return (DEF_OK);
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                       NetARP_CfgReqTimeout()
*
* Description : Configure timeout between ARP Request retries.
*
* Argument(s) : timeout_sec     Desired value for ARP Request pending ARP Reply timeout (in seconds).
*
* Return(s)   : DEF_OK, ARP Request timeout configured.
*
* Caller(s)   : Net_InitDflt(),
*               Application.
*
*               This function is a network protocol suite application interface (API) function & MAY be 
*               called by application function(s).
*
* Note(s)     : (1) Timeout in seconds converted to 'NET_TMR_TICK' ticks in order to pre-compute initial 
*                   timeout value in 'NET_TMR_TICK' ticks.
*********************************************************************************************************
*/

CPU_BOOLEAN  NetARP_CfgReqTimeout (CPU_INT08U  timeout_sec)
{
#if (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)
    CPU_SR        cpu_sr;
#endif
    NET_TMR_TICK  tick;


    if (timeout_sec < NET_ARP_REQ_RETRY_TIMEOUT_MIN_SEC) {
        timeout_sec = NET_ARP_REQ_RETRY_TIMEOUT_MIN_SEC;
    }
    if (timeout_sec > NET_ARP_REQ_RETRY_TIMEOUT_MAX_SEC) {
        timeout_sec = NET_ARP_REQ_RETRY_TIMEOUT_MAX_SEC;
    }

    tick                   = (NET_TMR_TICK)timeout_sec * NET_TMR_TIME_TICK_PER_SEC;
    CPU_CRITICAL_ENTER();
    NetARP_ReqTimeout_sec  =  timeout_sec;
    NetARP_ReqTimeout_tick =  tick;
    CPU_CRITICAL_EXIT();

    return (DEF_OK);
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                      NetARP_CfgReqMaxRetries()
*
* Description : Configure ARP Request maximum number of requests.
*
* Argument(s) : max_nbr_retries     Desired maximum number of ARP Request attempts.
*
* Return(s)   : DEF_OK, ARP Request maximum number of attempts configured.
*
* Caller(s)   : Net_InitDflt(),
*               Application.
*
*               This function is a network protocol suite application interface (API) function & MAY be 
*               called by application function(s).
*
* Note(s)     : (1) An ARP cache monitors the number of ARP Requests transmitted before receiving an ARP
*                   Reply.  In other words, an ARP cache monitors the number of ARP Request attempts.
*
*                   However, the maximum number of ARP Requests that each ARP cache is allowed to transmit
*                   is configured in terms of retries.  Thus, the total number of attempts is equal to the
*                   configured number of retries plus one (1).
*********************************************************************************************************
*/

CPU_BOOLEAN  NetARP_CfgReqMaxRetries (CPU_INT08U  max_nbr_retries)
{
#if (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)
    CPU_SR  cpu_sr;
#endif


#if (NET_ARP_REQ_RETRY_MIN > DEF_INT_08U_MIN_VAL)
    if (max_nbr_retries < NET_ARP_REQ_RETRY_MIN) {
        max_nbr_retries = NET_ARP_REQ_RETRY_MIN;
    }
#endif
#if (NET_ARP_REQ_RETRY_MAX < DEF_INT_08U_MAX_VAL)
    if (max_nbr_retries > NET_ARP_REQ_RETRY_MAX) {
        max_nbr_retries = NET_ARP_REQ_RETRY_MAX;
    }
#endif

    CPU_CRITICAL_ENTER();
    NetARP_ReqMaxAttempts_nbr = max_nbr_retries + 1;        /* Set max attempts as max retries + 1 (see Note #1).       */
    CPU_CRITICAL_EXIT();

    return (DEF_OK);
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                       NetARP_ProbeAddrOnNet()
*
* Description : (1) Transmit an ARP Request to probe the local network for a specific protocol address :
*
*                   (a) Remove ARP cache with desired protocol address from ARP Cache List, if available
*                   (b) Configure ARP cache :
*                       (1) Get default-configured ARP cache
*                       (2) ARP cache state
*                   (c) Transmit ARP Request to probe local network for desired protocol address
*
*
*               (2) NetARP_ProbeAddrOnNet() SHOULD be used in conjunction with NetARP_CacheGetAddrHW()
*                   to determine if a specific protocol address is available on the local network :
*
*                   (a) After successfully transmitting an ARP Request to probe the local network &  ...
*                   (b) After some time delay(s) [on the order of ARP Request timeouts & retries];   ...
*                   (c) Check ARP Cache for the hardware address of a host on the local network that
*                           corresponds to the desired protocol address.
*
*                   See also 'NetARP_CacheGetAddrHW()  Note #1'.
*
*
* Argument(s) : paddr_protocol      Pointer to protocol address to probe local network (see Note #3).
*
*               addr_protocol_len   Length  of protocol address (in octets) [see Note #4].
*
*               perr                Pointer to variable that will receive the return error code from this function :
*
*                                       NET_ARP_ERR_NONE                    ARP Request successfully transmitted to
*                                                                               probe local network for the desired 
*                                                                               protocol address (see Note #2).
*                                       NET_ARP_ERR_NULL_PTR                Argument 'paddr_protocol' passed a NULL
*                                                                              pointer.
*                                       NET_ARP_ERR_INVALID_PROTOCOL_LEN    Invalid ARP protocol address length.
*
*                                                                           --- RETURNED BY NetARP_CacheCfg() : ----
*                                       NET_ARP_ERR_CACHE_NONE_AVAIL        NO available ARP caches to allocate.
*                                       NET_ARP_ERR_CACHE_INVALID_TYPE      ARP cache is NOT a valid cache type.
*                                       NET_TMR_ERR_NULL_OBJ                Argument 'obj'  passed a NULL pointer.
*                                       NET_TMR_ERR_NULL_FNCT               Argument 'fnct' passed a NULL pointer.
*                                       NET_TMR_ERR_NONE_AVAIL              NO available timers to allocate.
*                                       NET_TMR_ERR_INVALID_TYPE            Network timer is NOT a valid timer type.
*
* Return(s)   : none.
*
* Caller(s)   : Application.
*
*               This function is a network protocol suite application interface (API) function & MAY be 
*               called by application function(s).
*
* Note(s)     : (3) 'paddr_protocol' MUST point to a valid protocol address that is in network-order.
*
*                   See also 'NetARP_CacheHandler()  Note #2e3'.
*
*               (4) The length of the protocol address MUST be equal to NET_ARP_CFG_PROTOCOL_ADDR_LEN
*                   & is included for correctness & completeness.
*********************************************************************************************************
*/
/*$PAGE*/
void  NetARP_ProbeAddrOnNet (CPU_INT08U        *paddr_protocol,
                             NET_ARP_ADDR_LEN   addr_protocol_len,
                             NET_ERR           *perr)
{
#if ((NET_ERR_CFG_ARG_CHK_EXT_EN == DEF_ENABLED)                    && \
     (NET_CTR_CFG_ERR_EN         == DEF_ENABLED)                    && \
     (CPU_CFG_CRITICAL_METHOD    == CPU_CRITICAL_METHOD_STATUS_LOCAL))
    CPU_SR          cpu_sr;
#endif
    NET_ARP_CACHE  *pcache;


#if (NET_ERR_CFG_ARG_CHK_EXT_EN == DEF_ENABLED)                 /* ------------ VALIDATE PROTOCOL ADDR BUF ------------ */
    if (paddr_protocol == (CPU_INT08U *)0) {
        NET_CTR_ERR_INC(NetARP_ErrNullPtrCtr);
       *perr = NET_ARP_ERR_NULL_PTR;
        return;
    }
    if (addr_protocol_len != NET_ARP_CFG_PROTOCOL_ADDR_LEN) {   /* Chk protocol addr len (see Note #4).                 */
        NET_CTR_ERR_INC(NetARP_ErrInvalidAddrLenProtocolCtr);
       *perr = NET_ARP_ERR_INVALID_PROTOCOL_LEN;
        return;
    }
#else
   (void)&addr_protocol_len;                                    /* Prevent compiler warning.                            */
#endif


                                                                /* --------- REMOVE PROTOCOL ADDR'S ARP CACHE --------- */
    pcache = NetARP_CacheSrch(paddr_protocol);
    if (pcache != (NET_ARP_CACHE *)0) {                         /* If protocol addr's ARP cache avail, ...              */
        NetARP_CacheRemove(pcache, DEF_YES);                    /* ... remove from ARP Cache List (see Note #1a).       */
    }


                                                                /* ------------------ CFG ARP CACHE ------------------- */
    pcache = NetARP_CacheCfg((CPU_INT08U *)0,
                             (CPU_INT08U *)paddr_protocol,
                             (CPU_FNCT_PTR)NetARP_CacheReqTimeout,
                             (NET_TMR_TICK)NetARP_ReqTimeout_tick,
                             (NET_ERR    *)perr);
    if (*perr != NET_ARP_ERR_NONE) {
         return;
    }

    pcache->State = NET_ARP_CACHE_STATE_PEND;

                                                                /* ------- INSERT ARP CACHE INTO ARP CACHE LIST ------- */
    NetARP_CacheInsert(pcache);

                                                                /* -------------------- TX ARP REQ -------------------- */
    NetARP_TxReq(pcache);                                       /* Tx ARP req to probe local net for protocol addr.     */


   *perr = NET_ARP_ERR_NONE;
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                             NetARP_Rx()
*
* Description : (1) Process received ARP packets & update ARP Cache List :
*
*                   (a) Validate ARP packet
*                   (b) Update   ARP cache
*                   (c) Prepare & transmit an ARP Reply for a received ARP Request
*                   (d) Free ARP packet
*                   (e) Update receive statistics
*
*
* Argument(s) : pbuf        Pointer to network buffer that received ARP packet.
*
*               perr        Pointer to variable that will receive the return error code from this function :
*
*                               NET_ARP_ERR_NONE                ARP packet successfully received & processed.
*                               NET_ERR_INIT_INCOMPLETE         Network initialization NOT complete.
*
*                                                               ---- RETURNED BY NetARP_RxPktDiscard() : ----
*                               NET_ERR_RX                      Receive error; packet discarded.
*
* Return(s)   : none.
*
* Caller(s)   : NetIF_RxPktFrameDemux().
*
*               This function is a network protocol suite to network interface (IF) function & SHOULD be 
*               called only by appropriate network interface function(s).
*
* Note(s)     : (2) NetARP_Rx() blocked until network initialization completes.
*********************************************************************************************************
*/

void  NetARP_Rx (NET_BUF  *pbuf,
                 NET_ERR  *perr)
{
#if ((((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)                    && \
       (NET_CTR_CFG_ERR_EN         == DEF_ENABLED))                   || \
       (NET_CTR_CFG_STAT_EN        == DEF_ENABLED))                   && \
       (CPU_CFG_CRITICAL_METHOD    == CPU_CRITICAL_METHOD_STATUS_LOCAL))
    CPU_SR        cpu_sr;
#endif
#if (NET_CTR_CFG_STAT_EN == DEF_ENABLED)
    NET_CTR      *pctr;
#endif
    NET_BUF_HDR  *pbuf_hdr;
    NET_ARP_HDR  *parp_hdr;


#if (NET_ERR_CFG_ARG_CHK_EXT_EN == DEF_ENABLED)
    if (Net_InitDone != DEF_YES) {                              /* If init NOT complete, exit rx (see Note #2).         */
       *perr = NET_ERR_INIT_INCOMPLETE;
        return;
    }
#endif


#if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)                 /* ------------------- VALIDATE PTR ------------------- */
    if (pbuf == (NET_BUF *)0) {
        NetARP_RxPktDiscard(pbuf, perr);
        NET_CTR_ERR_INC(NetARP_ErrNullPtrCtr);
        return;
    }
#endif


    NET_CTR_STAT_INC(NetARP_StatRxPktCtr);


                                                                /* ----------------- VALIDATE ARP PKT ----------------- */
    pbuf_hdr = &pbuf->Hdr;
#if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)
    NetARP_RxPktValidateBuf(pbuf_hdr, perr);                    /* Validate rx'd buf.                                   */
    switch (*perr) {
        case NET_ARP_ERR_NONE:                                  
             break;


        case NET_ERR_INVALID_PROTOCOL:
        case NET_BUF_ERR_INVALID_IX:
        default:
             NetARP_RxPktDiscard(pbuf, perr);
             return;                                            /* Prevent 'break NOT reachable' compiler warning.      */
    }
#endif
    parp_hdr = (NET_ARP_HDR *)&pbuf->Data[pbuf_hdr->ARP_MsgIx];
    NetARP_RxPktValidate(pbuf_hdr, parp_hdr, perr);             /* Validate rx'd pkt.                                   */


/*$PAGE*/
                                                                /* ----------------- UPDATE ARP CACHE ----------------- */
    switch (*perr) {                                            /* Chk err from NetARP_RxPktValidate().                 */
        case NET_ARP_ERR_NONE:
             NetARP_RxPktCacheUpdate(parp_hdr, perr);
             break;


        case NET_ARP_ERR_INVALID_HW_TYPE:
        case NET_ARP_ERR_INVALID_HW_LEN:
        case NET_ARP_ERR_INVALID_HW_ADDR:
        case NET_ARP_ERR_INVALID_PROTOCOL_TYPE:
        case NET_ARP_ERR_INVALID_PROTOCOL_LEN:
        case NET_ARP_ERR_INVALID_PROTOCOL_ADDR:
        case NET_ARP_ERR_INVALID_OP_CODE:
        case NET_ARP_ERR_INVALID_OP_ADDR:
        case NET_ARP_ERR_INVALID_LEN_MSG:
        default:
             NetARP_RxPktDiscard(pbuf, perr);
             return;                                            /* Prevent 'break NOT reachable' compiler warning.      */
    }


                                                                /* ------------------- TX ARP REPLY ------------------- */
    switch (*perr) {                                            /* Chk err from NetARP_RxPktCacheUpdate().              */
        case NET_ARP_ERR_CACHE_RESOLVED:
             NetARP_RxPktReply(parp_hdr, perr);
             break;


        case NET_ARP_ERR_CACHE_NONE_AVAIL:
        case NET_ARP_ERR_CACHE_INVALID_TYPE:
        case NET_ARP_ERR_INVALID_OP_CODE:
        case NET_ARP_ERR_RX_TARGET_REPLY:
        case NET_ARP_ERR_RX_TARGET_NOT_THIS_HOST:
        case NET_TMR_ERR_NULL_OBJ:
        case NET_TMR_ERR_NULL_FNCT:
        case NET_TMR_ERR_NONE_AVAIL:
        case NET_TMR_ERR_INVALID_TYPE:
        default:
             NetARP_RxPktDiscard(pbuf, perr);
             return;                                            /* Prevent 'break NOT reachable' compiler warning.      */
    }


                                                                /* ---------- FREE ARP PKT / UPDATE RX STATS ---------- */
    switch (*perr) {                                            /* Chk err from NetARP_RxPktReply().                    */
        case NET_ARP_ERR_RX_REQ_TX_REPLY:
#if (NET_CTR_CFG_STAT_EN == DEF_ENABLED)
             pctr = &NetARP_StatRxMsgReqProcessedCtr;
#endif
             break;


        case NET_ARP_ERR_RX_REPLY_TX_PKTS:
#if (NET_CTR_CFG_STAT_EN == DEF_ENABLED)
             pctr = &NetARP_StatRxMsgReplyProcessedCtr;
#endif
             break;


        case NET_ARP_ERR_INVALID_OP_CODE:
        case NET_ARP_ERR_RX_TARGET_NOT_THIS_HOST:
        default:
             NetARP_RxPktDiscard(pbuf, perr);
             return;                                            /* Prevent 'break NOT reachable' compiler warning.      */
    }

    NetARP_RxPktFree(pbuf);

    NET_CTR_STAT_INC(NetARP_StatRxMsgProcessedCtr);
    NET_CTR_STAT_INC(*pctr);


   *perr = NET_ARP_ERR_NONE;
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                        NetARP_CacheHandler()
*
* Description : (1) Resolve destination hardware address for transmit data packet :
*
*                   (a) Search ARP Cache List for ARP cache with corresponding protocol address
*                   (b) If ARP cache     found, handle packet based on ARP cache state :
*
*                       (1) PENDING  -> Enqueue transmit packet buffer to ARP cache
*                       (2) RESOLVED -> Copy ARP cache's hardware address to data packet;
*                                       Return to Network Interface to transmit data packet
*
*                   (c) If ARP cache NOT found, allocate new ARP cache in 'PENDING' state (see Note #1b1)
*
*                   See 'net_arp.h  ARP CACHE STATES' for ARP cache state diagram.
*
*               (2) This ARP cache handler function assumes the following :
*
*                   (a) ALL ARP caches in the ARP Cache List are valid [validated by NetARP_CacheGet()]
*                   (b) ANY ARP cache in the 'PENDING' state MAY have already enqueued at LEAST one 
*                           transmit packet buffer when ARP cache allocated [see NetARP_CacheGet()]
*                   (c) ALL ARP caches in the 'RESOLVED' state have valid hardware addresses
*                   (d) ALL transmit buffers enqueued on any ARP cache are valid
*                   (e) Buffer's ARP address pointers pre-configured by Network Interface to point to :
*
*                       (1) 'ARP_AddrProtocolPtr'               Pointer to the protocol address used to 
*                                                                   resolve the hardware address
*                       (2) 'ARP_AddrHW_Ptr'                    Pointer to memory buffer to return the 
*                                                                   resolved hardware address
*                       (3) ARP addresses which MUST be in network-order
*
*
* Argument(s) : pbuf        Pointer to network buffer to transmit.
*
*               perr        Pointer to variable that will receive the return error code from this function :
*
*                               NET_ARP_ERR_CACHE_RESOLVED          ARP cache resolved & hardware address
*                                                                       successfully copied.
*                               NET_ARP_ERR_CACHE_PEND              ARP cache in 'PENDING' state; transmit
*                                                                       buffer enqueued to ARP cache.
*                               NET_ARP_ERR_NULL_PTR                Argument 'pbuf' passed a NULL pointer;
*                                                                       OR
*                                                                   'pbuf's 'ARP_AddrProtocolPtr'/'ARP_AddrHWPtr'
*                                                                       are set as NULL pointers.
*
*                                                                   ---- RETURNED BY NetARP_CacheAddPend() : ----
*                               NET_ARP_ERR_CACHE_NONE_AVAIL        NO available ARP caches to allocate.
*                               NET_ARP_ERR_CACHE_INVALID_TYPE      ARP cache is NOT a valid cache type.
*                               NET_TMR_ERR_NULL_OBJ                Argument 'obj'  passed a NULL pointer.
*                               NET_TMR_ERR_NULL_FNCT               Argument 'fnct' passed a NULL pointer.
*                               NET_TMR_ERR_NONE_AVAIL              NO available timers to allocate.
*                               NET_TMR_ERR_INVALID_TYPE            Network timer is NOT a valid timer type.
*
* Return(s)   : none.
*
* Caller(s)   : NetIF_Tx().
*
*               This function is a network protocol suite to network interface (IF) function & SHOULD be 
*               called only by appropriate network interface function(s).
*
* Note(s)     : (3) (a) ARP Cache List is accessed by
*
*                       (1) NetARP_CacheHandler()
*                       (2) NetARP_CacheAddPend()                   via NetARP_CacheInsert()
*                       (3) NetARP_CacheAddResolved()               via NetARP_CacheInsert()
*                       (4) NetARP_CacheRemove()                    via NetARP_CacheUnlink()
*                       (5) NetARP_RxPktCacheUpdate()
*                       (6) ARP cache's 'TMR->Obj' pointer          via NetARP_CacheReqTimeout() &
*                                                                       NetARP_CacheTimeout()
*
*                   (b) Since the primary tasks of the network protocol suite are prevented from running 
*                       concurrently (see 'net.h  Note #2'), it is NOT necessary to protect the shared 
*                       resources of the ARP Cache List since no asynchronous access from other network
*                       tasks is possible.
*
*               (4) (a) RFC #1122, Section 2.3.2.2 states that "the link layer SHOULD" ... :
*
*                       (1) "Save (rather than discard) ... packets destined to the same unresolved
*                            IP address and" ...
*                       (2) "Transmit the saved packet[s] when the address has been resolved."
*
*                   (b) Since ARP Layer is the last layer to handle & queue the transmit network 
*                       buffer, it is NOT necessary to increment the network buffer's reference 
*                       counter to include the pending ARP cache buffer queue as a new reference
*                       to the network buffer.
*
*               (5) Some buffer controls were previously initialized in NetBuf_Get() when the packet
*                   was received at the network interface layer.  These buffer controls do NOT need
*                   to be re-initialized but are shown for completeness.
*********************************************************************************************************
*/
/*$PAGE*/
void  NetARP_CacheHandler (NET_BUF  *pbuf,
                           NET_ERR  *perr)
{
#if ((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)                    && \
     (NET_CTR_CFG_ERR_EN         == DEF_ENABLED)                    && \
     (CPU_CFG_CRITICAL_METHOD    == CPU_CRITICAL_METHOD_STATUS_LOCAL))
    CPU_SR          cpu_sr;
#endif
    CPU_INT08U     *paddr_hw;
    CPU_INT08U     *paddr_protocol;
    NET_BUF_HDR    *pbuf_hdr;
    NET_BUF_HDR    *ptail_buf_hdr;
    NET_BUF        *ptail_buf;
    NET_ARP_CACHE  *pcache;

                                                                /* ------------------- VALIDATE PTRS ------------------ */
#if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)
    if (pbuf == (NET_BUF *)0) {
        NET_CTR_ERR_INC(NetARP_ErrNullPtrCtr);
       *perr = NET_ARP_ERR_NULL_PTR;
        return;
    }
#endif

    pbuf_hdr       = &pbuf->Hdr;
    paddr_hw       =  pbuf_hdr->ARP_AddrHW_Ptr;
    paddr_protocol =  pbuf_hdr->ARP_AddrProtocolPtr;

#if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)
    if (paddr_hw == (CPU_INT08U *)0) {
        NET_CTR_ERR_INC(NetARP_ErrNullPtrCtr);
       *perr = NET_ARP_ERR_NULL_PTR;
        return;
    }
    if (paddr_protocol == (CPU_INT08U *)0) {
        NET_CTR_ERR_INC(NetARP_ErrNullPtrCtr);
       *perr = NET_ARP_ERR_NULL_PTR;
        return;
    }
#endif

                                                                /* ------------------ SRCH ARP CACHE ------------------ */
    pcache = NetARP_CacheSrch(paddr_protocol);
    if (pcache != (NET_ARP_CACHE *)0) {                         /* If ARP cache found, chk state.                       */
        switch (pcache->State) {
            case NET_ARP_CACHE_STATE_PEND:                      /* If ARP cache pend, append buf onto Q (see Note #4a1).*/
                 ptail_buf = pcache->BufQ_Tail;
                 if (ptail_buf != (NET_BUF *)0) {               /* If Q NOT empty,    append buf @ Q tail.              */
                     ptail_buf_hdr                 = &ptail_buf->Hdr;
                     ptail_buf_hdr->NextSecListPtr = (void    *)pbuf;
                     pbuf_hdr->PrevSecListPtr      = (void    *)ptail_buf;
                     pcache->BufQ_Tail             = (NET_BUF *)pbuf;

                 } else {                                       /* Else add buf as first q'd buf.                       */
                     pcache->BufQ_Head             = (NET_BUF *)pbuf;
                     pcache->BufQ_Tail             = (NET_BUF *)pbuf;
#if 0                                                           /* Init'd in NetBuf_Get() [see Note #5].                */
                     pbuf_hdr->PrevSecListPtr      = (void    *)0;
                     pbuf_hdr->NextSecListPtr      = (void    *)0;
#endif
                 }
                                                                /* Set ARP cache as unlink fnct/obj.                    */
                 pbuf_hdr->UnlinkFnctPtr = (CPU_FNCT_PTR)NetARP_CacheUnlinkBuf;
                 pbuf_hdr->UnlinkObjPtr  = (void       *)pcache;

                *perr                    =  NET_ARP_ERR_CACHE_PEND;
                 break;


            case NET_ARP_CACHE_STATE_RESOLVED:                  /* If ARP cache resolved, copy hw addr.                 */
                 Mem_Copy((void     *) paddr_hw,
                          (void     *)&pcache->HW_Addr[0],
                          (CPU_SIZE_T) NET_ARP_CFG_HW_ADDR_LEN);
                *perr = NET_ARP_ERR_CACHE_RESOLVED;
                 break;


            case NET_ARP_CACHE_STATE_NONE:
            case NET_ARP_CACHE_STATE_FREE:
            default:
                 NetARP_CacheRemove(pcache, DEF_YES);
                 NetARP_CacheAddPend(pbuf, pbuf_hdr, paddr_protocol, perr);
                 break;
        }

    } else {                                                    /* Else add new ARP cache into ARP Cache List.          */
        NetARP_CacheAddPend(pbuf, pbuf_hdr, paddr_protocol, perr);
    }
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                       NetARP_CacheCalcStat()
*
* Description : (1) Calculate ARP statistics :
*
*                   (a) ARP cache found percentage
*
*
* Argument(s) : none.
*
* Return(s)   : ARP  cache found percentage, if NO errors,
*
*               NULL cache found percentage, otherwise.
*
* Caller(s)   : Application.
*
*               This function is a network protocol suite application interface (API) function & MAY be 
*               called by application function(s).
*
* Note(s)     : (2) NetARP_CacheCalcStat() blocked until network initialization completes; return NULL
*                   ARP cache found percentage.
*
*               (3) These ARP statistics calculation(s) are potentially expensive operations.  Recommended
*                   only from low-priority, background tasks.
*********************************************************************************************************
*/

CPU_INT08U  NetARP_CacheCalcStat (void)
{
#if (NET_CTR_CFG_STAT_EN     == DEF_ENABLED)
#if (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)
    CPU_SR      cpu_sr;
#endif
    NET_CTR     pct_numer_hi;
    NET_CTR     pct_numer_lo;
    NET_CTR     pct_denom_hi;
    NET_CTR     pct_denom_lo;
#endif
    CPU_INT08U  pct;


#if (NET_ERR_CFG_ARG_CHK_EXT_EN == DEF_ENABLED)
    if (Net_InitDone != DEF_YES) {                              /* If init NOT complete, ...                            */
        return (0);                                             /* ... rtn NULL pct (see Note #2).                      */
    }
#endif

                                                                /* ------------- CALC ARP CACHE FOUND PCT ------------- */
#if (NET_CTR_CFG_STAT_EN == DEF_ENABLED)
    CPU_CRITICAL_ENTER();
    pct_numer_hi = NetARP_CacheFoundCtr_hi;
    pct_numer_lo = NetARP_CacheFoundCtr_lo;
    pct_denom_hi = NetARP_CacheSrchCtr_hi;
    pct_denom_lo = NetARP_CacheSrchCtr_lo;
    CPU_CRITICAL_EXIT();

    pct = NetCtr_CalcPctLarge(pct_numer_hi,
                              pct_numer_lo,
                              pct_denom_hi,
                              pct_denom_lo);

    CPU_CRITICAL_ENTER();
    NetARP_CacheFoundPct = pct;
    CPU_CRITICAL_EXIT();

#else
    pct = 0;
#endif

    return (pct);
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                       NetARP_CacheGetAddrHW()
*
* Description : (1) Get hardware address that corresponds to a specific ARP cache's protocol address :
*
*                   (a) Search ARP Cache List for ARP cache with desired protocol address
*                   (b) If corresponding ARP cache found, get/return     hardware address
*
*
* Argument(s) : paddr_hw            Pointer to a memory buffer that will receive the hardware address :
*                                       (see Notes #2a & 3a) :
*
*                                       Hardware address that corresponds to the desired protocol address,
*                                           if NO errors.
*
*                                       Hardware address cleared to all zeros (see Note #2a3), otherwise.
*
*               addr_hw_len_buf     Length of hardware address memory buffer (in octets) [see Note #2a1].
*
*               paddr_protocol      Pointer to the specific protocol address to search for corresponding 
*                                       hardware address (see Note #3b).
*
*               addr_protocol_len   Length of protocol address (in octets) [see Note #2b].
*
*               perr        Pointer to variable that will receive the return error code from this function :
*
*                               NET_ARP_ERR_NONE                    Hardware address successfully returned.
*                               NET_ARP_ERR_CACHE_NOT_FOUND         ARP cache with corresponding  hardware/protocol
*                                                                       address NOT found.
*                               NET_ARP_ERR_CACHE_PEND              ARP cache in 'PENDING' state; hardware address
*                                                                       NOT yet resolved (see Note #4).
*
*                               NET_ARP_ERR_NULL_PTR                Argument(s) 'paddr_hw'/'paddr_protocol' passed
*                                                                       a NULL pointer.
*                               NET_ARP_ERR_INVALID_HW_LEN          Invalid ARP hardware address length.
*                               NET_ARP_ERR_INVALID_PROTOCOL_LEN    Invalid ARP protocol address length.
*
* Return(s)   : Length of returned hardware address (see Note #2a2), if available.
*
*               0,                                                   otherwise.
*
* Caller(s)   : Application.
*
*               This function is a network protocol suite application interface (API) function & MAY be 
*               called by application function(s).
*
* Note(s)     : (2) (a) (1) The size of the memory buffer that will receive the returned hardware address 
*                           MUST be greater than or equal to NET_ARP_CFG_HW_ADDR_LEN.
*
*                       (2) The length of any returned hardware address is equal to NET_ARP_CFG_HW_ADDR_LEN.
*
*                       (3) Address memory array cleared in case of any error(s).
*
*                           (A) Address memory array SHOULD be initialized to return a NULL address PRIOR 
*                               to all other validation or function handling in case of any error(s).
*
*                   (b) The length of the protocol address MUST be equal to NET_ARP_CFG_PROTOCOL_ADDR_LEN
*                       & is included for correctness & completeness.
*
*               (3) ARP addresses MUST be in network-order :
*
*                   (a) 'paddr_hw'                       hardware address returned in network-order
*                   (b) 'paddr_protocol' MUST point to a protocol address that is  in network-order
*
*                   See also 'NetARP_CacheHandler()  Note #2e3'.
*
*               (4) While an ARP cache is in 'PENDING' state the hardware address is NOT yet resolved,
*                   but MAY be resolved in the near future by an awaited ARP Reply.
*********************************************************************************************************
*/
/*$PAGE*/
NET_ARP_ADDR_LEN  NetARP_CacheGetAddrHW (CPU_INT08U        *paddr_hw,
                                         NET_ARP_ADDR_LEN   addr_hw_len_buf,
                                         CPU_INT08U        *paddr_protocol,
                                         NET_ARP_ADDR_LEN   addr_protocol_len,
                                         NET_ERR           *perr)
{
#if ((NET_ERR_CFG_ARG_CHK_EXT_EN == DEF_ENABLED)                    && \
     (NET_CTR_CFG_ERR_EN         == DEF_ENABLED)                    && \
     (CPU_CFG_CRITICAL_METHOD    == CPU_CRITICAL_METHOD_STATUS_LOCAL))
    CPU_SR             cpu_sr;
#endif
    NET_ARP_ADDR_LEN   addr_hw_len;
    NET_ARP_CACHE     *pcache;


                                                                /* --------------- VALIDATE HW ADDR BUF --------------- */
#if (NET_ERR_CFG_ARG_CHK_EXT_EN == DEF_ENABLED)
    if (paddr_hw == (CPU_INT08U *)0) {
        NET_CTR_ERR_INC(NetARP_ErrNullPtrCtr);
       *perr =  NET_ARP_ERR_NULL_PTR;
        return (0);
    }
#endif
                                                                /* Clr hw addr         (see Note #2a3).                 */
    Mem_Clr((void     *)paddr_hw,
            (CPU_SIZE_T) addr_hw_len_buf);

    addr_hw_len = NET_ARP_CFG_HW_ADDR_LEN;                      /* Cfg hw addr     len (see Note #2a2).                 */

#if (NET_ERR_CFG_ARG_CHK_EXT_EN == DEF_ENABLED)
    if (addr_hw_len_buf < addr_hw_len) {                        /* Chk hw addr buf len (see Note #2a1).                 */
        NET_CTR_ERR_INC(NetARP_ErrInvalidAddrLenHW_Ctr);
       *perr =  NET_ARP_ERR_INVALID_HW_LEN;
        return (0);
    }
#endif


#if (NET_ERR_CFG_ARG_CHK_EXT_EN == DEF_ENABLED)                 /* ------------ VALIDATE PROTOCOL ADDR BUF ------------ */
    if (paddr_protocol == (CPU_INT08U *)0) {
        NET_CTR_ERR_INC(NetARP_ErrNullPtrCtr);
       *perr =  NET_ARP_ERR_NULL_PTR;
        return (0);
    }
    if (addr_protocol_len != NET_ARP_CFG_PROTOCOL_ADDR_LEN) {   /* Chk protocol addr len (see Note #2b).                */
        NET_CTR_ERR_INC(NetARP_ErrInvalidAddrLenProtocolCtr);
       *perr =  NET_ARP_ERR_INVALID_PROTOCOL_LEN;
        return (0);
    }
#else
   (void)&addr_protocol_len;                                    /* Prevent compiler warning.                            */
#endif


                                                                /* ------------------ SRCH ARP CACHE ------------------ */
    pcache = NetARP_CacheSrch(paddr_protocol);
    if (pcache == (NET_ARP_CACHE *)0) {
       *perr =  NET_ARP_ERR_CACHE_NOT_FOUND;
        return (0);
    }

    switch (pcache->State) {
        case NET_ARP_CACHE_STATE_PEND:                          /* If ARP cache pend,     hw addr NOT yet avail ...     */
            *perr =  NET_ARP_ERR_CACHE_PEND;                    /* ... (see Note #4).                                   */
             return (0);                                        /* Prevent 'break NOT reachable' compiler warning.      */


        case NET_ARP_CACHE_STATE_RESOLVED:                      /* If ARP cache resolved, hw addr         avail.        */
             break;


        case NET_ARP_CACHE_STATE_NONE:
        case NET_ARP_CACHE_STATE_FREE:
        default:
             NetARP_CacheRemove(pcache, DEF_YES);
            *perr =  NET_ARP_ERR_CACHE_NOT_FOUND;
             return (0);                                        /* Prevent 'break NOT reachable' compiler warning.      */
    }


                                                                /* ------------------- GET HW ADDR -------------------- */
    Mem_Copy((void     *) paddr_hw,
             (void     *)&pcache->HW_Addr[0],
             (CPU_SIZE_T) addr_hw_len);


   *perr =  NET_ARP_ERR_NONE;

    return (addr_hw_len);
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                      NetARP_CachePoolStatGet()
*
* Description : Get ARP statistics pool.
*
* Argument(s) : none.
*
* Return(s)   : ARP  statistics pool, if NO errors.
*
*               NULL statistics pool, otherwise.
*
* Caller(s)   : Application.
*
*               This function is a network protocol suite application interface (API) function & MAY be 
*               called by application function(s).
*
* Note(s)     : (1) NetARP_CachePoolStatGet() blocked until network initialization completes; return NULL
*                   statistics pool.
*********************************************************************************************************
*/

NET_STAT_POOL  NetARP_CachePoolStatGet (void)
{
#if (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)
    CPU_SR         cpu_sr;
#endif
#if (NET_ERR_CFG_ARG_CHK_EXT_EN == DEF_ENABLED)
    NET_ERR        err;
#endif
    NET_STAT_POOL  stat_pool;


#if (NET_ERR_CFG_ARG_CHK_EXT_EN == DEF_ENABLED)
    if (Net_InitDone != DEF_YES) {                              /* If init NOT complete, ...                            */
        NetStat_PoolClr(&stat_pool, &err);
        return (stat_pool);                                     /* ... rtn NULL stat pool (see Note #1).                */
    }
#endif


    CPU_CRITICAL_ENTER();
    stat_pool = NetARP_CachePoolStat;
    CPU_CRITICAL_EXIT();

    return (stat_pool);
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                 NetARP_CachePoolStatResetMaxUsed()
*
* Description : Reset ARP statistics pool's maximum number of entries used.
*
* Argument(s) : none.
*
* Return(s)   : none.
*
* Caller(s)   : Application.
*
*               This function is a network protocol suite application interface (API) function & MAY be 
*               called by application function(s).
*
* Note(s)     : none.
*********************************************************************************************************
*/

void  NetARP_CachePoolStatResetMaxUsed (void)
{
    NET_ERR  err;


    NetStat_PoolResetUsedMax(&NetARP_CachePoolStat, &err);
}


/*$PAGE*/
/*
*********************************************************************************************************
*********************************************************************************************************
*                                           LOCAL FUNCTIONS
*********************************************************************************************************
*********************************************************************************************************
*/

/*
*********************************************************************************************************
*                                      NetARP_RxPktValidateBuf()
*
* Description : Validate received buffer header as ARP protocol.
*
* Argument(s) : pbuf_hdr    Pointer to network buffer header that received ARP packet.
*               --------    Argument validated in NetARP_Rx().
*
*               perr        Pointer to variable that will receive the return error code from this function :
*
*                               NET_ARP_ERR_NONE                Received buffer's ARP header validated.
*                               NET_ERR_INVALID_PROTOCOL        Buffer's protocol type is NOT ARP.
*                               NET_BUF_ERR_INVALID_IX          Invalid buffer index.
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_Rx().
*
* Note(s)     : none.
*********************************************************************************************************
*/

#if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)
static  void  NetARP_RxPktValidateBuf (NET_BUF_HDR  *pbuf_hdr,
                                       NET_ERR      *perr)
{
#if ((NET_CTR_CFG_ERR_EN      == DEF_ENABLED)                    && \
     (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL))
    CPU_SR  cpu_sr;
#endif

                                                                /* --------------- VALIDATE ARP BUF HDR --------------- */
    if (pbuf_hdr->ProtocolHdrType != NET_PROTOCOL_TYPE_ARP) {
        NET_CTR_ERR_INC(Net_ErrInvalidProtocolCtr);
       *perr = NET_ERR_INVALID_PROTOCOL;
        return;
    }

    if (pbuf_hdr->ARP_MsgIx == NET_BUF_IX_NONE) {
        NET_CTR_ERR_INC(NetARP_ErrRxInvalidBufIxCtr);
       *perr = NET_BUF_ERR_INVALID_IX;
        return;
    }

   *perr = NET_ARP_ERR_NONE;
}
#endif


/*$PAGE*/
/*
*********************************************************************************************************
*                                       NetARP_RxPktValidate()
*
* Description : (1) Validate received ARP packet :
*
*                   (a) Convert the following ARP header fields from network-order to host-order :
*
*                       (1) Hardware  Type
*                       (2) Protocol  Type
*                       (3) Operation Code                                  See Note #1aB
*
*                           (A) These fields are NOT converted directly in the received packet buffer's 
*                               data area but are converted in local or network buffer variables ONLY.
*
*                           (B) To avoid storing the ARP operation code in a network buffer variable & 
*                               passing an additional pointer to the network buffer header that received 
*                               ARP packet, ARP operation code is converted in the following functions :
*
*                               (1) NetARP_RxPktValidate()
*                               (2) NetARP_RxPktCacheUpdate()
*                               (3) NetARP_RxPktReply()
*                               (4) NetARP_RxPktIsTargetThisHost()
*
*                           (C) Hardware & Protocol Addresses are NOT converted from network-order to 
*                               host-order & MUST be accessed as multi-octet arrays.
*
*                   (b) Validate the received packet's following ARP header fields :
*
*                       (1) Hardware  Type
*                       (2) Hardware  Address Length
*                       (3) Hardware  Address : Sender's
*                       (4) Protocol  Type
*                       (5) Protocol  Address Length
*                       (6) Protocol  Address : Sender's
*                       (7) Operation Code
*
*                   (c) Update network buffer's length controls.
*
*                   (d) Validate ARP message length :
*
*                       (1) The ARP message length is compared to the remaining packet length which should
*                           be identical.
*
*                       (2) However, some network interface frame types append 'pad' octets (octets with a
*                           dummy value to be ignored) if the data length for the frame does NOT meet the
*                           frame's required minimum size.  Thus, if the ARP message length is NOT greater
*                           than the frame's minimum size, then pad octets were appended & the ARP message
*                           length CANNOT be compared.
*
*
* Argument(s) : pbuf_hdr    Pointer to network buffer header that received ARP packet.
*               --------    Argument validated in NetARP_Rx().
*
*               parp_hdr    Pointer to received packet's ARP header.
*               --------    Argument validated in NetARP_Rx()/NetARP_RxPktValidateBuf().
*
*               perr        Pointer to variable that will receive the return error code from this function :
*
*                               NET_ARP_ERR_NONE                        Received packet validated.
*                               NET_ARP_ERR_INVALID_HW_TYPE             Invalid ARP hardware type.
*                               NET_ARP_ERR_INVALID_HW_LEN              Invalid ARP hardware address length.
*                               NET_ARP_ERR_INVALID_HW_ADDR             Invalid ARP hardware address.
*                               NET_ARP_ERR_INVALID_PROTOCOL_TYPE       Invalid ARP protocol type.
*                               NET_ARP_ERR_INVALID_PROTOCOL_LEN        Invalid ARP protocol address length.
*                               NET_ARP_ERR_INVALID_PROTOCOL_ADDR       Invalid ARP protocol address.
*                               NET_ARP_ERR_INVALID_OP_CODE             Invalid ARP operation code.
*                               NET_ARP_ERR_INVALID_OP_ADDR             Invalid address for ARP operation
*                                                                           (see Note #3).
*                               NET_ARP_ERR_INVALID_LEN_MSG             Invalid ARP message length
*                                                                           (see Note #1d).
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_Rx().
*
* Note(s)     : (2) See RFC #826, Section 'Packet Format' for ARP packet header format.
*
*               (3) (a) An ARP Request SHOULD be transmitted onto the network by broadcast (see RFC #826,
*                       Section 'Packet Generation').  Therefore, any ARP Request NOT received as a broadcast
*                       packet SHOULD be discarded.
*
*                   (b) An ARP Reply SHOULD be transmitted directly to the ARP-Requesting host (see RFC #826,
*                       Section 'Packet Reception' algorithm) & SHOULD NOT be broadcast onto the network.
*                       Therefore, any ARP Reply received as a broadcast packet SHOULD be discarded.
*********************************************************************************************************
*/
/*$PAGE*/
static  void  NetARP_RxPktValidate (NET_BUF_HDR  *pbuf_hdr,
                                    NET_ARP_HDR  *parp_hdr,
                                    NET_ERR      *perr)
{
#if ((NET_CTR_CFG_ERR_EN      == DEF_ENABLED)                    && \
     (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL))
    CPU_SR       cpu_sr;
#endif
    CPU_INT16U   hw_type;
    CPU_INT16U   protocol_type;
    CPU_INT16U   op_code;
    CPU_BOOLEAN  rx_broadcast;
    CPU_BOOLEAN  valid;


                                                                /* ------------ VALIDATE ARP HW TYPE/ADDR ------------- */
    NET_UTIL_VAL_COPY_GET_NET_16(&hw_type, &parp_hdr->HW_Type);
    if (hw_type != NET_ARP_CFG_HW_TYPE) {
        NET_CTR_ERR_INC(NetARP_ErrRxHdrHW_TypeCtr);
       *perr = NET_ARP_ERR_INVALID_HW_TYPE;
        return;
    }

    if (parp_hdr->HW_AddrLen != NET_ARP_CFG_HW_ADDR_LEN) {
        NET_CTR_ERR_INC(NetARP_ErrRxHdrHW_AddrLenCtr);
       *perr = NET_ARP_ERR_INVALID_HW_LEN;
        return;
    }

    valid = NetARP_IsValidAddrHW(&parp_hdr->HW_AddrSender[0]);
    if (valid != DEF_YES) {
        NET_CTR_ERR_INC(NetARP_ErrRxHdrHW_AddrCtr);
       *perr = NET_ARP_ERR_INVALID_HW_ADDR;
        return;
    }


                                                                /* --------- VALIDATE ARP PROTOCOL TYPE/ADDR ---------- */
    NET_UTIL_VAL_COPY_GET_NET_16(&protocol_type, &parp_hdr->ProtocolType);
    if (protocol_type != NET_ARP_CFG_PROTOCOL_TYPE) {
        NET_CTR_ERR_INC(NetARP_ErrRxHdrProtocolTypeCtr);
       *perr = NET_ARP_ERR_INVALID_PROTOCOL_TYPE;
        return;
    }

    if (parp_hdr->ProtocolAddrLen != NET_ARP_CFG_PROTOCOL_ADDR_LEN) {
        NET_CTR_ERR_INC(NetARP_ErrRxHdrProtocolAddrLenCtr);
       *perr = NET_ARP_ERR_INVALID_PROTOCOL_LEN;
        return;
    }

    valid = NetARP_IsValidAddrProtocol(&parp_hdr->ProtocolAddrSender[0]);
    if (valid != DEF_YES) {
        NET_CTR_ERR_INC(NetARP_ErrRxHdrProtocolAddrCtr);
       *perr = NET_ARP_ERR_INVALID_PROTOCOL_ADDR;
        return;
    }


                                                                /* --------------- VALIDATE ARP OP CODE --------------- */
    NET_UTIL_VAL_COPY_GET_NET_16(&op_code, &parp_hdr->OpCode);
    rx_broadcast = DEF_BIT_IS_SET(pbuf_hdr->Flags, NET_BUF_FLAG_BROADCAST_RX);
    switch (op_code) {
        case NET_ARP_HDR_OP_REQ:
             if (rx_broadcast != DEF_YES) {                     /* See Note #3a.                                        */
                 NET_CTR_ERR_INC(NetARP_ErrRxHdrOpAddrCtr);
                *perr = NET_ARP_ERR_INVALID_OP_ADDR;
                 return;
             }
             break;


        case NET_ARP_HDR_OP_REPLY:
             if (rx_broadcast != DEF_NO) {                      /* See Note #3b.                                        */
                 NET_CTR_ERR_INC(NetARP_ErrRxHdrOpAddrCtr);
                *perr = NET_ARP_ERR_INVALID_OP_ADDR;
                 return;
             }
             break;


        default:
             NET_CTR_ERR_INC(NetARP_ErrRxHdrOpCodeCtr);
            *perr = NET_ARP_ERR_INVALID_OP_CODE;
             return;                                            /* Prevent 'break NOT reachable' compiler warning.      */
    }


/*$PAGE*/
                                                                /* ----- UPDATE BUF CTRLS / VALIDATE ARP MSG LEN ------ */
    pbuf_hdr->ARP_MsgLen = NET_ARP_HDR_SIZE;
    if (pbuf_hdr->ARP_MsgLen >= pbuf_hdr->IF_DataLenMin) {      /* If ARP msg len >= min net IF data size & ..          */
        if (pbuf_hdr->ARP_MsgLen != pbuf_hdr->DataLen) {        /* .. ARP msg len != rem pkt len,           ..          */
            NET_CTR_ERR_INC(NetARP_ErrRxHdrMsgLenCtr);
           *perr = NET_ARP_ERR_INVALID_LEN_MSG;                 /* .. rtn err (see Note #1d).                           */
            return;
        }
    }

    pbuf_hdr->DataLen -= (NET_BUF_SIZE)pbuf_hdr->ARP_MsgLen;



   *perr = NET_ARP_ERR_NONE;
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                      NetARP_RxPktCacheUpdate()
*
* Description : (1) Update an ARP cache based on received ARP packet's sender addresses :
*
*                   (a) Verify ARP message's intended target address is this host
*                   (b) Search ARP Cache List
*                   (c) Update ARP cache
*
*
* Argument(s) : parp_hdr    Pointer to received packet's ARP header.
*               --------    Argument validated in NetARP_Rx().
*
*               perr        Pointer to variable that will receive the return error code from this function :
*
*                               NET_ARP_ERR_CACHE_RESOLVED              ARP cache resolved & hardware address
*                                                                           successfully copied.
*                               NET_ARP_ERR_RX_TARGET_REPLY             ARP Reply received but NO corresponding ARP
*                                                                           cache currently pending for ARP Reply.
*
*                                                                       --- RETURNED BY NetARP_CacheAddResolved() : ----
*                               NET_ARP_ERR_CACHE_NONE_AVAIL            NO available ARP caches to allocate.
*                               NET_ARP_ERR_CACHE_INVALID_TYPE          ARP cache is NOT a valid cache type.
*                               NET_TMR_ERR_NULL_OBJ                    Argument 'obj'  passed a NULL pointer.
*                               NET_TMR_ERR_NULL_FNCT                   Argument 'fnct' passed a NULL pointer.
*                               NET_TMR_ERR_NONE_AVAIL                  NO available timers to allocate.
*                               NET_TMR_ERR_INVALID_TYPE                Network timer is NOT a valid timer type.
*
*                                                                       - RETURNED BY NetARP_RxPktIsTargetThisHost() : -
*                               NET_ARP_ERR_RX_TARGET_NOT_THIS_HOST     Received ARP message NOT intended for this host.
*                               NET_ARP_ERR_INVALID_OP_CODE             Invalid ARP operation code.
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_Rx().
*
* Note(s)     : (2) (a) The ARP cache algorithm implies that ALL messages received at the ARP layer automatically
*                       update the ARP Cache List EVEN if this host is NOT the intended target host of a received
*                       ARP message -- but ONLY if an ARP cache for the sender's addresses is already cached (see
*                       RFC #826, Section 'Packet Reception').  However, if NO ARP cache exists for the sender's
*                       addresses, then even the ARP cache algorithm implies that a misdirected or incorrectly
*                       received ARP message is discarded.
*
*                   (b) A configurable ARP address filtering feature is provided to selectively filter & discard
*                       ALL misdirected or incorrectly received ARP messages (see 'net_cfg.h  ADDRESS RESOLUTION
*                       PROTOCOL LAYER CONFIGURATION') :
*
*                       (1) When ENABLED,  the ARP address filter discards :
*
*                           (A) ALL misdirected, broadcasted, or incorrectly received ARP messages.
*
*                           (B) Any ARP Reply received for this host for which NO corresponding ARP cache currently
*                               exists.  (Note : Such an ARP Reply may be a legitimate, yet late, ARP Reply to a
*                               pending ARP Request that has timed-out & been removed from the ARP Cache List.)
*
*                       (2) When DISABLED, the ARP address filter discards :
*
*                           (A) Any misdirected or incorrectly received ARP messages if the sender's address(s)
*                               are NOT already cached.
*
*               (3) (a) RFC # 826, Section 'Related issue' states that "perhaps receiving of a packet from a host
*                       should reset a timeout in the address resolution entry".
*
*                   (b) RFC #1122, Section 2.3.2.1 affirms "that this timeout should be restarted when the cache
*                       entry is 'refreshed'".
*********************************************************************************************************
*/

static  void  NetARP_RxPktCacheUpdate (NET_ARP_HDR  *parp_hdr,
                                       NET_ERR      *perr)
{
#if ((NET_CTR_CFG_ERR_EN      == DEF_ENABLED)                    && \
     (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL))
    CPU_SR          cpu_sr;
#endif
#if (NET_ARP_CFG_ADDR_FLTR_EN == DEF_ENABLED)
    CPU_INT16U      op_code;
#else
    CPU_BOOLEAN     cache_add;
#endif
    CPU_INT08U     *paddr_sender_hw;
    CPU_INT08U     *paddr_sender_protocol;
    NET_ARP_CACHE  *pcache;
    NET_BUF        *pbuf_head;


/*$PAGE*/
                                                                /* ----------------- CHK TARGET ADDR ------------------ */
    NetARP_RxPktIsTargetThisHost(parp_hdr, perr);
#if (NET_ARP_CFG_ADDR_FLTR_EN == DEF_ENABLED)
    if (*perr != NET_ARP_ERR_RX_TARGET_THIS_HOST) {             /* Filter misdirected rx'd ARP msgs (see Note #2b1A).   */
         NET_CTR_ERR_INC(NetARP_ErrRxPktTargetNotThisHostCtr);
         return;
    }
#else
    cache_add = (*perr == NET_ARP_ERR_RX_TARGET_THIS_HOST) ? DEF_YES : DEF_NO;
#endif


                                                                /* ------------------ SRCH ARP CACHE ------------------ */
    paddr_sender_hw       = parp_hdr->HW_AddrSender;
    paddr_sender_protocol = parp_hdr->ProtocolAddrSender;
    pcache                = NetARP_CacheSrch(paddr_sender_protocol);


                                                                /* ----------------- UPDATE ARP CACHE ----------------- */
    if (pcache != (NET_ARP_CACHE *)0) {                         /* If ARP cache found, chk state.                       */
        switch (pcache->State) {
            case NET_ARP_CACHE_STATE_PEND:                      /* If ARP cache pend, add sender's hw addr, ...         */
                 Mem_Copy((void     *)&pcache->HW_Addr[0],
                          (void     *) paddr_sender_hw,
                          (CPU_SIZE_T) NET_ARP_CFG_HW_ADDR_LEN);
                                                                /* Reset ARP cache tmr (see Note #3).                   */
                 NetTmr_Set((NET_TMR    *)pcache->TmrPtr,       
                            (CPU_FNCT_PTR)NetARP_CacheTimeout,
                            (NET_TMR_TICK)NetARP_CacheTimeout_tick,
                            (NET_ERR    *)perr);

                 pbuf_head         =  pcache->BufQ_Head;
                 pcache->BufQ_Head = (NET_BUF *)0;
                 pcache->BufQ_Tail = (NET_BUF *)0;
                 NetARP_CacheTxPktHandler(pbuf_head,            /* ... & handle/tx cache's buf Q.                       */
                                          paddr_sender_hw);           
                 pcache->State     =  NET_ARP_CACHE_STATE_RESOLVED;
                *perr              =  NET_ARP_ERR_CACHE_RESOLVED;
                 break;


            case NET_ARP_CACHE_STATE_RESOLVED:                  /* If ARP cache resolved, update sender's hw addr.      */
                 Mem_Copy((void     *)&pcache->HW_Addr[0],
                          (void     *) paddr_sender_hw,
                          (CPU_SIZE_T) NET_ARP_CFG_HW_ADDR_LEN);
                                                                /* Reset ARP cache tmr (see Note #3).                   */
                 NetTmr_Set((NET_TMR    *)pcache->TmrPtr,       
                            (CPU_FNCT_PTR)NetARP_CacheTimeout,
                            (NET_TMR_TICK)NetARP_CacheTimeout_tick,
                            (NET_ERR    *)perr);

                *perr = NET_ARP_ERR_CACHE_RESOLVED;
                 break;


            case NET_ARP_CACHE_STATE_NONE:
            case NET_ARP_CACHE_STATE_FREE:
            default:
                 NetARP_CacheRemove(pcache, DEF_YES);
                 NetARP_CacheAddResolved(paddr_sender_hw, paddr_sender_protocol, perr);
                 break;
        }

    } else {                                                    /* Else add new ARP cache into ARP Cache List.          */
#if (NET_ARP_CFG_ADDR_FLTR_EN == DEF_ENABLED)                   /* If ARP addr fltr en'd          ..                    */
        NET_UTIL_VAL_COPY_GET_NET_16(&op_code, &parp_hdr->OpCode);
        if (op_code != NET_ARP_HDR_OP_REQ) {                    /* .. but ARP pkt NOT an ARP Req, ..                    */
                                                                /* .. do  NOT add new ARP cache (see Note #2b1B).       */
            NET_CTR_ERR_INC(NetARP_ErrRxPktTargetReplyCtr);
           *perr = NET_ARP_ERR_RX_TARGET_REPLY;
            return;
        }
#else                                                           /* If ARP addr fltr dis'd            ..                 */
        if (cache_add != DEF_YES) {                             /* .. &   ARP pkt NOT for this host, ..                 */
                                                                /* .. do  NOT add new ARP cache (see Note #2b2A).       */
            NET_CTR_ERR_INC(NetARP_ErrRxPktTargetNotThisHostCtr);
            return;                                             /* Err rtn'd by NetARP_RxPktIsTargetThisHost().         */
        }
#endif
        NetARP_CacheAddResolved(paddr_sender_hw, paddr_sender_protocol, perr);
    }
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                         NetARP_RxPktReply()
*
* Description : Reply to any ARP Request received.
*
* Argument(s) : parp_hdr    Pointer to received packet's ARP header.
*               --------    Argument validated in NetARP_Rx().
*
*               perr        Pointer to variable that will receive the return error code from this function :
*
*                               NET_ARP_ERR_RX_REQ_TX_REPLY             ARP Request transmitted.
*                               NET_ARP_ERR_RX_REPLY_TX_PKTS            ARP Reply   received; pending ARP cache packets
*                                                                           already transmitted (see Note #2).
*                               NET_ARP_ERR_INVALID_OP_CODE             Invalid ARP operation code.
*
*                                                                       - RETURNED BY NetARP_RxPktIsTargetThisHost() : -
*                               NET_ARP_ERR_RX_TARGET_NOT_THIS_HOST     Received ARP message NOT intended for this host.
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_Rx().
*
* Note(s)     : (1) (a) ARP Request target hardware address already verified as the network interface's broadcast
*                       address in NetARP_RxPktValidate() [see 'NetARP_RxPktValidate()  Note #3a'].
*
*                   (b) (1) When ARP address filtering is DISABLED, an ARP Reply is transmitted for an ARP Request
*                           ONLY if this host is the intended protocol address target of the ARP Request.
*
*                       (2) When ARP address filtering is ENABLED,  an ARP Reply is automatically transmitted for an
*                           ARP Request since this host's protocol address was already verified as the target protocol
*                           address in NetARP_RxPktCacheUpdate() [see 'NetARP_RxPktCacheUpdate()  Note #2b1'].
*
*               (2) ARP Reply already transmitted the ARP cache's transmit buffer queue in NetARP_RxPktCacheUpdate();
*                   no further action required.
*
*               (3) Default case already invalidated in NetARP_RxPktValidate().  However, the default case is included
*                   as an extra precaution in case 'OpCode' is incorrectly modified.
*********************************************************************************************************
*/

static  void  NetARP_RxPktReply (NET_ARP_HDR  *parp_hdr,
                                 NET_ERR      *perr)
{
#if ((NET_CTR_CFG_ERR_EN      == DEF_ENABLED)                    && \
     (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL))
    CPU_SR      cpu_sr;
#endif
    CPU_INT16U  op_code;


    NET_UTIL_VAL_COPY_GET_NET_16(&op_code, &parp_hdr->OpCode);

    switch (op_code) {
        case NET_ARP_HDR_OP_REQ:                                /* Use rx'd ARP Req to tx ARP Reply.                    */
#if (NET_ARP_CFG_ADDR_FLTR_EN != DEF_ENABLED)                   /* See Note #1b1.                                       */
             NetARP_RxPktIsTargetThisHost(parp_hdr, perr);
             if (*perr != NET_ARP_ERR_RX_TARGET_THIS_HOST) {
                  NET_CTR_ERR_INC(NetARP_ErrRxPktTargetNotThisHostCtr);
                  return;
             }
#endif
             NetARP_TxReply(parp_hdr);
            *perr = NET_ARP_ERR_RX_REQ_TX_REPLY;
             break;


        case NET_ARP_HDR_OP_REPLY:                              /* See Note #2.                                         */ 
            *perr = NET_ARP_ERR_RX_REPLY_TX_PKTS;
             break;


        default:                                                /* See Note #3.                                         */
             NET_CTR_ERR_INC(NetARP_ErrRxHdrOpCodeCtr);
            *perr = NET_ARP_ERR_INVALID_OP_CODE;
             return;                                            /* Prevent 'break NOT reachable' compiler warning.      */
    }
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                   NetARP_RxPktIsTargetThisHost()
*
* Description : (1) Determine if this host is the intended target of the received ARP message :
*
*                   (a) Verify target hardware address
*                   (b) Verify target protocol address
*                   (c) Return target validation
*
*               (2) (a) When ARP address filtering is ENABLED, the target hardware address for the received
*                       ARP message is verified as either :
*
*                       (1) The network interface's broadcast address for a received ARP Request
*                             OR
*                       (2) This host's hardware address for a received ARP Reply
*
*                   (b) ARP Request target hardware address verified as the network interface's broadcast
*                       address in NetARP_RxPktValidate() [see 'NetARP_RxPktValidate()  Note #3a'].
*
*                   (c) When ARP address filtering is DISABLED, target hardware address verification required
*                       only for the handling of an ARP Request in NetARP_RxPktReply() [see Note #2b &
*                       'NetARP_RxPktReply()  Note #1b1'].
*
*                   See also 'NetARP_RxPktCacheUpdate()  Note #2'.
*
*
* Argument(s) : parp_hdr    Pointer to received packet's ARP header.
*               --------    Argument validated in NetARP_Rx().
*
*               perr        Pointer to variable that will receive the return error code from this function :
*
*                               NET_ARP_ERR_RX_TARGET_THIS_HOST         Received ARP message     intended 
*                                                                           for this host.
*                               NET_ARP_ERR_RX_TARGET_NOT_THIS_HOST     Received ARP message NOT intended
*                                                                           for this host.
*                               NET_ARP_ERR_INVALID_OP_CODE             Invalid ARP operation code.
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_RxPktCacheUpdate(),
*               NetARP_RxPktReply().
*
* Note(s)     : (3) Default case already invalidated in NetARP_RxPktValidate().  However, the default case 
*                   is included as an extra precaution in case 'OpCode' is incorrectly modified.
*********************************************************************************************************
*/
/*$PAGE*/
static  void  NetARP_RxPktIsTargetThisHost (NET_ARP_HDR  *parp_hdr,
                                            NET_ERR      *perr)
{
#if  (NET_ARP_CFG_ADDR_FLTR_EN == DEF_ENABLED)
#if ((NET_CTR_CFG_ERR_EN       == DEF_ENABLED)                    && \
     (CPU_CFG_CRITICAL_METHOD  == CPU_CRITICAL_METHOD_STATUS_LOCAL))
    CPU_SR        cpu_sr;
#endif
    CPU_INT16U    op_code;
    CPU_INT08U   *paddr_target_hw;
#endif
    CPU_INT08U   *paddr_target_protocol;
    CPU_BOOLEAN   target_hw;
    CPU_BOOLEAN   target_protocol;

                                                                /* -------------- VERIFY TARGET HW ADDR --------------- */
#if (NET_ARP_CFG_ADDR_FLTR_EN == DEF_ENABLED)                   /* See Note #2a.                                        */
    NET_UTIL_VAL_COPY_GET_NET_16(&op_code, &parp_hdr->OpCode);
    switch (op_code) {
        case NET_ARP_HDR_OP_REQ:                                /* See Note #2b.                                        */
             target_hw       = DEF_YES;
             break;


        case NET_ARP_HDR_OP_REPLY:
             paddr_target_hw = parp_hdr->HW_AddrTarget;
             target_hw       = Mem_Cmp((void     *)paddr_target_hw,
                                       (void     *)NetARP_HostAddrPtrHW,
                                       (CPU_SIZE_T)NET_ARP_CFG_HW_ADDR_LEN);
             break;


        default:                                                /* See Note #3.                                         */
             NET_CTR_ERR_INC(NetARP_ErrRxHdrOpCodeCtr);
            *perr = NET_ARP_ERR_INVALID_OP_CODE;
             return;                                            /* Prevent 'break NOT reachable' compiler warning.      */
    }

#else                                                           /* See Note #2c.                                        */
    target_hw = DEF_YES;
#endif

                                                                /* ----------- VERIFY TARGET PROTOCOL ADDR ------------ */
    paddr_target_protocol = parp_hdr->ProtocolAddrTarget;
    target_protocol       = Mem_Cmp((void     *)paddr_target_protocol,
                                    (void     *)NetARP_HostAddrPtrProtocol,
                                    (CPU_SIZE_T)NET_ARP_CFG_PROTOCOL_ADDR_LEN);

                                                                /* -------------- RTN TARGET VALIDATION --------------- */
   *perr = ((target_hw       == DEF_YES) &&
            (target_protocol == DEF_YES)) ? NET_ARP_ERR_RX_TARGET_THIS_HOST :
                                            NET_ARP_ERR_RX_TARGET_NOT_THIS_HOST;
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                         NetARP_RxPktFree()
*
* Description : Free network buffer.
*
* Argument(s) : pbuf        Pointer to network buffer.
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_Rx().
*
* Note(s)     : none.
*********************************************************************************************************
*/

static  void  NetARP_RxPktFree (NET_BUF  *pbuf)
{
    NetBuf_FreeBuf((NET_BUF *)pbuf,
                   (NET_CTR *)0);
}


/*
*********************************************************************************************************
*                                        NetARP_RxPktDiscard()
*
* Description : On any ARP Receive errors, discard ARP packet & buffer.
*
* Argument(s) : pbuf        Pointer to network buffer.
*
*               perr        Pointer to variable that will receive the return error code from this function :
*
*                               NET_ERR_RX                      Receive error; packet discarded.
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_Rx(). 
*
* Note(s)     : none.
*********************************************************************************************************
*/

static  void  NetARP_RxPktDiscard (NET_BUF  *pbuf,
                                   NET_ERR  *perr)
{
    NET_CTR  *pctr;


#if (NET_CTR_CFG_ERR_EN == DEF_ENABLED)
    pctr = (NET_CTR *)&NetARP_ErrRxPktDiscardedCtr;
#else
    pctr = (NET_CTR *) 0;
#endif
    NetBuf_FreeBuf((NET_BUF *)pbuf,
                   (NET_CTR *)pctr);

   *perr = NET_ERR_RX;
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                             NetARP_Tx()
*
* Description : (1) Prepare & transmit an ARP Request or ARP Reply :
*
*                   (a) Get network buffer for ARP transmit packet
*                   (b) Prepare & transmit packet
*                   (c) Free      transmit packet buffer(s)
*                   (d) Update    transmit statistics
*
*
* Argument(s) : paddr_hw_sender         Pointer to sender's hardware address (see Note #2).
*
*               paddr_hw_target         Pointer to target's hardware address (see Note #2).
*
*               paddr_protocol_sender   Pointer to sender's protocol address (see Note #2).
*
*               paddr_protocol_target   Pointer to target's protocol address (see Note #2).
*
*               op_code                 ARP operation : Request or Reply.
*
*               perr        Pointer to variable that will receive the return error code from this function :
*
*                               NET_ARP_ERR_NONE                ARP packet successfully transmitted.
*                               NET_ARP_ERR_NULL_PTR            Argument(s) passed a NULL pointer.
*                               NET_ARP_ERR_INVALID_OP_CODE     Invalid ARP operation code.
*
*                                                               ------- RETURNED BY NetBuf_Get() : -------
*                               NET_BUF_ERR_NONE_AVAIL          NO available buffers to allocate.
*                               NET_BUF_ERR_INVALID_TYPE        Network buffer is NOT a valid buffer type.
*                               NET_BUF_ERR_INVALID_SIZE        Requested size is greater then the maximum
*                                                                   buffer size available.
*
*                                                               -- RETURNED BY NetARP_TxPktDiscard() : ---
*                               NET_ERR_TX                      Transmit error; packet discarded.
*
*                                                               -------- RETURNED BY NetIF_Tx() : --------
*                               NET_ERR_INIT_INCOMPLETE         Network initialization NOT complete.
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_TxReq(),
*               NetARP_TxReply().
*
* Note(s)     : (2) ARP addresses MUST be in network-order.
*
*               (3) Assumes network buffer's protocol header size is large enough to accomodate ARP header
*                   size (see 'net_buf.h  NETWORK BUFFER INDEX & SIZE DEFINES  Note #1').
*
*               (4) Network buffer already freed by lower layer; only increment error counter.
*********************************************************************************************************
*/
/*$PAGE*/
static  void  NetARP_Tx (CPU_INT08U  *paddr_hw_sender,
                         CPU_INT08U  *paddr_hw_target,
                         CPU_INT08U  *paddr_protocol_sender,
                         CPU_INT08U  *paddr_protocol_target,
                         CPU_INT16U   op_code,
                         NET_ERR     *perr)
{
#if (((NET_CTR_CFG_STAT_EN     == DEF_ENABLED)                    || \
      (NET_CTR_CFG_ERR_EN      == DEF_ENABLED))                   && \
      (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL))
    CPU_SR       cpu_sr;
#endif
    CPU_INT16U   msg_ix;
    NET_BUF     *pbuf;


#if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)
                                                                /* ------------------- VALIDATE PTRS ------------------ */
    if (paddr_hw_sender == (CPU_INT08U *)0) {
        NET_CTR_ERR_INC(NetARP_ErrNullPtrCtr);
        NET_CTR_ERR_INC(NetARP_ErrTxPktDiscardedCtr);
       *perr = NET_ARP_ERR_NULL_PTR;
        return;
    }

    if (paddr_protocol_sender == (CPU_INT08U *)0) {
        NET_CTR_ERR_INC(NetARP_ErrNullPtrCtr);
        NET_CTR_ERR_INC(NetARP_ErrTxPktDiscardedCtr);
       *perr = NET_ARP_ERR_NULL_PTR;
        return;
    }

    if (paddr_protocol_target == (CPU_INT08U *)0) {
        NET_CTR_ERR_INC(NetARP_ErrNullPtrCtr);
        NET_CTR_ERR_INC(NetARP_ErrTxPktDiscardedCtr);
       *perr = NET_ARP_ERR_NULL_PTR;
        return;
    }

                                                                /* -------------------- VALIDATE OP ------------------- */
    switch (op_code) {
        case NET_ARP_HDR_OP_REQ:                                /* For ARP Req, NULL hw addr ptr expected.              */
             break;


        case NET_ARP_HDR_OP_REPLY:
             if (paddr_hw_target == (CPU_INT08U *)0) {
                 NET_CTR_ERR_INC(NetARP_ErrNullPtrCtr);
                 NET_CTR_ERR_INC(NetARP_ErrTxPktDiscardedCtr);
                *perr = NET_ARP_ERR_NULL_PTR;
                 return;
             }
             break;


        default:
             NET_CTR_ERR_INC(NetARP_ErrTxHdrOpCodeCtr);
             NET_CTR_ERR_INC(NetARP_ErrTxPktDiscardedCtr);
            *perr = NET_ARP_ERR_INVALID_OP_CODE;
             return;                                            /* Prevent 'break NOT reachable' compiler warning.      */
    }
#endif


/*$PAGE*/
                                                                /* --------------------- GET BUF ---------------------- */
#if (NET_BUF_DATA_TX_IX < NET_ARP_HDR_SIZE)                     /* See Note #3.                                         */
    NET_CTR_ERR_INC(NetARP_ErrTxInvalidBufIxCtr);
    return;
#endif
    msg_ix = NET_BUF_DATA_TX_IX - NET_ARP_HDR_SIZE;
    pbuf   = NetBuf_Get((NET_BUF_SIZE)NET_ARP_MSG_LEN_DATA,
                        (NET_BUF_SIZE)msg_ix,
                        (CPU_INT16U  )NET_BUF_FLAG_NONE,
                        (NET_ERR    *)perr);
    if (*perr != NET_BUF_ERR_NONE) {
         return;
    }


                                                                /* ---------------- PREPARE/TX ARP PKT ---------------- */
    NetARP_TxPktPrepareHdr(pbuf,
                           msg_ix,
                           paddr_hw_sender,
                           paddr_hw_target,
                           paddr_protocol_sender,
                           paddr_protocol_target,
                           op_code,
                           perr);
    switch (*perr) {
        case NET_ARP_ERR_NONE:
             NetIF_Tx(pbuf, perr);
             break;


        case NET_ARP_ERR_NULL_PTR:
        default:
             NetARP_TxPktDiscard(pbuf, perr);
             return;                                            /* Prevent 'break NOT reachable' compiler warning.      */
    }


                                                                /* ---------- FREE TX PKT / UPDATE TX STATS ----------- */
    switch (*perr) {                                            /* Chk err from NetIF_Tx().                             */
        case NET_IF_ERR_NONE:
             NetARP_TxPktFree(pbuf);
             NET_CTR_STAT_INC(NetARP_StatTxMsgCtr);
            *perr = NET_ARP_ERR_NONE;
             break;


        case NET_ERR_TX:
             NET_CTR_ERR_INC(NetARP_ErrTxPktDiscardedCtr);      /* See Note #4.                                         */
             return;                                            /* Prevent 'break NOT reachable' compiler warning.      */
        
                       
        case NET_ERR_INIT_INCOMPLETE:
        default:
             NetARP_TxPktDiscard(pbuf, perr);
             return;                                            /* Prevent 'break NOT reachable' compiler warning.      */
    }
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                          NetARP_TxReq()
*
* Description : (1) Prepare & transmit an ARP Request to resolve a pending ARP cache :
*
*                   (a) Configure sender's hardware address as this host's hardware address
*                   (b) Configure target's hardware address as NULL since unknown
*                   (c) Configure sender's protocol address as this host's protocol address
*                   (d) Configure target's protocol address as the protocol address listed in the ARP cache
*                   (e) Configure ARP operation as ARP Request
*
*
* Argument(s) : pcache      Pointer to an ARP cache.
*               ------      Argument checked in NetARP_CacheAddPend(),
*                                               NetARP_CacheReqTimeout().
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_CacheAddPend(),
*               NetARP_CacheReqTimeout().
*
* Note(s)     : (2) Do NOT need to verify success of ARP Request since failure will cause timeouts & retries.
*********************************************************************************************************
*/

static  void  NetARP_TxReq (NET_ARP_CACHE  *pcache)
{
#if ((NET_CTR_CFG_STAT_EN     == DEF_ENABLED)                    && \
     (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL))
    CPU_SR       cpu_sr;
#endif
    CPU_INT08U  *paddr_hw_sender;
    CPU_INT08U  *paddr_hw_target;
    CPU_INT08U  *paddr_protocol_sender;
    CPU_INT08U  *paddr_protocol_target;
    CPU_INT16U   op_code;
    NET_ERR      err_tx;

                                                                /* Cfg ARP Req from ARP cache data (see Note #1).       */
    paddr_hw_sender       = (CPU_INT08U *) NetARP_HostAddrPtrHW;
    paddr_hw_target       = (CPU_INT08U *) 0;
    paddr_protocol_sender = (CPU_INT08U *) NetARP_HostAddrPtrProtocol;
    paddr_protocol_target = (CPU_INT08U *)&pcache->ProtocolAddr[0];

    op_code               =  NET_ARP_HDR_OP_REQ;

    NetARP_Tx(paddr_hw_sender,
              paddr_hw_target,
              paddr_protocol_sender,
              paddr_protocol_target,
              op_code,
             &err_tx);

    if (err_tx == NET_ARP_ERR_NONE) {
        NET_CTR_STAT_INC(NetARP_StatTxMsgReqCtr);
    }


    pcache->ReqAttemptsCtr++;                                   /* Inc req attempts ctr.                                */
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                         NetARP_TxReply()
*
* Description : (1) Prepare & transmit an ARP Reply in response to an ARP Request :
*
*                   (a) Configure sender's hardware address as this host's hardware address
*                   (b) Configure target's hardware address from the ARP Request's sender hardware address
*                   (c) Configure sender's protocol address from the ARP Request's target protocol address
*                   (d) Configure target's protocol address from the ARP Request's sender protocol address
*                   (e) Configure ARP operation as ARP Reply
*
*
* Argument(s) : parp_hdr    Pointer to a packet's ARP header.
*               --------    Argument checked in NetARP_RxPktValidate().
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_RxPktReply().
*
* Note(s)     : (2) Do NOT need to verify success of ARP Reply since failure will cause timeouts & retries.
*********************************************************************************************************
*/

static  void  NetARP_TxReply (NET_ARP_HDR  *parp_hdr)
{
#if ((NET_CTR_CFG_STAT_EN     == DEF_ENABLED)                    && \
     (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL))
    CPU_SR       cpu_sr;
#endif
    CPU_INT08U  *paddr_hw_sender;
    CPU_INT08U  *paddr_hw_target;
    CPU_INT08U  *paddr_protocol_sender;
    CPU_INT08U  *paddr_protocol_target;
    CPU_INT16U   op_code;
    NET_ERR      err_tx;

                                                                /* Cfg ARP Reply from ARP Req (see Note #1).            */
    paddr_hw_sender       = (CPU_INT08U *) NetARP_HostAddrPtrHW;
    paddr_hw_target       = (CPU_INT08U *)&parp_hdr->HW_AddrSender[0];
    paddr_protocol_sender = (CPU_INT08U *)&parp_hdr->ProtocolAddrTarget[0];
    paddr_protocol_target = (CPU_INT08U *)&parp_hdr->ProtocolAddrSender[0];

    op_code               =  NET_ARP_HDR_OP_REPLY;

    NetARP_Tx(paddr_hw_sender,
              paddr_hw_target,
              paddr_protocol_sender,
              paddr_protocol_target,
              op_code,
             &err_tx);

    if (err_tx == NET_ARP_ERR_NONE) {
        NET_CTR_STAT_INC(NetARP_StatTxMsgReplyCtr);
    }
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                      NetARP_TxPktPrepareHdr()
*
* Description : (1) Prepare ARP packet header :
*
*                   (a) Update network buffer's index & length controls.
*
*                   (b) Prepare the transmit packet's following ARP header fields :
*
*                       (1) Hardware  Type
*                       (2) Protocol  Type
*                       (3) Hardware  Address Length
*                       (4) Protocol  Address Length
*                       (5) Operation Code
*                       (6) Sender's  Hardware Address
*                       (7) Sender's  Protocol Address
*                       (8) Target's  Hardware Address
*                       (9) Target's  Protocol Address
*
*                   (c) Convert the following ARP header fields from host-order to network-order :
*
*                       (1) Hardware  Type
*                       (2) Protocol  Type
*                       (3) Operation Code
*
*                   (d) Configure ARP protocol address pointer (see Note #2).
*
*
* Argument(s) : pbuf                    Pointer to network buffer to prepare ARP packet.
*
*               msg_ix                  Buffer index to prepare ARP packet.
*               ------                  Argument checked in NetARP_Tx().
*
*               paddr_hw_sender         Pointer to sender's hardware address (see Note #2).
*               ---------------         Argument checked in NetARP_Tx().
*
*               paddr_hw_target         Pointer to target's hardware address (see Note #2).
*               ---------------         Argument checked in NetARP_Tx().
*
*               paddr_protocol_sender   Pointer to sender's protocol address (see Note #2).
*               ---------------------   Argument checked in NetARP_Tx().
*
*               paddr_protocol_target   Pointer to target's protocol address (see Note #2).
*               ---------------------   Argument checked in NetARP_Tx().
*
*               op_code                 ARP operation : Request or Reply.
*               -------                 Argument checked in NetARP_Tx().
*
*               perr        Pointer to variable that will receive the return error code from this function :
*
*                               NET_ARP_ERR_NONE                ARP packet successfully prepared.
*                               NET_ARP_ERR_NULL_PTR            Argument 'pbuf' passed a NULL pointer.
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_Tx().
*
* Note(s)     : (2) ARP addresses MUST be in network-order.
*
*               (3) Some buffer controls were previously initialized in NetBuf_Get() when the buffer was
*                   allocated.  These buffer controls do NOT need to be re-initialized but are shown for
*                   completeness.
*********************************************************************************************************
*/
/*$PAGE*/
static  void  NetARP_TxPktPrepareHdr (NET_BUF     *pbuf,
                                      CPU_INT16U   msg_ix,
                                      CPU_INT08U  *paddr_hw_sender,
                                      CPU_INT08U  *paddr_hw_target,
                                      CPU_INT08U  *paddr_protocol_sender,
                                      CPU_INT08U  *paddr_protocol_target,
                                      CPU_INT16U   op_code,
                                      NET_ERR     *perr)
{
#if ((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)                    && \
     (NET_CTR_CFG_ERR_EN         == DEF_ENABLED)                    && \
     (CPU_CFG_CRITICAL_METHOD    == CPU_CRITICAL_METHOD_STATUS_LOCAL))
    CPU_SR        cpu_sr;
#endif
    NET_BUF_HDR  *pbuf_hdr;
    NET_ARP_HDR  *parp_hdr;


#if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)                         /* --------------- VALIDATE PTR --------------- */
    if (pbuf == (NET_BUF *)0) {
        NET_CTR_ERR_INC(NetARP_ErrNullPtrCtr);
       *perr = NET_ARP_ERR_NULL_PTR;
        return;
    }
#endif


                                                                        /* ------------- UPDATE BUF CTRLS ------------- */
    pbuf_hdr                  = &pbuf->Hdr;
    pbuf_hdr->ARP_MsgIx       = (CPU_INT16U  )msg_ix;
    pbuf_hdr->ARP_MsgLen      = (CPU_INT16U  )NET_ARP_HDR_SIZE;
    pbuf_hdr->TotLen          = (NET_BUF_SIZE)pbuf_hdr->ARP_MsgLen;
    pbuf_hdr->ProtocolHdrType =  NET_PROTOCOL_TYPE_ARP;
#if 0                                                                   /* Init'd in NetBuf_Get() [see Note #3].        */
    pbuf_hdr->DataIx          =  NET_BUF_IX_NONE;
    pbuf_hdr->DataLen         =  0;
#endif


                                                                        /* ------------- PREPARE ARP HDR -------------- */
    parp_hdr = (NET_ARP_HDR *)&pbuf->Data[pbuf_hdr->ARP_MsgIx];


                                                                        /* ------ PREPARE ARP HW/PROTOCOL TYPES ------- */
    NET_UTIL_VAL_SET_NET_16(&parp_hdr->HW_Type,      NET_ARP_CFG_HW_TYPE);
    NET_UTIL_VAL_SET_NET_16(&parp_hdr->ProtocolType, NET_ARP_CFG_PROTOCOL_TYPE);

                                                                        /* ---- PREPARE ARP HW/PROTOCOL ADDR LENS ----- */
    parp_hdr->HW_AddrLen      =  NET_ARP_CFG_HW_ADDR_LEN;
    parp_hdr->ProtocolAddrLen =  NET_ARP_CFG_PROTOCOL_ADDR_LEN;

                                                                        /* ----------- PREPARE ARP OP CODE ------------ */
    NET_UTIL_VAL_COPY_SET_NET_16(&parp_hdr->OpCode, &op_code);

                                                                        /* --- PREPARE ARP HW/PROTOCOL SENDER ADDRS --- */
    Mem_Copy((void     *)&parp_hdr->HW_AddrSender[0],
             (void     *) paddr_hw_sender,
             (CPU_SIZE_T) NET_ARP_CFG_HW_ADDR_LEN);

    Mem_Copy((void     *)&parp_hdr->ProtocolAddrSender[0],
             (void     *) paddr_protocol_sender,
             (CPU_SIZE_T) NET_ARP_CFG_PROTOCOL_ADDR_LEN);

                                                                        /* --- PREPARE ARP HW/PROTOCOL TARGET ADDRS --- */
    if (paddr_hw_target == (CPU_INT08U *)0) {                           /* If ARP target hw addr NULL for ARP Req, ...  */
        Mem_Clr( (void     *)&parp_hdr->HW_AddrTarget[0],               /* .. clr target hw addr octets.                */
                 (CPU_SIZE_T) NET_ARP_CFG_HW_ADDR_LEN);

        DEF_BIT_SET(pbuf_hdr->Flags, NET_BUF_FLAG_BROADCAST_TX);        /* ARP Req broadcast to ALL hosts on local net. */

    } else {                                                            /* Else copy target hw addr for ARP Reply.      */
        Mem_Copy((void     *)&parp_hdr->HW_AddrTarget[0],
                 (void     *) paddr_hw_target,
                 (CPU_SIZE_T) NET_ARP_CFG_HW_ADDR_LEN);
                                                                        /* ARP Reply tx'd directly to target host.      */
    }

    Mem_Copy((void     *)&parp_hdr->ProtocolAddrTarget[0],
             (void     *) paddr_protocol_target,
             (CPU_SIZE_T) NET_ARP_CFG_PROTOCOL_ADDR_LEN);

                                                                        /* -------- CFG ARP PROTOCOL ADDR PTR --------- */
    pbuf_hdr->ARP_AddrProtocolPtr = &parp_hdr->ProtocolAddrTarget[0];



   *perr = NET_ARP_ERR_NONE;
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                         NetARP_TxPktFree()
*
* Description : Free network buffer.
*
* Argument(s) : pbuf        Pointer to network buffer.
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_Tx().
*
* Note(s)     : (1) (a) Although ARP Transmit initially requests the network buffer for transmit, 
*                       the ARP layer does NOT maintain a reference to the buffer.
*
*                   (b) Also, since the network interface layer frees ALL unreferenced buffers 
*                       after successful transmission [see 'net_if_pkt.c  NetIF_Pkt_TxPktFree()'
*                       or 'net_if_char.c  NetIF_Char_TxPktFree()'], the ARP layer MUST not
*                       free the transmit buffer.
*********************************************************************************************************
*/

static  void  NetARP_TxPktFree (NET_BUF  *pbuf)
{
   (void)&pbuf;                                                 /* Prevent compiler warning (see Note #1).              */
}


/*
*********************************************************************************************************
*                                        NetARP_TxPktDiscard()
*
* Description : On any transmit handler errors, discard packet & buffer.
*
* Argument(s) : pbuf        Pointer to network buffer.
*
*               perr        Pointer to variable that will receive the return error code from this function :
*
*                               NET_ERR_TX                      Transmit error; packet discarded.
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_Tx().
*
* Note(s)     : none.
*********************************************************************************************************
*/

static  void  NetARP_TxPktDiscard (NET_BUF  *pbuf,
                                   NET_ERR  *perr)
{
    NET_CTR  *pctr;


#if (NET_CTR_CFG_ERR_EN == DEF_ENABLED)
    pctr = (NET_CTR *)&NetARP_ErrTxPktDiscardedCtr;
#else
    pctr = (NET_CTR *) 0;
#endif
    NetBuf_FreeBuf((NET_BUF *)pbuf,
                   (NET_CTR *)pctr);

   *perr = NET_ERR_TX;
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                     NetARP_CacheTxPktHandler()
*
* Description : (1) Transmit packet buffers from ARP cache transmit buffer queue :
*
*                   (a) Resolve  packet  buffer(s)' hardware address(s)
*                   (b) Update   packet  buffer(s)' unlink/reference values
*                   (c) Transmit packet  buffer(s)
*
*
* Argument(s) : pbuf_q      Pointer to network buffer(s) to transmit.
*
*               paddr_hw    Pointer to sender's hardware address (see Note #2).
*               --------    Argument checked in NetARP_RxPktCacheUpdate().
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_RxPktCacheUpdate().
*
* Note(s)     : (2) ARP addresses MUST be in network-order.
*
*               (3) RFC #1122, Section 2.3.2.2 states that "the link layer SHOULD" ... :
*
*                   (a) "Save (rather than discard) ... packets destined to the same unresolved
*                        IP address and" ...
*                   (b) "Transmit the saved packet[s] when the address has been resolved."
*********************************************************************************************************
*/

static  void  NetARP_CacheTxPktHandler (NET_BUF     *pbuf_q,
                                        CPU_INT08U  *paddr_hw)
{
    NET_BUF      *pbuf_list;
    NET_BUF      *pbuf_list_next;
    NET_BUF      *pbuf;
    NET_BUF      *pbuf_next;
    NET_BUF_HDR  *pbuf_hdr;
    CPU_INT08U   *pbuf_addr_hw;
    NET_ERR       err;


    pbuf_list = pbuf_q;
    while (pbuf_list  != (NET_BUF *)0) {                        /* Handle ALL buf lists in Q.                           */
        pbuf_hdr       = &pbuf_list->Hdr;
        pbuf_list_next = (NET_BUF *)pbuf_hdr->NextSecListPtr;

        pbuf           = (NET_BUF *)pbuf_list;
        while (pbuf   != (NET_BUF *)0) {                        /* Handle ALL bufs in buf list.                         */
            pbuf_hdr   = &pbuf->Hdr;
            pbuf_next  = (NET_BUF *)pbuf_hdr->NextBufPtr;

            pbuf_addr_hw = pbuf_hdr->ARP_AddrHW_Ptr;
            Mem_Copy((void     *)pbuf_addr_hw,                  /* Copy hw addr into pkt buf.                           */
                     (void     *)paddr_hw,
                     (CPU_SIZE_T)NET_ARP_CFG_HW_ADDR_LEN);
                                                                /* Clr buf sec list & unlink ptrs.                      */
            pbuf_hdr->PrevSecListPtr = (void       *)0;
            pbuf_hdr->NextSecListPtr = (void       *)0;
            pbuf_hdr->UnlinkFnctPtr  = (CPU_FNCT_PTR)0;
            pbuf_hdr->UnlinkObjPtr   = (void       *)0;

            NetIF_TxPkt(pbuf, &err);                            /* Tx pkt to IF (see Note #3b).                         */

            pbuf  = pbuf_next;
        }

        pbuf_list = pbuf_list_next;
    }
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                         NetARP_CacheSrch()
*
* Description : Search ARP Cache List for ARP cache with specific protocol address.
*
*               (1) (a) ARP Cache List resolves protocol-address-to-hardware-address bindings based on the
*                       following ARP cache fields :
*
*                           - Hardware Type                     Configured at compile time (see Note #1a1)
*                           - Hardware Address Length           Configured at compile time (see Note #1a1)
*                           - Protocol Type                     Configured at compile time (see Note #1a1)
*                           - Protocol Address Length           Configured at compile time (see Note #1a1)
*                           - Protocol Address                  Generated  at run time 
*
*                       (1) These fields are configured at compile time (see 'net_arp.h  ARP CACHE  Note #3').
*
*                   (b) ARP caches are linked to form an ARP Cache List.  In the diagram below, the 
*                       top horizontal row represents the list of ARP caches.
*
*                       (1) 'NetARP_CacheListHead' points to the head of the ARP Cache List; 
*                           'NetARP_CacheListTail' points to the tail of the ARP Cache List.  
*
*                       (2) ARP caches' 'PrevPtr' & 'NextPtr' doubly-link each ARP cache to form the
*                           ARP Cache List.
*
*                   (c) ARP caches in the 'PENDING' state are pending hardware address resolution by an 
*                       ARP Reply.  While in the 'PENDING' state, ALL transmit packet buffers are enqueued 
*                       for later transmission when the corresponding ARP Reply is received.
*
*                       (1) ARP caches' 'BufQ_Head' points to the head of the pending transmit packet queue;
*                           ARP caches' 'BufQ_Tail' points to the tail of the pending transmit packet queue.
*
*                       (2) Buffer's 'PrevSecListPtr' & 'NextSecListPtr' link each buffer in a pending transmit
*                           packet queue.
*
*                   (d) For any ARP cache lookup, all ARP caches are searched in order to find the ARP cache
*                       with the appropriate hardware address--i.e. the ARP cache with the corresponding 
*                       protocol address (see Note #1a).
*
*                   (e) New ARP caches are added at the head of the ARP Cache List.  This allows faster ARP
*                       cache lookup for recently added ARP caches.
*
*                   (f) As ARP caches are added into the list, older ARP caches migrate to the tail of the 
*                       ARP Cache List.  Once an ARP cache expires or is discarded, it is removed from the 
*                       ARP Cache List.  Also if the ARP Cache List is full & a new ARP cache is needed,
*                       then the oldest ARP cache at the tail of the list is removed for allocation.
*
*$PAGE*
*
*                                          |                                               |
*                                          |<------------ List of ARP Caches ------------->|
*                                          |                (see Note #1b)                 |
*
*                                      New ARP caches                           Oldest ARP cache
*                                     inserted at head                          in ARP Cache List
*                                      (see Note #1e)                            (see Note #1f)
*
*                                             |             NextPtr                     |
*                                             |         (see Note #1b2)                 |
*                                             v                   |                     v
*                                                                 |
*                           Head of        -------       -------  v    -------       -------    (see Note #1b1)
*                          ARP Cache  ---->|     |------>|     |------>|     |------>|     |
*                            List          |     |       |     |       |     |       |     |        Tail of
*                                          |     |<------|     |<------|     |<------|     |<----  ARP Cache
*                      (see Note #1b1)     -------       -------  ^    -------       -------         List
*                                            | |                  |      | |     
*                                            | |                  |      | |     
*                                            | ------       PrevPtr      | ------
*                            BufQ_Head  ---> |      |   (see Note #1b2)  |      | <---  BufQ_Tail
*                         (see Note #1c1)    v      |                    v      |    (see Note #1c1)
*               ---                        -------  |                  -------  |
*                ^                         |     |  |                  |     |  |
*                |                         |     |  |                  |     |  |
*                |                         |     |  |                  |     |  |
*                |                         |     |  |                  |     |  |
*                |                         -------  |                  -------  |
*                |                           | ^    |                    | ^    |   
*                |       NextSecListPtr ---> | |    |                    | | <----- PrevSecListPtr 
*                |       (see Note #1c2)     v |    |                    v |    |   (see Note #1c2)
*                |                         -------  |                  -------  |
*                                          |     |  |                  |     |<--
*        Buffers pending on                |     |  |                  |     |
*       ARP cache resolution               |     |  |                  -------
*          (see Note #1c)                  |     |  |
*                                          -------  |
*                |                           | ^    |
*                |                           | |    |
*                |                           v |    |
*                |                         -------  |
*                |                         |     |<--
*                |                         |     |
*                |                         |     |
*                v                         |     |
*               ---                        -------
*
*
* Argument(s) : paddr_protocol      Pointer to protocol address (see Note #2).
*               --------------      Argument checked in NetARP_CacheHandler().
*
* Return(s)   : Pointer to ARP cache with specific protocol address, if found.
*
*               Pointer to NULL,                                     otherwise.
*
* Caller(s)   : NetARP_CacheHandler().
*
* Note(s)     : (2) 'paddr_protocol' MUST point to a protocol address that is in network-order.
*
*                   See also 'NetARP_CacheHandler()  Note #2e3'.
*********************************************************************************************************
*/
/*$PAGE*/
static  NET_ARP_CACHE  *NetARP_CacheSrch (CPU_INT08U  *paddr_protocol)
{
#if ((NET_CTR_CFG_STAT_EN     == DEF_ENABLED)                    && \
     (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL))
    CPU_SR          cpu_sr;
#endif
    CPU_BOOLEAN     found;
    NET_ARP_CACHE  *pcache;
    NET_ARP_CACHE  *pcache_next;
    CPU_INT08U     *pcache_addr;


    NET_CTR_STAT_INC_LARGE(NetARP_CacheSrchCtr_hi, NetARP_CacheSrchCtr_lo);

    pcache = NetARP_CacheListHead;                                  /* Start @ ARP Cache List head.                     */
    found  = DEF_NO;

    while ((pcache != (NET_ARP_CACHE *)0) &&                        /* Srch ARP Cache List ...                          */
           (found  ==  DEF_NO)) {                                   /* ... until cache found.                           */

        pcache_next = (NET_ARP_CACHE *) pcache->NextPtr;
        pcache_addr = (CPU_INT08U    *)&pcache->ProtocolAddr[0];
                                                                    /* Cmp ARP cache protocol addr.                     */
        found       =  Mem_Cmp((void     *)paddr_protocol,
                               (void     *)pcache_addr,
                               (CPU_SIZE_T)NET_ARP_CFG_PROTOCOL_ADDR_LEN);

        if (found != DEF_YES) {                                     /* If NOT found, adv to next ARP cache.             */
            pcache = pcache_next;
        } else {                                                    /* Else rtn found ARP cache.                        */
            pcache->AccessedCtr++;
            if (pcache->AccessedCtr > NetARP_CacheAccessedTh_nbr) { /* If ARP cache accessed > th, & ...                */
                pcache->AccessedCtr = 0;
                if (pcache != NetARP_CacheListHead) {               /* .. ARP cache NOT @ list head, ...                */
                    NetARP_CacheUnlink(pcache);
                    NetARP_CacheInsert(pcache);                     /* .. promote ARP cache to list head.               */
                }
            }

            NET_CTR_STAT_INC_LARGE(NetARP_CacheFoundCtr_hi, NetARP_CacheFoundCtr_lo);
        }
    }

    return (pcache);
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                        NetARP_CacheAddPend()
*
* Description : (1) Add a 'PENDING' ARP cache into the ARP Cache List & transmit an ARP Request :
*
*                   (a) Configure ARP cache :
*                       (1) Get default-configured ARP cache
*                       (2) ARP cache state
*                       (3) Enqueue transmit buffer to ARP cache queue
*                   (b) Insert   ARP cache into ARP Cache List
*                   (c) Transmit ARP Request to resolve ARP cache
*
*
* Argument(s) : pbuf            Pointer to network buffer to transmit.
*               ----            Argument checked   in NetARP_CacheHandler().
*
*               pbuf_hdr        Pointer to network buffer header.
*               --------        Argument validated in NetARP_CacheHandler().
*
*               paddr_protocol  Pointer to protocol address (see Note #2).
*               --------------  Argument checked   in NetARP_CacheHandler().
*
*               perr            Pointer to variable that will receive the return error code from this function :
*
*                                   NET_ARP_ERR_CACHE_PEND              ARP cache added in 'PENDING' state.
*
*                                                                       --- RETURNED BY NetARP_CacheCfg() : ----
*                                   NET_ARP_ERR_CACHE_NONE_AVAIL        NO available ARP caches to allocate.
*                                   NET_ARP_ERR_CACHE_INVALID_TYPE      ARP cache is NOT a valid cache type.
*                                   NET_TMR_ERR_NULL_OBJ                Argument 'obj'  passed a NULL pointer.
*                                   NET_TMR_ERR_NULL_FNCT               Argument 'fnct' passed a NULL pointer.
*                                   NET_TMR_ERR_NONE_AVAIL              NO available timers to allocate.
*                                   NET_TMR_ERR_INVALID_TYPE            Network timer is NOT a valid timer type.
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_CacheHandler().
*
* Note(s)     : (2) Assumes 'paddr_protocol' points to a valid protocol address that is in network-order.
*
*                   See also 'NetARP_CacheHandler()  Note #2e3'.
*
*               (3) (a) RFC #1122, Section 2.3.2.2 states that "the link layer SHOULD" ... :
*
*                       (1) "Save (rather than discard) ... packets destined to the same unresolved
*                            IP address and" ...
*                       (2) "Transmit the saved packet[s] when the address has been resolved."
*
*                   (b) Since ARP Layer is the last layer to handle & queue the transmit network 
*                       buffer, it is NOT necessary to increment the network buffer's reference 
*                       counter to include the pending ARP cache buffer queue as a new reference
*                       to the network buffer.
*
*               (4) Some buffer controls were previously initialized in NetBuf_Get() when the packet
*                   was received at the network interface layer.  These buffer controls do NOT need
*                   to be re-initialized but are shown for completeness.
*********************************************************************************************************
*/
/*$PAGE*/
static  void  NetARP_CacheAddPend (NET_BUF      *pbuf,
                                   NET_BUF_HDR  *pbuf_hdr,
                                   CPU_INT08U   *paddr_protocol,
                                   NET_ERR      *perr)
{
    NET_ARP_CACHE  *pcache;

                                                                /* ------------------ CFG ARP CACHE ------------------- */
    pcache = NetARP_CacheCfg((CPU_INT08U *)0,
                             (CPU_INT08U *)paddr_protocol,
                             (CPU_FNCT_PTR)NetARP_CacheReqTimeout,
                             (NET_TMR_TICK)NetARP_ReqTimeout_tick,
                             (NET_ERR    *)perr);
    if (*perr != NET_ARP_ERR_NONE) {
         return;
    }

                                                                /* Cfg buf's unlink fnct/obj to ARP cache.              */
    pbuf_hdr->UnlinkFnctPtr  = (CPU_FNCT_PTR)NetARP_CacheUnlinkBuf;
    pbuf_hdr->UnlinkObjPtr   = (void       *)pcache;

#if 0                                                           /* Init'd in NetBuf_Get() [see Note #4].                */
    pbuf_hdr->PrevSecListPtr = (void       *)0;
    pbuf_hdr->NextSecListPtr = (void       *)0;
#endif
                                                                /* Q buf to ARP cache (see Note #3a1).                  */
    pcache->BufQ_Head        = (NET_BUF    *)pbuf;
    pcache->BufQ_Tail        = (NET_BUF    *)pbuf;

    pcache->State            =  NET_ARP_CACHE_STATE_PEND;

                                                                /* ------- INSERT ARP CACHE INTO ARP CACHE LIST ------- */
    NetARP_CacheInsert(pcache);

                                                                /* -------------------- TX ARP REQ -------------------- */
    NetARP_TxReq(pcache);                                       /* Tx ARP req to resolve ARP cache.                     */


   *perr = NET_ARP_ERR_CACHE_PEND;
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                      NetARP_CacheAddResolved()
*
* Description : (1) Add a 'RESOLVED' ARP cache into the ARP Cache List :
*
*                   (a) Configure ARP cache :
*                       (1) Get default-configured ARP cache
*                       (2) ARP cache state
*                   (b) Insert    ARP cache into ARP Cache List
*
*
* Argument(s) : paddr_hw        Pointer to hardware address (see Note #2).
*               --------        Argument checked in NetARP_RxPktCacheUpdate().
*
*               paddr_protocol  Pointer to protocol address (see Note #2).
*               --------------  Argument checked in NetARP_RxPktCacheUpdate().
*
*               perr            Pointer to variable that will receive the return error code from this function :
*
*                                   NET_ARP_ERR_CACHE_RESOLVED          ARP cache added in 'RESOLVED' state.
*
*                                                                       --- RETURNED BY NetARP_CacheCfg() : ----
*                                   NET_ARP_ERR_CACHE_NONE_AVAIL        NO available ARP caches to allocate.
*                                   NET_ARP_ERR_CACHE_INVALID_TYPE      ARP cache is NOT a valid cache type.
*                                   NET_TMR_ERR_NULL_OBJ                Argument 'obj'  passed a NULL pointer.
*                                   NET_TMR_ERR_NULL_FNCT               Argument 'fnct' passed a NULL pointer.
*                                   NET_TMR_ERR_NONE_AVAIL              NO available timers to allocate.
*                                   NET_TMR_ERR_INVALID_TYPE            Network timer is NOT a valid timer type.
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_RxPktCacheUpdate().
*
* Note(s)     : (2) Assumes 'paddr_hw' & 'paddr_protocol' point to valid hardware & protocol addresses 
*                   that are in network-order.
*
*                   See also 'NetARP_CacheHandler()  Note #2e3'.
*********************************************************************************************************
*/

static  void  NetARP_CacheAddResolved (CPU_INT08U  *paddr_hw,
                                       CPU_INT08U  *paddr_protocol,
                                       NET_ERR     *perr)
{
    NET_ARP_CACHE  *pcache;

                                                                /* ------------------ CFG ARP CACHE ------------------- */
    pcache = NetARP_CacheCfg((CPU_INT08U *)paddr_hw,
                             (CPU_INT08U *)paddr_protocol,
                             (CPU_FNCT_PTR)NetARP_CacheTimeout,
                             (NET_TMR_TICK)NetARP_CacheTimeout_tick,
                             (NET_ERR    *)perr);
    if (*perr != NET_ARP_ERR_NONE) {
         return;
    }

    pcache->State = NET_ARP_CACHE_STATE_RESOLVED;

                                                                /* ------- INSERT ARP CACHE INTO ARP CACHE LIST ------- */
    NetARP_CacheInsert(pcache);


   *perr = NET_ARP_ERR_CACHE_RESOLVED;
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                        NetARP_CacheRemove()
*
* Description : (1) Remove an ARP cache from the ARP Cache List :
*
*                   (a) Remove ARP cache from    ARP Cache List
*                   (b) Free   ARP cache back to ARP cache pool
*
*
* Argument(s) : pcache      Pointer to an ARP cache.
*               ------      Argument checked in NetARP_CacheHandler(),
*                                               NetARP_RxPktCacheUpdate(),
*                                               NetARP_CacheGet(),
*                                               NetARP_CacheReqTimeout(),
*                                               NetARP_CacheTimeout().
*
*               tmr_free    Indicate whether to free network timer :
*
*                               DEF_YES                Free network timer for ARP cache.
*                               DEF_NO          Do NOT free network timer for ARP cache
*                                                     [Freed by NetTmr_TaskHandler()].
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_CacheHandler(),
*               NetARP_RxPktCacheUpdate(),
*               NetARP_CacheGet(),
*               NetARP_CacheReqTimeout(),
*               NetARP_CacheTimeout().
*
* Note(s)     : none.
*********************************************************************************************************
*/

static  void  NetARP_CacheRemove (NET_ARP_CACHE  *pcache,
                                  CPU_BOOLEAN     tmr_free)
{
                                                                /* ------- REMOVE ARP CACHE FROM ARP CACHE LIST ------- */
    NetARP_CacheUnlink(pcache);
                                                                /* ------------------ FREE ARP CACHE ------------------ */
    NetARP_CacheFree(pcache, tmr_free);
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                        NetARP_CacheInsert()
*
* Description : Insert an ARP cache into the ARP Cache List at the head of the list.
*
* Argument(s) : pcache      Pointer to an ARP cache.
*               ------      Argument checked in NetARP_CacheSrch(),
*                                               NetARP_CacheAddPend(),
*                                               NetARP_CacheAddResolved().
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_CacheSrch(),
*               NetARP_CacheAddPend(),
*               NetARP_CacheAddResolved().
*
* Note(s)     : none.
*********************************************************************************************************
*/

static  void  NetARP_CacheInsert (NET_ARP_CACHE  *pcache)
{
    pcache->PrevPtr = (void *)0;
    pcache->NextPtr = (void *)NetARP_CacheListHead;

    if (NetARP_CacheListHead != (NET_ARP_CACHE *)0) {           /* If list NOT empty, insert before head.               */
        NetARP_CacheListHead->PrevPtr = (void          *)pcache;
    } else {                                                    /* Else add first ARP cache to list.                    */
        NetARP_CacheListTail          = (NET_ARP_CACHE *)pcache;
    }
    NetARP_CacheListHead = pcache;                              /* Insert ARP cache @ list head.                        */
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                        NetARP_CacheUnlink()
*
* Description : Unlink an ARP cache from the ARP Cache List.
*
* Argument(s) : pcache      Pointer to an ARP cache.
*               ------      Argument checked in NetARP_CacheSrch();
*                                               NetARP_CacheRemove()
*                                                 by NetARP_CacheHandler(),
*                                                    NetARP_CacheGet(),
*                                                    NetARP_CacheReqTimeout(),
*                                                    NetARP_CacheTimeout().
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_CacheSrch(),
*               NetARP_CacheRemove().
*
* Note(s)     : (1) Since NetARP_CacheUnlink() called ONLY to remove & then re-link or free ARP caches, 
*                   it is NOT necessary to clear the entry's previous & next pointers.  However, pointers
*                   cleared to NULL shown for correctness & completeness.
*********************************************************************************************************
*/

static  void  NetARP_CacheUnlink (NET_ARP_CACHE  *pcache)
{
    NET_ARP_CACHE  *pcache_prev;
    NET_ARP_CACHE  *pcache_next;

                                                                /* ------- UNLINK ARP CACHE FROM ARP CACHE LIST ------- */
    pcache_prev = (NET_ARP_CACHE *)pcache->PrevPtr;
    pcache_next = (NET_ARP_CACHE *)pcache->NextPtr;
                                                                /* Point prev ARP cache to next ARP cache.              */
    if (pcache_prev != (NET_ARP_CACHE *)0) {
        pcache_prev->NextPtr = (void          *)pcache_next;
    } else {
        NetARP_CacheListHead = (NET_ARP_CACHE *)pcache_next;
    }
                                                                /* Point next ARP cache to prev ARP cache.              */
    if (pcache_next != (NET_ARP_CACHE *)0) {
        pcache_next->PrevPtr = (void          *)pcache_prev;
    } else {
        NetARP_CacheListTail = (NET_ARP_CACHE *)pcache_prev;
    }

#if (NET_DBG_CFG_MEM_CLR_EN == DEF_ENABLED)                     /* Clr ARP cache's ptrs (see Note #1).                  */
    pcache->PrevPtr = (void *)0;
    pcache->NextPtr = (void *)0;
#endif
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                       NetARP_CacheUnlinkBuf()
*
* Description : Unlink a network buffer from a cache's transmit queue.
*
* Argument(s) : pbuf        Pointer to network buffer enqueued in an ARP cache transmit buffer queue.
*
* Return(s)   : none.
*
* Caller(s)   : Referenced in NetARP_CacheHandler().
*
* Note(s)     : none.
*********************************************************************************************************
*/

static  void  NetARP_CacheUnlinkBuf (NET_BUF  *pbuf)
{
#if ((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)                    && \
     (NET_CTR_CFG_ERR_EN         == DEF_ENABLED)                    && \
     (CPU_CFG_CRITICAL_METHOD    == CPU_CRITICAL_METHOD_STATUS_LOCAL))
    CPU_SR          cpu_sr;
#endif
#if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)
    CPU_BOOLEAN     used;
#endif
    NET_BUF        *pbuf_prev;
    NET_BUF        *pbuf_next;
    NET_BUF_HDR    *pbuf_hdr;
    NET_BUF_HDR    *pbuf_hdr_prev;
    NET_BUF_HDR    *pbuf_hdr_next;
    NET_ARP_CACHE  *pcache;


#if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)
                                                                /* ------------------- VALIDATE PTR ------------------- */
    if (pbuf == (NET_BUF *)0) {
        NET_CTR_ERR_INC(NetARP_ErrNullPtrCtr);
        return;
    }
                                                                /* ------------------- VALIDATE BUF ------------------- */
    used = NetBuf_IsUsed(pbuf);
    if (used != DEF_YES) {
        return;
    }
#endif

    pbuf_hdr = (NET_BUF_HDR   *)&pbuf->Hdr;
    pcache   = (NET_ARP_CACHE *) pbuf_hdr->UnlinkObjPtr;

#if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)                 /* ---------------- VALIDATE ARP CACHE ---------------- */
    used = NetARP_CacheIsUsed(pcache);
    if (used != DEF_YES) {
        return;
    }
#endif


                                                                /* ---------- UNLINK BUF FROM ARP CACHE TX Q ---------- */
    pbuf_prev = (NET_BUF *)pbuf_hdr->PrevSecListPtr;
    pbuf_next = (NET_BUF *)pbuf_hdr->NextSecListPtr;
                                                                /* Point prev ARP cache pending tx Q buf to next buf.   */
    if (pbuf_prev != (NET_BUF *)0) {
        pbuf_hdr_prev                 = (NET_BUF_HDR *)&pbuf_prev->Hdr;
        pbuf_hdr_prev->NextSecListPtr = (void        *) pbuf_next;
    } else {
        pcache->BufQ_Head             = (NET_BUF     *) pbuf_next;
    }
                                                                /* Point next ARP cache pending tx Q buf to prev buf.   */
    if (pbuf_next != (NET_BUF *)0) {
        pbuf_hdr_next                 = (NET_BUF_HDR *)&pbuf_next->Hdr;
        pbuf_hdr_next->PrevSecListPtr = (void        *) pbuf_prev;
    } else {
        pcache->BufQ_Tail             = (NET_BUF     *) pbuf_prev;
    }


                                                                /* -------------- CLR BUF'S UNLINK CTRLS -------------- */
    pbuf_hdr->PrevSecListPtr = (void       *)0;                 /* Clr pending tx Q ptrs.                               */
    pbuf_hdr->NextSecListPtr = (void       *)0;

    pbuf_hdr->UnlinkFnctPtr  = (CPU_FNCT_PTR)0;                 /* Clr unlink ptrs.                                     */
    pbuf_hdr->UnlinkObjPtr   = (void       *)0;
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                         NetARP_CacheTimeout()
*
* Description : Discard an ARP cache in the 'RESOLVED' state on timeout.
*
* Argument(s) : pcache_timeout      Pointer to an ARP cache (see Note #2b).
*
* Return(s)   : none.
*
* Caller(s)   : Referenced in NetARP_CacheAddResolved().
*
* Note(s)     : (1) RFC #1122, Section 2.3.2.1 states that "an implementation of the Address Resolution
*                   Protocol (ARP) ... MUST provide a mechanism to flush out-of-date cache entries".
*
*               (2) Ideally, network timer expiration functions could be defined as '[(void) (OBJECT *)]' 
*                   type functions -- even though network timer API functions cast expiration functions 
*                   to generic 'CPU_FNCT_PTR' type (i.e. '[(void) (void *)]').
*
*                   (a) (1) Unfortunately, ISO-IEC 9899-1999 ANSI-C, Section 6.3.2.3.7 states that "a 
*                           pointer to an object ... may be converted to a pointer to a different object 
*                           ... [but] if the resulting pointer is not correctly aligned ... the behavior
*                           is undefined".
*
*                           And since compilers may NOT correctly convert 'void' pointers to non-'void' 
*                           pointer arguments, network timer expiration functions MUST avoid incorrect
*                           pointer conversion behavior between 'void' pointer parameters & non-'void' 
*                           pointer arguments & therefore CANNOT be defined as '[(void) (OBJECT *)]'.
*
*                       (2) However, Section 6.3.2.3.1 states that "a pointer to void may be converted 
*                           to or from a pointer to any ... object ... A pointer to any ... object ... 
*                           may be converted to a pointer to void and back again; the result shall 
*                           compare equal to the original pointer".
*                   
*                   (b) Therefore, to correctly convert 'void' pointer objects back to appropriate
*                       network object pointer objects, network timer expiration functions MUST :
*
*                       (1) Be defined as 'CPU_FNCT_PTR' type (i.e. '[(void) (void *)]'); &       ...
*                       (2) Explicitly cast 'void' pointer arguments to specific object pointers; ...
*                           (A) ... in this case, a 'NET_ARP_CACHE' pointer.
*
*                   See also 'net_tmr.c  NetTmr_Get()  Note #3'.
*
*               (3) This function is a network timer expiration function :
*
*                   (a) Clear the timer pointer; ...
*                       (1) Cleared in NetARP_CacheFree() via NetARP_CacheRemove().
*
*                   (b) but do NOT re-free the timer.
*********************************************************************************************************
*/

static  void  NetARP_CacheTimeout (void  *pcache_timeout)
{
#if ((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)                    && \
     (NET_CTR_CFG_ERR_EN         == DEF_ENABLED)                    && \
     (CPU_CFG_CRITICAL_METHOD    == CPU_CRITICAL_METHOD_STATUS_LOCAL))
    CPU_SR          cpu_sr;
#endif
    NET_ARP_CACHE  *pcache;


    pcache = (NET_ARP_CACHE *)pcache_timeout;                   /* See Note #2b2A.                                      */

#if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)
                                                                /* ------------------ VALIDATE PTR -------------------- */
    if (pcache == (NET_ARP_CACHE *)0) {
        NET_CTR_ERR_INC(NetARP_ErrNullPtrCtr);
        return;
    }
                                                                /* ------------------ VALIDATE TYPE ------------------- */
    if (pcache->Type != NET_ARP_TYPE_CACHE) {
        NET_CTR_ERR_INC(NetARP_ErrInvalidTypeCtr);
        return;
    }
#endif

                                                                /* ---------------- DISCARD ARP CACHE ----------------- */
    NetARP_CacheRemove((NET_ARP_CACHE *)pcache,
                       (CPU_BOOLEAN    )DEF_NO);                /* Clr but do NOT free tmr (see Note #3).               */
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                      NetARP_CacheReqTimeout()
*
* Description : Retry ARP Request to resolve an ARP cache in the 'PENDING' state on ARP Request timeout.
*
* Argument(s) : pcache_timeout      Pointer to an ARP cache (see Note #1b).
*
* Return(s)   : none.
*
* Caller(s)   : Referenced in NetARP_CacheAddPend().
*
* Note(s)     : (1) Ideally, network timer expiration functions could be defined as '[(void) (OBJECT *)]' 
*                   type functions -- even though network timer API functions cast expiration functions 
*                   to generic 'CPU_FNCT_PTR' type (i.e. '[(void) (void *)]').
*
*                   (a) (1) Unfortunately, ISO-IEC 9899-1999 ANSI-C, Section 6.3.2.3.7 states that "a 
*                           pointer to an object ... may be converted to a pointer to a different object 
*                           ... [but] if the resulting pointer is not correctly aligned ... the behavior
*                           is undefined".
*
*                           And since compilers may NOT correctly convert 'void' pointers to non-'void' 
*                           pointer arguments, network timer expiration functions MUST avoid incorrect
*                           pointer conversion behavior between 'void' pointer parameters & non-'void' 
*                           pointer arguments & therefore CANNOT be defined as '[(void) (OBJECT *)]'.
*
*                       (2) However, Section 6.3.2.3.1 states that "a pointer to void may be converted 
*                           to or from a pointer to any ... object ... A pointer to any ... object ... 
*                           may be converted to a pointer to void and back again; the result shall 
*                           compare equal to the original pointer".
*                   
*                   (b) Therefore, to correctly convert 'void' pointer objects back to appropriate
*                       network object pointer objects, network timer expiration functions MUST :
*
*                       (1) Be defined as 'CPU_FNCT_PTR' type (i.e. '[(void) (void *)]'); &       ...
*                       (2) Explicitly cast 'void' pointer arguments to specific object pointers; ...
*                           (A) ... in this case, a 'NET_ARP_CACHE' pointer.
*
*                   See also 'net_tmr.c  NetTmr_Get()  Note #3'.
*
*               (2) This function is a network timer expiration function :
*
*                   (a) Clear the timer pointer; ...
*                       (1) Cleared in NetARP_CacheFree() via NetARP_CacheRemove(); or
*                       (2) Reset   by NetTmr_Get().
*
*                   (b) but do NOT re-free the timer.
*********************************************************************************************************
*/

static  void  NetARP_CacheReqTimeout (void  *pcache_timeout)
{
#if ((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)                    && \
     (NET_CTR_CFG_ERR_EN         == DEF_ENABLED)                    && \
     (CPU_CFG_CRITICAL_METHOD    == CPU_CRITICAL_METHOD_STATUS_LOCAL))
    CPU_SR          cpu_sr;
#endif
    NET_ARP_CACHE  *pcache;
    NET_ERR         err;


    pcache = (NET_ARP_CACHE *)pcache_timeout;                   /* See Note #1b2A.                                      */

#if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)
                                                                /* ------------------ VALIDATE PTR -------------------- */
    if (pcache == (NET_ARP_CACHE *)0) {
        NET_CTR_ERR_INC(NetARP_ErrNullPtrCtr);
        return;
    }
                                                                /* ------------------ VALIDATE TYPE ------------------- */
    if (pcache->Type != NET_ARP_TYPE_CACHE) {
        NET_CTR_ERR_INC(NetARP_ErrInvalidTypeCtr);
        return;
    }
#endif


    if (pcache->ReqAttemptsCtr >= NetARP_ReqMaxAttempts_nbr) {  /* If nbr attempts >= max, free ARP cache.              */
        NetARP_CacheRemove((NET_ARP_CACHE *)pcache,
                           (CPU_BOOLEAN    )DEF_NO);            /* Clr but do NOT free tmr (see Note #2).               */
        return;
    }

                                                                /* ------------------ RETRY ARP REQ ------------------- */
    pcache->TmrPtr = NetTmr_Get((void       *) pcache,
                                (CPU_FNCT_PTR) NetARP_CacheReqTimeout,
                                (NET_TMR_TICK) NetARP_ReqTimeout_tick,
                                (CPU_INT16U  ) NET_TMR_FLAG_NONE,
                                (NET_ERR    *)&err);
    if (err != NET_TMR_ERR_NONE) {                              /* If tmr unavail, free ARP cache.                      */
        NetARP_CacheRemove((NET_ARP_CACHE *)pcache,
                           (CPU_BOOLEAN    )DEF_NO);            /* Clr but do NOT free tmr (see Note #2).               */
        return;
    }

    NetARP_TxReq(pcache);
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                          NetARP_CacheCfg()
*
* Description : (1) Configure an ARP cache :
*
*                   (a) Get ARP cache from ARP cache pool
*                   (b) Get ARP cache timer
*                   (c) Configure ARP cache :
*                       (1) Configure ARP cache addresses :
*                           (A) Protocol address
*                           (B) Hardware address
*                       (2) Configure ARP cache controls
*
*
* Argument(s) : paddr_hw        Pointer to hardware address (see Note #2a).
*
*               paddr_protocol  Pointer to protocol address (see Note #2b).
*               --------------  Argument checked in NetARP_ProbeAddrOnNet(),
*                                                   NetARP_CacheAddPend(),
*                                                   NetARP_CacheAddResolved().
*
*               timeout_fnct    Pointer to timeout function.
*
*               timeout_tick    Timeout value (in 'NET_TMR_TICK' ticks).
*
*               perr            Pointer to variable that will receive the return error code from this function :
*
*                                   NET_ARP_ERR_NONE                    ARP cache successfully configured.
*
*                                                                       ---- RETURNED BY NetARP_CacheGet() : ---
*                                   NET_ARP_ERR_CACHE_NONE_AVAIL        NO available ARP caches to allocate.
*                                   NET_ARP_ERR_CACHE_INVALID_TYPE      ARP cache is NOT a valid cache type.
*
*                                                                       ------ RETURNED BY NetTmr_Get() : ------
*                                   NET_TMR_ERR_NULL_OBJ                Argument 'obj'  passed a NULL pointer.
*                                   NET_TMR_ERR_NULL_FNCT               Argument 'fnct' passed a NULL pointer.
*                                   NET_TMR_ERR_NONE_AVAIL              NO available timers to allocate.
*                                   NET_TMR_ERR_INVALID_TYPE            Network timer is NOT a valid timer type.
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_ProbeAddrOnNet(),
*               NetARP_CacheAddPend(),
*               NetARP_CacheAddResolved().
*
* Note(s)     : (2) (a) If 'paddr_hw' available, points to a valid hardware address that is in network-order.
*
*                   (b) Assumes 'paddr_protocol' points to a valid protocol address that is in network-order.
*
*                   See also 'NetARP_CacheHandler()  Note #2e3'.
*
*               (3) On ANY errors, network resources MUST be appropriately freed.
*
*               (4) During ARP cache initialization, some ARP cache controls were previously initialized
*                   in NetARP_CacheGet() when the ARP cache was allocated from the ARP cache pool.  These
*                   ARP cache controls do NOT need to be re-initialized but are shown for completeness.
*********************************************************************************************************
*/
/*$PAGE*/
static  NET_ARP_CACHE  *NetARP_CacheCfg (CPU_INT08U    *paddr_hw,
                                         CPU_INT08U    *paddr_protocol,
                                         CPU_FNCT_PTR   timeout_fnct,
                                         NET_TMR_TICK   timeout_tick,
                                         NET_ERR       *perr)
{
    NET_ARP_CACHE  *pcache;

                                                                /* ------------------ GET ARP CACHE ------------------- */
    pcache = NetARP_CacheGet(perr);
    if (pcache == (NET_ARP_CACHE *)0) {
        return   ((NET_ARP_CACHE *)0);                          /* Rtn err from NetARP_CacheGet().                      */
    }

                                                                /* ---------------- GET ARP CACHE TMR ----------------- */
    pcache->TmrPtr = NetTmr_Get((void       *)pcache,
                                (CPU_FNCT_PTR)timeout_fnct,
                                (NET_TMR_TICK)timeout_tick,
                                (CPU_INT16U  )NET_TMR_FLAG_NONE,
                                (NET_ERR    *)perr);
    if (*perr != NET_TMR_ERR_NONE) {                            /* If tmr unavail, ...                                  */
         NetARP_CacheFree(pcache, DEF_NO);                      /* ... free ARP cache (see Note #3).                    */
         return ((NET_ARP_CACHE *)0);
    }

                                                                /* ------------------ CFG ARP CACHE ------------------- */
                                                                /* Cfg ARP cache addr(s).                               */
    if (paddr_hw != (CPU_INT08U *)0) {                          /* If hw         addr avail, ...                        */
        Mem_Copy((void     *)&pcache->HW_Addr[0],               /* ... copy hw   addr into ARP cache (see Note #2a).    */
                 (void     *) paddr_hw,
                 (CPU_SIZE_T) NET_ARP_CFG_HW_ADDR_LEN);
    }

    Mem_Copy((void     *)&pcache->ProtocolAddr[0],              /* Copy protocol addr into ARP cache (see Note #2b).    */
             (void     *) paddr_protocol,
             (CPU_SIZE_T) NET_ARP_CFG_PROTOCOL_ADDR_LEN);

                                                                /* Cfg ARP cache ctrl(s).                               */
#if 0                                                           /* Init'd in NetARP_CacheGet() [see Note #4].           */
    pcache->AccessedCtr    =  0;
    pcache->ReqAttemptsCtr =  0;

    pcache->BufQ_Head      = (NET_BUF *)0;
    pcache->BufQ_Tail      = (NET_BUF *)0;
                                                                /* Cfg'd  in NetARP_CacheInsert().                      */
    pcache->PrevPtr        = (void    *)0;
    pcache->NextPtr        = (void    *)0;
#endif


   *perr =  NET_ARP_ERR_NONE;

    return (pcache);
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                          NetARP_CacheGet()
*
* Description : (1) Allocate & initialize an ARP cache :
*
*                   (a) Get an ARP cache
*                   (b) Validate   ARP cache
*                   (c) Initialize ARP cache
*                   (d) Update ARP cache pool statistics
*                   (e) Return pointer to ARP cache
*                         OR
*                       Null pointer & error code, on failure
*
*               (2) The ARP cache pool is implemented as a stack :
*
*                   (a) 'NetARP_CachePoolPtr' points to the head of the ARP cache pool.
*
*                   (b) ARP caches' 'NextPtr's link each ARP cache to form the ARP cache pool stack.
*
*                   (c) ARP caches are inserted & removed at the head of   the ARP cache pool stack.
*
*
*                                       ARP caches are
*                                     inserted & removed
*                                        at the head
*                                       (see Note #2c)
*
*                                             |                 NextPtr
*                                             |             (see Note #2b)
*                                             v                    |
*                                                                  |
*                                          -------       -------   v   -------       -------
*                            ARP Pool ---->|     |------>|     |------>|     |------>|     |
*                            Pointer       |     |       |     |       |     |       |     |
*                                          |     |       |     |       |     |       |     |
*                         (see Note #2a)   -------       -------       -------       -------
*
*                                          |                                               |
*                                          |<---------- Pool of Free ARP Caches ---------->|
*                                          |                (see Note #2)                  |
*
*
* Argument(s) : perr        Pointer to variable that will receive the return error code from this function :
*
*                               NET_ARP_ERR_NONE                    ARP cache successfully allocated &
*                                                                       initialized.
*                               NET_ARP_ERR_CACHE_NONE_AVAIL        NO available ARP caches to allocate.
*                               NET_ARP_ERR_CACHE_INVALID_TYPE      ARP cache is NOT a valid cache type.
*
* Return(s)   : Pointer to ARP cache, if NO errors.
*
*               Pointer to NULL,      otherwise.
*
* Caller(s)   : NetARP_CacheCfg().
*
* Note(s)     : (3) (a) ARP cache pool is accessed by 'NetARP_CachePoolPtr' during execution of
*
*                       (1) NetARP_Init()
*                       (2) NetARP_CacheGet()
*                       (3) NetARP_CacheFree()
*
*                   (b) Since the primary tasks of the network protocol suite are prevented from running 
*                       concurrently (see 'net.h  Note #2'), it is NOT necessary to protect the shared 
*                       resources of the ARP cache pool since no asynchronous access from other network
*                       tasks is possible.
*
*               (4) 'No cache available' case NOT possible during correct operation of  the ARP Cache.
*                   However, the 'else' case is included as an extra precaution in case the ARP Cache
*                   List is incorrectly modified &/or corrupted.
*********************************************************************************************************
*/
/*$PAGE*/
static  NET_ARP_CACHE  *NetARP_CacheGet (NET_ERR  *perr)
{
#if ((NET_CTR_CFG_ERR_EN      == DEF_ENABLED)                    && \
     (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL))
    CPU_SR          cpu_sr;
#endif
    NET_ARP_CACHE  *pcache;
    NET_ERR         stat_err;


                                                                /* ------------------ GET ARP CACHE ------------------- */
    if (NetARP_CachePoolPtr != (NET_ARP_CACHE *)0) {            /* If ARP cache pool NOT empty, get cache from pool.    */
        pcache               = (NET_ARP_CACHE *)NetARP_CachePoolPtr;
        NetARP_CachePoolPtr  = (NET_ARP_CACHE *)pcache->NextPtr;

    } else if (NetARP_CacheListTail != (NET_ARP_CACHE *)0) {    /* If ARP Cache List NOT empty, ...                     */
                                                                /* ... get ARP cache from list tail.                    */
        pcache               = (NET_ARP_CACHE *)NetARP_CacheListTail;
        NetARP_CacheRemove(pcache, DEF_YES);                    
        pcache               = (NET_ARP_CACHE *)NetARP_CachePoolPtr;
        NetARP_CachePoolPtr  = (NET_ARP_CACHE *)pcache->NextPtr;

    } else {                                                    /* Else none avail, rtn err (see Note #4).              */
        NET_CTR_ERR_INC(NetARP_ErrNoneAvailCtr);
       *perr =   NET_ARP_ERR_CACHE_NONE_AVAIL;
        return ((NET_ARP_CACHE *)0);

    }


#if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)                 /* ---------------- VALIDATE ARP CACHE ---------------- */
    if (pcache->Type != NET_ARP_TYPE_CACHE) {
        NetARP_CacheDiscard(pcache);
        NET_CTR_ERR_INC(NetARP_ErrInvalidTypeCtr);
       *perr =   NET_ARP_ERR_CACHE_INVALID_TYPE;
        return ((NET_ARP_CACHE *)0);
    }
#endif

                                                                /* ------------------ INIT ARP CACHE ------------------ */
    NetARP_CacheClr(pcache);
    DEF_BIT_SET(pcache->Flags, NET_ARP_FLAG_USED);              /* Set ARP cache as used.                               */

                                                                /* ----------- UPDATE ARP CACHE POOL STATS ------------ */
    NetStat_PoolEntryUsedInc(&NetARP_CachePoolStat, &stat_err);

   *perr =  NET_ARP_ERR_NONE;

    return (pcache);                                            /* ------------------ RTN ARP CACHE ------------------- */
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                         NetARP_CacheFree()
*
* Description : (1) Free an ARP cache :
*
*                   (a) Free   ARP cache timer
*                   (b) Free   ARP cache buffer queue
*                   (c) Clear  ARP cache controls
*                   (d) Free   ARP cache back to ARP cache pool
*                   (e) Update ARP cache pool statistics
*
*
* Argument(s) : pcache      Pointer to an ARP cache.
*               ------      Argument checked in NetARP_CacheCfg(),
*                                               NetARP_CacheRemove()
*                                                 by NetARP_CacheHandler(),
*                                                    NetARP_CacheGet(),
*                                                    NetARP_CacheReqTimeout(),
*                                                    NetARP_CacheTimeout().
*
*               tmr_free    Indicate whether to free network timer :
*
*                               DEF_YES                Free network timer for ARP cache.
*                               DEF_NO          Do NOT free network timer for ARP cache
*                                                     [Freed by  NetTmr_TaskHandler()
*                                                            via NetARP_CacheRemove()].
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_CacheCfg(),
*               NetARP_CacheRemove().
*
* Note(s)     : (2) #### To prevent freeing an ARP cache already freed via previous ARP cache free,
*                   NetARP_CacheFree() checks the ARP cache's 'USED' flag BEFORE freeing the cache.
*
*                   This prevention is only best-effort since any invalid duplicate ARP cache frees 
*                   MAY be asynchronous to potentially valid ARP cache gets.  Thus the invalid ARP 
*                   cache free(s) MAY corrupt the ARP cache's valid operation(s).
*
*                   However, since the primary tasks of the network protocol suite are prevented from
*                   running concurrently (see 'net.h  Note #2'), it is NOT necessary to protect ARP
*                   cache resources from possible corruption since no asynchronous access from other
*                   network tasks is possible.
*
*               (3) When an ARP cache in the 'PENDING' state is freed, it discards its transmit packet
*                   buffer queue.  The discard is performed by the network interface layer since it is
*                   the last layer to initiate transmission for these packet buffers.
*********************************************************************************************************
*/
/*$PAGE*/
static  void  NetARP_CacheFree (NET_ARP_CACHE  *pcache,
                                CPU_BOOLEAN     tmr_free)
{
#if ((NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)                    && \
     (NET_CTR_CFG_ERR_EN         == DEF_ENABLED)                    && \
     (CPU_CFG_CRITICAL_METHOD    == CPU_CRITICAL_METHOD_STATUS_LOCAL))
    CPU_SR        cpu_sr;
#endif
#if  (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)
    CPU_BOOLEAN   used;
#endif
    NET_CTR      *pctr;
    NET_ERR       err;


#if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)
                                                                    /* ---------------- VALIDATE TYPE ----------------- */
    if (pcache->Type != NET_ARP_TYPE_CACHE) {
        NetARP_CacheDiscard(pcache);
        NET_CTR_ERR_INC(NetARP_ErrInvalidTypeCtr);
        return;
    }
                                                                    /* ------------ VALIDATE ARP CACHE USED ----------- */
    used = DEF_BIT_IS_SET(pcache->Flags, NET_ARP_FLAG_USED);
    if (used != DEF_YES) {                                          /* If ARP cache NOT used, ...                       */
        NET_CTR_ERR_INC(NetARP_ErrNotUsedCtr);
        return;                                                     /* ... rtn but do NOT free (see Note #2).           */
    }
#endif

                                                                    /* -------------- FREE ARP CACHE TMR -------------- */
    if (tmr_free == DEF_YES) {
        if (pcache->TmrPtr != (NET_TMR *)0) {
            NetTmr_Free(pcache->TmrPtr);
        }
    }

                                                                    /* ------------- FREE ARP CACHE BUF Q ------------- */
#if (NET_CTR_CFG_ERR_EN == DEF_ENABLED)
    pctr = (NET_CTR *)&NetIF_ErrTxPktDiscardedCtr;                  /* See Note #3.                                     */
#else
    pctr = (NET_CTR *) 0;
#endif
    NetBuf_FreeBufQ_SecList((NET_BUF    *)pcache->BufQ_Head,
                            (NET_CTR    *)pctr,
                            (CPU_FNCT_PTR)NetARP_CacheUnlinkBuf);

                                                                    /* ----------------- CLR ARP CACHE ---------------- */
    pcache->State = NET_ARP_CACHE_STATE_FREE;                       /* Set ARP cache as freed/NOT used.                 */
    DEF_BIT_CLR(pcache->Flags, NET_ARP_FLAG_USED);
#if (NET_DBG_CFG_MEM_CLR_EN == DEF_ENABLED)
    NetARP_CacheClr(pcache);
#endif    

                                                                    /* ---------------- FREE ARP CACHE ---------------- */
    pcache->NextPtr     = (void          *)NetARP_CachePoolPtr;
    NetARP_CachePoolPtr = (NET_ARP_CACHE *)pcache;

                                                                    /* --------- UPDATE ARP CACHE POOL STATS ---------- */
    NetStat_PoolEntryUsedDec(&NetARP_CachePoolStat, &err);
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                          NetARP_CacheClr()
*
* Description : Clear ARP cache controls.
*
* Argument(s) : pcache      Pointer to an ARP cache.
*               ------      Argument validated in NetARP_Init(),
*                                                 NetARP_CacheGet(),
*                                                 NetARP_CacheFree().
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_Init(),
*               NetARP_CacheGet(),
*               NetARP_CacheFree().
*
* Note(s)     : none.
*********************************************************************************************************
*/

static  void  NetARP_CacheClr (NET_ARP_CACHE  *pcache)
{
    pcache->PrevPtr        = (void    *)0;
    pcache->NextPtr        = (void    *)0;
    pcache->BufQ_Head      = (NET_BUF *)0;
    pcache->BufQ_Tail      = (NET_BUF *)0;
    pcache->TmrPtr         = (NET_TMR *)0;
    pcache->AccessedCtr    =  0;
    pcache->ReqAttemptsCtr =  0;
    pcache->State          =  NET_ARP_CACHE_STATE_FREE;
    pcache->Flags          =  NET_ARP_FLAG_NONE;

    Mem_Clr((void     *)&pcache->HW_Addr[0],
            (CPU_SIZE_T) NET_ARP_CFG_HW_ADDR_LEN);
    Mem_Clr((void     *)&pcache->ProtocolAddr[0],
            (CPU_SIZE_T) NET_ARP_CFG_PROTOCOL_ADDR_LEN);
}


/*$PAGE*/
/*
*********************************************************************************************************
*                                        NetARP_CacheIsUsed()
*
* Description : Validate ARP cache in use.
*
* Argument(s) : pcache      Pointer to object to validate as an ARP cache in use.
*
* Return(s)   : DEF_YES, ARP cache   valid &      in use.
*
*               DEF_NO,  ARP cache invalid or NOT in use.
*
* Caller(s)   : various.
*
* Note(s)     : (1) NetARP_CacheIsUsed() MUST be called with the global network lock already acquired.
*********************************************************************************************************
*/

#if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)
static  CPU_BOOLEAN  NetARP_CacheIsUsed (NET_ARP_CACHE  *pcache)
{
    CPU_BOOLEAN  used;

                                                                /* ------------------ VALIDATE PTR -------------------- */
    if (pcache == (NET_ARP_CACHE *)0) {
        return (DEF_NO);
    }
                                                                /* ------------------ VALIDATE TYPE ------------------- */
    if (pcache->Type != NET_ARP_TYPE_CACHE) {
        return (DEF_NO);
    }

                                                                /* ------------- VALIDATE ARP CACHE USED -------------- */
    used = DEF_BIT_IS_SET(pcache->Flags, NET_ARP_FLAG_USED);

    return (used);
}
#endif


/*$PAGE*/
/*
*********************************************************************************************************
*                                        NetARP_CacheDiscard()
*
* Description : (1) Discard an invalid/corrupted ARP cache :
*
*                   (a) Discard ARP cache from available cache pool                 See Note #2
*                   (b) Update  ARP cache pool statistics
*
*               (2) Assumes ARP cache is invalid/corrupt & MUST be removed.  ARP cache removed
*                   simply by NOT returning the ARP cache back to the ARP cache pool.
*
*
* Argument(s) : pcache      Pointer to an invalid/corrupted ARP cache.
*
* Return(s)   : none.
*
* Caller(s)   : NetARP_CacheGet(),
*               NetARP_CacheFree().
*
* Note(s)     : none.
*********************************************************************************************************
*/

#if (NET_ERR_CFG_ARG_CHK_DBG_EN == DEF_ENABLED)
static  void  NetARP_CacheDiscard (NET_ARP_CACHE  *pcache)
{
#if ((NET_CTR_CFG_ERR_EN      == DEF_ENABLED)                    && \
     (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL))
    CPU_SR   cpu_sr;
#endif
    NET_ERR  stat_err;

                                                                /* ---------------- DISCARD ARP CACHE ----------------- */
    if (pcache == (NET_ARP_CACHE *)0) {
        NET_CTR_ERR_INC(NetARP_ErrNullPtrCtr);
        return;
    }

                                                                /* --------------- UPDATE DISCARD STATS --------------- */
    NetStat_PoolEntryLostInc(&NetARP_CachePoolStat, &stat_err);
}
#endif


/*$PAGE*/
/*
*********************************************************************************************************
*                                             MODULE END
*
* Note(s) : (1) See 'MODULE  Note #1' & 'net_arp.h  MODULE  Note #1'.
*********************************************************************************************************
*/

#endif                                                          /* End of ARP module include (see Note #1).             */

