bsps: Move shmdr to bsps

This patch is a part of the BSP source reorganization.

Update #3285.
This commit is contained in:
Sebastian Huber
2018-04-04 16:39:58 +02:00
parent 814eccb4cf
commit 4b28d3c797
19 changed files with 18 additions and 23 deletions

View File

@@ -51,3 +51,19 @@ libbsp_a_SOURCES += ../../../../../../bsps/shared/net/smc91111.c
libbsp_a_SOURCES += ../../../../../../bsps/shared/net/sonic.c
endif
libbsp_a_SOURCES += ../../../../../../bsps/shared/rtems-version.c
if HAS_MP
libbsp_a_SOURCES += ../../../../../../bsps/shared/shmdr/shmdr-addlq.c
libbsp_a_SOURCES += ../../../../../../bsps/shared/shmdr/shmdr-cnvpkt.c
libbsp_a_SOURCES += ../../../../../../bsps/shared/shmdr/shmdr-dump.c
libbsp_a_SOURCES += ../../../../../../bsps/shared/shmdr/shmdr-fatal.c
libbsp_a_SOURCES += ../../../../../../bsps/shared/shmdr/shmdr-getlq.c
libbsp_a_SOURCES += ../../../../../../bsps/shared/shmdr/shmdr-getpkt.c
libbsp_a_SOURCES += ../../../../../../bsps/shared/shmdr/shmdr-init.c
libbsp_a_SOURCES += ../../../../../../bsps/shared/shmdr/shmdr-initlq.c
libbsp_a_SOURCES += ../../../../../../bsps/shared/shmdr/shmdr-intr.c
libbsp_a_SOURCES += ../../../../../../bsps/shared/shmdr/shmdr-poll.c
libbsp_a_SOURCES += ../../../../../../bsps/shared/shmdr/shmdr-receive.c
libbsp_a_SOURCES += ../../../../../../bsps/shared/shmdr/shmdr-retpkt.c
libbsp_a_SOURCES += ../../../../../../bsps/shared/shmdr/shmdr-send.c
libbsp_a_SOURCES += ../../../../../../bsps/shared/shmdr/shmdr-shmisr.c
endif

5
bsps/shared/shmdr/README Normal file
View File

@@ -0,0 +1,5 @@
The mpci.h file provided in here is too simple for an MPCI with
multiple ways to get to a node.
This version of the shm driver needs to be reorganized to follow
the better model of the Ada version.

View File

@@ -0,0 +1,40 @@
/* void Shm_Locked_queue_Add( lq_cb, ecb )
*
* This routine adds an envelope control block to a shared memory queue.
*
* Input parameters:
* lq_cb - pointer to a locked queue control block
* ecb - pointer to an envelope control block
*
* Output parameters: NONE
*
* COPYRIGHT (c) 1989-1999.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <rtems.h>
#include "shm_driver.h"
void Shm_Locked_queue_Add(
Shm_Locked_queue_Control *lq_cb,
Shm_Envelope_control *ecb
)
{
uint32_t index;
ecb->next = Shm_Locked_queue_End_of_list;
ecb->queue = lq_cb->owner;
index = ecb->index;
Shm_Lock( lq_cb );
if ( Shm_Convert(lq_cb->front) != Shm_Locked_queue_End_of_list )
Shm_Envelopes[ Shm_Convert(lq_cb->rear) ].next = index;
else
lq_cb->front = index;
lq_cb->rear = index;
Shm_Unlock( lq_cb );
}

View File

@@ -0,0 +1,39 @@
/* void Shm_Convert_packet( &packet )
*
* This routine is the shared memory locked queue MPCI driver routine
* used to convert the RTEMS's information in a packet from non-native
* format to processor native format.
*
* Input parameters:
* packet - pointer to a packet
*
* Output parameters:
* *packet - packet in native format
*
* NOTE: Message buffers are not manipulated.
* Endian conversion is currently the only conversion.
*
* COPYRIGHT (c) 1989-1999.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <rtems.h>
#include "shm_driver.h"
void Shm_Convert_packet(
rtems_packet_prefix *packet
)
{
uint32_t *pkt, i;
pkt = (uint32_t*) packet;
for ( i=RTEMS_MINIMUN_HETERO_CONVERSION ; i ; i--, pkt++ )
*pkt = CPU_swap_u32( *pkt );
for ( i=packet->to_convert ; i ; i--, pkt++ )
*pkt = CPU_swap_u32( *pkt );
}

View File

@@ -0,0 +1,51 @@
/*
* This routine is invoked following a reset to report the statistics
* gathered during the previous execution.
*
* Input parameters: NONE
*
* Output parameters: NONE
*
* COPYRIGHT (c) 1989-2007.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <rtems.h>
#include <stdio.h>
#include <inttypes.h>
#include <rtems/bspIo.h>
#include "shm_driver.h"
void
Shm_Print_statistics(void)
{
uint32_t ticks;
uint32_t ticks_per_second;
uint32_t seconds;
uint32_t packets_per_second;
ticks = rtems_clock_get_ticks_since_boot();
ticks_per_second = rtems_clock_get_ticks_per_second();
seconds = ticks / ticks_per_second;
if ( seconds == 0 )
seconds = 1;
packets_per_second = Shm_Receive_message_count / seconds;
if ( (Shm_Receive_message_count % seconds) >= (seconds / 2) )
packets_per_second++;
printk( "\n\nSHMDR STATISTICS (NODE %" PRId32 ")\n",
Multiprocessing_configuration.node );
printk( "TICKS SINCE BOOT = %" PRId32 "\n", ticks );
printk( "TICKS PER SECOND = %" PRId32 "\n", ticks_per_second );
printk( "ISRs=%" PRId32 "\n", Shm_Interrupt_count );
printk( "RECV=%" PRId32 "\n", Shm_Receive_message_count );
printk( "NULL=%" PRId32 "\n", Shm_Null_message_count );
printk( "PKTS/SEC=%" PRId32 "\n", packets_per_second );
}

View File

@@ -0,0 +1,36 @@
/* void MPCI_Fatal( error )
*
* This routine is the shared memory driver fatal error handler.
*
* Input parameters:
* error - fatal error code
*
* Output parameters: NEVER RETURNS
*
* COPYRIGHT (c) 1989-1999.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <rtems.h>
#include "shm_driver.h"
void MPCI_Fatal(
rtems_fatal_source source,
bool always_set_to_false,
rtems_fatal_code error
)
{
/* Eventually need to attempt to broadcast a K_FATAL message
* without checking for all possible errors (do not want to
* recurse).
*
* Also need to avoid using Shm_Node_statuses if the driver has not been
* initialized.
*/
Shm_Local_node_status->error = Shm_Convert(error);
}

