Exemple #1
0
/*!
 * \brief Used by a task to clear the specified event bits in the ligtweight event.
 *
 * \param[in] event_ptr Pointer to the event.
 * \param[in] bit_mask  Bit mask. Each bit represents an event bit to clear.
 *
 * \return MQX_OK
 * \return MQX_LWEVENT_INVALID (Lightweight event is not valid.)
 *
 * \see _lwevent_create
 * \see _lwevent_destroy
 * \see _lwevent_set
 * \see _lwevent_set_auto_clear
 * \see _lwevent_test
 * \see _lwevent_wait_for
 * \see _lwevent_wait_ticks
 * \see _lwevent_wait_until
 * \see _lwevent_get_signalled
 * \see LWEVENT_STRUCT
 */
_mqx_uint _lwevent_clear
(
    LWEVENT_STRUCT_PTR  event_ptr,
    _mqx_uint           bit_mask
)
{
    _KLOGM(KERNEL_DATA_STRUCT_PTR kernel_data);

#if MQX_ENABLE_USER_MODE && MQX_ENABLE_USER_STDAPI
    if (MQX_RUN_IN_USER_MODE)
    {
        return _usr_lwevent_clear(event_ptr, bit_mask);
    }
#endif

    _KLOGM(_GET_KERNEL_DATA(kernel_data));
    _KLOGE3(KLOG_lwevent_clear, event_ptr, bit_mask);

    _INT_DISABLE();
#if MQX_CHECK_VALIDITY
    if (event_ptr->VALID != LWEVENT_VALID)
    {
        _int_enable();
        _KLOGX2(KLOG_lwevent_clear, MQX_LWEVENT_INVALID);
        return (MQX_LWEVENT_INVALID);
    } /* Endif */
#endif

    event_ptr->VALUE &= ~bit_mask;
    _INT_ENABLE();

    _KLOGX2(KLOG_lwevent_clear, MQX_OK);
    return (MQX_OK);
}
Exemple #2
0
/*!
 * \brief Used by a task to wait for the event for the number of ticks (in tick time).
 *
 * \param[in] event_ptr Pointer to the lightweight event.
 * \param[in] bit_mask  Bit mask. Each set bit represents an event bit to wait for.
 * \param[in] all       TRUE (wait for all bits in bit_mask to be set), FALSE
 * (wait for any bit in bit_mask to be set).
 * \param[in] tick_ptr  Pointer to the maximum number of ticks to wait for the
 * events to be set. If the value is NULL, then the timeout will be infinite.
 *
 * \return MQX_OK
 * \return LWEVENT_WAIT_TIMEOUT (The time elapsed before an event signalled.)
 * \return MQX_LWEVENT_INVALID (Lightweight event is no longer valid or was never
 * valid.)
 * \return MQX_CANNOT_CALL_FUNCTION_FROM_ISR (Function cannot be called from an ISR.)
 *
 * \see _lwevent_create
 * \see _lwevent_destroy
 * \see _lwevent_set
 * \see _lwevent_set_auto_clear
 * \see _lwevent_clear
 * \see _lwevent_wait_ticks
 * \see _lwevent_wait_until
 * \see _lwevent_get_signalled
 * \see LWEVENT_STRUCT
 * \see MQX_TICK_STRUCT
 */
_mqx_uint _lwevent_wait_for
(
    LWEVENT_STRUCT_PTR  event_ptr,
    _mqx_uint           bit_mask,
    bool             all,
    MQX_TICK_STRUCT_PTR tick_ptr
)
{
    _KLOGM(KERNEL_DATA_STRUCT_PTR kernel_data);
    _mqx_uint result;

#if MQX_ENABLE_USER_MODE && MQX_ENABLE_USER_STDAPI
    if (MQX_RUN_IN_USER_MODE)
    {
        return _usr_lwevent_wait_for(event_ptr, bit_mask, all, tick_ptr);
    }
#endif

    _KLOGM(_GET_KERNEL_DATA(kernel_data));

    _KLOGE5(KLOG_lwevent_wait_for, event_ptr, bit_mask, all, tick_ptr);

    result = _lwevent_wait_internal(event_ptr, bit_mask, all, tick_ptr, FALSE);

    _KLOGX2(KLOG_lwevent_wait_for, result);
    return (result);

}
Exemple #3
0
/*!
 * \brief Creates a system message pool.
 * 
 * Tasks can subsequently allocate messages from the pool by calling 
 * _msg_alloc_system().
 * 
 * \param[in] message_size Size (in single-addressable units) of the messages 
 * (including the message header) to be created for the message pool.
 * \param[in] num_messages Initial number of messages to be created for the 
 * message pool.
 * \param[in] grow_number  Number of messages to be added if all the messages 
 * are allocated.
 * \param[in] grow_limit   If grow_number is not equal to 0; one of the following:
 * \li Maximum number of messages that the pool can have.
 * \li 0 (Unlimited growth.)  
 * 
 * \return TRUE (Success.) or FALSE (Failure.)
 * 
 * \warning Creates the message component if it was not previously created.
 * \warning On failure, calls _task_set_error() to set the task error code as 
 * described for _msgpool_create().
 * 
 * \see _msgpool_create
 * \see _msgpool_destroy
 * \see _msg_alloc_system
 * \see _task_set_error
 * \see MQX_INITIALIZATION_STRUCT  
 */ 
