Index: trunk/kernel/kern/ksocket.c
===================================================================
--- trunk/kernel/kern/socket.h	(revision 657)
+++ trunk/kernel/kern/ksocket.c	(revision 662)
@@ -1,10 +1,10 @@
 /*
- * socket.h - socket descriptor and API definition.
- * 
- * Authors  Alain Greiner    (2016,2017,2018,2019,2020)
+ * ksocket.c - kernel socket API implementation.
+ *
+ * Authors  Alain Greiner   (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
  *
- * This file is part of ALMOS-MKH
+ * This file is part of ALMOS-MKH.
  *
  * ALMOS-MKH is free software; you can redistribute it and/or modify it
@@ -18,200 +18,2007 @@
  *
  * You should have received a copy of the GNU General Public License
- * along with ALMOS-MKH; if not, write to the Free Software Foundation,
+ * along with ALMOS-MKH.; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#ifndef _SOCKET_H_
-#define _SOCKET_H_
-
 #include <kernel_config.h>
 #include <hal_kernel_types.h>
-#include <xlist.h>
+#include <hal_remote.h>
+#include <hal_uspace.h>
+#include <shared_socket.h>
+#include <process.h>
 #include <remote_buf.h>
-#include <remote_busylock.h>
-
-/****************************************************************************************
- * This defines the three commands that can be requested by a client thread to
- * a TX server thread. These commands are registered in the socket descriptor.
- ****************************************************************************************/
-
-typedef enum sock_cmd_e
-{
-    SOCKET_TX_CONNECT = 20,   /*! request 3 steps handshake (TCP socket only)           */
-    SOCKET_TX_SEND    = 21,   /*! request to send data      (TCP or UDP socket)         */
-    SOCKET_TX_CLOSE   = 22,   /*! request 3 steps handshake (TCP socket only)           */
+#include <bits.h>
+#include <printk.h>
+#include <kmem.h>
+#include <thread.h>
+#include <vfs.h>
+#include <ksocket.h>
+#include <dev_nic.h>
+
+//////////////////////////////////////////////////////////////////////////////////////
+// Extern global variables
+//////////////////////////////////////////////////////////////////////////////////////
+
+extern chdev_directory_t  chdev_dir;         // allocated in kernel_init.c
+
+///////////////////////////////////////////
+char * socket_domain_str( uint32_t domain )
+{
+    switch( domain )
+    {
+        case AF_INET         : return "INET";
+        case AF_LOCAL        : return "LOCAL";
+
+        default              : return "undefined";
+    }
 }
-sock_cmd_t;
-
-/***************************************************************************************** 
- * This structure defines a socket descriptor. In order to parallelize the transfers,
- * the set of all registered sockets is split in several subsets.
- * The number of subsets is the number of  NIC channels. 
- * The distribution key is computed from the (remote_addr/remote_port) couple.
- * This computation is done by the NIC hardware for RX packets, 
- * and by the dev_nic_connect() function for the TX packets.
- *
- * A socket is attached to the NIC_TX[channel] & NIC_RX[channel] chdevs.
- * Each socket descriptor allows the TX and TX server threads to access various buffers:
- * - the user "send" buffer contains the data to be send by the TX server thread.
- * - the kernel "receive" buffer contains the data received by the RX server thread.
- * - the kernel "r2t" buffer allows the RX server thread to make direct requests
- *   to the associated TX server (to implement the TCP 3 steps handshake).
- *
- * The synchronisation mechanism between the clients threads and the servers threads
- * is different for TX and RX transfers:
- *
- * 1) For a TX transfer, it can exist only one client thread for a given socket,
- *    the transfer is always initiated by the local process, and all TX commands
- *    (CONNECT/SEND/CLOSE) are blocking for the client thread. The user buffer is
- *    used by TCP to handle retransmissions when required.in case of re
- *    The client thread registers the command in the thread descriptor, registers itself
- *    in the socket descriptor, unblocks the TX server thread from the BLOCKED_CLIENT 
- *    condition, blocks itself on the BLOCKED_IO condition, and deschedules. 
- *    When the command is completed, the TX server thread unblocks the client thread.
- *    The TX server blocks itself on the BLOCKED_CLIENT condition, when there is no
- *    pending commands and the R2T queue is empty. It is unblocked when a client
- *    register a new command, or when the TX server thread register a mew request
- *    in the R2T queue.
- *    The tx_valid flip-flop is SET by the client thread to signal a valid command. 
- *    It is RESET by the server thread when the command is completed: For a SEND, 
- *    all bytes have been sent (UDP) or acknowledged (TCP).
- *
- * 2) For an RX transfer, it can exist only one client thread for a given socket,
- *    but the transfer is initiated by the remote process, and the  RECV command
- *    is not really blocking: the data can arrive before the local RECV command is
- *    executed, and the server thread does not wait to receive all requested data
- *    to deliver data to client thread. Therefore each socket contains a receive
- *    buffer (rx_buf) handled as a single-writer/single-reader fifo.
- *    The client thread consumes data from the rx_buf when possible. It blocks on the
- *    BLOCKED_IO condition and deschedules when the rx_buf is empty.
- *    It is unblocked by the RX server thread when new data is available in the rx_buf.
- *    The RX server blocks itself on the BLOCKED_ISR condition When the NIC_RX packets
- *    queue is empty. It is unblocked by the hardware when new packets are available.
- * 
- * Note : the socket domains and types are defined in the "shared_socket.h" file.
- ****************************************************************************************/
-
-typedef enum udp_socket_state_e
-{
-    UDP_STATE_UNBOUND   = 0,
-    UDP_STATE_BOUND     = 1,
-    UDP_STATE_CONNECT   = 2,
+   
+///////////////////////////////////////
+char * socket_type_str( uint32_t type )
+{
+    switch( type )
+    {
+        case SOCK_DGRAM         : return "UDP";
+        case SOCK_STREAM        : return "TCP";
+
+        default                 : return "undefined";
+    }
 }
-udp_socket_state_t;
-
-typedef enum tcp_socket_state_e
-{
-    TCP_STATE_UNBOUND    = 10,
-    TCP_STATE_BOUND      = 11,
-    TCP_STATE_LISTEN     = 12,
-    TCP_STATE_SYN_SENT   = 13,
-    TCP_STATE_SYN_RCVD   = 14,
-    TCP_STATE_ESTAB      = 15,
-    TCP_STATE_FIN_WAIT1  = 16,
-    TCP_STATE_FIN_WAIT2  = 17,
-    TCP_STATE_CLOSING    = 18,
-    TCP_STATE_TIME_WAIT  = 19,
-    TCP_STATE_CLOSE_WAIT = 20,
-    TCP_STATE_LAST_ACK   = 21,
+   
+/////////////////////////////////////////
+char * socket_state_str( uint32_t state )
+{
+    switch( state )
+    {
+        case UDP_STATE_UNBOUND    : return "UDP_UNBOUND";
+        case UDP_STATE_BOUND      : return "UDP_BOUND";
+        case UDP_STATE_ESTAB      : return "UDP_ESTAB";
+
+        case TCP_STATE_UNBOUND    : return "TCP_UNBOUND";
+        case TCP_STATE_BOUND      : return "TCP_BOUND";
+        case TCP_STATE_LISTEN     : return "TCP_LISTEN";
+        case TCP_STATE_SYN_SENT   : return "TCP_SYN_SENT";
+        case TCP_STATE_SYN_RCVD   : return "TCP_SYN_RCVD";
+        case TCP_STATE_ESTAB      : return "TCP_ESTAB";
+        case TCP_STATE_FIN_WAIT1  : return "TCP_FIN_WAIT1";
+        case TCP_STATE_FIN_WAIT2  : return "TCP_FIN_WAIT2";
+        case TCP_STATE_CLOSING    : return "TCP_CLOSING";
+        case TCP_STATE_TIME_WAIT  : return "TCP_TIME_WAIT";
+        case TCP_STATE_CLOSE_WAIT : return "TCP_CLOSE_WAIT";
+        case TCP_STATE_LAST_ACK   : return "TCP_LAST_ACK";
+        case TCP_STATE_CLOSED     : return "TCP_CLOSED";
+
+        default                   : return "undefined";
+    }
 }
-tcp_socket_state_t;
-
-typedef struct socket_s
-{
-    remote_busylock_t lock;          /*! lock protecting socket state                  */
-    uint32_t          domain;        /*! domain : AF_LOCAL / AF_INET                   */
-    uint32_t          type;          /*! type : SOCK_DGRAM / SOCK_STREAM               */
-    pid_t             pid;           /*! owner process identifier                      */
-    uint32_t          state;         /*! see above                                     */
-    uint32_t          local_addr;    /*! local  socket IP address                      */
-    uint32_t          remote_addr;   /*! remote socket IP address                      */
-    uint32_t          local_port;    /*! local  socket port number                     */
-    uint32_t          remote_port;   /*! remote socket port number                     */
-    uint32_t          nic_channel;   /*! derived from (remote_addr,remote_port)        */
-
-    xlist_entry_t     tx_list;       /*! sockets attached to same NIC_TX chdev         */
-    xptr_t            tx_client;     /*! extended pointer on TX client thread          */
-    sock_cmd_t        tx_cmd;        /*! command type (CONNECT/SEND/CLOSE)             */
-    uint8_t         * tx_buf;        /*! pointer on user buffer in user space          */
-    uint32_t          tx_len;        /*! number of bytes to be sent in command         */
-    uint32_t          tx_todo;       /*! number of bytes not yet sent                  */
-    uint32_t          tx_error;      /*! signal a TX command error                     */
-
-    xlist_entry_t     rx_list;       /*! sockets attached to same NIC_RX chdev         */
-    xptr_t            rx_client;     /*! extended pointer on RX client thread          */
-    remote_buf_t      rx_buf;        /*! embedded receive buffer descriptor            */
-
-    remote_buf_t      r2tq;          /*! RX_to_TX requests queue descriptor            */
-
-    remote_buf_t      crqq;          /*! connection requests queue descriptor          */         
-    /* the following fields defines the TCB used for a TCP connection                  */
-
-    uint32_t          tx_una;        /*! first unack byte in TX_data stream            */
-    uint32_t          tx_nxt;        /*! next byte to send in TX_data stream           */
-    uint32_t          tx_wnd;        /*! number of acceptable bytes in TX_data stream  */
-
-    uint32_t          rx_nxt;        /*! next expected byte in RX_data stream          */
-    uint32_t          rx_wnd;        /*! number of acceptable bytes in RX_data stream  */
-    uint32_t          rx_irs;        /*! initial sequence number in RX_data stream     */
+
+///////////////////////////////////////////
+char * socket_cmd_type_str( uint32_t type )
+{
+    switch( type )
+    {
+        case CMD_TX_CONNECT  : return "TX_CONNECT";
+        case CMD_TX_ACCEPT   : return "TX_ACCEPT";
+        case CMD_TX_CLOSE    : return "TX_CLOSE";
+        case CMD_TX_SEND     : return "TX_SEND";
+
+        case CMD_RX_ACCEPT   : return "RX_ACCEPT";
+        case CMD_RX_RECV     : return "RX_RECV";
+        
+        default                 : return "undefined";
+    }
 }
-socket_t;
-
-/****************************************************************************************
- * This function returns a printable string for a client_to_tx_server command type.
- ****************************************************************************************
- * type   :  SOCKET_TX_CONNECT / SOCKET_TX_SEND / SOCKET_TX_CLOSE 
- ***************************************************************************************/
-char * socket_cmd_str( uint32_t type );
-
-/****************************************************************************************
- * This function returns a printable string for an UDP or TCP socket state.
- ****************************************************************************************
- * state  :  UDP_STATE_*** / TCP_STATE*** 
- ***************************************************************************************/
-char * socket_state_str( uint32_t state );
-
-/****************************************************************************************
- * This function allocates memory in the cluster defined by the <cxy> argument for a
- * socket descriptor defined by the <domain> and <type> aruments, for the associated
- * file descriptor, and for the various kernel buffers descriptors contained in the
- * socket descriptor : "rx_buf" (receive buffer), "r2tq" (RX to RX requests queue), and 
- * "crqq" (connect requests queue). It initialises the socket desccriptor static fields,
- * (other than local_addr, local_port, remote_addr, remote_port).
- * It registers the file descriptor in the reference process fd_array[], set the socket
- * the socket state to UNBOUND, and returns the file identifier in the <fdid> argument.
- ****************************************************************************************
- * @ cxy     : [in]  target cluster identifier.
- * @ domain  : [in]  socket protocol family (must be AF_INET).
- * @ type    : [in]  socket type (SOCK_DGRAM / SOCK_STREAM).
- * @ socket  : [out] buffer for a local pointer on created socket.
- * @ fdid    : [out] buffer for file identifier.
- * @ return 0 if success / return -1 if failure (no memory).
- ***************************************************************************************/
-error_t socket_create( cxy_t              cxy,
-                       uint32_t           domain,
-                       uint32_t           type,
-                       struct socket_s ** socket,
-                       uint32_t         * fdid );
-                       
-/****************************************************************************************
- * This functions releases all memory allocated to a socket identified by the <fdid>
- * argument. This include the file descriptor, the socket descriptor, and all buffers
- * referenced by the socket descriptor.
- ****************************************************************************************
- * @ fdid   : [in]  file descriptor index.
- ***************************************************************************************/
-void socket_destroy( uint32_t fdid );
-
-/****************************************************************************************
- * This functions registers the socket defined by the <socket_xp> argument into the
- * lists of sockets attached to the NIC_TX and NIC_TX chdevs identified by the
- * <nic_channel> argument.
- ****************************************************************************************
- * @ socket_xp   : [in]  extended pointer on socket descriptor
- * @ nic_channel : [in]  NIC channel index.
- ***************************************************************************************/
-void socket_link_to_servers( xptr_t    socket_xp,
-                             uint32_t  nic_channel );
+   
+///////////////////////////////////////////
+char * socket_cmd_sts_str( uint32_t sts )
+{
+    switch( sts )
+    {
+        case CMD_STS_SUCCESS  : return "TX_CONNECT";
+        case CMD_STS_EOF      : return "EOF";
+        case CMD_STS_RST      : return "RST";
+        case CMD_STS_BADACK   : return "BADACK";
+        case CMD_STS_BADSTATE : return "BADSTATE";
+        case CMD_STS_BADCMD   : return "BADCMD";
+        
+        default               : return "undefined";
+    }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// This static function registers the socket defined by the <socket_xp> argument into
+// the lists of sockets attached to the relevant NIC_TX and NIC_TX chdevs identified
+// by the <channel> argument, and update the channel field in socket descriptor.
+/////////////////////////////////////////////////////////////////////////////////////////
+// @ socket_xp   : [in]  extended pointer on socket descriptor.
+// @ channel     : [in]  NIC channel index.
+/////////////////////////////////////////////////////////////////////////////////////////
+static void socket_link_to_servers( xptr_t   socket_xp,
+                                    uint32_t channel )
+{
+    cxy_t      socket_cxy = GET_CXY( socket_xp );
+    socket_t * socket_ptr = GET_PTR( socket_xp );
+
+#if DEBUG_SOCKET_LINK
+thread_t  * this        = CURRENT_THREAD;
+process_t * process     = this->process;
+pid_t       socket_pid  = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->pid ));
+fdid_t      socket_fdid = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->pid ));
+uint32_t   cycle        = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_LINK < cycle )
+printk("\n[%s] thread[%x,%x] enter for socket[%x,%d] / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, socket_pid, socket_fdid, cycle ); 
+#endif
+
+    // get pointers on NIC_TX[channel] chdev
+    xptr_t    tx_chdev_xp  = chdev_dir.nic_tx[channel];
+    chdev_t * tx_chdev_ptr = GET_PTR( tx_chdev_xp );
+    cxy_t     tx_chdev_cxy = GET_CXY( tx_chdev_xp );
+
+    // build various TX extended pointers 
+    xptr_t    tx_root_xp = XPTR( tx_chdev_cxy , &tx_chdev_ptr->wait_root );
+    xptr_t    tx_lock_xp = XPTR( tx_chdev_cxy , &tx_chdev_ptr->wait_lock );
+    xptr_t    tx_list_xp = XPTR( socket_cxy   , &socket_ptr->tx_list );
+
+    // get pointers on NIC_RX[channel] chdev
+    xptr_t    rx_chdev_xp  = chdev_dir.nic_rx[channel];
+    chdev_t * rx_chdev_ptr = GET_PTR( rx_chdev_xp );
+    cxy_t     rx_chdev_cxy = GET_CXY( rx_chdev_xp );
+
+    // build various RX extended pointers
+    xptr_t    rx_root_xp = XPTR( rx_chdev_cxy , &rx_chdev_ptr->wait_root );
+    xptr_t    rx_lock_xp = XPTR( rx_chdev_cxy , &rx_chdev_ptr->wait_lock );
+    xptr_t    rx_list_xp = XPTR( socket_cxy   , &socket_ptr->rx_list );
+
+    // register socket in the NIC_TX[channel] chdev clients queue
+    remote_busylock_acquire( tx_lock_xp );
+    xlist_add_last( tx_root_xp , tx_list_xp );
+    remote_busylock_release( tx_lock_xp );
+
+    // register socket in the NIC_RX[channel] chdev clients queue
+    remote_busylock_acquire( rx_lock_xp );
+    xlist_add_last( rx_root_xp , rx_list_xp );
+    remote_busylock_release( rx_lock_xp );
+
+#if DEBUG_SOCKET_LINK
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_LINK < cycle )
+printk("\n[%s] thread[%x,%x] linked socket[%x,%d] to channel %d / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, socket_pid, socket_fdid, channel, cycle );
+#endif
+
+}  // end socket_link_to_servers()
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// This function removes the socket defined by the <socket_xp> argument from the
+// lists of sockets attached to the relevant NIC_TX and NIC_TX chdevs.
+/////////////////////////////////////////////////////////////////////////////////////////
+// @ socket_xp   : [in]  extended pointer on socket descriptor
+/////////////////////////////////////////////////////////////////////////////////////////
+static void socket_unlink_from_servers( xptr_t socket_xp )
+{
+    cxy_t      socket_cxy = GET_CXY( socket_xp );
+    socket_t * socket_ptr = GET_PTR( socket_xp );
+
+#if DEBUG_SOCKET_LINK
+thread_t  * this        = CURRENT_THREAD;
+process_t * process     = this->process;
+pid_t       socket_pid  = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->pid ));
+fdid_t      socket_fdid = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->pid ));
+uint32_t   cycle        = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_LINK < cycle )
+printk("\n[%s] thread[%x,%x] enter for socket[%x,%d] / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, socket_pid, socket_fdid, cycle ); 
+#endif
+
+    // get NIC channel
+    uint32_t channel = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->nic_channel ));
+
+    // get pointers on NIC_TX[channel] chdev
+    xptr_t    tx_chdev_xp  = chdev_dir.nic_tx[channel];
+    chdev_t * tx_chdev_ptr = GET_PTR( tx_chdev_xp );
+    cxy_t     tx_chdev_cxy = GET_CXY( tx_chdev_xp );
+
+    // build various TX extended pointers 
+    xptr_t    tx_lock_xp = XPTR( tx_chdev_cxy , &tx_chdev_ptr->wait_lock );
+    xptr_t    tx_list_xp = XPTR( socket_cxy   , &socket_ptr->tx_list );
+
+    // get pointers on NIC_RX[channel] chdev
+    xptr_t    rx_chdev_xp  = chdev_dir.nic_rx[channel];
+    chdev_t * rx_chdev_ptr = GET_PTR( rx_chdev_xp );
+    cxy_t     rx_chdev_cxy = GET_CXY( rx_chdev_xp );
+
+    // build various RX extended pointers
+    xptr_t    rx_lock_xp = XPTR( rx_chdev_cxy , &rx_chdev_ptr->wait_lock );
+    xptr_t    rx_list_xp = XPTR( socket_cxy   , &socket_ptr->rx_list );
+
+    // remove socket from the NIC_TX[channel] chdev clients queue
+    remote_busylock_acquire( tx_lock_xp );
+    xlist_unlink( tx_list_xp );
+    remote_busylock_release( tx_lock_xp );
+
+    // remove socket from the NIC_RX[channel] chdev clients queue
+    remote_busylock_acquire( rx_lock_xp );
+    xlist_unlink( rx_list_xp );
+    remote_busylock_release( rx_lock_xp );
+
+#if DEBUG_SOCKET_LINK
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_LINK < cycle )
+printk("\n[%s] thread[%x,%x] unlinked socket [%x,%d] / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, socket_pid, socket_fdid, cycle ); 
+#endif
+
+}  // end socket_unlink_from_servers()
+        
+/////////////////////////////////////////////////////////////////////////////////////////
+// This static function is called by the socket_build() and socket_accept() functions.
+// It allocates memory in cluster defined by the <cxy> argument for all structures
+// associated to a socket: file descriptor, socket descriptor, RX buffer, R2T queue,
+// and CRQ queue. It allocates an fdid, and register it in the process fd_array.
+// It initialise the  the socket desccriptor static fields, other than local_addr, 
+// local_port, remote_addr, remote_port), and set the socket state to UNBOUND.
+// It returns the local pointer on socket descriptor and the fdid value in buffers
+// defined by the <socket_ptr> & <fdid_ptr> arguments.
+/////////////////////////////////////////////////////////////////////////////////////////
+// @ cxy        : [in]  target cluster fo socket & file descriptors.
+// @ domain     : [in]  socket domain.
+// @ type       : [in]  socket type.
+// @ socket_ptr : [out] local pointer on buffer for socket pointer.
+// @ fdid_ptr   : [out] local pointer on buffer for fdid value.
+// # return 0 if success / return -1 if no memory.
+/////////////////////////////////////////////////////////////////////////////////////////
+static error_t socket_create( cxy_t       cxy,
+                              uint32_t    domain,
+                              uint32_t    type,
+                              socket_t ** socket_ptr,
+                              uint32_t  * fdid_ptr )
+{
+    uint32_t    fdid;
+
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
+
+    kmem_req_t     req;
+    socket_t     * socket;
+    vfs_file_t   * file;
+    uint32_t       state;
+    error_t        error;
+
+#if DEBUG_SOCKET_CREATE
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_CREATE < cycle )
+printk("\n[%s] thread[%x,%x] enter / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, cycle ); 
+#endif
+   
+    // allocate memory for socket descriptor 
+    req.type   = KMEM_KCM;
+    req.order  = bits_log2( sizeof(socket_t) );
+    req.flags  = AF_ZERO;
+    socket     = kmem_remote_alloc( cxy , &req );
+
+    if( socket == NULL )
+    {
+        printk("\n[ERROR] in %s : cannot allocate socket descriptor / thread[%x,%x]\n",
+        __FUNCTION__, process->pid, this->trdid );
+        return -1;
+    }
+
+    // allocate memory for rx_buf buffer
+    error = remote_buf_init( XPTR( cxy , &socket->rx_buf ),
+                             NIC_RX_BUF_SIZE );
+
+    if( error )
+    {
+        printk("\n[ERROR] in %s : cannot allocate rx_buf / thread[%x,%x]\n",
+        __FUNCTION__, process->pid, this->trdid );
+        req.type = KMEM_KCM;
+        req.ptr  = socket;
+        kmem_remote_free( cxy , &req );
+        return -1;
+    }
+
+    // allocate memory for r2tq queue 
+    error = remote_buf_init( XPTR( cxy , &socket->r2tq ),
+                             NIC_R2T_QUEUE_SIZE );
+    if( error )
+    {
+        printk("\n[ERROR] in %s : cannot allocate R2T queue / thread[%x,%x]\n",
+        __FUNCTION__, process->pid, this->trdid );
+        remote_buf_destroy( XPTR( cxy , &socket->rx_buf ) );
+        req.type = KMEM_KCM;
+        req.ptr  = socket;
+        kmem_remote_free( cxy , &req );
+        return -1;
+    }
+
+    // don't allocate memory for crqq queue, as it is done by the socket_listen function
+
+    //  allocate memory for file descriptor
+	req.type  = KMEM_KCM;
+	req.order = bits_log2( sizeof(vfs_file_t) );
+    req.flags = AF_ZERO;
+	file      = kmem_remote_alloc( cxy , &req );
+
+    if( file == NULL ) 
+    {
+        printk("\n[ERROR] in %s : cannot allocate file descriptor / thread[%x,%x]\n",
+        __FUNCTION__, process->pid, this->trdid );
+        remote_buf_destroy( XPTR( cxy , &socket->r2tq ) );
+        remote_buf_destroy( XPTR( cxy , &socket->rx_buf ) );
+        req.type = KMEM_KCM;
+        req.ptr  = socket;
+        kmem_remote_free( cxy , &req );
+        return -1;
+    }
+    
+    // get an fdid value, and register file descriptor in fd_array[]
+    error = process_fd_register( process->ref_xp,
+                                 XPTR( cxy , file ),
+                                 &fdid );
+    if ( error ) 
+    {
+        printk("\n[ERROR] in %s : cannot register file descriptor / thread[%x,%x]\n",
+        __FUNCTION__, process->pid, this->trdid );
+        req.type = KMEM_KCM;
+        req.ptr  = file;
+        kmem_free( &req );
+        remote_buf_destroy( XPTR( cxy , &socket->r2tq ) );
+        remote_buf_destroy( XPTR( cxy , &socket->rx_buf ) );
+        req.ptr  = socket;
+        kmem_free( &req );
+        return -1;
+    }
+
+    state = (type == SOCK_STREAM) ? TCP_STATE_UNBOUND : UDP_STATE_UNBOUND;
+
+    // initialise socket descriptor 
+    hal_remote_s32( XPTR( cxy , &socket->pid         ) , process->pid );
+    hal_remote_s32( XPTR( cxy , &socket->fdid        ) , fdid );
+    hal_remote_s32( XPTR( cxy , &socket->domain      ) , domain );
+    hal_remote_s32( XPTR( cxy , &socket->type        ) , type );
+    hal_remote_s32( XPTR( cxy , &socket->state       ) , state );
+    hal_remote_s64( XPTR( cxy , &socket->tx_client   ) , XPTR_NULL );
+    hal_remote_s64( XPTR( cxy , &socket->rx_client   ) , XPTR_NULL );
+    hal_remote_s32( XPTR( cxy , &socket->tx_valid    ) , false );
+    hal_remote_s32( XPTR( cxy , &socket->rx_valid    ) , false );
+    hal_remote_s32( XPTR( cxy , &socket->nic_channel ) , 0 );
+
+    // initialize file descriptor 
+    hal_remote_s32( XPTR( cxy , &file->type        ) , INODE_TYPE_SOCK );
+    hal_remote_spt( XPTR( cxy , &file->socket      ) , socket );
+    hal_remote_s32( XPTR( cxy , &file->refcount    ) , 1 );
+
+    // initialize socket lock
+    remote_queuelock_init( XPTR( cxy , &socket->lock ) , LOCK_SOCKET_STATE );
+
+#if DEBUG_SOCKET_CREATE
+if( DEBUG_SOCKET_CREATE < cycle )
+printk("\n[%s] thread[%x,%x] exit / socket[%x,%d] / xptr[%x,%x] / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, fdid, cxy, socket, cycle );
+#endif
+   
+    // return success
+    *socket_ptr = socket;
+    *fdid_ptr   = fdid;
+
+    return 0;
+
+}  // end socket_create
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// This static function is called by the socket_close() function to destroy a socket
+// identified by the <file_xp> argument.
+// It remove the associated file from the reference process fd_array. It unlink the
+// socket from the NIC_TX [k] and NIC_RX[k] chdevs. It release all memory allocated 
+// for the structures associated to the target socket socket : file descriptor,
+// socket descriptor, RX buffer, R2T queue, CRQ queue.
+/////////////////////////////////////////////////////////////////////////////////////////
+// @ file_xp  : extended pointer on the file descriptor.
+/////////////////////////////////////////////////////////////////////////////////////////
+static void socket_destroy( xptr_t file_xp )
+{
+    kmem_req_t          req;
+
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
+
+// check file_xp argument 
+assert( (file_xp != XPTR_NULL), "illegal argument\n" );
+
+    // get cluster & local pointer for file descriptor
+    vfs_file_t * file_ptr = GET_PTR( file_xp );
+    cxy_t        file_cxy = GET_CXY( file_xp );
+
+#if DEBUG_SOCKET_DESTROY
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_DESTROY < cycle )
+printk("\n[%s] thread[%x,%x] enter / file[%x,%x] / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, file_cxy, file_ptr, cycle );
+#endif
+
+    // get local pointer for socket and file type
+    socket_t * socket_ptr = hal_remote_lpt( XPTR( file_cxy , &file_ptr->socket ) );
+    uint32_t   file_type  = hal_remote_l32( XPTR( file_cxy , &file_ptr->type ) );
+    
+// check file descriptor type
+assert( (file_type == INODE_TYPE_SOCK), "illegal file type\n" );
+
+    // get socket nic_channel and fdid
+    uint32_t channel = hal_remote_l32( XPTR( file_cxy , &socket_ptr->nic_channel ));
+    uint32_t fdid    = hal_remote_l32( XPTR( file_cxy , &socket_ptr->fdid ));
+
+    // remove socket from NIC_TX & NIC_RX chdev queues when socket is connected
+    if( channel < LOCAL_CLUSTER->nb_nic_channels )
+    {
+        socket_unlink_from_servers( XPTR( file_cxy , socket_ptr ) );
+    }
+
+    // remove the file descriptor from the fd_array
+    process_fd_remove( process->owner_xp , fdid );
+
+    // release memory allocated for file descriptor
+    req.type = KMEM_KCM;
+    req.ptr  = file_ptr;
+    kmem_remote_free( file_cxy , &req );
+
+    // release memory allocated for buffers attached to socket descriptor
+    remote_buf_destroy( XPTR( file_cxy , &socket_ptr->crqq ) );
+    remote_buf_destroy( XPTR( file_cxy , &socket_ptr->r2tq ) );
+    remote_buf_destroy( XPTR( file_cxy , &socket_ptr->rx_buf ) );
+
+    // release memory allocated for socket descriptor
+    req.type = KMEM_KCM;
+    req.ptr  = socket_ptr;
+    kmem_remote_free( file_cxy , &req );
+
+#if DEBUG_SOCKET_DESTROY
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_DESTROY < cycle )
+printk("\n[%s] thread[%x,%x] exit / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, cycle );
+#endif
+
+}  // end socket_destroy()
+
+////////////////////////////////////////////////
+void socket_put_r2t_request( xptr_t    queue_xp,
+                             uint32_t  flags,
+                             uint32_t  channel )
+{
+    xptr_t     chdev_xp;
+    cxy_t      chdev_cxy;
+    chdev_t  * chdev_ptr;
+    thread_t * server_ptr;
+    xptr_t     server_xp;
+
+    while( 1 )
+    {
+        // try to register R2T request
+        error_t error = remote_buf_put_from_kernel( queue_xp,
+                                                    (uint8_t *)(&flags),
+                                                    1 );
+        if( error )
+        {
+            // queue full => wait and retry
+            sched_yield( "waiting R2T queue" );
+        }
+        else
+        {
+            // get NIC_TX chdev pointers
+            chdev_xp = chdev_dir.nic_tx[channel];
+            chdev_cxy = GET_CXY( chdev_xp );
+            chdev_ptr = GET_PTR( chdev_xp );
  
-#endif	/* _SOCKET_H_ */
+            // get NIC_TX server thread pointers
+            server_ptr = hal_remote_lpt( XPTR( chdev_cxy , &chdev_ptr->server ) );
+            server_xp  = XPTR( chdev_cxy , server_ptr );
+
+            // unblocks NIC_TX server thread
+            thread_unblock( server_xp , THREAD_BLOCKED_CLIENT );
+
+            return;
+        }
+    }
+}  // end socket_put_r2t_request()
+  
+///////////////////////////////////////////////////
+error_t socket_put_crq_request( xptr_t    queue_xp,
+                                uint32_t  remote_addr,
+                                uint32_t  remote_port,
+                                uint32_t  remote_iss,
+                                uint32_t  remote_window )
+{
+    connect_request_t   req;
+
+    // build request
+    req.addr   = remote_addr;
+    req.port   = remote_port;
+    req.iss    = remote_iss;
+    req.window = remote_window;
+
+    // try to register request in CRQ
+    return remote_buf_put_from_kernel( queue_xp,
+                                       (uint8_t *)(&req),
+                                       sizeof(connect_request_t) );
+}  // end socket_put_crq_request()
+  
+////////////////////////////////////////////////////
+error_t socket_get_crq_request( xptr_t     queue_xp,
+                                uint32_t * remote_addr,
+                                uint32_t * remote_port,
+                                uint32_t * remote_iss,
+                                uint32_t * remote_window )
+{
+    connect_request_t   req;
+    error_t             error;
+
+    // get request from CRQ
+    error = remote_buf_get_to_kernel( queue_xp,
+                                      (uint8_t *)(&req),
+                                      sizeof(connect_request_t) );
+    // extract request arguments
+    *remote_addr   = req.addr;
+    *remote_port   = req.port;
+    *remote_iss    = req.iss;
+    *remote_window = req.window;
+
+    return error;
+
+}  // end socket_get_crq_request()
+  
+
+/////////////////////////////////////////////////////////////////////////////////////////
+//                 Functions implementing the SOCKET related syscalls
+/////////////////////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////
+int socket_build( uint32_t   domain,
+                  uint32_t   type )
+{
+    uint32_t    fdid;
+    socket_t  * socket;
+    error_t     error;
+
+#if DEBUG_SOCKET_BUILD
+uint32_t    cycle   = (uint32_t)hal_get_cycles();
+thread_t  * this    = CURRENT_THREAD;
+process_t * process = this->process;
+if( DEBUG_SOCKET_BUILD < cycle )
+printk("\n[%s] thread[%x,%x] enter / %s / %s / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, 
+socket_domain_str(domain), socket_type_str(type), cycle );
+#endif
+
+
+    // allocate memory for the file descriptor and for the socket
+    error = socket_create( local_cxy,
+                           domain,
+                           type,
+                           &socket, 
+                           &fdid );
+
+#if DEBUG_SOCKET_BUILD
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_BUILD < cycle )
+printk("\n[%s] thread[%x,%x] exit / socket %x / fdid %d / %s / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, socket, fdid, 
+socket_state_str(hal_remote_l32(XPTR(local_cxy , &socket->state))),
+cycle );
+#endif
+
+    if( error ) return -1;
+    return fdid;
+}
+
+////////////////////////////////
+int socket_bind( uint32_t  fdid,
+                 uint32_t  addr,
+                 uint16_t  port )
+{
+    vfs_inode_type_t    file_type;
+    socket_t          * socket;
+    uint32_t            socket_type;
+    uint32_t            socket_state;
+
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
+
+#if DEBUG_SOCKET_BIND
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_BIND < cycle )
+printk("\n[%s] thread[%x,%x] enter / socket[%x,%d] / addr %x / port %x / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, fdid, addr, port, cycle );
+#endif
+
+    // get pointers on file descriptor
+    xptr_t       file_xp  = process_fd_get_xptr_from_local( process , fdid );
+    vfs_file_t * file_ptr = GET_PTR( file_xp );
+    cxy_t        file_cxy = GET_CXY( file_xp );
+
+    // check file_xp
+    if( file_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s : undefined fdid %d / thread[%x,%x]\n",
+        __FUNCTION__, fdid, process->pid, this->trdid );
+        return -1;
+    }
+
+    file_type = hal_remote_l32( XPTR( file_cxy , &file_ptr->type ) );
+    socket    = hal_remote_lpt( XPTR( file_cxy , &file_ptr->socket ) );
+
+    // check file descriptor type
+    if( file_type != INODE_TYPE_SOCK )
+    {
+        printk("\n[ERROR] in %s : illegal file type %s / thread[%x,%x]",
+        __FUNCTION__, vfs_inode_type_str( file_type ), process->pid, this->trdid );
+        return -1;
+    }
+
+    // get socket type
+    socket_type = hal_remote_l32(XPTR( file_cxy , &socket->type ));
+
+    // compute socket state
+    socket_state = (socket_type == SOCK_STREAM) ? TCP_STATE_BOUND : UDP_STATE_BOUND;
+
+    // update the socket descriptor
+    hal_remote_s32( XPTR( file_cxy , &socket->local_addr ) , addr );
+    hal_remote_s32( XPTR( file_cxy , &socket->local_port ) , port );
+    hal_remote_s32( XPTR( file_cxy , &socket->state      ) , socket_state );
+
+#if DEBUG_SOCKET_BIND
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_BIND < cycle )
+printk("\n[%s] thread[%x,%x] exit / socket[%x,%d] / %s / addr %x / port %x / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, fdid,
+socket_state_str(hal_remote_l32( XPTR( file_cxy , &socket->state ))),
+hal_remote_l32( XPTR( file_cxy , &socket->local_addr )),
+hal_remote_l32( XPTR( file_cxy , &socket->local_port )),
+cycle );
+#endif
+
+    return 0;
+
+}  // end socket_bind()
+
+//////////////////////////////////
+int socket_listen( uint32_t fdid,
+                   uint32_t crq_depth )
+{
+    xptr_t              file_xp;
+    vfs_file_t        * file_ptr;
+    cxy_t               file_cxy;
+    vfs_inode_type_t    file_type;
+    socket_t          * socket_ptr;
+    uint32_t            socket_type;
+    uint32_t            socket_state;
+    uint32_t            socket_local_addr;
+    uint32_t            socket_local_port;
+    error_t             error;
+
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
+
+#if DEBUG_SOCKET_LISTEN
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_LISTEN < cycle )
+printk("\n[%s] thread[%x,%x] enter / socket[%x,%d] / crq_depth %x / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, fdid, crq_depth, cycle );
+#endif
+
+    // get pointers on file descriptor
+    file_xp  = process_fd_get_xptr_from_local( process , fdid );
+    file_ptr = GET_PTR( file_xp );
+    file_cxy = GET_CXY( file_xp );
+
+    // check file_xp
+    if( file_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s : undefined fdid %d / thread[%x,%x]\n",
+        __FUNCTION__, fdid, process->pid, this->trdid );
+        return -1;
+    }
+
+    file_type  = hal_remote_l32( XPTR( file_cxy , &file_ptr->type ) );
+    socket_ptr = hal_remote_lpt( XPTR( file_cxy , &file_ptr->socket ) );
+
+    // check file descriptor type
+    if( file_type != INODE_TYPE_SOCK )
+    {
+        printk("\n[ERROR] in %s : illegal file type %s / thread[%x,%x]\n",
+        __FUNCTION__, vfs_inode_type_str(file_type), process->pid, this->trdid );
+        return -1;
+    }
+
+    // get relevant infos from <fdid> socket descriptor
+    socket_type       = hal_remote_l32( XPTR( file_cxy , &socket_ptr->type )); 
+    socket_state      = hal_remote_l32( XPTR( file_cxy , &socket_ptr->state )); 
+    socket_local_addr = hal_remote_l32( XPTR( file_cxy , &socket_ptr->local_addr )); 
+    socket_local_port = hal_remote_l32( XPTR( file_cxy , &socket_ptr->local_port )); 
+
+    // check socket type 
+    if( socket_type != SOCK_STREAM )
+    {
+        printk("\n[ERROR] in %s : illegal socket type %s / thread[%x,%x]\n",
+        __FUNCTION__, socket_type_str(socket_type), process->pid, this->trdid );
+        return -1;
+    }
+    
+    // check socket state
+    if( socket_state != TCP_STATE_BOUND )
+    {
+        printk("\n[ERROR] in %s : illegal socket state %s / thread[%x,%x]\n",
+        __FUNCTION__, socket_state_str(socket_state), process->pid, this->trdid );
+        return -1;
+    }
+    
+    // compute CRQ queue depth : max( crq_depth , NIC_CRQ_QUEUE_SIZE )
+    uint32_t depth = ( crq_depth > NIC_CRQ_QUEUE_SIZE ) ? crq_depth : NIC_CRQ_QUEUE_SIZE;
+
+    // allocate memory for the CRQ queue
+    error = remote_buf_init( XPTR( file_cxy , &socket_ptr->crqq ),
+                                   depth * sizeof(connect_request_t) );
+    if( error )
+    {
+        printk("\n[ERROR] in %s : cannot allocate CRQ queue / thread[%x,%x]\n",
+        __FUNCTION__, process->pid, this->trdid );
+        return -1;
+    }
+
+    // update socket.state
+    hal_remote_s32( XPTR( file_cxy , &socket_ptr->state ) , TCP_STATE_LISTEN );
+
+    // 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 );
+
+    // build extended pointer on socket rx_list field
+    xptr_t    list_entry_xp = XPTR( file_cxy , &socket_ptr->rx_list );
+
+    // register  <fdid> socket in listening sockets list
+    remote_busylock_acquire( rx0_lock_xp );
+    xlist_add_last( rx0_root_xp , list_entry_xp );
+    remote_busylock_release( rx0_lock_xp );
+
+#if DEBUG_SOCKET_LISTEN
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_LISTEN < cycle )
+printk("\n[%s] thread[%x,%x] exit / socket[%x,%d] / %s / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, fdid,
+socket_state_str(socket_state), cycle );
+#endif
+
+    return 0;
+
+}  // end socket_listen()
+
+///////////////////////////////////
+int socket_accept( uint32_t   fdid,
+                   uint32_t * remote_addr,
+                   uint16_t * remote_port )
+{
+    xptr_t              file_xp;             // extended pointer on remote file
+    vfs_file_t        * file_ptr;
+    cxy_t               file_cxy;
+    vfs_inode_type_t    file_type;           // file descriptor type
+    socket_t          * socket_ptr;          // local pointer on remote waiting socket
+    uint32_t            socket_type;         // listening socket type    
+    uint32_t            socket_state;        // listening socket state
+    uint32_t            socket_domain;       // listening socket domain
+    uint32_t            socket_local_addr;   // listening socket local IP address
+    uint32_t            socket_local_port;   // listening socket local port
+    uint32_t            socket_tx_nxt;       // listening socket tx_nxt
+    bool_t              socket_tx_valid;     // listening socket tx_valid
+    xptr_t              socket_tx_client;    // listening socket tx_client thread
+    bool_t              socket_rx_valid;     // listening socket rx_valid
+    xptr_t              socket_rx_client;    // listening socket rx_client thread
+    xptr_t              socket_lock_xp;      // listening socket lock
+    xptr_t              crq_xp;              // listening socket CRQ queue
+    uint32_t            crq_status;          // number of bytes in CRQ
+    cxy_t               new_socket_cxy;      // new socket cluster identifier
+    socket_t          * new_socket_ptr;      // local pointer on new socket
+    xptr_t              new_socket_xp;       // extended pointer on new socket
+    volatile uint32_t   new_state;           // new socket state (modified by NIC_RX thread)
+    uint32_t            new_fdid;            // new socket file descriptor index
+    uint32_t            new_remote_addr;     // new socket remote IP address
+    uint32_t            new_remote_port;     // new socket remote port
+    uint32_t            new_remote_iss;      // new socket remote iss
+    uint32_t            new_remote_window;   // new socket receive window
+    xptr_t              tx_server_xp;        // extended pointer on TX server thread
+    thread_t          * tx_server_ptr;       // local pointer on TX server thread
+    uint32_t            cmd_status;          // command status (rx_sts or tx_sts)
+    bool_t              cmd_valid;           // valid command (rx_valid or tx_valid)
+    error_t             error;
+
+    thread_t  * this      = CURRENT_THREAD;
+    xptr_t      client_xp = XPTR( local_cxy , this );
+    process_t * process   = this->process;
+
+#if DEBUG_SOCKET_ACCEPT
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_ACCEPT < cycle )
+printk("\n[%s] thread[%x,%x] enter for socket[%x,%d] / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, fdid, cycle );
+#endif
+
+    // 1) get pointers on file descriptor
+    file_xp  = process_fd_get_xptr_from_local( process , fdid );
+    file_ptr = GET_PTR( file_xp );
+    file_cxy = GET_CXY( file_xp );
+
+    // check file_xp
+    if( file_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s : undefined fdid %d",
+        __FUNCTION__, fdid );
+        return -1;
+    }
+  
+    file_type  = hal_remote_l32( XPTR( file_cxy , &file_ptr->type ) );
+    socket_ptr = hal_remote_lpt( XPTR( file_cxy , &file_ptr->socket ) );
+
+    // check file descriptor type
+    if( file_type != INODE_TYPE_SOCK )
+    {
+        printk("\n[ERROR] in %s : illegal file type %s / thread[%x,%x]\n",
+        __FUNCTION__, vfs_inode_type_str(file_type), process->pid, this->trdid );
+        return -1;
+    }
+
+    // build extended pointer on listening socket lock
+    socket_lock_xp = XPTR( file_cxy , &socket_ptr->lock );
+
+    // acquire listening socket lock
+    remote_queuelock_acquire( socket_lock_xp );
+                    
+    // get listening socket type, domain, state, local_addr, local_port & tx_nxt 
+    socket_type       = hal_remote_l32( XPTR( file_cxy , &socket_ptr->type )); 
+    socket_state      = hal_remote_l32( XPTR( file_cxy , &socket_ptr->state )); 
+    socket_domain     = hal_remote_l32( XPTR( file_cxy , &socket_ptr->domain )); 
+    socket_local_addr = hal_remote_l32( XPTR( file_cxy , &socket_ptr->local_addr )); 
+    socket_local_port = hal_remote_l32( XPTR( file_cxy , &socket_ptr->local_port )); 
+    socket_tx_nxt     = hal_remote_l32( XPTR( file_cxy , &socket_ptr->tx_nxt ));
+    socket_tx_valid   = hal_remote_l32( XPTR( file_cxy , &socket_ptr->tx_valid )); 
+    socket_tx_client  = hal_remote_l64( XPTR( file_cxy , &socket_ptr->tx_client ));
+    socket_rx_valid   = hal_remote_l32( XPTR( file_cxy , &socket_ptr->rx_valid )); 
+    socket_rx_client  = hal_remote_l64( XPTR( file_cxy , &socket_ptr->rx_client ));
+
+    // check socket type 
+    if( socket_type != SOCK_STREAM )
+    {
+        // release listening socket lock
+        remote_queuelock_release( socket_lock_xp );
+                    
+        printk("\n[ERROR] in %s : illegal socket type %s / thread[%x,%x]\n",
+        __FUNCTION__, socket_type_str(socket_type), process->pid , this->trdid );
+        return -1;
+    }
+    
+    // check socket state
+    if( socket_state != TCP_STATE_LISTEN ) 
+    {
+        // release listening socket lock
+        remote_queuelock_release( socket_lock_xp );
+                    
+        printk("\n[ERROR] in %s : illegal socket state %s / thread[%x,%x]\n",
+        __FUNCTION__, socket_state_str(socket_state), process->pid, this->trdid );
+        return -1;
+    }
+    
+    // check no previous RX command
+    if( (socket_rx_valid == true) || (socket_rx_client != XPTR_NULL) )
+    { 
+        // release listening socket lock
+        remote_queuelock_release( socket_lock_xp );
+                    
+        printk("\n[ERROR] in %s : previous RX cmd on socket[%x,%d] / thread[%x,%x]\n",
+        __FUNCTION__, process->pid, fdid, process->pid, this->trdid );
+        return -1;
+    }
+
+    // check no previous TX command
+    if( (socket_tx_valid == true) || (socket_tx_client != XPTR_NULL) )
+    { 
+        // release socket lock
+        remote_queuelock_release( socket_lock_xp );
+                    
+        printk("\n[ERROR] in %s : previous TX cmd on socket[%x,%d] / thread[%x,%x]\n",
+        __FUNCTION__, process->pid, fdid, process->pid, this->trdid );
+        return -1;
+    }
+
+    // 2) build extended pointer on listening socket.crq
+    crq_xp  = XPTR( file_cxy , &socket_ptr->crqq );
+
+    // get CRQ status
+    crq_status = remote_buf_status( crq_xp );
+
+    // block & deschedule when CRQ empty
+    if( crq_status == 0 )
+    {
+        // register command arguments in listening socket
+        hal_remote_s32( XPTR( file_cxy , &socket_ptr->rx_cmd    ), CMD_RX_ACCEPT );
+        hal_remote_s64( XPTR( file_cxy , &socket_ptr->rx_client ), client_xp );
+        hal_remote_s32( XPTR( file_cxy , &socket_ptr->rx_valid  ), true );
+
+        // release listening socket lock
+        remote_queuelock_release( socket_lock_xp );
+
+#if DEBUG_SOCKET_ACCEPT
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_ACCEPT < cycle )
+printk("\n[%s] thread[%x,%x] socket[%x,%d] / CRQ empty => blocks on <IO> / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, fdid, cycle );
+#endif
+        // block & deschedule when CRQQ empty
+        thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_IO );
+        sched_yield( "CRQ queue empty");
+
+#if DEBUG_SOCKET_ACCEPT
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_ACCEPT < cycle )
+printk("\n[%s] thread[%x,%x] socket[%x,%d] / resumes / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, fdid, cycle );
+#endif
+        // take listening socket lock
+        remote_queuelock_acquire( socket_lock_xp );
+
+        // get CRQ status & command status
+        cmd_valid    = hal_remote_l32( XPTR( file_cxy , &socket_ptr->rx_valid ) );
+        cmd_status   = hal_remote_l32( XPTR( file_cxy , &socket_ptr->rx_sts ) );
+        crq_status   = remote_buf_status( crq_xp );
+
+assert( (((crq_status > 0) || (cmd_status!= CMD_STS_SUCCESS)) && (cmd_valid == false)),
+"illegal socket state when client thread resumes after RX_ACCEPT" );
+
+        // reset socket.rx_client
+        hal_remote_s32( XPTR( file_cxy , &socket_ptr->rx_client ) , XPTR_NULL );
+
+        if( cmd_status != CMD_STS_SUCCESS )
+        {
+            // release socket lock
+            remote_queuelock_release( socket_lock_xp );
+
+            printk("\n[ERROR] in %s for RX_ACCEPT command / socket[%x,%d] / thread[%x,%x]\n",
+            __FUNCTION__, process->pid, fdid, process->pid, this->trdid );
+            return -1;
+        }
+
+        // extract first request from the listening socket CRQ
+        error = socket_get_crq_request( crq_xp,
+                                    &new_remote_addr,
+                                    &new_remote_port,
+                                    &new_remote_iss,
+                                    &new_remote_window );
+
+assert( (error == 0),
+"cannot get a connection request from a non-empty CRQ" ); 
+
+        // reset listening socket rx_client
+        hal_remote_s32( XPTR( file_cxy , &socket_ptr->rx_client ) , XPTR_NULL );
+
+        // release socket lock
+        remote_queuelock_release( socket_lock_xp );
+
+    }  // end blocking on CRQ status 
+
+    // from this point, we can create a new socket 
+    // and ask the NIC_TX to send a SYN-ACK segment
+
+#if DEBUG_SOCKET_ACCEPT
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_ACCEPT < cycle )
+printk("\n[%s] thread[%x,%x] socket[%x,%d] / got a CRQ request / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, fdid, cycle );
+#endif
+
+    // 3) select a cluster for the new socket
+    new_socket_cxy = cluster_random_select();
+
+    // allocate memory for the new socket descriptor 
+    error = socket_create( new_socket_cxy,
+                           socket_domain,
+                           socket_type,
+                           &new_socket_ptr,
+                           &new_fdid );
+    if( error )
+    {
+        printk("\n[ERROR] in %s : cannot allocate new socket / thread[%x,%x]\n",
+        __FUNCTION__, process->pid, this->trdid );
+        return -1;
+    }
+   
+    // build extended poiner on new socket
+    new_socket_xp = XPTR( new_socket_cxy , new_socket_ptr );
+
+#if DEBUG_SOCKET_ACCEPT
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_ACCEPT < cycle )
+printk("\n[%s] thread[%x,%x] created new socket[%x,%d] / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, new_fdid, cycle );
+#endif
+        
+    // compute NIC channel index from remote_addr and remote_port
+    uint32_t new_nic_channel = dev_nic_get_key( new_remote_addr , new_remote_port );
+
+    // update new socket descriptor 
+    hal_remote_s32(XPTR(new_socket_cxy , &new_socket_ptr->local_addr ) , socket_local_addr );
+    hal_remote_s32(XPTR(new_socket_cxy , &new_socket_ptr->local_port ) , socket_local_port );
+    hal_remote_s32(XPTR(new_socket_cxy , &new_socket_ptr->remote_addr) , new_remote_addr );
+    hal_remote_s32(XPTR(new_socket_cxy , &new_socket_ptr->remote_port) , new_remote_port );
+    hal_remote_s32(XPTR(new_socket_cxy , &new_socket_ptr->nic_channel) , new_nic_channel );
+    hal_remote_s32(XPTR(new_socket_cxy , &new_socket_ptr->state      ) , TCP_STATE_SYN_RCVD );
+
+    // set new socket TCB : increment tx_nxt / initialize rx_nxt, rx_irs, rx_wnd
+    hal_remote_s32( XPTR( new_socket_cxy , &new_socket_ptr->tx_nxt ), socket_tx_nxt + 1 );
+    hal_remote_s32( XPTR( new_socket_cxy , &new_socket_ptr->rx_nxt ), new_remote_iss + 1 );
+    hal_remote_s32( XPTR( new_socket_cxy , &new_socket_ptr->rx_irs ), new_remote_iss );
+    hal_remote_s32( XPTR( new_socket_cxy , &new_socket_ptr->rx_wnd ), new_remote_window );
+
+    // link new socket to chdev servers
+    socket_link_to_servers( new_socket_xp , new_nic_channel );
+
+    // 3) get pointers on NIC_TX[channel] chdev
+    xptr_t    tx_chdev_xp  = chdev_dir.nic_tx[new_nic_channel];
+    chdev_t * tx_chdev_ptr = GET_PTR( tx_chdev_xp );
+    cxy_t     tx_chdev_cxy = GET_CXY( tx_chdev_xp );
+
+    // get pointers on NIC_TX[channel] server thread
+    tx_server_ptr = hal_remote_lpt( XPTR( tx_chdev_cxy , &tx_chdev_ptr->server ));
+    tx_server_xp  = XPTR( tx_chdev_cxy , tx_server_ptr );
+
+    // register command arguments in new socket to request a SYN_ACK segment
+    hal_remote_s32( XPTR( new_socket_cxy , &new_socket_ptr->tx_cmd    ), CMD_TX_ACCEPT );
+    hal_remote_s64( XPTR( new_socket_cxy , &new_socket_ptr->tx_client ), client_xp );
+    hal_remote_s32( XPTR( new_socket_cxy , &new_socket_ptr->tx_valid  ), true );
+
+    // unblock NIC_TX server thread
+    thread_unblock( tx_server_xp , THREAD_BLOCKED_CLIENT );
+ 
+#if DEBUG_SOCKET_ACCEPT
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_ACCEPT < cycle )
+printk("\n[%s] thread[%x,%x] new_socket[%x,%d] blocks on <IO> waiting ESTAB / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, new_fdid, cycle );
+#endif
+
+    // client thread blocks & deschedules 
+    thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_IO );
+    sched_yield( "waiting new socket connection");
+
+#if DEBUG_SOCKET_ACCEPT
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_ACCEPT < cycle )
+printk("\n[%s] thread[%x,%x] new_socket[%x,%d] resumes  / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, new_fdid, cycle );
+#endif
+
+    // get new socket state, tx_valid and tx_sts
+    new_state  = hal_remote_l32( XPTR( new_socket_cxy , &new_socket_ptr->state ));
+    cmd_valid  = hal_remote_l32( XPTR( new_socket_cxy , &new_socket_ptr->tx_valid ));
+    cmd_status = hal_remote_l32( XPTR( new_socket_cxy , &new_socket_ptr->tx_sts ));
+
+assert( (((new_state == TCP_STATE_ESTAB) || (cmd_status != CMD_STS_SUCCESS))
+        && (cmd_valid == false)), 
+"illegal socket state when client thread resumes after TX_ACCEPT" ); 
+
+    // reset socket.tx_client
+    hal_remote_s64( XPTR( new_socket_cxy , &new_socket_ptr->tx_client ) , XPTR_NULL );
+
+    if( cmd_status != CMD_STS_SUCCESS ) 
+    {
+        printk("\n[ERROR] in %s for TX_ACCEPT command / socket[%x,%d] / thread[%x,%x]\n",
+        __FUNCTION__, process->pid, new_fdid, process->pid, this->trdid );
+        return -1;
+    }
+    else
+    {
+
+#if DEBUG_SOCKET_ACCEPT
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_ACCEPT < cycle )
+printk("\n[%s] thread[%x,%x] new_socket[%x,%d] / state %s / addr %x / port %x / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, new_fdid,
+socket_state_str(new_state), new_remote_addr, new_remote_port, cycle );
+#endif
+
+        // return success
+        *remote_addr = new_remote_addr;
+        *remote_port = new_remote_port;
+        return new_fdid;
+    }
+ 
+}  // end socket_accept()
+
+//////////////////////////////////
+int socket_connect( uint32_t fdid,
+                    uint32_t remote_addr,
+                    uint16_t remote_port )
+{
+    vfs_inode_type_t    file_type;
+    socket_t          * socket_ptr;       // local pointer on thread descriptor
+    volatile uint32_t   socket_state;     // socket state (modified by the NIC_TX thread)
+    uint32_t            socket_type;      // socket type  
+    uint32_t            local_addr;       // local IP address
+    uint32_t            local_port;       // local port
+    xptr_t              tx_server_xp;     // extended pointer on TX server thread
+    thread_t          * tx_server_ptr;    // local pointer on TX server thread
+    uint32_t            nic_channel;      // NIC channel index
+    uint32_t            cmd_status;       // command status (tx_sts field)
+    bool_t              cmd_valid;        // command valid (tx_valid field)
+
+    thread_t  * this      = CURRENT_THREAD;
+    xptr_t      client_xp = XPTR( local_cxy , this );
+
+    // get pointers on file descriptor
+    xptr_t       file_xp  = process_fd_get_xptr_from_local( this->process , fdid );
+    vfs_file_t * file_ptr = GET_PTR( file_xp );
+    cxy_t        file_cxy = GET_CXY( file_xp );
+
+    // check file_xp
+    if( file_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s : undefined fdid %d",
+        __FUNCTION__, fdid );
+        return -1;
+    }
+
+    file_type  = hal_remote_l32( XPTR( file_cxy , &file_ptr->type ) );
+    socket_ptr = hal_remote_lpt( XPTR( file_cxy , &file_ptr->socket ) );
+
+#if DEBUG_SOCKET_CONNECT
+uint32_t cycle = (uint32_t)hal_get_cycles();
+pid_t    pid   = this->process->pid;
+trdid_t  trdid = this->trdid;
+if( DEBUG_SOCKET_CONNECT < cycle )
+printk("\n[%s] thread[%x,%x] enter for socket[%x,%d] / addr %x / port %d / cycle %d\n",
+__FUNCTION__,  pid, trdid, pid, fdid, remote_addr, remote_port, cycle );
+#endif
+
+    // check file descriptor type
+    if( file_type != INODE_TYPE_SOCK )
+    {
+        printk("\n[ERROR] in %s : illegal file type %s",
+        __FUNCTION__, vfs_inode_type_str( file_type ) );
+        return -1;
+    }
+
+    // get relevant socket infos
+    socket_type   = hal_remote_l32( XPTR( file_cxy , &socket_ptr->type ) );
+    socket_state  = hal_remote_l32( XPTR( file_cxy , &socket_ptr->state ) );
+    local_addr    = hal_remote_l32( XPTR( file_cxy , &socket_ptr->local_addr ) );
+    local_port    = hal_remote_l32( XPTR( file_cxy , &socket_ptr->local_port ) );
+
+    if( socket_type == SOCK_DGRAM )       // UDP
+    {
+        if( socket_state != UDP_STATE_BOUND )
+        {
+            printk("\n[ERROR] in %s : illegal socket state %s for type %s",
+            __FUNCTION__, socket_state_str(socket_state), socket_type_str(socket_type) );
+            return -1;
+        }
+    }
+    else if( socket_type == SOCK_STREAM )  // TCP
+    {
+        if( socket_state != TCP_STATE_BOUND )
+        {
+            printk("\n[ERROR] in %s : illegal socket state %s for type %s",
+            __FUNCTION__, socket_state_str(socket_state), socket_type_str(socket_type) );
+            return -1;
+        }
+    }
+    else
+    {
+        printk("\n[ERROR] in %s : illegal socket type %s",
+        __FUNCTION__,  socket_type_str(socket_type) );
+        return -1;
+    }
+
+    // compute nic_channel index from remote_addr and remote_port
+    nic_channel = dev_nic_get_key( remote_addr , remote_port );
+
+    // link socket to chdev servers
+    socket_link_to_servers( XPTR( file_cxy , socket_ptr ), nic_channel );
+
+    // update the socket descriptor
+    hal_remote_s32( XPTR( file_cxy , &socket_ptr->remote_addr ) , remote_addr  );
+    hal_remote_s32( XPTR( file_cxy , &socket_ptr->remote_port ) , remote_port  );
+    hal_remote_s32( XPTR( file_cxy , &socket_ptr->nic_channel ) , nic_channel  );
+
+    // the actual connection mechanism depends on socket type
+    // UDP : client thread updates the local socket state without blocking
+    // TCP : client thread request TX server thread to start the 3 steps handshake
+
+    if( socket_type == SOCK_DGRAM )  // UDP
+    {
+        // directly update the local socket state
+        hal_remote_s32( XPTR( file_cxy , &socket_ptr->state ) , UDP_STATE_ESTAB );
+
+        return 0;
+    }
+    else                             // TCP
+    {
+        // get pointers on NIC_TX[channel] chdev
+        xptr_t    tx_chdev_xp  = chdev_dir.nic_tx[nic_channel];
+        chdev_t * tx_chdev_ptr = GET_PTR( tx_chdev_xp );
+        cxy_t     tx_chdev_cxy = GET_CXY( tx_chdev_xp );
+
+        // get pointers on NIC_TX[channel] server thread
+        tx_server_ptr = hal_remote_lpt( XPTR( tx_chdev_cxy , &tx_chdev_ptr->server ));
+        tx_server_xp  = XPTR( tx_chdev_cxy , tx_server_ptr );
+
+        // register command arguments in socket descriptor for a SYN segment
+        hal_remote_s32( XPTR( file_cxy , &socket_ptr->tx_cmd    ), CMD_TX_CONNECT );
+        hal_remote_s64( XPTR( file_cxy , &socket_ptr->tx_client ), client_xp );
+        hal_remote_s32( XPTR( file_cxy , &socket_ptr->tx_valid  ), true );
+
+        // unblock NIC_TX server thread
+        thread_unblock( tx_server_xp , THREAD_BLOCKED_CLIENT );
+ 
+#if DEBUG_SOCKET_CONNECT
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_CONNECT < cycle )
+printk("\n[%s] thread[%x,%x] socket[%x,%d] blocks on <IO> waiting connexion / cycle %d \n",
+__FUNCTION__, pid, trdid, pid, fdid, cycle );
+#endif
+        // block itself and deschedule
+        thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_IO );
+        sched_yield( "waiting connection" );
+
+#if DEBUG_SOCKET_CONNECT
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_CONNECT < cycle )
+printk("\n[%s] thread[%x,%x] socket[%x,%d] / resumes / cycle %d \n",
+__FUNCTION__, pid, trdid, pid, fdid, cycle );
+#endif
+
+        // get socket state, tx_valid and tx_sts
+        cmd_valid    = hal_remote_l32( XPTR( file_cxy , &socket_ptr->tx_valid ));
+        cmd_status   = hal_remote_l32( XPTR( file_cxy , &socket_ptr->tx_sts ));
+        socket_state = hal_remote_l32( XPTR( file_cxy , &socket_ptr->state ));
+
+assert( (((socket_state == TCP_STATE_ESTAB) || (cmd_status != CMD_STS_SUCCESS))
+        && (cmd_valid == false)),
+"illegal socket state when client thread resumes after TX_CONNECT" );
+
+        // reset socket.tx_client
+        hal_remote_s32( XPTR( file_cxy , &socket_ptr->tx_client ) , XPTR_NULL );
+
+        if( cmd_status != CMD_STS_SUCCESS )
+        {
+            printk("\n[ERROR] in %s : for command TX_CONNECT / socket[%x,%d] / thread[%x,%x]\n",
+            __FUNCTION__, pid, fdid, pid, trdid );
+            return -1;
+        }
+        else
+        {
+
+#if DEBUG_SOCKET_CONNECT
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_CONNECT < cycle )
+printk("\n[%s] thread[%x,%x] exit for socket[%x,%d] / %s / cycle %d \n",
+__FUNCTION__, pid, trdid, pid, fdid, socket_state_str(socket_state),cycle );
+#endif
+             return 0;
+        }
+    }  // end TCP
+
+}  // end socket_connect()
+
+///////////////////////////////////
+int socket_close( xptr_t   file_xp,
+                  uint32_t fdid )
+{
+    uint32_t     socket_type;
+    uint32_t     socket_state;
+    uint32_t     nic_channel;
+    uint32_t     cmd_status;      // socket.tx_sts
+    bool_t       cmd_valid;       // socket.tx_valid
+    thread_t   * tx_server_ptr;   // local pointer on NIC_TX server thread
+    xptr_t       tx_server_xp;    // extended pointer on NIC_TX server thread
+    xptr_t       socket_lock_xp;  // extended pointer on socket lock
+
+    thread_t   * this      = CURRENT_THREAD;
+    xptr_t       client_xp = XPTR( local_cxy , this );
+    process_t  * process   = this->process;
+
+    // get pointer on socket descriptor
+    cxy_t        file_cxy    = GET_CXY( file_xp );
+    vfs_file_t * file_ptr    = GET_PTR( file_xp );
+    socket_t   * socket_ptr = hal_remote_lpt( XPTR( file_cxy , &file_ptr->socket ) );
+
+assert( (hal_remote_l32( XPTR( file_cxy , &socket_ptr->fdid )) == fdid),
+"unconsistent file_xp & fdid arguments");
+
+#if DEBUG_SOCKET_CLOSE
+uint32_t cycle = (uint32_t)hal_get_cycles();
+pid_t    pid   = this->process->pid;
+if (DEBUG_SOCKET_CLOSE < cycle )
+printk("\n[%s] thread[%x,%x] enters for socket[%x,%d] / cycle %d\n",
+__FUNCTION__, pid, this->trdid, pid, fdid, cycle );
+#endif
+
+    // build extended pointer on lock protecting socket 
+    socket_lock_xp = XPTR( file_cxy , &socket_ptr->lock );
+
+    // take socket lock
+    remote_queuelock_acquire( socket_lock_xp );
+
+    // check no previous TX command
+    if( (hal_remote_l32( XPTR( file_cxy , &socket_ptr->tx_valid )) == true) || 
+        (hal_remote_l64( XPTR( file_cxy , &socket_ptr->tx_client)) != XPTR_NULL) )
+    { 
+        // release socket lock
+        remote_queuelock_release( socket_lock_xp );
+                    
+        printk("\n[ERROR] in %s : previous TX cmd on socket[%x,%d] / thread[%x,%x]\n",
+        __FUNCTION__, process->pid, fdid, process->pid, this->trdid );
+        return -1;
+    }
+
+    // get relevant socket infos
+    socket_type   = hal_remote_l32( XPTR( file_cxy , &socket_ptr->type ));
+    nic_channel   = hal_remote_l32( XPTR( file_cxy , &socket_ptr->nic_channel ));
+    socket_state  = hal_remote_l32( XPTR( file_cxy , &socket_ptr->state ));
+
+
+    // the actual close mechanism depends on socket type and state:
+    // UDP or TCP not connected : client thread directly destroy the socket descriptor
+    // TCP connected : client thread request TX server thread to make the TCP close handshake
+
+    if( socket_type == SOCK_DGRAM )                   // UDP 
+    {
+
+#if DEBUG_SOCKET_CLOSE
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_DEV_NIC_TX )
+printk("\n[%s] thread[%x,%x] socket[%x,%d] %s / destroy socket / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, pid, fdid,
+socket_state_str( socket_state ), cycle );
+#endif
+        // directly destroy socket
+        socket_destroy( file_xp );
+
+        return 0;
+    }
+    else if( (socket_state == TCP_STATE_BOUND) ||
+             (socket_state == TCP_STATE_LISTEN) ||
+             (socket_state == TCP_STATE_SYN_SENT) )   // TCP not connected
+    {
+
+#if DEBUG_SOCKET_CLOSE
+cycle = (uint32_t)hal_get_cycles();
+if( cycle > DEBUG_DEV_NIC_TX )
+printk("\n[%s] thread[%x,%x] socket[%x,%d] %s / destroy socket / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, pid, fdid,
+socket_state_str( socket_state ), cycle );
+#endif
+        // directly destroy socket
+        socket_destroy( file_xp );
+
+        return 0;
+    }
+    else                                             // TCP connected
+    {
+        // get pointers on NIC_TX[index] chdev
+        xptr_t    tx_chdev_xp  = chdev_dir.nic_tx[nic_channel];
+        chdev_t * tx_chdev_ptr = GET_PTR( tx_chdev_xp );
+        cxy_t     tx_chdev_cxy = GET_CXY( tx_chdev_xp );
+
+        // get pointers on NIC_TX[channel] server thread
+        tx_server_ptr = hal_remote_lpt( XPTR( tx_chdev_cxy , &tx_chdev_ptr->server ));
+        tx_server_xp  = XPTR( tx_chdev_cxy , tx_server_ptr );
+
+        // register command arguments in socket descriptor
+        hal_remote_s32( XPTR( file_cxy , &socket_ptr->tx_cmd    ), CMD_TX_CLOSE );
+        hal_remote_s64( XPTR( file_cxy , &socket_ptr->tx_client ), client_xp );
+        hal_remote_s32( XPTR( file_cxy , &socket_ptr->tx_valid  ), true );
+        
+        // unblock NIC_TX server thread
+        thread_unblock( tx_server_xp , THREAD_BLOCKED_CLIENT );
+ 
+        // release socket lock
+        remote_queuelock_release( socket_lock_xp );
+
+#if DEBUG_SOCKET_CLOSE
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_CLOSE < cycle )
+printk("\n[%s] thread[%x,%x] socket[%x,%d] blocks on <IO> waiting close / cycle %d \n",
+__FUNCTION__, pid, this->trdid, pid, fdid, cycle );
+#endif
+        // block itself and deschedule
+        thread_block( client_xp , THREAD_BLOCKED_IO );
+        sched_yield( "blocked in close" );
+
+#if DEBUG_SOCKET_CLOSE
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_CLOSE < cycle )
+printk("\n[%s] thread[%x,%x] socket[%x,%d] / resumes / cycle %d \n",
+__FUNCTION__, pid, this->trdid, pid, fdid, cycle );
+#endif
+        // take socket lock
+        remote_queuelock_acquire( socket_lock_xp );
+
+        // get socket state & command status
+        socket_state = hal_remote_l32( XPTR( file_cxy , &socket_ptr->state ) );
+        cmd_status   = hal_remote_l32( XPTR( file_cxy , &socket_ptr->tx_sts) );
+        cmd_valid    = hal_remote_l32( XPTR( file_cxy , &socket_ptr->tx_valid ) );
+
+assert( (((socket_state == TCP_STATE_CLOSED) || (cmd_status != CMD_STS_SUCCESS))
+         && (cmd_valid == false)),
+"illegal socket state when client thread resumes after TX_CLOSE\n"
+" socket_state = %s / cmd_status = %d / cmd_valid = %d\n",
+socket_state_str(socket_state), cmd_status, cmd_valid );
+
+        // reset socket.tx_client
+        hal_remote_s32( XPTR( file_cxy , &socket_ptr->tx_client ) , XPTR_NULL );
+
+        if( cmd_status != CMD_STS_SUCCESS )  // error reported
+        {
+            printk("\n[ERROR] in %s for command TX_CLOSE / socket[%x,%d] / thread[%x,%x]\n",
+            __FUNCTION__, pid, fdid, pid, this->trdid );
+            return -1;
+        }
+        else                                 // success
+        {
+
+#if DEBUG_SOCKET_CLOSE
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_CLOSE < cycle )
+printk("\n[%s] thread[%x,%x] socket[%x,%d] / destroy socket / cycle %d\n",
+__FUNCTION__, pid, this->trdid, pid, fdid, socket_state_str(socket_state) , cycle );
+#endif
+            // destroy socket
+            socket_destroy( file_xp );
+
+            return 0;
+        }
+    }   // end if TCP
+}  // end socket_close()
+
+////////////////////////////////////////////////////////////////////////////////////////
+// This static and blocking function is executed by an user thread calling one of the
+// four functions: socket_send() / socket_recv() / socket_sendto() / socket_recvfrom()
+// It can be used for both UDP and TCP sockets.
+////////////////////////////////////////////////////////////////////////////////////////
+// @ is_send   : send when true / receive when false.
+// @ fdid      : socket identifier.
+// @ u_buf     : pointer on user buffer in user space.
+// @ length    : number of bytes.
+// @ explicit  : explicit remote IP address and port when true.
+////////////////////////////////////////////////////////////////////////////////////////
+// Implementation note : The behavior is different for SEND & RECV
+// - For a SEND, the client thread checks that there is no TX command registered
+//   in the socket. It registers the command arguments in the socket descriptor
+//   (tx_client, tx_cmd, tx_buf, tx_len). Then the client thread unblocks the 
+//   TX server thread from the BLOCKED_CLIENT condition, blocks itself on the
+//   BLOCKED_IO condition, and deschedules. It is unblocked by the TX server thread
+//   when the last byte has been sent (for UDP) or acknowledged (for TCP).
+//   When the client thread resumes, it reset the command in socket, and returns.
+// - For a RECV, the client thread checks that there is no RX command registered
+//   in the socket. It registers itself in socket (rx_client). It checks the status
+//   of the receive buffer. It the rx_buf is empty, it blocks on the BLOCKED_IO
+//   condition, and deschedules. It is unblocked by the RX server thread when an UDP
+//   packet or TCP segment has been writen in the rx_buf. When it resumes, it moves
+//   the available data from the rx_buf to the user buffer, reset its registration
+//   in socket (reset the rx_buf for an UDP socket), and returns.
+////////////////////////////////////////////////////////////////////////////////////////
+int socket_move_data( bool_t     is_send,
+                      uint32_t   fdid,
+                      uint8_t  * u_buf,
+                      uint32_t   length,
+                      bool_t     explicit,
+                      uint32_t   explicit_addr,
+                      uint32_t   explicit_port )
+{
+    vfs_inode_type_t    file_type;       // file descriptor type
+    socket_t          * socket_ptr;      // local pointer on socket descriptor
+    uint32_t            socket_state;    // current socket state
+    uint32_t            socket_type;     // socket type (UDP/TCP)
+    uint32_t            nic_channel;     // NIC channel for this socket
+    xptr_t              socket_lock_xp;  // extended pointer on socket lock
+    xptr_t              file_xp;         // extended pointer on file descriptor
+    vfs_file_t        * file_ptr;
+    cxy_t               file_cxy;
+    xptr_t              chdev_xp;        // extended pointer on NIC_TX[channel] chdev
+    chdev_t           * chdev_ptr;
+    cxy_t               chdev_cxy;
+    uint32_t            remote_addr;
+    uint32_t            remote_port;
+    uint32_t            buf_status;      // number of bytes in rx_buf
+    int32_t             moved_bytes;     // total number of moved bytes (fot return)
+    xptr_t              server_xp;       // extended pointer on NIC_TX / NIC_RX server thread
+    thread_t          * server_ptr;      // local pointer on NIC_TX / NIC_RX server thread
+    kmem_req_t          req;             // KCM request for TX kernel buffer
+    uint8_t           * tx_buf;          // kernel buffer for TX transfer
+    bool_t              cmd_valid;       // from socket descriptor
+    uint32_t            cmd_status;      // from socket descriptor
+    uint32_t            tx_todo;         // from socket descriptor
+
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
+
+    // build extended pointer on client thread
+    xptr_t client_xp = XPTR( local_cxy , this );
+
+    // get pointers on file descriptor identifying the socket
+    file_xp  = process_fd_get_xptr_from_local( process , fdid );
+    file_ptr = GET_PTR( file_xp );
+    file_cxy = GET_CXY( file_xp );
+
+    if( file_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s : undefined fdid %d / thread%x,%x]\n",
+        __FUNCTION__, fdid , process->pid, this->trdid );
+        return -1;
+    }
+ 
+    // get file type and socket pointer
+    file_type  = hal_remote_l32( XPTR( file_cxy , &file_ptr->type ) );
+
+    // get local pointer on socket
+    socket_ptr = hal_remote_lpt( XPTR( file_cxy , &file_ptr->socket ) );
+
+    // check file descriptor type
+    if( file_type != INODE_TYPE_SOCK )
+    {
+        printk("\n[ERROR] in %s : illegal file type %s / socket[%x,%d]\n",
+        __FUNCTION__, vfs_inode_type_str(file_type), process->pid, fdid );
+        return -1;
+    }
+
+    // build extended pointer on lock protecting socket 
+    socket_lock_xp = XPTR( file_cxy , &socket_ptr->lock );
+
+    // take the socket lock
+    remote_queuelock_acquire( socket_lock_xp );
+
+    // get socket type, state, and channel
+    socket_type  = hal_remote_l32( XPTR( file_cxy , &socket_ptr->type ));
+    socket_state = hal_remote_l32( XPTR( file_cxy , &socket_ptr->state ));
+    nic_channel  = hal_remote_l32( XPTR( file_cxy , &socket_ptr->nic_channel ));
+
+    // handle the explicit remote address and port
+    if( socket_type == SOCK_DGRAM )                  // UDP socket
+    {
+        if( socket_state == UDP_STATE_UNBOUND )
+        {
+            // release socket lock
+            remote_queuelock_release( socket_lock_xp );
+                    
+            printk("\n[ERROR] in %s : SEND/RECV for socket[%x,%d] in state %s\n",
+            __FUNCTION__, process->pid, fdid, socket_state_str(socket_state) );
+            return -1;
+        }
+
+        if( explicit )
+        {
+            // update remote IP address and port into socket descriptor
+            hal_remote_s32( XPTR( file_cxy , &socket_ptr->remote_addr ), explicit_addr );
+            hal_remote_s32( XPTR( file_cxy , &socket_ptr->remote_port ), explicit_port );
+
+            // update socket state if required
+            if( socket_state == UDP_STATE_BOUND )
+            {
+                hal_remote_s32( XPTR( file_cxy , &socket_ptr->state ), UDP_STATE_ESTAB );
+            }
+        }
+    }
+    else                                            // TCP socket
+    {
+        if( explicit )
+        {
+            // get remote IP address and port from socket descriptor
+            remote_addr = hal_remote_l32( XPTR( file_cxy , &socket_ptr->remote_addr ));
+            remote_port = hal_remote_l32( XPTR( file_cxy , &socket_ptr->remote_port ));
+
+            if( (remote_addr != explicit_addr) || (remote_port != explicit_port) )
+            {
+                // release socket lock
+                remote_queuelock_release( socket_lock_xp );
+                    
+                printk("\n[ERROR] in %s : wrong expliciy access for socket[%x,%d]\n",
+                __FUNCTION__, process->pid, fdid );
+                return -1;
+            }
+        }
+    }
+
+    ///////////////////////////////////////////////////////
+    if( is_send )                       // TX_SEND command
+    {
+
+#if DEBUG_SOCKET_SEND
+uint32_t    cycle = (uint32_t)hal_get_cycles();
+if (DEBUG_SOCKET_SEND < cycle )
+printk("\n[%s] thread[%x,%x] received SEND command for socket[%x,%d] / length %d / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, fdid, length, cycle );
+#endif
+        // check no previous TX command
+        if( (hal_remote_l32( XPTR( file_cxy , &socket_ptr->tx_valid )) == true) || 
+            (hal_remote_l64( XPTR( file_cxy , &socket_ptr->tx_client)) != XPTR_NULL) )
+        { 
+            // release socket lock
+            remote_queuelock_release( socket_lock_xp );
+                    
+            printk("\n[ERROR] in %s : previous TX command / socket[%x,%d] / thread[%x,%x]\n",
+            __FUNCTION__, process->pid, fdid, process->pid, this->trdid );
+            return -1;
+        }
+
+        // allocate a temporary kernel buffer
+        req.type  = KMEM_KCM;
+        req.order = bits_log2( length );
+        req.flags = AF_NONE;
+        tx_buf    = kmem_alloc( &req );  
+
+        if( tx_buf == NULL )
+        {
+            // release socket lock
+            remote_queuelock_release( socket_lock_xp );
+                    
+            printk("\n[ERROR] in %s : no memory for tx_buf / socket[%x,%d] / thread[%x,%x]\n",
+            __FUNCTION__, process->pid, fdid, process->pid, this->trdid );
+            return -1;
+        }
+
+        // copy data from user u_buf to kernel tx_buf   
+        hal_copy_from_uspace( XPTR( local_cxy , tx_buf ),
+                              u_buf,
+                              length );
+
+        // register command in socket descriptor
+        hal_remote_s64( XPTR( file_cxy , &socket_ptr->tx_client ) , client_xp );
+        hal_remote_s32( XPTR( file_cxy , &socket_ptr->tx_cmd    ) , CMD_TX_SEND );
+        hal_remote_spt( XPTR( file_cxy , &socket_ptr->tx_buf    ) , tx_buf );
+        hal_remote_s32( XPTR( file_cxy , &socket_ptr->tx_len    ) , length );
+        hal_remote_s32( XPTR( file_cxy , &socket_ptr->tx_todo   ) , length );
+        hal_remote_s32( XPTR( file_cxy , &socket_ptr->tx_valid  ) , true );
+
+        // release socket lock
+        remote_queuelock_release( socket_lock_xp );
+                    
+        // get pointers on relevant chdev
+        chdev_xp  = chdev_dir.nic_tx[nic_channel];
+        chdev_ptr = GET_PTR( chdev_xp );
+        chdev_cxy = GET_CXY( chdev_xp );
+
+        // get pointers on NIC_TX[channel] server thread
+        server_ptr = hal_remote_lpt( XPTR( chdev_cxy , &chdev_ptr->server ));
+        server_xp  = XPTR( chdev_cxy , server_ptr );
+
+        // unblocks the NIC_TX server thread
+        thread_unblock( server_xp , THREAD_BLOCKED_CLIENT );
+
+#if DEBUG_SOCKET_SEND   
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_SEND < cycle )
+printk("\n[%s] thread[%x,%x] socket[%x,%d] register SEND => blocks on <IO> / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, fdid, cycle );
+#endif
+        // client thread blocks itself and deschedules
+        thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_IO );
+        sched_yield( "blocked in nic_io" );
+
+#if DEBUG_SOCKET_SEND   
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_SEND < cycle )
+printk("\n[%s] thread[%x,%x] socket[%x,%d] for SEND resumes / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, fdid, cycle );
+#endif
+        // take socket lock
+        remote_queuelock_acquire( socket_lock_xp );
+      
+        // get tx_valid, tx_todo, and tx_sts
+        tx_todo    = hal_remote_l32( XPTR( file_cxy , &socket_ptr->tx_todo ));
+        cmd_valid  = hal_remote_l32( XPTR( file_cxy , &socket_ptr->tx_valid ));
+        cmd_status = hal_remote_l32( XPTR( file_cxy , &socket_ptr->tx_sts ));
+
+        // reset tx_client in socket descriptor
+        hal_remote_s64( XPTR( file_cxy , &socket_ptr->tx_client  ) , XPTR_NULL );
+
+        // release socket lock
+        remote_queuelock_release( socket_lock_xp );
+      
+// check SEND command completed when TX client thread resumes
+assert( (((tx_todo == 0) || (cmd_status != CMD_STS_SUCCESS)) && (cmd_valid == false)),
+"illegal socket state when client thread resumes after TX_SEND\n"
+" tx_todo = %d / tx_status = %d / tx_valid = %d\n",
+tx_todo, cmd_status, cmd_valid );
+
+        // release the tx_buf
+        req.ptr = tx_buf;
+        kmem_free( &req );
+
+        if( cmd_status != CMD_STS_SUCCESS )
+        {
+
+#if DEBUG_SOCKET_SEND
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_RECV < cycle )
+printk("\n[%s] error %s for TX_SEND / socket[%x,%d] / thread[%x,%x]\n",
+__FUNCTION__, socket_cmd_sts_str(cmd_status), process->pid, fdid, process->pid, this->trdid );
+#endif
+            return -1;
+        }
+        else
+        {
+
+#if DEBUG_SOCKET_SEND
+cycle = (uint32_t)hal_get_cycles();
+if (DEBUG_SOCKET_SEND < cycle )
+printk("\n[%s] thread[%x,%x] success for SEND / socket[%x,%d] / length %d / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, fdid, length, cycle );
+#endif
+            return length;
+        }
+
+    }  // end TX_SEND command
+
+    ////////////////////////////////////////////////////////
+    else                                 // RX_RECV command
+    {
+
+#if DEBUG_SOCKET_RECV
+uint32_t    cycle = (uint32_t)hal_get_cycles();
+if (DEBUG_SOCKET_SEND < cycle )
+printk("\n[%s] thread[%x,%x] received RECV command for socket[%x,%d] / length %d / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, fdid, length, cycle );
+#endif
+        // check no previous RX command
+        if( (hal_remote_l32( XPTR( file_cxy , &socket_ptr->rx_valid )) == true) || 
+            (hal_remote_l64( XPTR( file_cxy , &socket_ptr->rx_client)) != XPTR_NULL) )
+        {
+            // release socket lock
+            remote_queuelock_release( socket_lock_xp );
+                    
+            printk("\n[ERROR] in %s : previous RX command on socket[%x,%d] / thread[%x,%x]\n",
+            __FUNCTION__, process->pid, fdid, process->pid, this->trdid );
+            return -1;
+        }
+
+        // return EOF for a TCP socket not in ESTAB state
+        if( (socket_type == SOCK_STREAM ) && (socket_state != TCP_STATE_ESTAB) )
+        { 
+            // release socket lock
+            remote_queuelock_release( socket_lock_xp );
+                    
+#if DEBUG_SOCKET_RECV  
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_RECV < cycle )
+printk("\n[%s] thread[%x,%x] socket[%x,%d] TCP connection closed / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, fdid, cycle );
+#endif
+            return 0;
+        }
+        // build extended pointer on socket.rx_buf
+        xptr_t rx_buf_xp   = XPTR( file_cxy , &socket_ptr->rx_buf );
+
+        // get rx_buf status
+        buf_status = remote_buf_status( rx_buf_xp );
+
+        if( buf_status == 0 )
+        {
+            // registers RX_RECV command in socket descriptor
+            hal_remote_s32( XPTR( file_cxy , &socket_ptr->rx_cmd    ) , CMD_RX_RECV );
+            hal_remote_s64( XPTR( file_cxy , &socket_ptr->rx_client ) , client_xp );
+            hal_remote_s32( XPTR( file_cxy , &socket_ptr->rx_valid  ) , true );
+
+            // release socket lock
+            remote_queuelock_release( socket_lock_xp );
+
+#if DEBUG_SOCKET_RECV  
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_RECV < cycle )
+printk("\n[%s] thread[%x,%x] socket[%x,%d] rx_buf empty => blocks on <IO> / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, fdid, cycle );
+#endif
+            // client thread blocks itself and deschedules
+            thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_IO );
+            sched_yield( "blocked in nic_io" );
+
+#if DEBUG_SOCKET_RECV  
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_RECV < cycle )
+printk("\n[%s] thread[%x,%x] socket[%x,%d] for RECV resumes / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, fdid, cycle );
+#endif
+            // take socket lock
+            remote_queuelock_acquire( socket_lock_xp );
+
+            // get rx_sts and rx_buf status
+            cmd_valid  = hal_remote_l32( XPTR( file_cxy , &socket_ptr->rx_valid ));
+            cmd_status = hal_remote_l32( XPTR( file_cxy , &socket_ptr->rx_sts ));
+            buf_status = remote_buf_status( rx_buf_xp );
+        
+assert( (((buf_status != 0) || (cmd_status != CMD_STS_SUCCESS)) && (cmd_valid == false)),
+"illegal socket state when client thread resumes after RX_RECV\n"
+" buf_status = %d / rx_sts = %d / rx_valid = %d\n",
+buf_status , cmd_status , cmd_valid );
+
+            // reset rx_client in socket descriptor
+            hal_remote_s64( XPTR( file_cxy , &socket_ptr->rx_client  ) , XPTR_NULL );
+
+            // reset rx_buf for an UDP socket
+            if( socket_type == SOCK_DGRAM ) remote_buf_reset( rx_buf_xp );
+
+            // release socket lock
+            remote_queuelock_release( socket_lock_xp );
+
+            if( cmd_status == CMD_STS_EOF )           // EOF (remote close) reported
+            {
+
+#if DEBUG_SOCKET_RECV
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_RECV < cycle )
+printk("\n[%s] EOF for RX_RECV / socket[%x,%d] / thread[%x,%x]\n",
+__FUNCTION__, process->pid, fdid, process->pid, this->trdid );
+#endif
+                return 0;
+            }
+            else if( cmd_status != CMD_STS_SUCCESS )   // other error reported
+            {
+
+#if DEBUG_SOCKET_RECV
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_SOCKET_RECV < cycle )
+printk("\n[%s] error %s for RX_RECV / socket[%x,%d] / thread[%x,%x]\n",
+__FUNCTION__, socket_cmd_sts_str(cmd_status), process->pid, fdid, process->pid, this->trdid );
+#endif
+                return -1;
+            }
+
+        }
+
+        // number of bytes extracted from rx_buf cannot be larger than u_buf size
+        moved_bytes = ( length < buf_status ) ? length : buf_status;
+
+        // move data from kernel rx_buf to user u_buf
+        remote_buf_get_to_user( rx_buf_xp,
+                                u_buf,
+                                moved_bytes );
+#if DEBUG_SOCKET_SEND
+cycle = (uint32_t)hal_get_cycles();
+if (DEBUG_SOCKET_SEND < cycle )
+printk("\n[%s] thread[%x,%x] success for RECV / socket[%x,%d] / length %d / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, process->pid, fdid, moved_bytes, cycle );
+#endif
+        return moved_bytes;
+
+    }  // end RX_RECV command
+} // end socket_move_data()
+
+
+///////////////////////////////////
+int socket_send( uint32_t    fdid,
+                 uint8_t   * u_buf,
+                 uint32_t    length )
+{
+    int nbytes = socket_move_data( true,           // SEND
+                                   fdid,
+                                   u_buf,
+                                   length,
+                                   false, 0, 0 );  // no explicit remote socket
+    return nbytes;
+
+}  // end socket_send()
+
+/////////////////////////////////////
+int socket_sendto( uint32_t    fdid,
+                   uint8_t   * u_buf,
+                   uint32_t    length,
+                   uint32_t    remote_addr,
+                   uint32_t    remote_port )
+{
+    int nbytes = socket_move_data( true,          // SEND
+                                   fdid,
+                                   u_buf,
+                                   length,
+                                   true,          // explicit remote socket
+                                   remote_addr,
+                                   remote_port );
+    return nbytes;
+
+}  // end socket_sendto()
+
+///////////////////////////////////
+int socket_recv( uint32_t    fdid,
+                 uint8_t   * u_buf,
+                 uint32_t    length )
+{
+    int nbytes = socket_move_data( false,          // RECV
+                                   fdid,
+                                   u_buf,
+                                   length,
+                                   false, 0, 0 );  // no explicit remote socket
+    return nbytes;
+
+} // end socket_recv()
+
+
+///////////////////////////////////////
+int socket_recvfrom( uint32_t    fdid,
+                     uint8_t   * u_buf,
+                     uint32_t    length,
+                     uint32_t    remote_addr,
+                     uint32_t    remote_port )
+{
+    int nbytes = socket_move_data( false,         // RECV
+                                   fdid,
+                                   u_buf,
+                                   length,
+                                   true,          // explicit remote socket
+                                   remote_addr,
+                                   remote_port );
+    return nbytes;
+
+}  // end socket_recvfrom()
+
+////////////////////////////////////////////
+void socket_display( xptr_t       socket_xp,
+                     const char * func_str )
+{
+    socket_t * socket = GET_PTR( socket_xp );
+    cxy_t      cxy    = GET_CXY( socket_xp );
+
+    pid_t      pid         = hal_remote_l32( XPTR( cxy , &socket->pid ));
+    fdid_t     fdid        = hal_remote_l32( XPTR( cxy , &socket->fdid ));
+    uint32_t   state       = hal_remote_l32( XPTR( cxy , &socket->state ));
+    uint32_t   channel     = hal_remote_l32( XPTR( cxy , &socket->nic_channel ));
+    uint32_t   local_addr  = hal_remote_l32( XPTR( cxy , &socket->local_addr ));
+    uint32_t   local_port  = hal_remote_l32( XPTR( cxy , &socket->local_port ));
+    uint32_t   remote_addr = hal_remote_l32( XPTR( cxy , &socket->remote_addr ));
+    uint32_t   remote_port = hal_remote_l32( XPTR( cxy , &socket->remote_port ));
+    uint32_t   tx_valid    = hal_remote_l32( XPTR( cxy , &socket->tx_valid ));
+    uint32_t   tx_cmd      = hal_remote_l32( XPTR( cxy , &socket->tx_cmd ));
+    uint32_t   tx_sts      = hal_remote_l32( XPTR( cxy , &socket->tx_sts ));
+    uint32_t   tx_len      = hal_remote_l32( XPTR( cxy , &socket->tx_len ));
+    uint32_t   tx_todo     = hal_remote_l32( XPTR( cxy , &socket->tx_todo ));
+    uint32_t   tx_una      = hal_remote_l32( XPTR( cxy , &socket->tx_una ));          
+    uint32_t   tx_nxt      = hal_remote_l32( XPTR( cxy , &socket->tx_nxt ));          
+    uint32_t   tx_wnd      = hal_remote_l32( XPTR( cxy , &socket->tx_wnd ));          
+    uint32_t   rx_valid    = hal_remote_l32( XPTR( cxy , &socket->rx_valid ));
+    uint32_t   rx_cmd      = hal_remote_l32( XPTR( cxy , &socket->rx_cmd ));
+    uint32_t   rx_sts      = hal_remote_l32( XPTR( cxy , &socket->rx_sts ));
+    uint32_t   rx_nxt      = hal_remote_l32( XPTR( cxy , &socket->rx_nxt ));          
+    uint32_t   rx_wnd      = hal_remote_l32( XPTR( cxy , &socket->rx_wnd ));          
+    uint32_t   rx_irs      = hal_remote_l32( XPTR( cxy , &socket->rx_irs ));          
+
+    if( func_str == NULL )
+    {
+        printk("\n****** socket[%x,%d] / xptr[%x,%x]*****\n",
+        pid, fdid, cxy, socket );
+    }
+    else
+    {
+        printk("\n***** socket[%x,%d] / xptr[%x,%x] / from %s *****\n",
+        pid, fdid, cxy, socket, func_str );
+    }
+    printk(" - state %s / channel %d\n"
+           " - local_addr %x / local_port %x\n"
+           " - remote_addr %x / remote_port %x\n"
+           " - tx_valid %d (%s) / tx_sts %d / tx_len %x / tx_todo %x\n"
+           " - tx_una %x / tx_nxt %x / tx_wnd %x\n"
+           " - rx_valid %d (%s) / rx_sts %d\n"
+           " - rx_nxt %x / rx_wnd %x / rx_irs %x\n",
+           socket_state_str(state), channel ,
+           local_addr, local_port,
+           remote_addr, remote_port,
+           tx_valid, socket_cmd_type_str(tx_cmd), tx_sts, tx_len, tx_todo,
+           tx_una, tx_nxt, tx_wnd,
+           rx_valid, socket_cmd_type_str(rx_cmd), rx_sts,
+           rx_nxt, rx_wnd, rx_irs );
+
+}  // end socket_display()
+
+
+
+
+