View File

@@ -0,0 +1,45 @@
/* Shm_Envelope_control *Shm_Locked_queue_Get( lq_cb )
*
* This routine returns an envelope control block from a shared
* memory queue.
*
* Input parameters:
* lq_cb - pointer to a locked queue control block
*
* Output parameters:
* returns - pointer to an envelope control block
* - NULL if no envelopes on specified queue
*
* COPYRIGHT (c) 1989-1999.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <rtems.h>
#include <shm_driver.h>
Shm_Envelope_control *Shm_Locked_queue_Get(
Shm_Locked_queue_Control *lq_cb
)
{
Shm_Envelope_control *tmp_ecb;
uint32_t tmpfront;
tmp_ecb = NULL;
Shm_Lock( lq_cb );
tmpfront = Shm_Convert(lq_cb->front);
if ( tmpfront != Shm_Locked_queue_End_of_list ) {
tmp_ecb = &Shm_Envelopes[ tmpfront ];
lq_cb->front = tmp_ecb->next;
if ( tmp_ecb->next == Shm_Locked_queue_End_of_list )
lq_cb->rear = Shm_Locked_queue_End_of_list;
tmp_ecb->next = Shm_Locked_queue_Not_on_list;
}
Shm_Unlock( lq_cb );
return( tmp_ecb );
}

View File

@@ -0,0 +1,33 @@
/* Shm_Get_packet
*
* This routine is the shared memory locked queue MPCI driver
* routine used to obtain an empty message packet.
*
* Input parameters:
* packet - address of pointer to packet
*
* Output parameters:
* *(cpb->get_packet) - address of allocated packet
*
* COPYRIGHT (c) 1989-1999.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <rtems.h>
#include "shm_driver.h"
rtems_mpci_entry Shm_Get_packet(
rtems_packet_prefix **packet
)
{
Shm_Envelope_control *ecb;
ecb = Shm_Allocate_envelope();
if ( !ecb )
rtems_fatal_error_occurred ( SHM_NO_FREE_PKTS );
*packet = Shm_Envelope_control_to_packet_prefix_pointer( ecb );
}

View File

@@ -0,0 +1,248 @@
/* Shm_Initialization
*
* This routine is the shared memory communications initerface
* driver initialization routine.
*
* Input parameters: NONE
*
* Output parameters: NONE
*
* COPYRIGHT (c) 1989-1999.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#define _SHM_INIT
#include <rtems.h>
#include <shm_driver.h>
#include <string.h> /* memset() */
#include <stdlib.h> /* malloc() */
#include <assert.h>
/*
* User extension to install MPCI_Fatal as a fatal error
* handler extension
*/
rtems_extensions_table MPCI_Shm_extensions;
/*
* MP configuration table from confdefs.h
*/
rtems_mpci_entry Shm_Initialization( void )
{
uint32_t i, all_initialized;
uint32_t interrupt_cause, interrupt_value;
void *interrupt_address;
Shm_Node_status_control *nscb;
uint32_t extension_id; /* for installation of MPCI_Fatal */
uint32_t remaining_memory;
uint32_t local_node;
local_node = _Configuration_MP_table->node;
Shm_Get_configuration( local_node, &Shm_Configuration );
Shm_Interrupt_table = (Shm_Interrupt_information *) malloc(
sizeof(Shm_Interrupt_information) * (SHM_MAXIMUM_NODES + 1)
);
assert( Shm_Interrupt_table );
Shm_Receive_message_count = 0;
Shm_Null_message_count = 0;
Shm_Interrupt_count = 0;
/*
* Set the Node Status indicators
*/
Shm_Pending_initialization =
Shm_Convert(rtems_build_name( 'P', 'E', 'N', 'D' ));
Shm_Initialization_complete =
Shm_Convert(rtems_build_name( 'C', 'O', 'M', 'P' ));
Shm_Active_node =
Shm_Convert(rtems_build_name( 'A', 'C', 'T', 'V' ));
/*
* Initialize the constants used by the Locked Queue code.
*/
Shm_Locked_queue_End_of_list = Shm_Convert( 0xffffffff );
Shm_Locked_queue_Not_on_list = Shm_Convert( 0xfffffffe );
/*
* Set the base addresses for the:
* + Node Status Table
* + Free Pool and Receive Queues
* + Envelopes
*/
Shm_Node_statuses = (Shm_Node_status_control *) START_NS_CBS;
Shm_Locked_queues = (Shm_Locked_queue_Control *) START_LQ_CBS;
Shm_Envelopes = (Shm_Envelope_control *) START_ENVELOPES;
/*
* Calculate the maximum number of envelopes which can be
* placed the remaining shared memory.
*/
remaining_memory =
((void *)Shm_Configuration->base + Shm_Configuration->length) -
((void *)Shm_Envelopes);
Shm_Maximum_envelopes = remaining_memory / sizeof( Shm_Envelope_control );
Shm_Maximum_envelopes -= 1;
/*
* Set the pointer to the receive queue for the local node.
* When we receive a node, we will get it from here before
* processing it.
*/
Shm_Local_receive_queue = &Shm_Locked_queues[ local_node ];
Shm_Local_node_status = &Shm_Node_statuses[ local_node ];
/*
* Convert local interrupt cause information into the
* neutral format so other nodes will be able to
* understand it.
*/
interrupt_address =
(void *) Shm_Convert( (uint32_t)Shm_Configuration->Intr.address );
interrupt_value = Shm_Convert( Shm_Configuration->Intr.value );
interrupt_cause = Shm_Convert( Shm_Configuration->Intr.length );
if ( Shm_Configuration->poll_intr == POLLED_MODE ) Shm_install_timer();
else Shm_setvec();
if ( Shm_Is_master_node() ) {
/*
* Zero out the shared memory area.
*/
(void) memset(
(void *) Shm_Configuration->base,
0,
Shm_Configuration->length
);
/*
* Initialize all of the locked queues (the free envelope
* pool and a receive queue per node) and set all of the
* node's status so they will be waiting to initialization
* to complete.
*/
Shm_Locked_queue_Initialize( FREE_ENV_CB, FREE_ENV_POOL );
for ( i=SHM_FIRST_NODE ; i<=SHM_MAXIMUM_NODES ; i++ ) {
Shm_Initialize_receive_queue( i );
Shm_Node_statuses[ i ].status = Shm_Pending_initialization;
Shm_Node_statuses[ i ].error = 0;
}
/*
* Initialize all of the envelopes and place them in the
* free pool.
*/
for ( i=0 ; i<Shm_Maximum_envelopes ; i++ ) {
Shm_Envelopes[ i ].index = Shm_Convert(i);
Shm_Free_envelope( &Shm_Envelopes[ i ] );
}
/*
* Initialize this node's interrupt information in the
* shared area so other nodes can interrupt us.
*/
Shm_Local_node_status->int_address = (uint32_t) interrupt_address;
Shm_Local_node_status->int_value = interrupt_value;
Shm_Local_node_status->int_length = interrupt_cause;
Shm_Local_node_status->status = Shm_Initialization_complete;
/*
* Loop until all nodes have completed initialization.
*/
do {
all_initialized = 1;
for ( i = SHM_FIRST_NODE ; i <= SHM_MAXIMUM_NODES ; i++ )
if ( Shm_Node_statuses[ i ].status != Shm_Initialization_complete )
all_initialized = 0;
} while ( all_initialized == 0 );
/*
* Tell the other nodes we think that the system is up.
*/
for ( i = SHM_FIRST_NODE ; i <= SHM_MAXIMUM_NODES ; i++ )
Shm_Node_statuses[ i ].status = Shm_Active_node;
} else { /* is not MASTER node */
/*
* Initialize the node status for the non-master nodes.
* Because the master node zeroes out memory, it is
* necessary for them to keep putting their values in
* the node status area until the master says they
* should become active.
*/
Shm_Local_node_status->status = Shm_Pending_initialization;
do {
if ( Shm_Local_node_status->status == Shm_Pending_initialization ) {
/*
* Initialize this node's interrupt information in the
* shared area so other nodes can interrupt us.
*/
Shm_Local_node_status->int_address =
(uint32_t) interrupt_address;
Shm_Local_node_status->int_value = interrupt_value;
Shm_Local_node_status->int_length = interrupt_cause;
Shm_Local_node_status->status = Shm_Initialization_complete;
}
} while ( Shm_Local_node_status->status != Shm_Active_node ) ;
}
/*
* Initialize the Interrupt Information Table
*/
for ( i = SHM_FIRST_NODE ; i <= SHM_MAXIMUM_NODES ; i++ ) {
nscb = &Shm_Node_statuses[ i ];
Shm_Interrupt_table[i].address = Shm_Convert_address(
(void *)Shm_Convert(((vol_u32) nscb->int_address))
);
Shm_Interrupt_table[i].value = Shm_Convert( nscb->int_value );
Shm_Interrupt_table[i].length = Shm_Convert( nscb->int_length );
}
MPCI_Shm_extensions.fatal = MPCI_Fatal;
(void) rtems_extension_create(
rtems_build_name( 'M', 'P', 'E', 'X' ),
&MPCI_Shm_extensions,
&extension_id
);
}