bool _msgpool_create_system
(
    uint16_t message_size,
    uint16_t num_messages,
    uint16_t grow_number,
    uint16_t grow_limit
)
{ /* Body */
    _KLOGM(KERNEL_DATA_STRUCT_PTR kernel_data);
    _pool_id ret_value;

    _KLOGM(_GET_KERNEL_DATA(kernel_data));
    _KLOGE5(KLOG_msgpool_create_system, message_size, num_messages, grow_number, grow_limit);

    ret_value = _msgpool_create_internal(
        message_size, num_messages, grow_number, grow_limit, SYSTEM_MSG_POOL
    );

    _KLOGX2(KLOG_msgpool_create_system, ret_value == (_pool_id)0 );

    if ( ret_value == (_pool_id)0 )
    {
        return FALSE;
    }
    else
    {
        return TRUE;
    } /* Endif */

} /* Endbody */
Exemple #4
0
/*!
 * \brief Set the time slice in tick time.
 *
 * \param[in]  task_id             One of the following:
 * \n - Task ID for a task on this processor for which to set info.
 * \n - MQX_DEFAULT_TASK_ID (Set the time slice for the processor.)
 * \n - MQX_NULL_TASK_ID (Set the time slice for the calling task.)
 * \param[in]  new_rr_interval_ptr Pointer to the new time slice (in tick time).
 * \param[out] old_rr_interval_ptr Pointer to the previous time slice (in tick time).
 *
 * \return Previous time slice (Success.)
 * \return MAX_UINT_32
 *
 * \warning On failure, calls _task_set_error() to set the task error code to
 * MQX_SCHED_INVALID_TASK_ID.
 *
 * \see _sched_set_rr_interval
 * \see _sched_get_rr_interval
 * \see _sched_get_rr_interval_ticks
 * \see _task_set_error
 * \see MQX_TICK_STRUCT
 */
_mqx_uint _sched_set_rr_interval_ticks
(
    _task_id            task_id,
    MQX_TICK_STRUCT_PTR new_rr_interval_ptr,
    MQX_TICK_STRUCT_PTR old_rr_interval_ptr

)
{ /* Body */
    _KLOGM(KERNEL_DATA_STRUCT_PTR kernel_data);
    _mqx_uint result;

    _KLOGM(_GET_KERNEL_DATA(kernel_data));

    _KLOGE2(KLOG_sched_set_rr_interval_ticks, task_id);

    result = _sched_set_rr_interval_internal(task_id, new_rr_interval_ptr,
                    old_rr_interval_ptr);

    if (result != MQX_OK)
    {
        _task_set_error(result);
    } /* Endif */

    _KLOGX2(KLOG_sched_set_rr_interval_ticks, result);

    return result;

} /* Endbody */
/*!
 * \brief Adds the lightweight timer to the periodic queue.
 *
 * This function inserts the timer in the queue in order of increasing offset
 * from the queue's start time.
 *
 * \param[in] period_ptr Pointer to the periodic queue.
 * \param[in] timer_ptr  Pointer to the lightweight timer to add to the queue,
 * must be smaller than queue.
 * \param[in] ticks      Tick offset from the timers period to expire at.
 * \param[in] func       Function to call when the timer expires.
 * \param[in] parameter  Parameter to pass to the function.
 *
 * \return MQX_OK
 * \return MQX_LWTIMER_INVALID (Period_ptr points to an invalid periodic queue.)
 * \return MQX_INVALID_PARAMETER (Ticks is greater than or equal to the
 * periodic queue's period.)
 *
 * \see _lwtimer_cancel_period
 * \see _lwtimer_cancel_timer
 * \see _lwtimer_create_periodic_queue
 * \see LWTIMER_PERIOD_STRUCT
 * \see LWTIMER_STRUCT
 */
 _mqx_uint _lwtimer_add_timer_to_queue
