
/*
 * dev_nic.c - NIC (Network Controler) generic device API implementation.
 * 
 * Author  Alain Greiner    (2016,2017,2018,2019,2020)
 *
 * Copyright (c) UPMC Sorbonne Universites
 *
 * This file is part of ALMOS-MKH.
 *
 * ALMOS-MKH is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2.0 of the License.
 *
 * ALMOS-MKH is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <hal_kernel_types.h>
#include <hal_special.h>
#include <hal_uspace.h>
#include <remote_buf.h>
#include <memcpy.h>
#include <printk.h>
#include <chdev.h>
#include <thread.h>
#include <ksocket.h>
#include <hal_drivers.h>
#include <dev_nic.h>
#include <vfs.h>
#include <shared_socket.h>

/////////////////////////////////////////////////////////////////////////////////////////
//                 Extern global variables
/////////////////////////////////////////////////////////////////////////////////////////

extern chdev_directory_t  chdev_dir;         // allocated in kernel_init.c

////////////////////////////////////
void dev_nic_init( chdev_t * chdev )
{
    thread_t * new_thread;
    error_t    error;

    // get "channel" & "is_rx" fields from chdev descriptor
    uint32_t  channel = chdev->channel;
    bool_t    is_rx   = chdev->is_rx;

    // set chdev name
    if( is_rx ) snprintf( chdev->name , 16 , "nic%d_rx" , channel );
    else        snprintf( chdev->name , 16 , "nic%d_tx" , channel );

    // initialize the root of the listening sockets list
    xlist_root_init( XPTR( local_cxy , &chdev->ext.nic.root ) );

    // initialize the lock protecting this list
    remote_busylock_init( XPTR( local_cxy , &chdev->ext.nic.lock ),
                          LOCK_LISTEN_SOCKET );

    // call driver init function for this chdev
    hal_drivers_nic_init( chdev );

    // select a core to execute the NIC server thread
    lid_t lid = cluster_select_local_core( local_cxy );

    // bind the NIC IRQ to the selected core
    dev_pic_bind_irq( lid , chdev );

    // build pointer on server function
    void * func = is_rx ? &dev_nic_rx_server : &dev_nic_tx_server;

    // create server thread
    error = thread_kernel_create( &new_thread,
                                  THREAD_DEV,
                                  func,
                                  chdev,
                                  lid ); 
    if( error )
    {
        printk("\n[PANIC] in %s : cannot create server thread\n", __FUNCTION__ );
        return;
    }

    // set "server" field in chdev descriptor
    chdev->server = new_thread;
    
    // set "chdev" field in thread descriptor
    new_thread->chdev = chdev;

    // unblock server thread
    thread_unblock( XPTR( local_cxy , new_thread ) , THREAD_BLOCKED_GLOBAL );

#if (DEBUG_DEV_NIC_TX || DEBUG_DEV_NIC_RX)
thread_t * this = CURRENT_THREAD;
if( is_rx )
printk("\n[%s] thread[%x,%x] initialized NIC_RX[%d] / server %x\n",
__FUNCTION__, this->process->pid, this->trdid, channel, new_thread->trdid );
else
printk("\n[%s] thread[%x,%x] initialized NIC_TX[%d] / server %x\n",
__FUNCTION__, this->process->pid, this->trdid, channel, new_thread->trdid );
#endif
   
}  // end dev_nic_init()


///////////////////////////////////////////////////////////////////////////////////////////
//      Functions directly called by the client threads
///////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////
uint32_t dev_nic_get_key( uint32_t addr,
                          uint16_t port )
{
    thread_t * this = CURRENT_THREAD;
    
    xptr_t dev_xp = chdev_dir.nic_tx[0];
    chdev_t * dev_ptr = GET_PTR( dev_xp );

    if( dev_xp == XPTR_NULL ) return -1;
    
    // set command arguments in client thread descriptor
    this->nic_cmd.buffer = (uint8_t *)addr;
    this->nic_cmd.length = (uint32_t)port;
    this->nic_cmd.dev_xp = dev_xp;
    this->nic_cmd.type   = NIC_CMD_GET_KEY;

    // call driver 
    dev_ptr->cmd( XPTR( local_cxy , this ) );

    // get "status" 
    return this->nic_cmd.status;
}

//////////////////////////////////////////
error_t dev_nic_set_run( uint32_t channel,
                         uint32_t run )
{
    thread_t * this = CURRENT_THREAD;

    if( channel >= LOCAL_CLUSTER->nb_nic_channels ) return -1;

    xptr_t    dev_xp  = chdev_dir.nic_tx[0];
    chdev_t * dev_ptr = GET_PTR( dev_xp );

    if( dev_xp == XPTR_NULL ) return -1;
    
    // set command arguments in client thread descriptor
    this->nic_cmd.dev_xp = dev_xp;
    this->nic_cmd.type   = NIC_CMD_SET_RUN;
    this->nic_cmd.length = channel;
    this->nic_cmd.status = run;

    // call driver 
    dev_ptr->cmd( XPTR( local_cxy , this ) );

    // return "error" 
    return this->nic_cmd.error;
}

//////////////////////////////////
error_t dev_nic_get_instru( void )
{
    thread_t * this = CURRENT_THREAD;

    xptr_t    dev_xp  = chdev_dir.nic_tx[0];
    chdev_t * dev_ptr = GET_PTR( dev_xp );
    
    if( dev_xp == XPTR_NULL ) return -1;
    
    // set command arguments in client thread descriptor
    this->nic_cmd.dev_xp = dev_xp;
    this->nic_cmd.type   = NIC_CMD_GET_INSTRU;

    // call driver 
    dev_ptr->cmd( XPTR( local_cxy , this ) );

    // return "error" 
    return this->nic_cmd.error;
}

////////////////////////////////////
error_t dev_nic_clear_instru( void )
{
    thread_t * this = CURRENT_THREAD;

    xptr_t    dev_xp  = chdev_dir.nic_tx[0];
    chdev_t * dev_ptr = GET_PTR( dev_xp );
    
    if( dev_xp == XPTR_NULL ) return -1;
    
    // set command arguments in client thread descriptor
    this->nic_cmd.dev_xp = dev_xp;
    this->nic_cmd.type   = NIC_CMD_GET_INSTRU;

    // call driver 
    dev_ptr->cmd( XPTR( local_cxy , this ) );

    // return "error" 
    return this->nic_cmd.error;
}


////////////////////////////////////////////////////////////////////////////////////////////
//      Static functions called by the NIC_RX server & NIC_TX server threads
////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////
// This static function is used by the dev_nic_rx_handle_tcp() function
// to check acceptability of a given sequence number. It returns true when 
// the <seq> argument is contained in a wrap-around window defined by the 
// <min> and <max> arguments. The window wrap-around when (min > max).
////////////////////////////////////////////////////////////////////////////////////////////
// @ seq   : [in] value to be checked.
// @ min   : [in] first  base.
// @ max   : [in] window size.
////////////////////////////////////////////////////////////////////////////////////////////
static inline bool_t is_in_window( uint32_t seq,
                                   uint32_t min,
                                   uint32_t max )
{
    if( max >= min )    // no wrap_around => only one window [min,max]
    {
        return( (seq >= min) && (seq <= max) ); 
    }
    else                // window wrap-around => two windows [min,0xFFFFFFFF] and [0,max]
    {
        return( (seq <= max) || (seq >= min) );
    }
}  

////////////////////////////////////////////////////////////////////////////////////////
// This static function computes the checksum for an IP packet header.
// The "checksum" field itself is not taken into account for this computation.
////////////////////////////////////////////////////////////////////////////////////////
// @ buffer      : [in] pointer on IP packet header (20 bytes)
// @ return the checksum value on 16 bits
////////////////////////////////////////////////////////////////////////////////////////
static uint16_t dev_nic_ip_checksum( uint8_t  * buffer )
{
    uint32_t   i;           
    uint32_t   cs;      // 32 bits accumulator
    uint16_t * buf;     
    
    buf = (uint16_t *)buffer;

    // compute checksum 
    for( i = 0 , cs = 0 ; i < 10 ; i++ )
    {
        if( i != 5 )  cs += buf[i];
    }

    // one's complement
    return ~cs;
}

////////////////////////////////////////////////////////////////////////////////////////
// This static function computes the checksum for an UDP packet defined by 
// the <buffer> and <size> arguments.
////////////////////////////////////////////////////////////////////////////////////////
// @ buffer      : [in] pointer on UDP packet base.
// @ size        : [in] number of bytes in this packet (including header).
// @ return the checksum value on 16 bits
////////////////////////////////////////////////////////////////////////////////////////
static uint16_t dev_nic_udp_checksum( uint8_t  * buffer,
                                      uint32_t   size )
{
    uint32_t   i;           
    uint32_t   carry;
    uint32_t   cs;      // 32 bits accumulator
    uint16_t * buf;     
    uint32_t   max;     // number of uint16_t in packet
    
    // compute max & buf
    buf = (uint16_t *)buffer;
    max = size >> 1;

    // extend buffer[] if required
    if( size & 1 )
    {
        max++;
        buffer[size] = 0;
    }

    // compute checksum for UDP packet
    for( i = 0 , cs = 0 ; i < size ; i++ )  cs += buf[i];

    // handle carry
    carry = (cs >> 16);
    if( carry ) 
    {
        cs += carry; 
        carry = (cs >> 16);
        if( carry ) cs += carry;
    } 

    // one's complement
    return ~cs;
}

////////////////////////////////////////////////////////////////////////////////////////
// This static function computes the checksum for a TCP segment defined by the <buffer> 
// and <size> arguments. It includes the pseudo header defined by the <src_ip_addr>,
// <dst_ip_addr>, <size> arguments, and by the TCP_PROTOCOL code.
////////////////////////////////////////////////////////////////////////////////////////
// @ buffer      : [in] pointer on TCP segment base.
// @ tcp_length  : [in] number of bytes in this TCP segment (including header).
// @ src_ip_addr : [in] source IP address (pseudo header)
// @ dst_ip_addr : [in] destination IP address (pseudo header)
// @ return the checksum value on 16 bits
////////////////////////////////////////////////////////////////////////////////////////
static uint16_t dev_nic_tcp_checksum( uint8_t  * buffer,
                                      uint32_t   tcp_length,
                                      uint32_t   src_ip_addr,
                                      uint32_t   dst_ip_addr )
{
    uint32_t   i;           
    uint32_t   carry;
    uint32_t   cs;      // 32 bits accumulator
    uint16_t * buf;
    uint32_t   max;     // number of uint16_t in segment

    // compute max & buf
    buf = (uint16_t *)buffer;
    max = tcp_length >> 1;

    // extend buffer[] if required
    if( tcp_length & 1 )
    {
        max++;
        buffer[tcp_length] = 0;
    }

    // compute checksum for TCP segment
    for( i = 0 , cs = 0 ; i < tcp_length ; i++ )  cs += buf[i];

    // complete checksum for pseudo-header
    cs += src_ip_addr;
    cs += dst_ip_addr;
    cs += PROTOCOL_TCP;
    cs += tcp_length;

    // handle carry
    carry = (cs >> 16);
    if( carry ) 
    {
        cs += carry; 
        carry = (cs >> 16);
        if( carry ) cs += carry;
    } 

    // one's complement
    return ~cs;
}

///////////////////////////////////////////////////////////////////////////////////////////
// This static function can be called by the NIC_TX or NIC_RX server threads to unblock
// the TX client thread after completion (success or error) of a TX command registered
// in a socket identified by the <socket_xp> argument. The <status> argument defines
// the command success/failure status: a null value signals a success, a non-null value
// signals a failure. For all commands, it copies the status value in the tx_sts field,
// and print an error message on TXT0 in case of failure.
///////////////////////////////////////////////////////////////////////////////////////////
// @ socket_xp  : [in] extended pointer on socket
// @ status     : [in] command status (see above)
///////////////////////////////////////////////////////////////////////////////////////////
static void dev_nic_unblock_tx_client( xptr_t   socket_xp,
                                       int32_t  status )
{
    // get socket thread cluster and local pointer
    socket_t * socket_ptr = GET_PTR( socket_xp );
    cxy_t      socket_cxy = GET_CXY( socket_xp );

    if( status != CMD_STS_SUCCESS )
    {
        uint32_t sock_state = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->state ));
        uint32_t cmd_type   = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->tx_cmd ));
        pid_t    pid        = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->pid ));
        fdid_t   fdid       = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->fdid ));

        printk("\n[ERROR] reported by %s : socket[%x,%d] / %s / cmd %s / error %s \n",
        __FUNCTION__, pid, fdid, socket_state_str(sock_state),
        socket_cmd_type_str(cmd_type), socket_cmd_sts_str(status) );
    }

    // set tx_sts field in socket descriptor
    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_sts ) , status );

    // get extended point on TX client thread
    xptr_t client_xp = hal_remote_l64( XPTR( socket_cxy , &socket_ptr->tx_client ));

    // unblock the client thread
    thread_unblock( client_xp , THREAD_BLOCKED_IO );

}  // end dev_nic_unblock_tx_client()

///////////////////////////////////////////////////////////////////////////////////////////
// This static function can be called by the NIC_TX or NIC_RX server threads to unblock
// the RX client thread after completion (success or error) of an RX command registered
// in a socket identified by the <socket_xp> argument. The <status> argument defines
// the command success/failure status: a null value signals a success, a non-null value
// signals a failure. For all commands, it copies the status value in the rx_sts field,
// and print an error message on TXT0 in case of failure.
///////////////////////////////////////////////////////////////////////////////////////////
// @ socket_xp  : [in] extended pointer on socket
// @ status     : [in] command status (see above)
///////////////////////////////////////////////////////////////////////////////////////////
static void dev_nic_unblock_rx_client( xptr_t   socket_xp,
                                       int32_t  status )
{
    // get socket thread cluster and local pointer
    socket_t * socket_ptr = GET_PTR( socket_xp );
    cxy_t      socket_cxy = GET_CXY( socket_xp );

    if( status != CMD_STS_SUCCESS )
    {
        uint32_t sock_state = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->state ));
        uint32_t cmd_type   = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->rx_cmd ));
        pid_t    pid        = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->pid ));
        fdid_t   fdid       = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->fdid ));

        printk("\n[ERROR] reported by %s : socket[%x,%d] / %s / cmd %s / error %s\n",
        __FUNCTION__, pid, fdid, socket_state_str(sock_state),
        socket_cmd_type_str(cmd_type), socket_cmd_sts_str(status) );
    }

    // set rx_sts field in socket descriptor
    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_sts ) , status );

    // get extended point on RX client thread
    xptr_t client_xp = hal_remote_l64( XPTR( socket_cxy , &socket_ptr->rx_client ));

    // unblock the client thread
    thread_unblock( client_xp , THREAD_BLOCKED_IO );

}  // end dev_nic_unblock_rx_client()



///////////////////////////////////////////////////////////////////////////////////////////
//               Functions called by the NIC_RX server thread
///////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////
// This static function is called by the dev_nic_rx_server() function.
// It analyses an Ethernet frame contained in the kernel buffer defined
// by the <buffer> argument, and returns in the  <ip_length> argument the length
// of the IP packet contained in the Ethernet packet payload.
///////////////////////////////////////////////////////////////////////////////////////////
// @ buffer     : [in] pointer on a received Ethernet packet
// @ ip_length  : [out] length of IP packet (in bytes).
// @ return 0 if success / return -1 if illegal packet length. 
///////////////////////////////////////////////////////////////////////////////////////////
static error_t dev_nic_rx_check_eth( uint8_t  * buffer,
                                     uint32_t * ip_length )
{
    uint32_t length = ((uint32_t)buffer[12] << 8) | (uint32_t)buffer[13];

    *ip_length = length;

    return 0;
}
   
///////////////////////////////////////////////////////////////////////////////////////////
// This static function analyses the IP packet contained in the kernel buffer
// defined by the <buffer> argument, and returns in the <ip_src_addr>, <ip_dst_addr>, 
// <header_length> and <protocol> arguments the informations contained in the IP header.
// It checks the IP packet length versus the value contained in Ethernet header.
// It checks the IP header checksum.
///////////////////////////////////////////////////////////////////////////////////////////
// @ buffer          : [in] pointer on the IP packet.
// @ expected_length : [in] expected IP packet length (from Ethernet header). 
// @ ip_src_addr     : [out] source IP address.
// @ ip_dst_addr     : [out] destination IP address.
// @ protocol        : [out] transport protocol type.
// @ return 0 if success / return -1 if illegal packet. 
///////////////////////////////////////////////////////////////////////////////////////////
static error_t dev_nic_rx_check_ip( uint8_t  * buffer,
                                    uint32_t   expected_length, 
                                    uint32_t * ip_src_addr, 
                                    uint32_t * ip_dst_addr,
                                    uint32_t * trsp_protocol )
{

#if DEBUG_DEV_NIC_RX
thread_t * this  = CURRENT_THREAD;
uint32_t   cycle = (uint32_t)hal_get_cycles();
#endif

    // get packet length
    uint32_t length = ((uint32_t)buffer[2] << 8) | (uint32_t)buffer[3];

    // discard packet if eth_payload_length != ip_length
    if( length != expected_length )
    {

#if DEBUG_DEV_NIC_RX
if( DEBUG_DEV_NIC_RX < cycle )
printk("\n[%s] thread[%x,%x] failure : length (%d) != expected_length (%d)\n",
__FUNCTION__, this->process->pid, this->trdid, length, expected_length );
#endif
        return -1;
    }

    // get transport protocol type
    uint8_t protocol = buffer[9];

    // discard packet if unsupported protocol 
    if( (protocol != PROTOCOL_TCP) && (protocol != PROTOCOL_UDP) )
    {

#if DEBUG_DEV_NIC_RX
if( DEBUG_DEV_NIC_RX < cycle )
printk("\n[%s] thread[%x,%x] failure : unsupported transport protocol (%d)\n",
__FUNCTION__, this->process->pid, this->trdid, protocol );
#endif
        return -1;

    }
    // compute IP header checksum 
    uint32_t computed_cs = (uint32_t)dev_nic_ip_checksum( buffer );

    // extract IP header checksum
    uint32_t received_cs = ((uint32_t)buffer[10] << 8) | ((uint32_t)buffer[11]);

    // discard packet if bad checksum
    if( received_cs != computed_cs )
    {

#if DEBUG_DEV_NIC_RX
if( DEBUG_DEV_NIC_RX < cycle )
printk("\n[%s] thread[%x,%x] failure : computed checksum (%d) != received checksum (%d)\n",
__FUNCTION__, this->process->pid, this->trdid, computed_cs, received_cs );
#endif
        return -1;
    }


    *ip_src_addr = ((uint32_t)buffer[12] << 24) |
                   ((uint32_t)buffer[13] << 16) |
                   ((uint32_t)buffer[14] <<  8) |
                   ((uint32_t)buffer[15]      ) ;

    *ip_dst_addr = ((uint32_t)buffer[16] << 24) |
                   ((uint32_t)buffer[17] << 16) |
                   ((uint32_t)buffer[18] <<  8) |
                   ((uint32_t)buffer[19]      ) ;

    *trsp_protocol = protocol;
   
    return 0;
}

///////////////////////////////////////////////////////////////////////////////////////////
// This static function analyses the UDP packet contained in the kernel buffer
// defined by the <k_buf> and <k_length> arguments.
// It checks the UDP checksum, and discard corrupted packets.
// It scans the list of sockets attached to the NIC_RX chdev to find a matching socket,
// and discard the received packet if no UDP socket found.
// Finally, it copies the payload to the socket "rx_buf", as long as the packet payload
// is not larger than the rx_buf. 
// It set the "rx_valid" flip-flop, and unblock the client thread when the last expected
// byte has been received.
///////////////////////////////////////////////////////////////////////////////////////////
// @ chdev         : [in] local pointer on local NIC_RX chdev descriptor.
// @ k_buf         : [in] pointer on the UDP packet in local kernel buffer.
// @ k_length      : [in] number of bytes in buffer (including UDP header).
// @ pkt_src_addr  : [in] source IP address (from IP packet header).
// @ pkt_dst_addr  : [in] destination IP address (from IP packet header).
///////////////////////////////////////////////////////////////////////////////////////////
static void dev_nic_rx_handle_udp_packet( chdev_t  * chdev,
                                          uint8_t  * k_buf,
                                          uint32_t   k_length,
                                          uint32_t   pkt_src_addr,
                                          uint32_t   pkt_dst_addr )
{
    xptr_t     root_xp;           // extended pointer on attached sockets list root 
    xptr_t     lock_xp;           // extended pointer on chdev lock 
    xptr_t     iter_xp;           // iterator on socket list
    xptr_t     socket_xp;         // extended pointer on socket descriptor
    cxy_t      socket_cxy;
    socket_t * socket_ptr;
    uint32_t   socket_type;       // socket type
    uint32_t   socket_state;      // socket state
    uint32_t   local_addr;        // local IP address from socket
    uint32_t   local_port;        // local port from socket
    uint32_t   remote_addr;       // remote IP address from socket
    uint32_t   remote_port;       // remote port from socket
    bool_t     match_socket;      // matching socket found
    uint16_t   checksum;          // computed checksum
    uint16_t   pkt_checksum;      // received checksum
    xptr_t     socket_rbuf_xp;    // extended pointer on socket rx_buf
    xptr_t     socket_lock_xp;    // extended pointer on socket lock
    xptr_t     socket_client_xp;  // extended pointer on socket rx_client field
    xptr_t     client_xp;         // extended pointer on client thread descriptor
    uint32_t   payload;           // number of bytes in payload
    uint32_t   status;            // number of bytes in rx_buf
    uint32_t   space;             // number of free slots in rx_buf
    uint32_t   moved_bytes;       // number of bytes actually moved to rx_buf

    // build extended pointers on list of sockets attached to NIC_RX chdev
    root_xp = XPTR( local_cxy , &chdev->wait_root );
    lock_xp = XPTR( local_cxy , &chdev->wait_lock );

    // compute UDP packet checksum 
    checksum = dev_nic_udp_checksum( k_buf , k_length );

    // get checksum from received packet header 
    pkt_checksum = ((uint16_t)k_buf[6] << 8) | (uint16_t)k_buf[7];

    // discard corrupted packet  
    if( pkt_checksum != checksum ) return;
    
    // get src_port and dst_port from UDP header
    uint32_t pkt_src_port = ((uint32_t)k_buf[0] << 8) | (uint32_t)k_buf[1];
    uint32_t pkt_dst_port = ((uint32_t)k_buf[2] << 8) | (uint32_t)k_buf[3];

    // discard unexpected packet
    if( xlist_is_empty( root_xp ) ) return;
 
    // take the lock protecting the sockets list
    remote_busylock_acquire( lock_xp );

    match_socket = false;

    // scan sockets list to find a match
    XLIST_FOREACH( root_xp , iter_xp )
    {
        // get socket cluster and local pointer
        socket_xp  = XLIST_ELEMENT( iter_xp , socket_t , rx_list );
        socket_ptr = GET_PTR( socket_xp );
        socket_cxy = GET_CXY( socket_xp );

        // get socket type 
        socket_type  = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->type ));
        socket_state = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->state ));
                
        // skip TCP socket
        if( socket_type == SOCK_STREAM ) continue;

        // get relevant info from socket descriptor
        local_addr  = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_addr )); 
        remote_addr = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_addr )); 
        local_port  = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_port )); 
        remote_port = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_port )); 

        // compute matching
        bool_t local_match  = (local_addr  == pkt_dst_addr) && 
                              (local_port  == pkt_dst_port);

        bool_t remote_match = (remote_addr == pkt_src_addr) &&
                              (remote_port == pkt_src_port);

        if (socket_state == UDP_STATE_ESTAB ) match_socket = local_match && remote_match;
        else                                  match_socket = local_match;

        // exit loop when socket found
        if( match_socket ) break;
    }

    // release the lock protecting the sockets list
    remote_busylock_release( lock_xp );

    // discard unexpected packet
    if( match_socket == false ) return;
   
    // build extended pointers on various socket fields
    socket_rbuf_xp   = XPTR( socket_cxy , &socket_ptr->rx_buf );
    socket_lock_xp   = XPTR( socket_cxy , &socket_ptr->lock ); 
    socket_client_xp = XPTR( socket_cxy , &socket_ptr->rx_client );

    // take the lock protecting the socket 
    remote_queuelock_acquire( socket_lock_xp );

    // get status & space from rx_buf
    status = remote_buf_status( socket_rbuf_xp );
    space  = NIC_RX_BUF_SIZE - status;

    // get client thread 
    client_xp  = hal_remote_l64( socket_client_xp );

    // get number of bytes in payload
    payload = k_length - UDP_HEAD_LEN;

    // compute number of bytes to move : min (space , seg_payload)
    moved_bytes = ( space < payload ) ? space : payload;

    // move payload from kernel buffer to socket rx_buf
    remote_buf_put_from_kernel( socket_rbuf_xp,
                                 k_buf + UDP_HEAD_LEN,
                                 moved_bytes );
    // unblock client thread 
    if( client_xp != XPTR_NULL )
    {
        thread_unblock( client_xp , THREAD_BLOCKED_IO );
    }

    // release the lock protecting the socket
    remote_queuelock_release( socket_lock_xp );

}  // end dev_nic_rx_handle_udp_packet()

///////////////////////////////////////////////////////////////////////////////////////////
// This static function is called by the dev_nic_rx_server() function to handle one RX
// TCP segment contained in a kernel buffer defined by the <k_buf> & <k_length> arguments.
// The <seg_remote_addr> and <seg_local_addr> arguments are obtained from the received
// IP packet header. It the received segment doesn't match any connected socket attached
// to the selected chdev[k], or any listening socket waiting connection, or if the segment
// is corrupted, this segment is discarded. 
// If required by the TCP flags, it registers an R2T request in the socket R2T queue 
// to implement the TCP handcheck for close and connect.
///////////////////////////////////////////////////////////////////////////////////////////
// Implementation note:
// A "connected" socket is actually a TCP socket already attached to a given NIC_RX[k]
// chdev, and can therefore receive a TCP segment on the NIC channel <k>. A "listening"
// socket is a TCP socket in the LISTEN state. This function operates in 6 steps:
// 1) It checks the TCP checksum, and discard the corrupted segment if corrupted.
// 2) It scans the list of sockets attached to the NIC_RX[k] chdev, to find one TCP
//    socket matching the TCP segment header. 
// 3) When a matching connected socket is found, it handles the received segment, including 
//    the SYN, FIN, ACK and RST flags. It updates the socket state when required, moves 
//    data to the rx_buf when possible, and return. It takes the lock protecting the socket,
//    because an connected socket is accessed by both the NIC_TX and NIC_RX server threads.
// 4) If no matching connected socket has been found, it scans the list of listening 
//    sockets to find a matching listening socket. 
// 5) When a matching listening socket is found, it simply registers a new connection
//    request in the listening socket CRQ queue, when the SYN flag is set, and insert 
//    a SYN-ACK request in the socket R2T queue.
// 6) It discards the packet if no connected or listening socket has been found.
///////////////////////////////////////////////////////////////////////////////////////////
// @ chdev           : [in] local pointer on local NIC_RX chdev descriptor.
// @ k_buf           : [in] pointer on the TCP packet in local kernel buffer.
// @ k_length        : [in] number of bytes in buffer (including TCP header).
// @ seg_remote_addr : [in] remote IP address (from IP packet header).
// @ seg_local_addr  : [in] local IP address (from IP packet header).
///////////////////////////////////////////////////////////////////////////////////////////
static void dev_nic_rx_handle_tcp_segment( chdev_t  * chdev,
                                           uint8_t  * k_buf,
                                           uint32_t   k_length,
                                           uint32_t   seg_remote_addr,
                                           uint32_t   seg_local_addr )
{
    xptr_t     root_xp;            // extended pointer on connected sockets list root 
    xptr_t     lock_xp;            // extended pointer on chdev lock 
    xptr_t     iter_xp;            // iterator for these queues
    bool_t     attached_match;     // true if one attached socket match 
    bool_t     listening_match;    // true if one listening socket match
    xptr_t     socket_xp;          // extended pointer on matching socket
    cxy_t      socket_cxy;         // cluster identifier of matching socket
    socket_t * socket_ptr;         // local pointer on matching socket
    uint32_t   socket_local_addr;  // local IP address from socket
    uint32_t   socket_local_port;  // local port from socket
    uint32_t   socket_remote_addr; // remote IP address from socket
    uint32_t   socket_remote_port; // remote port from socket
    uint32_t   socket_state;       // socket state
    uint32_t   socket_type;        // socket type
    bool_t     socket_tx_valid;    // TX command valid
    uint32_t   socket_tx_cmd;      // TX command type
    uint32_t   socket_tx_todo;     // number of TX bytes not sent yet 
    uint32_t   socket_tx_nxt;      // next byte to send in TX stream
    uint32_t   socket_tx_una;      // first unacknowledged byte in TX stream
    bool_t     socket_rx_valid;    // RX command valid
    uint32_t   socket_rx_cmd;      // TX command type
    uint32_t   socket_rx_nxt;      // next expected byte in RX stream
    uint32_t   socket_rx_wnd;      // current window value in RX stream
    uint32_t   socket_rx_irs;      // initial sequence index in RX stream
    xptr_t     socket_lock_xp;     // extended pointer on lock protecting socket state
    xptr_t     socket_rx_buf_xp;   // extended pointer on socket rx_buf
    xptr_t     socket_r2tq_xp;     // extended pointer on socket R2T queue
    xptr_t     socket_crqq_xp;     // extended pointer on socket CRQ queue
    uint16_t   checksum;           // computed TCP segment chechsum
    error_t    error;

    // get relevant infos from TCP segment header
    uint32_t seg_remote_port = ((uint32_t)k_buf[0] << 8) | (uint32_t)k_buf[1];
    uint32_t seg_local_port  = ((uint32_t)k_buf[2] << 8) | (uint32_t)k_buf[3];

    uint32_t seg_seq_num  = ((uint32_t)k_buf[4]  << 24) | 
                            ((uint32_t)k_buf[5]  << 16) |
                            ((uint32_t)k_buf[6]  <<  8) |
                            ((uint32_t)k_buf[7]       );

    uint32_t seg_ack_num  = ((uint32_t)k_buf[8]  << 24) | 
                            ((uint32_t)k_buf[9]  << 16) |
                            ((uint32_t)k_buf[10] <<  8) |
                            ((uint32_t)k_buf[11]      );

    uint8_t  seg_hlen     = k_buf[12] << 2;       // TCP header length in bytes
 
    uint8_t  seg_flags    = k_buf[13];

    bool_t   seg_ack_set  = ((seg_flags & TCP_FLAG_ACK) != 0);
    bool_t   seg_syn_set  = ((seg_flags & TCP_FLAG_SYN) != 0);
    bool_t   seg_fin_set  = ((seg_flags & TCP_FLAG_FIN) != 0);
    bool_t   seg_rst_set  = ((seg_flags & TCP_FLAG_RST) != 0);

    uint16_t seg_window   = ((uint32_t)k_buf[14] << 8) | (uint32_t)k_buf[15];

    uint16_t seg_checksum = ((uint32_t)k_buf[16] << 8) | (uint32_t)k_buf[17];

    uint32_t seg_data_len = k_length - seg_hlen;  // number of bytes in payload

#if DEBUG_DEV_NIC_RX
thread_t * this = CURRENT_THREAD;
uint32_t   cycle;
uint32_t   fdid;
pid_t      pid;
#endif

#if DEBUG_DEV_NIC_RX
cycle = (uint32_t)hal_get_cycles();
if( cycle > DEBUG_DEV_NIC_RX )
printk("\n[%s] thread[%x,%x] enters / tcp_length %d / tcp_flags %x / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, k_length, seg_flags , cycle );
#endif

    // compute and check TCP checksum
    k_buf[16] = 0;
    k_buf[17] = 0;
    checksum = dev_nic_tcp_checksum( k_buf,
                                     k_length,
                                     seg_remote_addr,
                                     seg_local_addr );

    // discard segment if corrupted 
    if( seg_checksum != checksum )
    {

#if DEBUG_DEV_NIC_RX
if( cycle > DEBUG_DEV_NIC_RX )
printk("\n[%s] thread[%x,%x] tcp checksum failure : received %x / computed %x\n",
__FUNCTION__, this->process->pid, this->trdid, seg_checksum, checksum );
#endif
        return;
    }
    
    // scan list of attached sockets to find a matching TCP socket
    attached_match = false;

    // build extended pointer on xlist of sockets attached to NIC_RX chdev
    root_xp = XPTR( local_cxy , &chdev->wait_root );
    lock_xp = XPTR( local_cxy , &chdev->wait_lock );

    // take the lock protecting the list of attached sockets
    remote_busylock_acquire( lock_xp );

    XLIST_FOREACH( root_xp , iter_xp )
    {
        // get socket cluster and local pointer
        socket_xp  = XLIST_ELEMENT( iter_xp , socket_t , rx_list );
        socket_ptr = GET_PTR( socket_xp );
        socket_cxy = GET_CXY( socket_xp );

        // get socket type and state
        socket_type  = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->type ));
        socket_state = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->state ));
                
        // skip UDP socket
        if( socket_type == SOCK_DGRAM ) continue;

        // get relevant socket infos for matching
        socket_local_addr   = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_addr )); 
        socket_remote_addr  = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_addr )); 
        socket_local_port   = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_port )); 
        socket_remote_port  = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_port )); 
                
        // compute matching condition for a connected socket
        attached_match = (socket_local_addr  == seg_local_addr) && 
                         (socket_local_port  == seg_local_port) &&
                         (socket_remote_addr == seg_remote_addr) &&
                         (socket_remote_port == seg_remote_port) ;

        // exit loop if matching
        if( attached_match ) 
        {

#if DEBUG_DEV_NIC_RX
fdid = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->fdid ) );
pid  = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->pid ) );
if( cycle > DEBUG_DEV_NIC_RX )
printk("\n[%s] thread[%x,%x] matching attached socket[%d,%d] / state %s\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state) );
#endif
            break;
        }

    }  // end loop on attached sockets

    // release the lock protecting the list of attached sockets
    remote_busylock_release( lock_xp );

    // handle TCP segment for an attached socket
    if( attached_match )
    {
        // The actions depend on both the socket state and the received segment flags :
        // - update socket state,
        // - move data to rx_buf,
        // - register a request in R2T queue when required
 
        // build extended pointers on various socket fields
        socket_lock_xp    = XPTR( socket_cxy , &socket_ptr->lock );
        socket_rx_buf_xp  = XPTR( socket_cxy , &socket_ptr->rx_buf );
        socket_r2tq_xp    = XPTR( socket_cxy , &socket_ptr->r2tq );

        // take the lock protecting the matching socket
        remote_queuelock_acquire( socket_lock_xp );

        // get relevant socket infos from socket descriptor
        socket_tx_valid = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->tx_valid ));
        socket_tx_cmd   = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->tx_cmd ));
        socket_tx_todo  = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->tx_todo ));
        socket_tx_nxt   = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->tx_nxt )); 
        socket_tx_una   = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->tx_una ));

        socket_rx_valid = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->rx_valid ));
        socket_rx_cmd   = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->rx_cmd ));
        socket_rx_nxt   = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->rx_nxt )); 
        socket_rx_wnd   = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->rx_wnd )); 
        socket_rx_irs   = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->rx_irs ));

        // handle the received segment, depending on the matching socket state 
        switch( socket_state )
        {
            ////////////////////////
            case TCP_STATE_SYN_SENT:  // TCP client waiting for SYN-ACK in connect handshake
            {
                // [1] check ACK flag 
                if( seg_ack_set )
                {
                    if( seg_ack_num != TCP_ISS_CLIENT + 1 )  // bad ACK => report error
                    {

#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : expect ack_num %x / get %x\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid,
socket_state_str(socket_state), TCP_ISS_CLIENT + 1, seg_ack_num );
#endif
                        // make an RST request to R2T queue
                        socket_put_r2t_request( socket_r2tq_xp,
                                                TCP_FLAG_RST,
                                                chdev->channel );

                        // report error to local TX client thread
                        dev_nic_unblock_tx_client( socket_xp , CMD_STS_BADACK );

                        break;
                    }
                }

                // [2] check RST flag                       // receive RST => report error
                if( seg_rst_set )
                {

#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : received RST flag\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state) );
#endif
                    // update socket state
                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
                                          TCP_STATE_BOUND );

                    // signal error to local TX client thread
                    dev_nic_unblock_tx_client( socket_xp , CMD_STS_RST );

                    break;
                }

                // [3] handle security & precedence TODO ... someday

                // [4] handle SYN-ACK
                if( seg_syn_set && seg_ack_set )  // received SYN and ACK => report success
                { 

#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : received expected SYN-ACK\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid , socket_state_str(socket_state) );
#endif
                    // set socket.tx_una  
                    hal_remote_s32( XPTR(socket_cxy , &socket_ptr->tx_una), seg_ack_num );

                    // set socket.rx_irs
                    hal_remote_s32( XPTR(socket_cxy , &socket_ptr->rx_irs), seg_seq_num );

                    // set socket.rx_nxt
                    hal_remote_s32( XPTR(socket_cxy , &socket_ptr->rx_nxt), seg_seq_num + 1 );

                    // update socket.state
                    hal_remote_s32( XPTR(socket_cxy , &socket_ptr->state), TCP_STATE_ESTAB );

                    // make an ACK request to R2T queue
                    socket_put_r2t_request( socket_r2tq_xp,
                                            TCP_FLAG_ACK,
                                            chdev->channel );

                    // report succes to local TX client thread
                    dev_nic_unblock_tx_client( socket_xp , CMD_STS_SUCCESS );
                }
                else        // received SYN without ACK => client becomes server
                {

#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] for socket[%x,%d] %s : received SYN-ACK => become server\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid , socket_state_str(socket_state) );
#endif
                    // update socket.state
                    hal_remote_s32( XPTR(socket_cxy,&socket_ptr->state), TCP_STATE_SYN_RCVD );

                    // set socket.tx_nxt 
                    hal_remote_s32( XPTR(socket_cxy , &socket_ptr->tx_nxt), TCP_ISS_SERVER );

                    // set socket.rx_nxt to seg_seq_num + 1
                    hal_remote_s32( XPTR(socket_cxy,&socket_ptr->rx_nxt), seg_seq_num + 1 );

                    // make a SYN.ACK request to R2T queue
                    socket_put_r2t_request( socket_r2tq_xp,
                                            TCP_FLAG_SYN | TCP_FLAG_ACK,
                                            chdev->channel );
                }
                break;
            }
            ////////////////////////
            case TCP_STATE_SYN_RCVD:  // TCP server waiting last ACK in connect handshake 
            {
                // [1] check sequence number
                if( seg_seq_num != socket_rx_nxt )        // unexpected SEQ_NUM => discard
                {

#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : expect seq_num %x / get %x\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid,
socket_state_str(socket_state), socket_rx_nxt, seg_seq_num );
#endif
                    // discard segment without reporting
                    break;
                } 

                // [2] handle RST flag                    // received RST => report error
                if( seg_rst_set )
                {

#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : received RST flag\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid , socket_state_str(socket_state) );
#endif
                    // update socket state
                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), TCP_STATE_BOUND );

                    // report error to local TX client thread
                    dev_nic_unblock_tx_client( socket_xp , CMD_STS_RST );

                    break;
                }

                // [3] handle security & precedence TODO ... someday

                // [4] handle SYN flag 
                if( seg_syn_set )                           // received SYN => discard
                {

#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : received SYN flag\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid , socket_state_str(socket_state) );
#endif
                    // discard segment without reporting
                    break;
                } 

                // [5] handle  ACK flag 
                if( seg_ack_set == false )                      // missing ACK => discard
                {

#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : no ACK in TCP segment\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid , socket_state_str(socket_state) );
#endif
                    // discard segment without reporting
                    break;
                }
                else if( seg_ack_num != (TCP_ISS_SERVER + 1) )  // unacceptable ACK
                {

#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : expect ack_num %x / get %x\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid, 
socket_state_str(socket_state), TCP_ISS_SERVER + 1, seg_ack_num );
#endif

                    // register an RST request to R2TQ for remote TCP client 
                    socket_put_r2t_request( socket_r2tq_xp,
                                            TCP_FLAG_RST,
                                            chdev->channel );

                    // report error to local TX client thread
                    dev_nic_unblock_tx_client( socket_xp , CMD_STS_BADACK );
                }
                else                                           // acceptable ACK
                {

#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : received expected ACK\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid , socket_state_str(socket_state) );
#endif
                    // set socket.tx_una  
                    hal_remote_s32( XPTR(socket_cxy , &socket_ptr->tx_una), seg_ack_num );

                    // update socket.state
                    hal_remote_s32( XPTR(socket_cxy , &socket_ptr->state), 
                                    TCP_STATE_ESTAB );

                    // report success to local TX client thread
                    dev_nic_unblock_tx_client( socket_xp , CMD_STS_SUCCESS );
                }
                break;
            }
            /////////////////////
            case TCP_STATE_ESTAB:
            case TCP_STATE_FIN_WAIT1:
            case TCP_STATE_FIN_WAIT2:
            case TCP_STATE_CLOSE_WAIT:
            case TCP_STATE_CLOSING:
            case TCP_STATE_LAST_ACK:
            case TCP_STATE_TIME_WAIT:
            {
                // [1] check sequence number : out_of_order segments not accepted
                if( seg_seq_num != socket_rx_nxt )
                {

#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : illegal SEQ_NUM %x / expected %x\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid,
socket_state_str(socket_state), seg_seq_num, socket_rx_nxt );
#endif
                    // discard segment
                    break;
                } 

                // check all bytes in window when the payload exist
                // TODO : we could accept bytes that are in window, 
                // but this implementation reject all bytes in segment 
                if( seg_data_len > 0 )
                {
                    // compute min & max acceptable sequence numbers
                    uint32_t seq_min  = socket_rx_nxt;
                    uint32_t seq_max  = socket_rx_nxt + socket_rx_wnd - 1;

                    // compute sequence number for last byte in segment 
                    uint32_t seg_seq_last = seg_seq_num + seg_data_len - 1;
                      
                    if( is_in_window( seg_seq_last, seq_min, seq_max ) == false ) 
                    {

#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : last SEQ_NUM %x not in [%x,%x]\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid,
socket_state_str(socket_state), seg_seq_last, seq_min, seq_max );
#endif
                        // discard segment
                        break;
                    }
                } 

                // [2] handle RST flag
                if( seg_rst_set )
                {

#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : received RST flag\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state) );
#endif
                    if( (socket_state == TCP_STATE_ESTAB     ) ||
                        (socket_state == TCP_STATE_FIN_WAIT1 ) ||
                        (socket_state == TCP_STATE_FIN_WAIT2 ) ||
                        (socket_state == TCP_STATE_CLOSE_WAIT) )
                    {
                        // TODO all pending send & received commands 
                        // must receive "reset" responses

                        // TODO destroy the socket
                    }
                    else  // states CLOSING / LAST_ACK / TIME_WAIT
                    {
                        // TODO          
                    }
                    break;
                }

                // [3] handle security & precedence TODO ... someday

                // [4] check SYN flag 
                if( seg_syn_set )                                // received SYN => ERROR
                {

#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : received unexpected SYN\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid , socket_state_str(socket_state) );
#endif
                    // TODO signal error to user

                    // make an RST request to R2T queue 
                    socket_put_r2t_request( socket_r2tq_xp, 
                                            TCP_FLAG_RST,
                                            chdev->channel );

                    // update socket state
                    hal_remote_s32( XPTR(socket_cxy , &socket_ptr->state), TCP_STATE_BOUND );

                    break;
                }

                // [5] check ACK  
                if( seg_ack_set == false )                           // missing ACK
                {

#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : no ACK flag\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state) );
#endif
                    // discard segment
                    break;
                }
                else if( is_in_window( seg_ack_num,
                                       socket_tx_una,
                                       socket_tx_nxt ) == false )    // unacceptable ACK
                {

#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : ACK_NUM %x not in [%x,%x]\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state),
seg_ack_num, socket_tx_una, socket_tx_nxt );
#endif
                    // discard segment
                    break;
                }
                else                                                // acceptable ack
                {
                    // update socket.tx_una
                    hal_remote_s32( XPTR(socket_cxy , &socket_ptr->tx_una), seg_ack_num );

                    // update socket.tx_wnd  
                    hal_remote_s32( XPTR(socket_cxy , &socket_ptr->tx_wnd), seg_window );

                    // check last data byte acknowledged for a SEND command
                    if( (socket_tx_todo == 0) && 
                        (seg_ack_num == socket_tx_nxt) &&
                        (socket_tx_cmd == CMD_TX_SEND) )
                    {
                        // signal success to TX client thread
                        dev_nic_unblock_tx_client( socket_xp , CMD_STS_SUCCESS );
                    }
                }
                    
                // [7] handle URG flag  TODO ... someday

                // [8] Move DATA to rx_buf / ACK request to R2T queue / unblock rx_client
                if( seg_data_len )
                {
                    if( (socket_state == TCP_STATE_ESTAB)     ||
                        (socket_state == TCP_STATE_FIN_WAIT1) ||
                        (socket_state == TCP_STATE_FIN_WAIT2) )
                    {
                        // get number of bytes already stored in rx_buf
                        uint32_t status = remote_buf_status( socket_rx_buf_xp );

                        // compute empty space in rx_buf
                        uint32_t space = NIC_RX_BUF_SIZE - status;

                        // compute number of bytes to move : min (space , seg_data_len)
                        uint32_t nbytes = ( space < seg_data_len ) ? space : seg_data_len;

                        // move payload from k_buf to rx_buf
                        remote_buf_put_from_kernel( socket_rx_buf_xp, 
                                                    k_buf + seg_hlen,
                                                    nbytes );
#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : move %d bytes to rx_buf\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid,
socket_state_str(socket_state), nbytes );
#endif
                        // update socket.rx_nxt 
                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_nxt ),
                                              socket_rx_nxt + nbytes );

                        // update socket.rx_wnd 
                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_wnd ),
                                              socket_rx_wnd - nbytes ); 

                        // make an ACK request to R2T queue 
                        socket_put_r2t_request( socket_r2tq_xp,
                                                TCP_FLAG_ACK,
                                                chdev->channel );

                        // check pending RX_RECV command
                        if( (socket_rx_valid == true) &&
                            (socket_rx_cmd == CMD_RX_RECV) )
                        {
                            // reset rx_valid
                            hal_remote_s32( XPTR(socket_cxy,&socket_ptr->rx_valid), false );

                            // report success to RX client thread
                            dev_nic_unblock_rx_client( socket_xp , CMD_STS_SUCCESS );
#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : unblock waiting RX client thread\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid,
socket_state_str(socket_state) );
#endif
                        }
                    }
                }

                // [9] handle FIN flag 
                if( socket_state == TCP_STATE_ESTAB )
                {
                    if( seg_fin_set )  // received ACK & FIN    
                    {

#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : FIN-ACK => goes CLOSE_WAIT\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid,
socket_state_str(socket_state) );
#endif
                        // update socket.rx_nxt when FIN received
                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_nxt ),
                                        socket_rx_nxt + 1 ); 

                        // update socket state
                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
                                        TCP_STATE_CLOSE_WAIT );

                        // make an ACK request to R2T queue
                        socket_put_r2t_request( socket_r2tq_xp,
                                                TCP_FLAG_ACK,
                                                chdev->channel );

                        // check pending RX_RECV command
                        if( (socket_rx_valid == true) &&
                            (socket_rx_cmd == CMD_RX_RECV) )
                        {
                            // reset rx_valid
                            hal_remote_s32( XPTR(socket_cxy,&socket_ptr->rx_valid), false );

                            // report error to RX client thread
                            dev_nic_unblock_rx_client( socket_xp , CMD_STS_EOF );
#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : unblock RX client waiting on RECV\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid,
socket_state_str(socket_state) );
#endif
                        } 
                    }
                }
                else if( socket_state == TCP_STATE_FIN_WAIT1 )
                {
                    if( seg_fin_set )  // received ACK & FIN
                    { 

#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : FIN-ACK => goes CLOSING\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid,
socket_state_str(socket_state) );
#endif
                        // update socket.rx_nxt when FIN received
                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_nxt ),
                                        socket_rx_nxt + 1 ); 

                        // update socket state
                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
                                        TCP_STATE_CLOSING );

                        // make an ACK request to R2T queue
                        socket_put_r2t_request( socket_r2tq_xp,
                                                TCP_FLAG_ACK,
                                                chdev->channel );
                    }
                    else              // received ACK only 
                    {

#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : only ACK => goes FIN_WAIT2\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid,
socket_state_str(socket_state) );
#endif
                        // update socket state
                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
                                        TCP_STATE_FIN_WAIT2 );
                    }
                }
                else if( socket_state == TCP_STATE_FIN_WAIT2 )
                {
                    if( seg_fin_set )  // received ACK & FIN
                    { 

#if DEBUG_DEV_NIC_RX
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : FIN-ACK => goes CLOSED / unblock client\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid,
socket_state_str(socket_state) );
#endif
                        // update socket.rx_nxt when FIN received
                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_nxt ),
                                        socket_rx_nxt + 1 ); 

                        // update socket.state 
                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
                                        TCP_STATE_CLOSED );  // TODO change to TIME_WAIT

                        // make an ACK request to R2T queue
                        socket_put_r2t_request( socket_r2tq_xp,
                                                TCP_FLAG_ACK,
                                                chdev->channel );
                
                        // report success to TX client thread
                        dev_nic_unblock_tx_client( socket_xp , CMD_STS_SUCCESS );

                        // TODO start the MSL timer / turn off others timers

                    }
                }
                else if( socket_state == TCP_STATE_CLOSING )   // received ACK
                {
                    // update socket.state
                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
                                    TCP_STATE_CLOSED );  // todo change to TIME_WAIT

                    // TODO start the MSL timer / turn off others timers

                }
                else if( socket_state == TCP_STATE_TIME_WAIT )
                {
                    // TODO wait msl_time_out before unblocking TX thread

                    // update socket.state when ACK received
                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
                                    TCP_STATE_CLOSED );

                    // unblock TX client thead for success
                    dev_nic_unblock_tx_client( socket_xp , CMD_STS_SUCCESS );
                }
                else if( socket_state == TCP_STATE_CLOSE_WAIT )
                {
                    // do nothing 
                }
                else if( socket_state == TCP_STATE_LAST_ACK )
                {
                    // update socket.state when ACK received
                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
                                    TCP_STATE_CLOSED );

                    // unblock TX client thead for success
                    dev_nic_unblock_tx_client( socket_xp , CMD_STS_SUCCESS );
                }
            }  // end case connecteded states
        }  // end switch socket state

        // release the lock protecting socket state
        remote_queuelock_release( socket_lock_xp );

        return;

    }  // end if attached socket

    // 4. scan the list of listening sockets
    listening_match = false;
 
    // get pointers on NIC_RX[0] chdev
    xptr_t    rx0_chdev_xp  = chdev_dir.nic_rx[0];
    chdev_t * rx0_chdev_ptr = GET_PTR( rx0_chdev_xp );
    cxy_t     rx0_chdev_cxy = GET_CXY( rx0_chdev_xp );
   
    // build extended pointers on list of listening sockets
    xptr_t    rx0_root_xp = XPTR( rx0_chdev_cxy , &rx0_chdev_ptr->ext.nic.root );
    xptr_t    rx0_lock_xp = XPTR( rx0_chdev_cxy , &rx0_chdev_ptr->ext.nic.lock );

    // take the lock protecting the list of listening sockets
    remote_busylock_acquire( rx0_lock_xp );

    // scan the xlist of listening socket
    XLIST_FOREACH( rx0_root_xp , iter_xp )
    {
        // get socket cluster and local pointer
        socket_xp  = XLIST_ELEMENT( iter_xp , socket_t , rx_list );
        socket_ptr = GET_PTR( socket_xp );
        socket_cxy = GET_CXY( socket_xp );

        // get relevant socket type and state
        socket_type     = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->type ));
        socket_state    = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->state ));
                
// check socket type and state
assert( (socket_type  == SOCK_STREAM ) , "illegal socket type" );
assert( (socket_state == TCP_STATE_LISTEN ) , "illegal socket state" );

        // get relevant socket infos for matching
        socket_local_addr   = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_addr )); 
        socket_local_port   = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_port )); 
                
        // compute matching condition for a listening socket
        listening_match = (socket_local_addr  == seg_local_addr) && 
                          (socket_local_port  == seg_local_port);

        // exit loop if matching
        if( listening_match ) 
        {

#if DEBUG_DEV_NIC_RX
fdid = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->fdid ) );
pid  = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->pid ) );
if( cycle > DEBUG_DEV_NIC_RX )
printk("\n[%s] thread[%x,%x] matching listening socket[%d,%d] / state %s\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state) );
#endif
            break;
        }
    }   // end loop on listening sockets

    // release the lock protecting the list of listening sockets
    remote_busylock_release( rx0_lock_xp );

    // 5. handle TCP segment for a matching listening socket
    if( listening_match )
    {
        // The actions depend on the received segment flags
        // - discard segment for RST or ACK
        // - update socket state & remote IP address, 
        //   register connect request in socket CRQ queue,
        //   and unblock client thread for SYN 
 
        // discard segment if RST flag
        if( seg_rst_set )
        {

#if DEBUG_DEV_NIC_RX
if( cycle > DEBUG_DEV_NIC_RX )
printk("\n[%s] thread[%x,%x] for listening socket[%x,%d] : received RST\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid );
#endif
            return;
        }

        // discard segment if ACK flag
        if( seg_ack_set ) 
        {

#if DEBUG_DEV_NIC_RX
if( cycle > DEBUG_DEV_NIC_RX )
printk("\n[%s] thread[%x,%x] for listening socket[%x,%d] : received ACK\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid );
#endif
            return;
        }

        // SYN flag == CONNECT request / seq_num cannot be wrong
        if( seg_syn_set )
        {
            // build extended pointer on listening socket CRQ
            socket_crqq_xp = XPTR( socket_cxy , &socket_ptr->crqq );

            // try to register request into CRQ queue
            error = socket_put_crq_request( socket_crqq_xp,
                                            seg_remote_addr, 
                                            seg_remote_port,
                                            seg_seq_num,
                                            seg_window );

            if ( error )   // CRQ full
            {

#if DEBUG_DEV_NIC_RX
if( cycle > DEBUG_DEV_NIC_RX )
printk("\n[%s] thread[%x,%x] listening socket[%x,%d] CRQ full => send RST\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid );
#endif
                // make an RST request to R2T queue 
                socket_put_r2t_request( socket_r2tq_xp,
                                        TCP_FLAG_RST,
                                        chdev->channel );
            }
            else          // new connection request registered in CRQ
            {

#if DEBUG_DEV_NIC_RX
if( cycle > DEBUG_DEV_NIC_RX )
printk("\n[%s] thread[%x,%x] for listening socket[%x,%d] : register request in CRQ\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid );
#endif
                // check pending RX_ACCEPT command
                if( (hal_remote_l32(XPTR(socket_cxy,&socket_ptr->rx_valid)) == true) &&
                    (hal_remote_l32(XPTR(socket_cxy,&socket_ptr->rx_cmd)) == CMD_RX_ACCEPT) )
                {
                    // reset rx_valid
                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_valid ), false );
                     
                    // report success to RX client thread
                    dev_nic_unblock_rx_client( socket_xp , CMD_STS_SUCCESS );

#if DEBUG_DEV_NIC_RX
if( cycle > DEBUG_DEV_NIC_RX )
printk("\n[%s] thread[%x,%x] for listening socket[%x,%d] unblock RX client thread\n", 
__FUNCTION__, this->process->pid, this->trdid, pid, fdid );
#endif
                }
            }   // end register request in CRQ
        }   // end if SYN
        
        return;

    }  // end if listening_match

    // 6. no socket found => discard segment

#if DEBUG_DEV_NIC_RX
if( cycle > DEBUG_DEV_NIC_RX )
printk("\n[%s] thread[%x,%x] exit failure : no socket found => discard segment\n",
__FUNCTION__, this->process->pid, this->trdid );
#endif

}  // end dev_nic_rx_handle_tcp_segment()


/////////////////////////////////////////
void dev_nic_rx_server( chdev_t * chdev )
{
    uint8_t       k_buf[2048];          // kernel buffer for one ETH/IP/UDP packet
    uint32_t      pkt_src_addr;         // packet source IP address 
    uint32_t      pkt_dst_addr;         // packet destination IP address
    uint32_t      trsp_protocol;        // transport protocol (TCP / UDP)
    uint32_t      eth_length;           // size of Ethernet packet (bytes)
    uint32_t      ip_length;            // size of IP packet in bytes
    error_t       error;

    thread_t    * this = CURRENT_THREAD;

// check thread can yield
thread_assert_can_yield( this , __FUNCTION__ );

// check chdev direction and type
assert( (chdev->func == DEV_FUNC_NIC) && (chdev->is_rx == true) ,
"illegal chdev type or direction" );

#if DEBUG_DEV_NIC_RX
uint32_t   cycle = (uint32_t)hal_get_cycles();
if( cycle > DEBUG_DEV_NIC_RX )
printk("\n[%s] thread[%x,%x] starts / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, cycle );
#endif

    // get extended pointers on server tread and chdev 
    xptr_t     thread_xp = XPTR( local_cxy , this );
    xptr_t     chdev_xp  = XPTR( local_cxy , chdev );

    while( 1 )
    {
        // call NIC driver to move one packet from NIC_RX queue to kernel buffer
        this->nic_cmd.dev_xp  = chdev_xp;
        this->nic_cmd.type    = NIC_CMD_READ;
        this->nic_cmd.buffer  = k_buf;
        chdev->cmd( XPTR( local_cxy , this ) );

        // get packet length   
        eth_length = this->nic_cmd.status;

        // check success
        if( eth_length == 0 )  // queue empty => block and deschedule 
        {

#if DEBUG_DEV_NIC_RX
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_DEV_NIC_RX < cycle )
printk("\n[%s] thread[%x,%x] NIC_RX_QUEUE empty => blocks on <ISR> / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, cycle );
#endif
            // enable NIC_RX IRQ 
            dev_pic_enable_irq( this->core->lid , chdev_xp );

            // block and deschedule
            thread_block( thread_xp , THREAD_BLOCKED_ISR );
            sched_yield("client blocked on NIC_TX queue full");

            // disable NIC-RX IRQ 
            dev_pic_disable_irq( this->core->lid , chdev_xp );

#if DEBUG_DEV_NIC_RX
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_DEV_NIC_RX < cycle )
printk("\n[%s] thread[%x,%x] resumes / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, cycle );
#endif

        }
        else                 // success => handle packet
        {

#if DEBUG_DEV_NIC_RX
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_DEV_NIC_RX < cycle )
dev_nic_packet_display( false,               // is_tx
                        this->process->pid,
                        this->trdid,
                        cycle,
                        k_buf );
#endif

            // analyse the ETH header
            error = dev_nic_rx_check_eth( k_buf,
                                          &ip_length );

            // discard packet if error reported by Ethernet layer
            if( error )
            {

#if DEBUG_DEV_NIC_RX
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_DEV_NIC_RX < cycle )
printk("\n[%s] thread[%x,%x] discard ETH packet / cycle %d\n", 
__FUNCTION__, this->process->pid, this->trdid, cycle );
#endif
                continue; 
            }

#if (DEBUG_DEV_NIC_RX & 1)
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_DEV_NIC_RX < cycle )
printk("\n[%s] thread[%x,%x] successfully checked ETH packet / cycle %d\n", 
__FUNCTION__, this->process->pid, this->trdid, cycle );
#endif
            // analyse the IP header
            error = dev_nic_rx_check_ip( k_buf + ETH_HEAD_LEN,
                                         ip_length, 
                                         &pkt_src_addr, 
                                         &pkt_dst_addr,
                                         &trsp_protocol );

            // discard packet if error reported by IP layer
            if( error ) 
            {

#if DEBUG_DEV_NIC_RX
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_DEV_NIC_RX < cycle )
printk("\n[%s] thread[%x,%x] discarded IP packet / cycle %d\n", 
__FUNCTION__, this->process->pid, this->trdid, cycle );
#endif
                continue; 
            }

#if (DEBUG_DEV_NIC_RX & 1 )
cycle = (uint32_t)hal_get_cycles();
if( (DEBUG_DEV_NIC_RX < cycle) && (trsp_protocol == PROTOCOL_UDP) )
printk("\n[%s] thread[%x,%x] successfully checked UDP packet / cycle %d\n", 
__FUNCTION__, this->process->pid, this->trdid, cycle );
if( (DEBUG_DEV_NIC_RX < cycle) && (trsp_protocol == PROTOCOL_TCP) )
printk("\n[%s] thread[%x,%x] successfully checked TCP segment / cycle %d\n", 
__FUNCTION__, this->process->pid, this->trdid, cycle );
#endif

            // call relevant transport protocol
            if( trsp_protocol == PROTOCOL_UDP )   
            { 
                dev_nic_rx_handle_udp_packet( chdev,
                                              k_buf + ETH_HEAD_LEN + IP_HEAD_LEN,
                                              ip_length - IP_HEAD_LEN,
                                              pkt_src_addr,
                                              pkt_dst_addr );
            }
            else if ( trsp_protocol == PROTOCOL_TCP)
            {
                dev_nic_rx_handle_tcp_segment( chdev,
                                               k_buf + ETH_HEAD_LEN + IP_HEAD_LEN,
                                               ip_length - IP_HEAD_LEN,
                                               pkt_src_addr,
                                               pkt_dst_addr );
            }
            else // discard packet if unsupported transport protocol
            {
 
#if DEBUG_DEV_NIC_RX
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_DEV_NIC_RX < cycle )
printk("\n[%s] thread[%x,%x] discarded unsupported transport protocol %d\n", 
__FUNCTION__, this->process->pid, this->trdid, trsp_protocol, cycle );
#endif
                continue;
            }
        }
    } // end of while loop
}  // end dev_nic_rx_server()




///////////////////////////////////////////////////////////////////////////////////////////
//              Functions used by the NIC_TX server thread
///////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////
// This static function is called by the dev_nic_tx_build_packet() function.
// It moves one ETH/IP/UDP packet from the kernel buffer identified by the <buffer> and 
// <length> arguments to the NIC_TX_QUEUE identified the <chdev> argument.
// It blocks and deschedules on the BLOCKED_ISR condition if the queue is full.
///////////////////////////////////////////////////////////////////////////////////////////
// @ chdev   : [in] local pointer on NIC_TX chdev.
// @ k_buf   : [in] pointer on a local kernel buffer (2K bytes).
// @ length  : [in] actual Ethernet packet length in bytes.
///////////////////////////////////////////////////////////////////////////////////////////
static void dev_nic_tx_move_packet( chdev_t  * chdev,
                                    uint8_t  * k_buf,
                                    uint32_t   length )
{
    thread_t * this = CURRENT_THREAD;

    // get extended pointers on server tread and chdev 
    xptr_t     thread_xp = XPTR( local_cxy , this );
    xptr_t     chdev_xp  = XPTR( local_cxy , chdev );

// check thread can yield
thread_assert_can_yield( this , __FUNCTION__ );

#if (DEBUG_DEV_NIC_TX & 1)
uint32_t cycle = (uint32_t)hal_get_cycles();
if( DEBUG_DEV_NIC_TX < cycle )
printk("\n[%s] thread[%x,%x] enter / buf %x / length %d / cycle %d\n", 
__FUNCTION__, this->process->pid, this->trdid, k_buf, length, cycle );
#endif

    // initialize WRITE command in server thread descriptor
    this->nic_cmd.dev_xp = chdev_xp;
    this->nic_cmd.type   = NIC_CMD_WRITE;
    this->nic_cmd.buffer = k_buf;
    this->nic_cmd.length = length;

    while( 1 )
    {
        // call driver to move TX packet
        chdev->cmd( thread_xp );

        // exit while if success
        if( this->nic_cmd.status == length )    // exit while & return if success
        {

#if (DEBUG_DEV_NIC_TX & 1)
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_DEV_NIC_TX < cycle )
printk("\n[%s] thread[%x,%x] exit SUCCESS / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, cycle );
#endif
           break; 
        }
        else                                    // block and deschedule if queue full
        {

#if (DEBUG_DEV_NIC_TX & 1)
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_DEV_NIC_TX < cycle )
printk("\n[%s] thread[%x,%x] NIC_TX_QUEUE full => blocks on ISR / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, cycle );
#endif
            // enable NIC_TX IRQ 
            dev_pic_enable_irq( this->core->lid , chdev_xp );

            // TX server thread blocks and deschedules
            thread_block( thread_xp , THREAD_BLOCKED_ISR );
            sched_yield("client blocked on NIC_TX queue full");

            // disable NIC-TX IRQ 
            dev_pic_disable_irq( this->core->lid , chdev_xp );

#if (DEBUG_DEV_NIC_TX & 1)
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_DEV_NIC_TX < cycle )
printk("\n[%s] thread[%x,%x] resumes / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, cycle );
#endif
        }
    }
}  // end dev_nic_tx_move_packet()

///////////////////////////////////////////////////////////////////////////////////////////
// This static function is called by the dev_nic_tx_server() function to build an UDP
// header in the kernel buffer defined by the <k_buf> arguement,  as specified by the 
// <socket_xp> argument. The <length> argument defines the number of bytes in payload.
// It set the "src_port", "dst_port", "total_length" and "checksum" fields in UDP header.
// The payload must be previouly loaded in the pernel buffer.
///////////////////////////////////////////////////////////////////////////////////////////
// @ k_buf      : [in]  pointer on first byte of UDP header in kernel buffer.
// @ socket_xp  : [in]  extended pointer on socket.
// @ length     : [in]  number of bytes in payload.
///////////////////////////////////////////////////////////////////////////////////////////
static void dev_nic_tx_build_udp_header( uint8_t  * k_buf,
                                         xptr_t     socket_xp,
                                         uint32_t   length )
{
    uint16_t   checksum;        // checksum value
    uint32_t   total_length;    // total UDP packet length
    uint32_t   local_addr;      // local IP address
    uint32_t   remote_addr;     // remote IP address
    uint32_t   local_port;      // local port
    uint32_t   remote_port;     // remote port

    // get socket cluster an local pointer
    socket_t * socket_ptr = GET_PTR( socket_xp );
    cxy_t      socket_cxy = GET_CXY( socket_xp );

    // get relevant infos from socket
    local_addr  = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_addr )); 
    remote_addr = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_addr )); 
    local_port  = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_port )); 
    remote_port = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_port )); 

    // compute UDP packet total length
    total_length = length + UDP_HEAD_LEN;

    // set src_port and dst_port in header
    k_buf[0] = local_port >> 8; 
    k_buf[1] = local_port; 
    k_buf[2] = remote_port >> 8; 
    k_buf[3] = remote_port; 

    // set packet length in header
    k_buf[4] = total_length >> 8;
    k_buf[5] = total_length;
    
    // compute UDP packet checksum
    checksum = dev_nic_udp_checksum( k_buf , total_length );

    // set checksum
    k_buf[6] = checksum >> 8;
    k_buf[7] = checksum;

}  // end dev_nic_tx_build_udp_header()

///////////////////////////////////////////////////////////////////////////////////////////
// This static function is called by the dev_nic_tx_server() function. 
// It builds a TCP header in the kernel buffer defined by the <k_buf> argument.
// The payload must have been previouly registered in this buffer.
// The "local_addr", "local_port", "remote_addr", "remote_port", seq_num", "ack_num",
// and "window" fields are obtained from the <socket_xp> argument.
// The <length> argument defines the number of bytes in payload, and the <flags> argument
// defines the flags to be set in TCP header.
///////////////////////////////////////////////////////////////////////////////////////////
// @ k_buf      : [in]  pointer on first byte of TCP header in kernel buffer.
// @ socket_xp  : [in]  extended pointer on socket.
// @ length     : [in]  number of bytes in payload.
// @ flags      : [in]  flags to be set in TCP header.
///////////////////////////////////////////////////////////////////////////////////////////
static void dev_nic_tx_build_tcp_header( uint8_t  * k_buf,
                                         xptr_t     socket_xp,
                                         uint32_t   length,
                                         uint8_t    flags )
{
    uint16_t   checksum;        // global segment checksum
    uint32_t   total_length;    // total UDP packet length
    uint32_t   src_addr;        // local IP address
    uint32_t   dst_addr;        // remote IP address
    uint16_t   src_port;        // local port
    uint16_t   dst_port;        // remote port
    uint32_t   seq_num;         // first byte of segment in TX stream
    uint32_t   ack_num;         // next expected byte in RX stream
    uint32_t   window;          // window of accepted segments in RX stream

    // get socket cluster an local pointer
    socket_t * sock_ptr = GET_PTR( socket_xp );
    cxy_t      sock_cxy = GET_CXY( socket_xp );

    // get relevant infos from socket
    src_addr = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->local_addr )); 
    dst_addr = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->remote_addr )); 
    src_port = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->local_port )); 
    dst_port = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->remote_port )); 
    seq_num  = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->tx_nxt ));
    ack_num  = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->rx_nxt ));
    window   = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->rx_wnd ));

    // compute TCP segment total length
    total_length = length + TCP_HEAD_LEN;

    // set "src_port" and "dst_port" 
    k_buf[0]  = src_port >> 8; 
    k_buf[1]  = src_port; 
    k_buf[2]  = dst_port >> 8; 
    k_buf[3]  = dst_port; 

    // set "seq_num"  
    k_buf[4]  = seq_num >> 24;
    k_buf[5]  = seq_num >> 16;
    k_buf[6]  = seq_num >>  8;
    k_buf[7]  = seq_num;

    // set "ack_num"  
    k_buf[8]  = ack_num >> 24;
    k_buf[9]  = ack_num >> 16;
    k_buf[10] = ack_num >>  8;
    k_buf[11] = ack_num;

    // set "hlen"
    k_buf[12] = 5;

    // set "flags"
    k_buf[13] = flags & 0x3F;

    // set "window"
    k_buf[14] = window >> 8;
    k_buf[15] = window;

    // reset "checksum"
    k_buf[16] = 0;
    k_buf[17] = 0;
    
    // set "urgent_ptr"
    k_buf[18] = 0;
    k_buf[19] = 0;
  
    // compute TCP segment checksum
    checksum = dev_nic_tcp_checksum( k_buf,
                                     total_length,
                                     src_addr,
                                     dst_addr );
    // set "checksum"
    k_buf[16] = checksum >> 8;
    k_buf[17] = checksum;

}  // end dev_nic_tx_build_tcp_header()


///////////////////////////////////////////////////////////////////////////////////////////
// This static function is called by the dev_nic_tx_server() function.
// It builds the IP header in the 20 first bytes of <buffer>.
///////////////////////////////////////////////////////////////////////////////////////////
// @ buffer     : pointer on first byte of IP header in kernel buffer
// @ src_addr   : source IP address.
// @ dst_addr   : destination IP address.
// @ length     : number of bytes in IP packet payload.
///////////////////////////////////////////////////////////////////////////////////////////
static void dev_nic_tx_build_ip_header( uint8_t * buffer,
                                        uint32_t  src_addr,
                                        uint32_t  dst_addr,
                                        uint8_t   protocol,
                                        uint16_t  length )
{
    uint16_t   hcs;

    uint16_t   total = length + IP_HEAD_LEN;

    buffer[0]  = 0x45;         // IPV4 / IHL = 20 bytes  
    buffer[1]  = 0;            // DSCP / ECN
    buffer[2]  = total >> 8;  
    buffer[3]  = total;  

    buffer[4]  = 0x40;         // Don't Fragment
    buffer[5]  = 0; 
    buffer[6]  = 0;
    buffer[7]  = 0; 

    buffer[8]  = 0xFF;         // TTL
    buffer[9]  = protocol;     // transport protocol 
    
    buffer[12] = src_addr >> 24; 
    buffer[13] = src_addr >> 16; 
    buffer[14] = src_addr >> 8; 
    buffer[15] = src_addr; 

    buffer[16] = dst_addr >> 24; 
    buffer[17] = dst_addr >> 16; 
    buffer[18] = dst_addr >> 8;
    buffer[19] = dst_addr; 

    // compute IP header checksum
    hcs = dev_nic_ip_checksum( buffer ); 

    // set checksum 
    buffer[10] = hcs >> 8;
    buffer[11] = hcs; 

}  // end dev_nic_tx_build_ip_header

///////////////////////////////////////////////////////////////////////////////////////////
// This static function is called by the dev_nic_tx_server() function.
// It builds the Ethernet header in the 14 first bytes of <buffer>.
///////////////////////////////////////////////////////////////////////////////////////////
// @ buffer     : pointer on first byte of Ethernet header in kernel buffer
// @ src_mac_54 : two MSB bytes in source MAC address.
// @ src_mac_32 : two MED bytes in source MAC address.
// @ src_mac_10 : two LSB bytes in source MAC address.
// @ dst_mac_54 : two MSB bytes in destination MAC address.
// @ dst_mac_32 : two MED bytes in destination MAC address.
// @ dst_mac_10 : two LSB bytes in destination MAC address.
// @ length     : number of bytes in Ethernet frame payload.
///////////////////////////////////////////////////////////////////////////////////////////
static void dev_nic_tx_build_eth_header( uint8_t * buffer,
                                         uint8_t   src_mac_5,
                                         uint8_t   src_mac_4,
                                         uint8_t   src_mac_3,
                                         uint8_t   src_mac_2,
                                         uint8_t   src_mac_1,
                                         uint8_t   src_mac_0,
                                         uint8_t   dst_mac_5,
                                         uint8_t   dst_mac_4,
                                         uint8_t   dst_mac_3,
                                         uint8_t   dst_mac_2,
                                         uint8_t   dst_mac_1,
                                         uint8_t   dst_mac_0,
                                         uint32_t  length )
{
    buffer[0]  = dst_mac_5;
    buffer[1]  = dst_mac_4;
    buffer[2]  = dst_mac_3;
    buffer[3]  = dst_mac_2;
    buffer[4]  = dst_mac_1;
    buffer[5]  = dst_mac_0;

    buffer[6]  = src_mac_5;
    buffer[7]  = src_mac_4;
    buffer[8]  = src_mac_3;
    buffer[9]  = src_mac_2;
    buffer[10] = src_mac_1;
    buffer[11] = src_mac_0;

    buffer[12] = length >> 8;
    buffer[13] = length;

}  // end dev_nic_tx_build_eth_header()

///////////////////////////////////////////////////////////////////////////////////////////
// This static function is called by the dev_nic_tx_server() function to handle one TX 
// command, or one R2T request, as defined by the <cmd_valid> and <r2t_valid> arguments,
// for the socket identified by the <socket_xp> argument. It builds an ETH/IP/UDP packet 
// or ETH/IP/TCP segment, in the buffer defined by the <k_buf> argument, and registers
// it in the NIC_TX queue defined by the <chdev> argument. 
// For a TCP header, the "seq_num", ack_num", and "window" fiels are defined by the
// "socket.tx_next", "socket.rx_next" and "socket.rx_wnd" fields respectively. 
// It updates the "socket.state", "socket.tx_nxt", "socket.r2tq", and "socket.crqq" 
// The supported TX command types are CONNECT / ACCEPT / SEND / CLOSE.
// fields as required by the command type.
// - For an UDP socket, it reset the "socket.tx_valid" field, and unblock the client
//   thread when the packet has been sent, or when an error must be reported.
// - For a TCP socket, it reset the "socket.tx_valid" field when the segment has been
//   sent, but does not unblocks the client thread, that will be unblocqued by the 
//   NIC_RX thread when the TX command is fully completed.
///////////////////////////////////////////////////////////////////////////////////////////
// To build a packet, it makes the following actions:
// 1) it takes the lock protecting the socket state.
// 2) it get the command arguments from socket descriptor.
// 3) it build an UDP packet or a TCP segment, depending on command type and socket state. 
// 4) it updates the socket state.
// 5) it releases the lock protecting the socket.
// 6) it build the IP header.
// 7) it build the ETH header.
// 8) it copies the packet in the NIC_TX queue.
///////////////////////////////////////////////////////////////////////////////////////////
// @ cmd_state   : [in] TX command valid in socket descriptor.
// @ r2t_valid   : [in] R2T request valid in command descriptor. 
// @ socket_xp   : [in] extended pointer on client socket.  
// @ k_buf       : [in] local pointer on kernel buffer (2 Kbytes).
// @ chdev       : [in] local pointer on NIC_RX chdev.
///////////////////////////////////////////////////////////////////////////////////////////
static void dev_nic_tx_build_packet( bool_t    cmd_valid,
                                     bool_t    r2t_valid,
                                     xptr_t    socket_xp,
                                     uint8_t * k_buf,
                                     chdev_t * chdev )
{
    socket_t  * socket_ptr;
    cxy_t       socket_cxy;
    xptr_t      client_xp;       // extended pointer on client thread  
    uint32_t    cmd_type;        // NIC command type
    uint8_t   * tx_buf;          // local pointer on kernel buffer for payload
    uint32_t    len;             // tx_buf length (bytes)
    uint32_t    todo;            // number of bytes not yet sent
    uint32_t    socket_type;     // socket type (UDP/TCP)
    uint32_t    socket_state;    // socket state        
    xptr_t      socket_lock_xp;  // extended pointer on socket lock
    xptr_t      socket_r2tq_xp;  // extended pointer on R2T queue
    uint32_t    src_ip_addr;     // source IP address
    uint32_t    dst_ip_addr;     // destination IP address
    uint32_t    tx_nxt;          // next sequence number in TX stream
    uint32_t    nbytes;          // number of bytes in UDP/TCP packet payload
    uint8_t   * k_trsp_base;     // pointer on UDP/TCP packet in kernel buffer
    uint32_t    trsp_length;     // length of TCP/UDP packet
    uint8_t     trsp_protocol;   // transport protocol type (UDP/TCP)
    uint8_t     r2t_flags;       // flags defined by one R2T queue request
    bool_t      do_send;         // build & send a packet when true
  
    // get socket cluster and local pointer
    socket_cxy = GET_CXY( socket_xp );
    socket_ptr = GET_PTR( socket_xp );

#if DEBUG_DEV_NIC_TX 
thread_t * this = CURRENT_THREAD;;
uint32_t   cycle = (uint32_t)hal_get_cycles();
uint32_t   fdid  = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->fdid ));
uint32_t   pid   = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->pid ));
if( cycle > DEBUG_DEV_NIC_TX )
printk("\n[%s] thread[%x,%x] enter for socket[%x,%d] : cmd_valid %d / r2t_valid %d / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid, cmd_valid, r2t_valid, cycle );
#endif

    // build extended pointers on socket lock and r2t queue
    socket_lock_xp = XPTR( socket_cxy , &socket_ptr->lock );    
    socket_r2tq_xp = XPTR( socket_cxy , &socket_ptr->r2tq );

    // 1. take lock protecting this socket
    remote_queuelock_acquire( socket_lock_xp );

    // get relevant socket infos
    socket_type  = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->type ));
    socket_state = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->state ));
    src_ip_addr  = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->local_addr ));
    dst_ip_addr  = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->remote_addr ));

    // compute UDP/TCP packet base in kernel buffer
    k_trsp_base = k_buf + ETH_HEAD_LEN + IP_HEAD_LEN;

    // set default values
    do_send     = false;
    trsp_length = 0;
    nbytes      = 0;

    if( cmd_valid )  // handle TX command 
    {
        // 2. get command arguments from socket
        cmd_type  = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->tx_cmd ));
        tx_buf    = hal_remote_lpt( XPTR( socket_cxy , &socket_ptr->tx_buf ));
        len       = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->tx_len ));
        todo      = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->tx_todo ));
        client_xp = hal_remote_l64( XPTR( socket_cxy , &socket_ptr->tx_client ));
        
#if DEBUG_DEV_NIC_TX 
cycle = (uint32_t)hal_get_cycles();
if( cycle > DEBUG_DEV_NIC_TX )
printk("\n[%s] thread[%x,%x] cmd_valid for socket[%x,%d] : %s / %s / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid,
socket_cmd_type_str(cmd_type), socket_state_str(socket_state), cycle );
#endif

        //////////////////////////////////////////////////////////
        // 3. UDP : build UDP packet and update UDP socket state
        if( socket_type == SOCK_DGRAM )        
        {
            trsp_protocol = PROTOCOL_UDP;

            if( socket_state != UDP_STATE_ESTAB )
            {
                // reset tx_valid
                hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ) , false );

                // unblock client thread / report error
                dev_nic_unblock_tx_client( socket_xp , CMD_STS_BADSTATE );
            }
            else
            {
                if( cmd_type == CMD_TX_SEND ) 
                {
                    // compute payload length
                    nbytes = ( PAYLOAD_MAX_LEN < todo ) ? PAYLOAD_MAX_LEN : todo;

                    // move payload from tx_buf to 2 Kbytes kernel buffer
                    memcpy( k_trsp_base + UDP_HEAD_LEN,
                            tx_buf + (len - todo),
                            nbytes );

                    // build UDP header 
                    dev_nic_tx_build_udp_header( k_trsp_base,
                                                 socket_xp,
                                                 nbytes );

                    // update "tx_todo" in socket descriptor 
                    hal_remote_s32( XPTR(socket_cxy , &socket_ptr->tx_todo), todo - nbytes );

                    // send UDP packet 
                    trsp_length = UDP_HEAD_LEN + nbytes;
                    do_send     = true;

#if( DEBUG_DEV_NIC_TX & 1)
cycle = (uint32_t)hal_get_cycles();
if( cycle > DEBUG_DEV_NIC_TX )
printk("\n[%s] thread[%x,%x] socket[%x,%d] UDP packet build / length %d / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, trsp_length , cycle );
#endif
                    if( nbytes == todo )    // last byte sent
                    {
                        // reset tx_valid
                        hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ) , false );

                        // report success to TX client
                        dev_nic_unblock_tx_client( socket_xp , CMD_STS_SUCCESS );
                    }
                }
                else // CONNECT, ACCEPT, or CLOSE commands are illegal for UDP
                {
                    // reset tx_valid
                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ) , false );

                    // report error
                    dev_nic_unblock_tx_client( socket_xp , CMD_STS_BADCMD );
                }
            }
        }  // end UDP

        ///////////////////////////////////////////////////////////
        // 3. TCP : build TCP segment and update TCP socket state
        else if( socket_type == SOCK_STREAM ) 
        {
            trsp_protocol = PROTOCOL_TCP;

            // handle R2T request / initialize r2t_flags
            if( r2t_valid )
            {
                // build extended pointers on r2t queue
                socket_r2tq_xp = XPTR( socket_cxy , &socket_ptr->r2tq );
        
                // get one request from R2T queue 
                remote_buf_get_to_kernel( socket_r2tq_xp , &r2t_flags , 1 );
            }
            else
            {
                r2t_flags = 0;
            }

            ////////////////////////////////
            if( cmd_type == CMD_TX_CONNECT )  // always executed by a TCP client
            {
                if( (socket_state == TCP_STATE_BOUND) ||
                    (socket_state == TCP_STATE_LISTEN) )  // send a SYN segment
                {
                    // initialises socket tx_nxt, and rx_wnd
                    hal_remote_s32(XPTR(socket_cxy , &socket_ptr->tx_nxt), TCP_ISS_CLIENT );
                    hal_remote_s32(XPTR(socket_cxy , &socket_ptr->rx_wnd), TCP_MAX_WINDOW );

                    // build TCP SYN segment
                    dev_nic_tx_build_tcp_header( k_trsp_base,
                                                 socket_xp,
                                                 0,        // length
                                                 TCP_FLAG_SYN );   
                    // send segment
                    trsp_length = TCP_HEAD_LEN;
                    do_send     = true;

#if DEBUG_DEV_NIC_TX 
cycle = (uint32_t)hal_get_cycles();
if( cycle > DEBUG_DEV_NIC_TX )
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s / CONNECT / "
"TCP SYN build / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid,
socket_state_str( socket_state ), cycle );
#endif
                    // update socket.state 
                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
                                          TCP_STATE_SYN_SENT );

                    // update socket.tx_nxt
                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_nxt ),
                                          TCP_ISS_CLIENT + 1 );

                    // reset tx_valid but do not unblock client thread
                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ), false );
                } 
                else                      // report error for all other socket states
                {
                    // reset tx_valid
                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ) , false );
                   
                    // report error
                    dev_nic_unblock_tx_client( socket_xp , CMD_STS_BADSTATE ); 
                }
            }
            ////////////////////////////////////
            else if( cmd_type == CMD_TX_ACCEPT )   // always executed by a TCP server
            {

                if( socket_state == TCP_STATE_SYN_RCVD )  // send a SYN-ACK segment
                {
                    // initialize socket tx_nxt, and rx_wnd
                    hal_remote_s32(XPTR(socket_cxy , &socket_ptr->tx_nxt), TCP_ISS_SERVER );
                    hal_remote_s32(XPTR(socket_cxy , &socket_ptr->rx_wnd), NIC_RX_BUF_SIZE);
                
                    // build TCP ACK-SYN segment
                    dev_nic_tx_build_tcp_header( k_trsp_base,
                                                 socket_xp,
                                                 0,         //  length
                                                 TCP_FLAG_SYN | TCP_FLAG_ACK );
                    // send segment
                    trsp_length = TCP_HEAD_LEN;
                    do_send     = true;

#if DEBUG_DEV_NIC_TX 
cycle = (uint32_t)hal_get_cycles();
if( cycle > DEBUG_DEV_NIC_TX )
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s / ACCEPT / send SYN-ACK / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid,
socket_state_str( socket_state ), cycle );
#endif
                    // update socket.state 
                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ),
                                          TCP_STATE_SYN_RCVD );

                    // update socket.tx_nxt
                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_nxt ),
                                          TCP_ISS_SERVER + 1 );

                    // reset tx_valid but do not unblock client thread
                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ), false );
                }
                else                     // report error in all other socket states
                {
                    // reset tx_valid
                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ), false );

                    // report error to TX client thread
                    dev_nic_unblock_tx_client( socket_xp , CMD_STS_BADSTATE );
                }
            }
            ///////////////////////////////////
            else if( cmd_type == CMD_TX_CLOSE )
            {
                if( (socket_state == TCP_STATE_SYN_RCVD  ) ||
                    (socket_state == TCP_STATE_ESTAB     ) ||
                    (socket_state == TCP_STATE_CLOSE_WAIT) )   // send a FIN-ACK segment
                {
                    // get  "tx_nxt" from socket descriptor
                    tx_nxt = hal_remote_l32( XPTR(socket_cxy , &socket_ptr->tx_nxt ));

                    // compute next state
                    uint32_t state = (socket_state == TCP_STATE_CLOSE_WAIT) ?
                                     TCP_STATE_LAST_ACK : TCP_STATE_FIN_WAIT1;

                    // update socket state 
                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), state );

                    // build TCP FIN segment
                    dev_nic_tx_build_tcp_header( k_trsp_base,
                                                 socket_xp,
                                                 0,         // length
                                                 TCP_FLAG_FIN | TCP_FLAG_ACK );   

                    // update "tx_nxt" in socket descriptor
                    hal_remote_s32( XPTR(socket_cxy , &socket_ptr->tx_nxt), tx_nxt + 1 );

                    // send segment
                    trsp_length = TCP_HEAD_LEN;
                    do_send     = true;

#if DEBUG_DEV_NIC_TX 
cycle = (uint32_t)hal_get_cycles();
if( cycle > DEBUG_DEV_NIC_TX )
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s / CLOSE / send FIN-ACK / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid,
socket_state_str( socket_state ), cycle );
#endif
                    // reset tx_valid but do not unblock client thread
                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ), false );
                }
                else                                 // all other states => signal error
                {

#if DEBUG_DEV_NIC_TX 
cycle = (uint32_t)hal_get_cycles();
if( cycle > DEBUG_DEV_NIC_TX )
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s / CLOSE / error BADSTATE / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid,
socket_state_str( socket_state ), cycle );
#endif
                    // reset tx_valid
                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ), false );

                    // report error 
                    dev_nic_unblock_tx_client( socket_xp , CMD_STS_BADSTATE );
                }
            }
            /////////////////////////////////////
            else if( cmd_type == CMD_TX_SEND )
            {
                if( (socket_state == TCP_STATE_ESTAB     ) ||
                    (socket_state == TCP_STATE_CLOSE_WAIT) )
                {
                    // get  "tx_nxt" from socket descriptor
                    tx_nxt = hal_remote_l32( XPTR(socket_cxy , &socket_ptr->tx_nxt ));

                    // compute actual payload length
                    nbytes = ( PAYLOAD_MAX_LEN < todo ) ? PAYLOAD_MAX_LEN : todo;

                    // compute TCP segment base in kernel buffer
                    k_trsp_base = k_buf + ETH_HEAD_LEN + IP_HEAD_LEN;

                    // move payload to k_buf
                    memcpy( k_trsp_base + TCP_HEAD_LEN,
                            tx_buf + (len - todo),
                            nbytes );

                    // build TCP header 
                    dev_nic_tx_build_tcp_header( k_trsp_base,
                                                 socket_xp,
                                                 nbytes,                       // payload
                                                 TCP_FLAG_ACK | r2t_flags );   // flags

                    // update "tx_todo" in socket descriptor
                    hal_remote_s32( XPTR(socket_cxy , &socket_ptr->tx_todo), todo - nbytes );

                    // update "tx_nxt" in socket descriptor
                    hal_remote_s32( XPTR(socket_cxy , &socket_ptr->tx_nxt), tx_nxt + nbytes );

                    // send TCP segment
                    trsp_length = TCP_HEAD_LEN + nbytes;
                    do_send     = true;

                    if( todo == nbytes )   // last byte sent 
                    {
                        // reset tx_valid when last byte has been sent
                        hal_remote_s32( XPTR(socket_cxy , &socket_ptr->tx_valid), false );
                    }

#if DEBUG_DEV_NIC_TX 
cycle = (uint32_t)hal_get_cycles();
if( cycle > DEBUG_DEV_NIC_TX )
printk("\n[%s] thread[%x,%x] socket[%x,%d] %s / SEND / "
"TCP DATA build / payload %d / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid,
socket_state_str( socket_state ), nbytes, cycle );
#endif
                }
                else  // all other socket states
                {
                    // reset tx_valid
                    hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ), false );

                    // report error to TX client thread
                    dev_nic_unblock_tx_client( socket_xp , CMD_STS_BADSTATE );
                }
            }
            ///////////////////////////////////
            else  // undefined TX command type
            {
                // reset tx_valid
                hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ), false );

                // report error to TX client thread
                dev_nic_unblock_tx_client( socket_xp , CMD_STS_BADCMD );
            }
        }  // end TCP 
    }
    else         // no valid TX command => handle R2T request only
    {
        // get one request from R2T queue 
        remote_buf_get_to_kernel( socket_r2tq_xp , &r2t_flags , 1 );

#if DEBUG_DEV_NIC_TX 
cycle = (uint32_t)hal_get_cycles();
if( cycle > DEBUG_DEV_NIC_TX )
printk("\n[%s] thread[%x,%x] only r2t_valid for socket[%x,%d] / flags %x / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid, r2t_flags, cycle );
#endif

        // build TCP header 
        dev_nic_tx_build_tcp_header( k_trsp_base,
                                     socket_xp,
                                     0,             // payload length
                                     r2t_flags );   // flags
        // send TCP segment
        trsp_protocol = PROTOCOL_TCP;
        trsp_length   = TCP_HEAD_LEN;
        do_send       = true;
    }

    // 4. release the lock protecting the socket
    remote_queuelock_release( socket_lock_xp );

    // return if no packet to send
    if( do_send == false ) return;

    // 5. build IP header
    dev_nic_tx_build_ip_header( k_buf + ETH_HEAD_LEN,
                                src_ip_addr,
                                dst_ip_addr,
                                trsp_protocol,
                                trsp_length );

#if( DEBUG_DEV_NIC_TX & 1)
cycle = (uint32_t)hal_get_cycles();
if( cycle > DEBUG_DEV_NIC_TX )
printk("\n[%s] thread[%x,%x] IP header build / length %d / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, IP_HEAD_LEN + trsp_length , cycle );
#endif

    // 6. build ETH header
    dev_nic_tx_build_eth_header( k_buf,
                                 (uint8_t)DST_MAC_5,
                                 (uint8_t)DST_MAC_4,
                                 (uint8_t)DST_MAC_3,
                                 (uint8_t)DST_MAC_2,
                                 (uint8_t)DST_MAC_1,
                                 (uint8_t)DST_MAC_0,
                                 (uint8_t)SRC_MAC_5,
                                 (uint8_t)SRC_MAC_4,
                                 (uint8_t)SRC_MAC_3,
                                 (uint8_t)SRC_MAC_2,
                                 (uint8_t)SRC_MAC_1,
                                 (uint8_t)SRC_MAC_0,
                                 IP_HEAD_LEN + trsp_length );

#if( DEBUG_DEV_NIC_TX & 1)
cycle = (uint32_t)hal_get_cycles();
if( cycle > DEBUG_DEV_NIC_TX )
printk("\n[%s] thread[%x,%x] ETH header build / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, cycle );
#endif

    // 7. move packet to NIC_TX queue (blocking function)
    dev_nic_tx_move_packet( chdev,
                            k_buf,
                            ETH_HEAD_LEN + IP_HEAD_LEN + trsp_length );

#if DEBUG_DEV_NIC_TX
cycle = (uint32_t)hal_get_cycles();
if( cycle > DEBUG_DEV_NIC_TX )
printk("\n[%s] thread[%x,%x] for socket[%x,%d] moved packet to NIC_TX / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid, cycle );
#endif

}  // end dev_nic_tx_build_packet()


/////////////////////////////////////////
void dev_nic_tx_server( chdev_t * chdev )
{
    uint8_t       k_buf[NIC_KERNEL_BUF_SIZE];  // buffer for one packet

    xptr_t        queue_root_xp;       // extended pointer on sockets list root
    xptr_t        queue_lock_xp;       // extended pointer on lock protecting this list
    xptr_t        socket_xp;           // extended pointer on on registered socket 
    socket_t    * socket_ptr;
    cxy_t         socket_cxy;
    xptr_t        iter_xp;             // iterator for loop on registered sockets
    xlist_entry_t temp_root;           // root of temporary list of sockets
    xptr_t        temp_root_xp;        // extended pointer on temporary list of sockets
    uint32_t      temp_nr;             // number of active registered sockets
    bool_t        cmd_valid;           // TX command valid in socket descriptor
    bool_t        r2t_valid;           // valid R2T request in socket descriptor

    thread_t * this = CURRENT_THREAD;

#if DEBUG_DEV_NIC_TX
uint32_t cycle = (uint32_t)hal_get_cycles();
uint32_t pid;
uint32_t fdid;
if( cycle > DEBUG_DEV_NIC_TX )
printk("\n[%s] thread[%x,%x] starts / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, cycle );
#endif

// check chdev direction and type
assert( (chdev->func == DEV_FUNC_NIC) && (chdev->is_rx == false) ,
"illegal chdev type or direction" );

// check thread can yield
thread_assert_can_yield( this , __FUNCTION__ );

    // build extended pointer on temporary list
    temp_root_xp = XPTR( local_cxy , &temp_root );
                                                         
    // build extended pointer on client sockets queue (lock & root) 
    queue_lock_xp = XPTR( local_cxy , &chdev->wait_lock );
    queue_root_xp = XPTR( local_cxy , &chdev->wait_root );

    while( 1 )  // TX server infinite loop
    {
        // initialize temporary list of registered sockets as empty
        xlist_root_init( temp_root_xp );
        temp_nr = 0;

        // take the lock protecting the client sockets queue
        remote_busylock_acquire( queue_lock_xp );

        // build temporary list of all registered sockets
        if( xlist_is_empty( queue_root_xp ) == false )  
        {
            XLIST_FOREACH( queue_root_xp , iter_xp )
            {
                // get client socket cluster and local pointer
                socket_xp  = XLIST_ELEMENT( iter_xp , socket_t , tx_list );
                socket_ptr = GET_PTR( socket_xp );
                socket_cxy = GET_CXY( socket_xp );

                // register socket in temporary list
                xlist_add_last( temp_root_xp , XPTR( socket_cxy , &socket_ptr->tx_temp ));
                temp_nr++;
            }
        }

        // release the lock protecting the client sockets queue
        remote_busylock_release( queue_lock_xp );

        if( temp_nr > 0 )
        {
            // loop on temporary list
            XLIST_FOREACH( temp_root_xp , iter_xp )
            {
                // get client socket cluster and local pointer
                socket_xp  = XLIST_ELEMENT( iter_xp , socket_t , tx_temp );
                socket_ptr = GET_PTR( socket_xp );
                socket_cxy = GET_CXY( socket_xp );

                // get cmd_valid & t2t_valid from socket descriptor
                cmd_valid = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->tx_valid ));

                // get r2t_valid from socket descriptor
                r2t_valid = (bool_t)remote_buf_status( XPTR( socket_cxy , &socket_ptr->r2tq ));

                // test if socket is active
                if( cmd_valid || r2t_valid )  // active socket 
                {

#if DEBUG_DEV_NIC_TX
cycle = (uint32_t)hal_get_cycles();
pid  = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->pid ));
fdid = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->fdid ));
if( cycle > DEBUG_DEV_NIC_TX )
printk("\n[%s] thread[%x,%x] found socket[%x,%d] / cmd_valid %d / r2t_valid %d / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, pid, fdid, cmd_valid, r2t_valid, cycle );
#endif
                    // build and send one packet/segment for this socket
                    dev_nic_tx_build_packet( cmd_valid,
                                             r2t_valid,
                                             socket_xp,
                                             k_buf,
                                             chdev );
#if DEBUG_DEV_NIC_TX
cycle = (uint32_t)hal_get_cycles();
if( cycle > DEBUG_DEV_NIC_TX )
dev_nic_packet_display( true,                // is_tx
                        this->process->pid,
                        this->trdid,
                        cycle,
                        k_buf );
#endif
                }
                else                          // inactive socket
                {
                   temp_nr--;
                }
            }  // end loop on temporary list
        }

        // block & deschedule if no active socket found in current iteration
        if( temp_nr == 0 )
        {
 
#if DEBUG_DEV_NIC_TX
cycle = (uint32_t)hal_get_cycles();
if( cycle > DEBUG_DEV_NIC_TX )
printk("\n[%s] thread[%x,%x] no active socket => blocks on <CLIENT> / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, cycle );
#endif

            // block and deschedule
            thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_CLIENT );
            sched_yield( "waiting client" );

#if DEBUG_DEV_NIC_TX
cycle = (uint32_t)hal_get_cycles();
if( cycle > DEBUG_DEV_NIC_TX )
printk("\n[%s] thread[%x,%x] resumes / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, cycle );
#endif
        }
    }   // end infinite while loop
}  // end dev_nic_tx_server()


/////////////////////////////////////////////
void dev_nic_packet_display( bool_t    is_tx,
                             pid_t     thread_pid,
                             trdid_t   thread_trdid,
                             uint32_t  cycle,
                             uint8_t * buf )
{
    // get ETH header fields
    uint64_t eth_dst_mac = ((uint64_t)buf[5]  << 40) |
                           ((uint64_t)buf[4]  << 32) |
                           ((uint64_t)buf[3]  << 24) |
                           ((uint64_t)buf[2]  << 16) |
                           ((uint64_t)buf[1]  <<  8) |
                           ((uint64_t)buf[0]       ) ;

    uint64_t eth_src_mac = ((uint64_t)buf[11] << 40) |
                           ((uint64_t)buf[10] << 32) |
                           ((uint64_t)buf[9]  << 24) |
                           ((uint64_t)buf[8]  << 16) |
                           ((uint64_t)buf[7]  <<  8) |
                           ((uint64_t)buf[6]       ) ;

    uint16_t eth_length  = ((uint16_t)buf[12] <<  8) |
                           ((uint16_t)buf[13]      ) ;

    // get IP header fields
    uint8_t  ip_version  = buf[14];
    uint8_t  ip_tos      = buf[15];
    uint16_t ip_length   = ((uint16_t)buf[16] << 8) |
                           ((uint16_t)buf[17]     ) ;

    uint16_t ip_ident    = ((uint16_t)buf[18] << 8) |
                           ((uint16_t)buf[19]     ) ;
    uint16_t ip_offset   = ((uint16_t)buf[20] << 8) |
                           ((uint16_t)buf[21]     ) ;

    uint8_t  ip_ttl      = buf[22];
    uint8_t  ip_protocol = buf[23];
    uint16_t ip_checksum = ((uint16_t)buf[24] << 8) |
                           ((uint16_t)buf[25]     ) ;

    uint32_t ip_src_addr = ((uint32_t)buf[26] << 24) |
                           ((uint32_t)buf[27] << 16) |
                           ((uint32_t)buf[28] <<  8) |
                           ((uint32_t)buf[29]      ) ;

    uint32_t ip_dst_addr = ((uint32_t)buf[30] << 24) |
                           ((uint32_t)buf[31] << 16) |
                           ((uint32_t)buf[32] <<  8) |
                           ((uint32_t)buf[33]      ) ;
    
    // get pointers on TXT0 chdev
    xptr_t    txt0_xp  = chdev_dir.txt_tx[0];
    cxy_t     txt0_cxy = GET_CXY( txt0_xp );
    chdev_t * txt0_ptr = GET_PTR( txt0_xp );

    // get extended pointer on remote TXT0 chdev lock
    xptr_t  lock_xp = XPTR( txt0_cxy , &txt0_ptr->wait_lock );

    // get TXT0 lock 
    remote_busylock_acquire( lock_xp );

    if( is_tx )
    {
        nolock_printk("\n*** NIC_TX server thread[%x,%x] send packet / cycle %d\n",
        thread_pid, thread_trdid, cycle );
    }
    else
    {
        nolock_printk("\n*** NIC_RX server thread[%x,%x] get packet / cycle %d\n",
        thread_pid, thread_trdid, cycle );
    }

    nolock_printk("\n***** ETH header *****\n");
    nolock_printk(" - dst_mac  [6] = %l\n" , eth_dst_mac );
    nolock_printk(" - src_mac  [6] = %l\n" , eth_src_mac );
    nolock_printk(" - length   [2] = %d\n" , (uint32_t)eth_length );
    nolock_printk("***** IP  header *****\n");
    nolock_printk(" - version  [1] = %x\n" , (uint32_t)ip_version );
    nolock_printk(" - tos      [1] = %x\n" , (uint32_t)ip_tos );
    nolock_printk(" - length   [2] = %d\n" , (uint32_t)ip_length );
    nolock_printk(" - ident    [2] = %x\n" , (uint32_t)ip_ident  );
    nolock_printk(" - offset   [2] = %x\n" , (uint32_t)ip_offset );
    nolock_printk(" - ttl      [1] = %x\n" , (uint32_t)ip_ttl );
    nolock_printk(" - protocol [1] = %x\n" , (uint32_t)ip_protocol );
    nolock_printk(" - checksum [2] = %x\n" , (uint32_t)ip_checksum );
    nolock_printk(" - src_addr [4] = %x\n" , (uint32_t)ip_src_addr );
    nolock_printk(" - dst_addr [4] = %x\n" , (uint32_t)ip_dst_addr );

    // get UDP / TCP fields
    if ( ip_protocol == PROTOCOL_UDP )
    {
        uint16_t udp_src_port = ((uint16_t)buf[34] << 8) |
                                ((uint16_t)buf[35]     ) ;
        uint16_t udp_dst_port = ((uint16_t)buf[36] << 8) |
                                ((uint16_t)buf[37]     ) ;

        nolock_printk("***** UDP header *****\n");
        nolock_printk(" - src_port [2] = %d\n" , (uint32_t)udp_src_port );
        nolock_printk(" - dst_port [2] = %d\n" , (uint32_t)udp_dst_port );
    }
    else if( ip_protocol == PROTOCOL_TCP )
    {
        uint16_t tcp_src_port = ((uint16_t)buf[34] << 8) |
                                ((uint16_t)buf[35]     ) ;
        uint16_t tcp_dst_port = ((uint16_t)buf[36] << 8) |
                                ((uint16_t)buf[37]     ) ;

        uint32_t tcp_seq_num  = ((uint32_t)buf[38] << 24) |
                                ((uint32_t)buf[39] << 16) |
                                ((uint32_t)buf[40] <<  8) |
                                ((uint32_t)buf[41]      ) ;

        uint32_t tcp_ack_num  = ((uint32_t)buf[42] << 24) |
                                ((uint32_t)buf[43] << 16) |
                                ((uint32_t)buf[44] <<  8) |
                                ((uint32_t)buf[45]      ) ;

        uint8_t  tcp_hlen      = buf[46];
        uint8_t  tcp_flags     = buf[47];
        uint16_t tcp_window    = ((uint16_t)buf[48] << 8) |
                                 ((uint16_t)buf[49]     ) ;

        uint16_t tcp_checksum  = ((uint16_t)buf[50] << 8) |
                                 ((uint16_t)buf[51]     ) ;
        uint16_t tcp_urgent    = ((uint16_t)buf[52] << 8) |
                                 ((uint16_t)buf[53]     ) ;

        nolock_printk("***** TCP header *****\n");
        nolock_printk(" - src_port [2] = %x\n" , (uint32_t)tcp_src_port );
        nolock_printk(" - dst_port [2] = %x\n" , (uint32_t)tcp_dst_port );
        nolock_printk(" - seq_num  [4] = %x\n" , (uint32_t)tcp_seq_num );
        nolock_printk(" - ack_num  [4] = %x\n" , (uint32_t)tcp_ack_num );
        nolock_printk(" - hlen     [1] = %d\n" , (uint32_t)tcp_hlen );
        nolock_printk(" - flags    [1] = %x\n" , (uint32_t)tcp_flags );
        nolock_printk(" - window   [2] = %x\n" , (uint32_t)tcp_window );
        nolock_printk(" - checksum [2] = %x\n" , (uint32_t)tcp_checksum );
        nolock_printk(" - urgent   [2] = %x\n" , (uint32_t)tcp_urgent );
    }
    else
    {
        nolock_printk("!!!!! undefined transport protocol !!!!!\n");
    }

    // release TXT0 lock 
    remote_busylock_release( lock_xp );

}  // end dev_nic_packet_display()