View File

@@ -0,0 +1,32 @@
/* void Shm_Locked_queue_Initialize( lq_cb, owner )
*
* This routine initializes a shared memory locked queue.
*
* Input parameters:
* lq_cb - pointer to the control block of the queue
* to be initialized
* owner - unique idenitifier of who owns this queue.
*
* Output parameters: NONE
*
* COPYRIGHT (c) 1989-1999.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <rtems.h>
#include "shm_driver.h"
void Shm_Locked_queue_Initialize(
Shm_Locked_queue_Control *lq_cb,
uint32_t owner
)
{
Shm_Initialize_lock( lq_cb );
lq_cb->front = Shm_Locked_queue_End_of_list;
lq_cb->rear = Shm_Locked_queue_End_of_list;
lq_cb->owner = Shm_Convert(owner);
}

View File

@@ -0,0 +1,55 @@
/* void Shm_Cause_interrupt( node )
*
* This routine is the shared memory driver routine which
* generates interrupts to other CPUs.
*
* It uses the information placed in the node status control
* block by each node. For example, when used with the Motorola
* MVME136 board, the MPCSR is used.
*
* Input parameters:
* node - destination of this packet (0 = broadcast)
*
* Output parameters: NONE
*
* COPYRIGHT (c) 1989-1999.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <rtems.h>
#include "shm_driver.h"
void Shm_Cause_interrupt(
uint32_t node
)
{
Shm_Interrupt_information *intr;
uint8_t *u8;
uint16_t *u16;
uint32_t *u32;
uint32_t value;
intr = &Shm_Interrupt_table[node];
value = intr->value;
switch ( intr->length ) {
case NO_INTERRUPT:
break;
case BYTE:
u8 = (uint8_t*)intr->address;
*u8 = (uint8_t) value;
break;
case WORD:
u16 = (uint16_t*)intr->address;
*u16 = (uint16_t) value;
break;
case LONG:
u32 = (uint32_t*)intr->address;
*u32 = (uint32_t) value;
break;
}
}

View File

@@ -0,0 +1,18 @@
/* _Shm_isr()
*
* COPYRIGHT (c) 1989-1999.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <rtems.h>
#include "shm_driver.h"
void Shm_isr(void)
{
Shm_Interrupt_count += 1;
rtems_multiprocessing_announce();
}

View File

@@ -0,0 +1,57 @@
/**
* @file
* This routine polls to see if a packet has arrived. If one
* has it informs the executive. It uses a Classic API Timer
*/
/*
* COPYRIGHT (c) 1989-2008, 2016.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <rtems.h>
#include <rtems/score/sysstate.h>
#include <rtems/libio.h>
#include <assert.h>
#include "shm_driver.h"
static rtems_timer_service_routine Shm_Poll_TSR(
rtems_id id,
void *ignored_address
)
{
uint32_t tmpfront;
/*
* This should NEVER happen but just in case.
*/
if (!_System_state_Is_up(_System_state_Get()))
return;
tmpfront = Shm_Local_receive_queue->front;
if ( Shm_Convert(tmpfront) != Shm_Locked_queue_End_of_list ) {
rtems_multiprocessing_announce();
Shm_Interrupt_count++;
}
(void) rtems_timer_reset( id );
}
void Shm_install_timer(void)
{
rtems_id id;
rtems_status_code status;
status = rtems_timer_create( rtems_build_name( 'S', 'H', 'P', 'L' ), &id );
assert( !status );
status = rtems_timer_fire_after( id, 1, Shm_Poll_TSR, NULL );
assert( !status );
}