(
    LWTIMER_PERIOD_STRUCT_PTR period_ptr,
    LWTIMER_STRUCT_PTR        timer_ptr,
    _mqx_uint                 ticks,
    LWTIMER_ISR_FPTR          func,
    void                     *parameter
)
{ /* Body */
    _KLOGM(KERNEL_DATA_STRUCT_PTR kernel_data);
    LWTIMER_STRUCT_PTR     qe_ptr;
    _mqx_uint              i;

    _KLOGM(_GET_KERNEL_DATA(kernel_data));
    _KLOGE4(KLOG_lwtimer_add_timer_to_queue, period_ptr, timer_ptr, ticks);

#if MQX_CHECK_ERRORS
    if (period_ptr->VALID != LWTIMER_VALID)
    {
        _KLOGX2(KLOG_lwtimer_add_timer_to_queue, MQX_LWTIMER_INVALID);
        return (MQX_LWTIMER_INVALID);
    } /* Endif */
    if (ticks >= period_ptr->PERIOD)
    {
        _KLOGX2(KLOG_lwtimer_add_timer_to_queue, MQX_INVALID_PARAMETER);
        return (MQX_INVALID_PARAMETER);
    } /* Endif */
#endif

    timer_ptr->TIMER_FUNCTION = func;
    timer_ptr->PARAMETER      = parameter;
    timer_ptr->PERIOD_PTR     = period_ptr;
    timer_ptr->RELATIVE_TICKS = ticks;
    _int_disable();
    /* Insert into queue in order of increasing offset from start time */
    qe_ptr = (void *) &period_ptr->TIMERS.NEXT;
    i = _QUEUE_GET_SIZE(&period_ptr->TIMERS) + 1;
    while (--i)
    {
        qe_ptr = (void *) qe_ptr->LINK.NEXT;
        if (qe_ptr->RELATIVE_TICKS >= ticks)
        {
            qe_ptr = (void *) qe_ptr->LINK.PREV;
            break;
        } /* Endif */
    } /* Endwhile */
    timer_ptr->VALID = LWTIMER_VALID;
    _QUEUE_INSERT(&period_ptr->TIMERS, qe_ptr, &timer_ptr->LINK);
    _int_enable();

    _KLOGX2(KLOG_lwtimer_add_timer_to_queue, MQX_OK);
    return (MQX_OK);

} /* Endbody */
Exemple #6
0
/*!
 * \brief Set the time slice in milliseconds.
 *
 * \param[in] task_id     One of the following:
 * \n - Task ID for a task on this processor for which to set info.
 * \n - MQX_DEFAULT_TASK_ID (Set the time slice for the processor.)
 * \n - MQX_NULL_TASK_ID (Set the time slice for the calling task.)
 * \param[in] rr_interval New time slice (in milliseconds).
 *
 * \return old_rr_interval Previous time slice (Success.)
 * \return MAX_UINT_32
 *
 * \warning On failure, calls _task_set_error() to set the task error code to
 * MQX_SCHED_INVALID_TASK_ID.
 *
 * \see _sched_set_rr_interval_ticks
 * \see _sched_get_rr_interval
 * \see _sched_get_rr_interval_ticks
 * \see _task_set_error
 */
uint32_t _sched_set_rr_interval
(
    _task_id task_id,
    uint32_t  rr_interval
)
{ /* Body */
    _KLOGM(KERNEL_DATA_STRUCT_PTR kernel_data);
    uint32_t         old_rr_interval;
    MQX_TICK_STRUCT ticks;
    MQX_TICK_STRUCT old_ticks;
    _mqx_uint       result;

    _KLOGM(_GET_KERNEL_DATA(kernel_data));

    _KLOGE3(KLOG_sched_set_rr_interval, (_mqx_uint)task_id, rr_interval);

#if MQX_CHECK_ERRORS
    /* Validate parameters */
    if (0 == rr_interval)
    {
        _KLOGX2(KLOG_sched_set_rr_interval, MAX_UINT_32);
        _task_set_error( MQX_SCHED_INVALID_PARAMETER_PTR );
        return (MAX_UINT_32);
    } /* Endif */
#endif

    /* Compute the number of tick events required to accomplish the least amount of time[ms]. */
    /* tick_events = (required_time[ms] + (time_per_tick[ms] - 1)) / time_per_tick[ms])  -->
     * tick_events = ((required_time[ms] - 1) / time_per_tick[ms]) + 1
     */
    rr_interval--;
    /* Convert milliseconds to ticks, truncated */
    PSP_MILLISECONDS_TO_TICKS_QUICK(rr_interval, &ticks);
    /* Resolve truncation by adding one tick. */
    PSP_ADD_TICKS_TO_TICK_STRUCT(&ticks, 1, &ticks);

    result = _sched_set_rr_interval_internal(task_id, &ticks, &old_ticks);

    if (result != MQX_OK)
    {
        _task_set_error(result);
        _KLOGX2(KLOG_sched_set_rr_interval, MAX_UINT_32);
        return(MAX_UINT_32);
    } /* Endif */

    old_rr_interval = PSP_TICKS_TO_MILLISECONDS(&old_ticks, &result);

    _KLOGX2(KLOG_sched_set_rr_interval, old_rr_interval);

    return(old_rr_interval);

} /* Endbody */
Exemple #7
0
/*!
 * \brief Marks the message as "free".
 * 
 * Only the task that has the message as its resource can free the message. A 
 * message becomes a task's resource when the task allocates the message, and it 
 * continues to be a resource until the task either frees it or puts it in a 
 * message queue. A message becomes a resource of the task that got it from a 
 * message queue.
 * \n The function returns the message to the message pool from which it was 
 * allocated.
 * 
 * \param[in] msg_ptr Pointer to a message struct which is to be freed.
 * 
 * \warning On failure, calls _task_set_error() to set one the following task 
 * error codes:
 * \li MQX_INVALID_POINTER (Msg_ptr does not point to a valid message.)
 * \li MQX_NOT_RESOURCE_OWNER (Message is already freed.)
 * \li MSGQ_MESSAGE_IS_QUEUED (Message is in a queue.)
 * 
 * \see _msgpool_create
 * \see _msgpool_create_system
 * \see _msgpool_destroy
 * \see _msg_alloc_system
 * \see _msg_alloc
 * \see _task_set_error
 * \see MESSAGE_HEADER_STRUCT    
 */ 