View File

@@ -0,0 +1,41 @@
/* Shm_Receive_packet
*
* This routine is the shared memory locked queue MPCI driver routine
* used to obtain a packet containing a message from this node's
* receive queue.
*
* Input parameters:
* packet - address of a pointer to a packet
*
* Output parameters:
* *(rpb->packet) - pointer to packet
* NULL if no packet currently available
*
* COPYRIGHT (c) 1989-1999.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <rtems.h>
#include "shm_driver.h"
rtems_mpci_entry Shm_Receive_packet(
rtems_packet_prefix **packet
)
{
Shm_Envelope_control *ecb;
ecb = Shm_Locked_queue_Get( Shm_Local_receive_queue );
if ( ecb ) {
*(packet) = Shm_Envelope_control_to_packet_prefix_pointer( ecb );
if ( ecb->Preamble.endian != Shm_Configuration->format )
Shm_Convert_packet( *packet );
Shm_Receive_message_count++;
} else {
*(packet) = NULL;
Shm_Null_message_count++;
}
}

View File

@@ -0,0 +1,28 @@
/* Shm_Return_packet
*
* This routine is the shared memory locked queue MPCI driver
* routine used to return a message packet to a free envelope
* pool accessible by this node.
*
* Input parameters:
* packet - address of pointer to packet
*
* Output parameters: NONE
*
* COPYRIGHT (c) 1989-1999.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <rtems.h>
#include "shm_driver.h"
rtems_mpci_entry Shm_Return_packet(
rtems_packet_prefix *packet
)
{
Shm_Free_envelope( Shm_Packet_prefix_to_envelope_control_pointer(packet) );
}

View File

@@ -0,0 +1,64 @@
/**
* @file
*/
/*
* COPYRIGHT (c) 1989-1999, 2016.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <rtems.h>
#include "shm_driver.h"
struct pkt_cpy {
uint32_t packet[MAX_PACKET_SIZE/4];
};
/**
* This routine is the shared memory driver locked queue write
* MPCI driver routine. This routine sends the specified packet
* to the destination specified by "node". A "node" value of
* zero designates that this packet is to be broadcasted.
*
* @param node is the destination of this packet (0 = broadcast)
* @param packet is the address of packet
*
* @return NONE
*/
rtems_mpci_entry Shm_Send_packet(
uint32_t node,
rtems_packet_prefix *packet
)
{
Shm_Envelope_control *ecb, *tmp_ecb;
uint32_t nnum;
ecb = Shm_Packet_prefix_to_envelope_control_pointer( packet );
if ( node ) {
Shm_Build_preamble( ecb, node );
Shm_Build_postamble( ecb );
Shm_Append_to_receive_queue( node, ecb );
(*Shm_Configuration->cause_intr)( node );
}
else {
for( nnum = SHM_FIRST_NODE ; nnum <= SHM_MAXIMUM_NODES ; nnum++ )
if ( _Configuration_MP_table->node != nnum ) {
struct pkt_cpy *pkt;
tmp_ecb = Shm_Allocate_envelope();
if ( !tmp_ecb )
rtems_fatal_error_occurred( SHM_NO_FREE_PKTS );
Shm_Build_preamble( tmp_ecb, nnum );
pkt = (struct pkt_cpy *)tmp_ecb->packet;
*pkt = *((struct pkt_cpy *)packet);
Shm_Build_postamble( tmp_ecb );
Shm_Append_to_receive_queue( nnum, tmp_ecb );
(*Shm_Configuration->cause_intr)( nnum );
}
Shm_Free_envelope( ecb );
}
}