void _msg_free
(
    void   *msg_ptr
)
{ /* Body */
    _KLOGM(KERNEL_DATA_STRUCT_PTR       kernel_data);
    register INTERNAL_MESSAGE_STRUCT_PTR imsg_ptr;
    register MSGPOOL_STRUCT_PTR          msgpool_ptr;

    _KLOGM(_GET_KERNEL_DATA(kernel_data));
    _KLOGE2(KLOG_msg_free, msg_ptr);

    imsg_ptr = GET_INTERNAL_MESSAGE_PTR(msg_ptr);
#if MQX_CHECK_VALIDITY
    if ( imsg_ptr->VALID != MSG_VALID )
    {
        _KLOGX2(KLOG_msg_free, MQX_INVALID_POINTER);
        _task_set_error(MQX_INVALID_POINTER);
        return;
    } /* Endif */
#endif

#if MQX_CHECK_ERRORS
    if (imsg_ptr->FREE)
    {
        _KLOGX2(KLOG_msg_free, MQX_NOT_RESOURCE_OWNER);
        _task_set_error(MQX_NOT_RESOURCE_OWNER);
        return;
    } /* Endif */
    if (imsg_ptr->QUEUED)
    {
        _KLOGX2(KLOG_msg_free, MSGQ_MESSAGE_IS_QUEUED);
        _task_set_error(MSGQ_MESSAGE_IS_QUEUED);
        return;
    } /* Endif */
#endif

    msgpool_ptr = imsg_ptr->MSGPOOL_PTR;
    imsg_ptr->FREE = TRUE;
    imsg_ptr->QUEUED = FALSE;

    _INT_DISABLE();
    /* Link onto the free list */
    imsg_ptr->NEXT = msgpool_ptr->MSG_FREE_LIST_PTR;
    msgpool_ptr->MSG_FREE_LIST_PTR = imsg_ptr;
    ++msgpool_ptr->SIZE;
    _INT_ENABLE();

    _KLOGX2(KLOG_msg_free, MQX_OK);

} /* Endbody */
Exemple #8
0
/*!
 * \brief Creates a private message pool.
 * 
 * Any task can allocate messages from the pool by calling _msg_alloc() with the 
 * pool ID.
 * 
 * \param[in] message_size Size (in single-addressable units) of the messages 
 * (including the message header) to be created for the message pool.
 * \param[in] num_messages Initial number of messages to be created for the 
 * message pool.
 * \param[in] grow_number  Number of messages to be added if all the messages 
 * are allocated.
 * \param[in] grow_limit   If grow_number is not equal to 0; one of the following:
 * \li Maximum number of messages that the pool can have.
 * \li 0 (Unlimited growth.)
 * 
 * \return Pool ID to access the message pool (success).
 * \return 0 (Failure.)  
 * 
 * \warning Creates the message component if it was not previously created.
 * \warning On failure, calls _task_set_error() to set one of the following task 
 * error codes:
 * \li MSGPOOL_MESSAGE_SIZE_TOO_SMALL (Message_size is less than the size of the 
 * message header structure.)
 * \li MQX_OUT_OF_MEMORY (MQX cannot allocate memory to create the message pool).
 * \li MSGPOOL_OUT_OF_POOLS (Maximum number of message pools have been created, 
 * where the number is defined at initialization time in MAX_MSGPOOLS in the MQX 
 * initialization structure.)
 * \li Task error codes from _mem_alloc_system()
 * \li Task error codes from _msg_create_component()
 * 
 * \see _msgpool_create_system
 * \see _msgpool_destroy
 * \see _msg_alloc
 * \see _task_set_error
 * \see _mem_alloc
 * \see _mem_alloc_from
 * \see _mem_alloc_system
 * \see _mem_alloc_system_from
 * \see _mem_alloc_system_zero
 * \see _mem_alloc_system_zero_from
 * \see _mem_alloc_zero
 * \see _mem_alloc_zero_from
 * \see _mem_alloc_align
 * \see _mem_alloc_align_from
 * \see _mem_alloc_at
 * \see _msg_create_component
 * \see MQX_INITIALIZATION_STRUCT  
 */ 
_pool_id _msgpool_create
(
    uint16_t message_size,
    uint16_t num_messages,
    uint16_t grow_number,
    uint16_t grow_limit
)
{ /* Body */
    _KLOGM(KERNEL_DATA_STRUCT_PTR kernel_data);
    _pool_id result;

    _KLOGM(_GET_KERNEL_DATA(kernel_data));
    _KLOGE5(KLOG_msgpool_create, message_size, num_messages, grow_number, grow_limit);

    result = _msgpool_create_internal(message_size, num_messages, grow_number, grow_limit, MSG_POOL);

    _KLOGX2(KLOG_msgpool_create, result);
    return(result);

} /* Endbody */
/*!
 * \brief Cancels an outstanding timer request.
 *
 * \param[in] timer_ptr Pointer to the lightweight timer to cancel.
 *
 * \return MQX_OK
 * \return MQX_LWTIMER_INVALID (Timer_ptr points to either an invalid timer or
 * to a timer with a periodic queue.)
 *
 * \see _lwtimer_add_timer_to_queue
 * \see _lwtimer_cancel_period
 * \see _lwtimer_create_periodic_queue
 * \see LWTIMER_STRUCT
 */
_mqx_uint _lwtimer_cancel_timer
(
    LWTIMER_STRUCT_PTR timer_ptr
)
{ /* Body */
    _KLOGM(KERNEL_DATA_STRUCT_PTR    kernel_data);
    LWTIMER_PERIOD_STRUCT_PTR period_ptr;

    _KLOGM(_GET_KERNEL_DATA(kernel_data));
    _KLOGE2(KLOG_lwtimer_cancel_timer, timer_ptr);

#if MQX_CHECK_VALIDITY
    if (timer_ptr->VALID != LWTIMER_VALID)
    {
        _KLOGX2(KLOG_lwtimer_cancel_timer, MQX_LWTIMER_INVALID);
        return MQX_LWTIMER_INVALID;
    } /* Endif */
#endif

    period_ptr = timer_ptr->PERIOD_PTR;
    _int_disable();
#if MQX_CHECK_VALIDITY
    if (period_ptr->VALID != LWTIMER_VALID)
    {
        _int_enable();
        _KLOGX2(KLOG_lwtimer_cancel_timer, MQX_LWTIMER_INVALID);
        return MQX_LWTIMER_INVALID;
    } /* Endif */
#endif
    timer_ptr->VALID = 0;
    if (timer_ptr == period_ptr->TIMER_PTR)
    {
        period_ptr->TIMER_PTR = (void *) timer_ptr->LINK.PREV;
    } /* Endif */
    _QUEUE_REMOVE(&period_ptr->TIMERS, timer_ptr);
    _int_enable();

    _KLOGX2(KLOG_lwtimer_cancel_timer, MQX_OK);
    return (MQX_OK);

} /* Endbody */
Exemple #10
0
/*!
 * \brief Used by a task to wait for the event for the number of ticks.
 *
 * \param[in] event_ptr        Pointer to the lightweight event.
 * \param[in] bit_mask         Bit mask. Each set bit represents an event bit to wait for.
 * \param[in] all              TRUE (wait for all bits in bit_mask to be set),
 * FALSE (wait for any bit in bit_mask to be set).
 * \param[in] timeout_in_ticks The maximum number of ticks to wait for the events
 * to be set. If the value is 0, then the timeout will be infinite.
 *
 * \return MQX_OK
 * \return LWEVENT_WAIT_TIMEOUT (The time elapsed before an event signalled.)
 * \return MQX_LWEVENT_INVALID (Lightweight event is no longer valid or was never
 * valid.)
 * \return MQX_CANNOT_CALL_FUNCTION_FROM_ISR (Function cannot be called from an ISR.)
 *
 * \see _lwevent_create
 * \see _lwevent_destroy
 * \see _lwevent_set
 * \see _lwevent_set_auto_clear
 * \see _lwevent_clear
 * \see _lwevent_wait_for
 * \see _lwevent_wait_until
 * \see _lwevent_get_signalled
 * \see LWEVENT_STRUCT
 */
_mqx_uint _lwevent_wait_ticks
(
    LWEVENT_STRUCT_PTR  event_ptr,
    _mqx_uint           bit_mask,
    bool             all,
    _mqx_uint           timeout_in_ticks
)
{
    MQX_TICK_STRUCT ticks;
    _KLOGM(KERNEL_DATA_STRUCT_PTR kernel_data);
    _mqx_uint result;

#if MQX_ENABLE_USER_MODE && MQX_ENABLE_USER_STDAPI
    if (MQX_RUN_IN_USER_MODE)
    {
        return _usr_lwevent_wait_ticks(event_ptr, bit_mask, all, timeout_in_ticks);
    }
#endif

    _KLOGM(_GET_KERNEL_DATA(kernel_data));

    _KLOGE5(KLOG_lwevent_wait_ticks, event_ptr, bit_mask, all,
                    timeout_in_ticks);

    if (timeout_in_ticks)
    {
        ticks = _mqx_zero_tick_struct;

        PSP_ADD_TICKS_TO_TICK_STRUCT(&ticks, timeout_in_ticks, &ticks);
        result = _lwevent_wait_internal(event_ptr, bit_mask, all, &ticks, FALSE);
    }
    else
    {
        result = _lwevent_wait_internal(event_ptr, bit_mask, all, NULL, FALSE);
    } /* Endif */

    _KLOGX2(KLOG_lwevent_wait_ticks, result);
    return (result);

}
Exemple #11
0
uint32_t ENET_send
   (
      /* [IN] the Ethernet state structure */
      _enet_handle   handle,

      /* [IN] the packet to send */
      PCB_PTR        packet,

      /* [IN] the protocol */
      uint16_t        type,

      /* [IN] the destination Ethernet address */
      _enet_address  dest,

      /* [IN] optional flags, zero = default */
      uint32_t        flags
   )
{ 
   ENET_CONTEXT_STRUCT_PTR  enet_ptr = (ENET_CONTEXT_STRUCT_PTR)handle;
   ENET_HEADER_PTR         packet_ptr;
   unsigned char               *type_ptr;
   PCB_FRAGMENT_PTR        frag_ptr;
   uint32_t                 swhdr, size, frags;
   uint32_t                 error;
   _KLOGM(KERNEL_DATA_STRUCT_PTR kernel_data);
   
   _KLOGM(_GET_KERNEL_DATA(kernel_data));
   _KLOGE6(KLOG_ENET_send, handle, packet, type, dest, flags);

   if (flags & ENET_OPT_8021QTAG) {
       swhdr = ENET_FRAMESIZE_HEAD_VLAN;
   } else {
       swhdr = ENET_FRAMESIZE_HEAD;
   } 

   /*
   ** Make sure the first fragment is long enough for the Ethernet
   ** frame header.  This isn't strictly necessary, but it's impractical
   ** to split a 14-26 byte header over multiple fragments.
   */
#if MQX_CHECK_ERRORS
   if (packet->FRAG[0].LENGTH < swhdr) {
      ENET_INC_STATS(COMMON.ST_TX_DISCARDED);
      error = ENETERR_SEND_SHORT;
      goto ERROR;
   }
#endif

   /*
   ** Make sure that no fragment exceeds a maximum packet length.
   ** We check every fragment because we want to prevent something
   ** like FRAG[0].LENGTH = 2000, FRAG[1].LENGTH = -1000.  This
   ** situation would not be detected if we only check the total
   ** length.
   */
   size = frags = 0;
   for (frag_ptr = packet->FRAG; frag_ptr->LENGTH; frag_ptr++) {
#if MQX_CHECK_ERRORS
      if (frag_ptr->LENGTH > enet_ptr->MaxTxFrameSize) {   
         ENET_INC_STATS(COMMON.ST_TX_DISCARDED);
         error = ENETERR_SEND_LONG;
         goto ERROR;
      } 
#endif
      size += frag_ptr->LENGTH;
      frags++;
   } 

   /*
   ** Make sure that the total sum of the fragments doesn't exceed
   ** a maximum packet length.
   */
#if MQX_CHECK_ERRORS
   if (size > enet_ptr->MaxTxFrameSize) {
      ENET_INC_STATS(COMMON.ST_TX_DISCARDED);
      error = ENETERR_SEND_LONG;
      goto ERROR;
   } 
#endif

   /*
   ** Everything checks out -- fill in the header.
   */
   packet_ptr = (ENET_HEADER_PTR)packet->FRAG[0].FRAGMENT;
   htone(packet_ptr->DEST, dest);
   htone(packet_ptr->SOURCE, enet_ptr->ADDRESS);
   type_ptr = packet_ptr->TYPE;

   if (flags & ENET_OPT_8021QTAG) {
      ENET_8021QTAG_HEADER_PTR tag_ptr = (ENET_8021QTAG_HEADER_PTR)(type_ptr+2);
      uint16_t tag;
      tag = ENET_GETOPT_8021QPRIO(flags) << 13;
      mqx_htons(type_ptr, ENETPROT_8021Q);
      mqx_htons(tag_ptr->TAG, tag);
      type_ptr = tag_ptr->TYPE;
   } 

   if (flags & ENET_OPT_8023) {
      ENET_8022_HEADER_PTR llc_ptr = (ENET_8022_HEADER_PTR)(type_ptr+2);
      (void)mqx_htons(type_ptr, size - swhdr);
      mqx_htonc(llc_ptr->DSAP, 0xAA);
      mqx_htonc(llc_ptr->SSAP, 0xAA);
      mqx_htonc(llc_ptr->COMMAND, 0x03);
      mqx_htonc(&llc_ptr->OUI[0], 0x00);
      mqx_htonc(&llc_ptr->OUI[1], 0x00);
      mqx_htonc(&llc_ptr->OUI[2], 0x00);
      type_ptr = llc_ptr->TYPE;
   } 

   mqx_htons(type_ptr, type);

   /*
   ** This function can be called from any context, and it needs mutual
   ** exclusion with itself, and with ENET_ISR().
   */
   ENET_lock_context(enet_ptr);
   error = (*enet_ptr->PARAM_PTR->ENET_IF->MAC_IF->SEND)(handle, packet, size, frags, flags);
   ENET_unlock_context(enet_ptr);

   if (error) {
   ERROR:
      PCB_free(packet);
   }
   
   _KLOGX4(KLOG_ENET_send, handle, packet, error);
   
   return error;
}