Index: trunk/kernel/devices/dev_txt.c
===================================================================
--- trunk/kernel/devices/dev_txt.c	(revision 690)
+++ trunk/kernel/devices/dev_txt.c	(revision 1)
@@ -1,6 +1,6 @@
 /*
  * dev_txt.c - TXT (Text Terminal) generic device API implementation.
- *
- * Author  Alain Greiner    (2016,2017,2018,2019)
+ * 
+ * Author  Alain Greiner    (2016)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -22,11 +22,9 @@
  */
 
-#include <hal_kernel_types.h>
+#include <hal_types.h>
 #include <hal_special.h>
 #include <hal_remote.h>
-#include <hal_drivers.h>
+#include <soclib_tty.h>
 #include <thread.h>
-#include <remote_busylock.h>
-#include <chdev.h>
 #include <rpc.h>
 #include <printk.h>
@@ -37,99 +35,185 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 
-extern chdev_directory_t  chdev_dir;         // allocated in kernel_init.c
-
-
-#if (DEBUG_SYS_READ & 1)
-extern uint32_t enter_txt_read;
-extern uint32_t exit_txt_read;
-#endif
-
-#if (DEBUG_SYS_WRITE & 1)
-extern uint32_t enter_txt_write;
-extern uint32_t exit_txt_write;
-#endif
-
-///////////////////////////////////////////////////
-const char * dev_txt_type_str( dev_txt_cmd_t type )
-{
-    switch (type)
-    {
-        case (TXT_SYNC_WRITE): return "TXT_SYNC_WRITE";
-        case (TXT_READ):       return "TXT_READ";
-        case (TXT_WRITE):      return "TXT_WRITE";
-        default:               return "undefined";
-    }
-}
+extern devices_directory_t  devices_dir;         // allocated in kernel_init.c
+
+extern devices_input_irq_t  devices_input_irq;   // allocated in kernel_init.c
 
 //////////////////////////////////
-void dev_txt_init( chdev_t * txt )
-{
-    // For all TXT channels other than the TXT0 (kernel terminal), 
-    // the PIC chdev must be initialized before the TXT chdev, because
-    // the TXT chdev initialization requires the routing of an external IRQ
-
-    xptr_t    pic_xp  = chdev_dir.pic;
-    uint32_t  channel = txt->channel;
-    uint32_t  impl    = txt->impl;
-    bool_t    is_rx   = txt->is_rx;
-
-assert( __FUNCTION__, ((pic_xp != XPTR_NULL) || (channel == 0)) ,
-"PIC not initialised before TXT" );
-
-    // set chdev name
-    if( is_rx ) snprintk( txt->name , 16 , "txt%d_rx" , channel );
-    else        snprintk( txt->name , 16 , "txt%d_tx" , channel );
-
-    // set TXT chdev extension
-    txt->ext.txt.owner_xp = XPTR_NULL;
-    xlist_root_init( XPTR( local_cxy, &txt->ext.txt.root ) );
-    remote_busylock_init( XPTR( local_cxy , &txt->ext.txt.lock ), LOCK_CHDEV_TXTLIST );
+void dev_txt_init( xptr_t  dev_xp )
+{
+    // get device descriptor cluster and local pointer
+    cxy_t      dev_cxy = GET_CXY( dev_xp );
+    device_t * dev_ptr = (device_t *)GET_PTR( dev_xp );
+
+    // get implementation index from device descriptor
+    uint32_t  impl = hal_remote_lw( XPTR( dev_cxy , &dev_ptr->impl ) );
+
+    // get channel index from device descriptor
+    // uint32_t  channel = hal_remote_lw( XPTR( dev_cxy , &dev_ptr->channel ) );
+
+    // set fields "cmd", "isr", and "name" in device descriptor
+    // and call implementation specific init function
+
+    // TODO replace fixed "TXT_TTY" name by a channel indexed "TXT_TTY%d" name
+    // as soon as the sprintk() function is available
+
+    if( impl == IMPL_TXT_TTY )
+    {
+        hal_remote_spt( XPTR( dev_cxy , &dev_ptr->cmd ) , &soclib_tty_command );
+        hal_remote_spt( XPTR( dev_cxy , &dev_ptr->isr ) , &soclib_tty_isr );
+        // sprintk( name , "TXT_TTY%d" , channel )
+        hal_remote_memcpy( XPTR( dev_cxy , &dev_ptr->name ),
+                           XPTR( local_cxy , "TXT_TTY" ) , 16 );
+        soclib_tty_init( dev_xp );
+    }
+    else
+    {
+        printk("\n[PANIC] in %s: undefined TXT device implementation\n", __FUNCTION__ );
+        hal_core_sleep();
+    }
+
+    // create server thread
+    xptr_t     new_thread_xp;
+    thread_t * new_thread_ptr;
+    error_t    error;
+
+    if( dev_cxy == local_cxy )         // device cluster is local
+    {
+        error = thread_kernel_create( &new_thread_ptr,
+                                      THREAD_DEV,
+                                      &dev_txt_server,
+                                      dev_ptr,
+                                      cluster_select_local_core() );
+ 
+        new_thread_xp = XPTR( local_cxy , new_thread_ptr );
+    }
+    else                                        // device cluster is remote
+    {
+        rpc_thread_kernel_create_client( dev_cxy,
+                                         THREAD_DEV,
+                                         &dev_txt_server,
+                                         dev_ptr,
+                                         &new_thread_xp,
+                                         &error );
+
+        new_thread_ptr = (thread_t *)GET_PTR( new_thread_xp );
+    }
+    if( error )
+    {
+        printk("\n[PANIC] in %s : cannot create server thread\n", __FUNCTION__ );
+        hal_core_sleep();
+    }
+
+    // initialises server field in device descriptor
+    hal_remote_spt( XPTR( dev_cxy , &dev_ptr->server ) , new_thread_ptr );
     
-    // call driver init function
-    hal_drivers_txt_init( txt );
-
-    // no server thread and no IRQ routing for TXT0 
-    // except for LETI where there MUST be IRQ routing
-    // no server thread for IMPL_TXT_RS2 (multiplexed in software, not hardware)
-    if( channel != 0 && impl != IMPL_TXT_RS2 )
-    {
-        // select a core to execute the server thread
-        lid_t lid = cluster_select_local_core( local_cxy );
-
-        // The unique IRQ from cluster 00's MTTY must be bound to a RX chdev
-        // so that all IRQs are considered as RX IRQs and treated as such
-        // Indeed, IRQs are raised by the MTTY only on user keystroke, so IRQs must
-        // always be considered as RX IRQs.
-        if ( ( impl != IMPL_TXT_MTY ) || ( channel == 1 && is_rx == 1 ) ) 
+    // start server thread
+    thread_unblock( new_thread_xp , THREAD_BLOCKED_GLOBAL );
+
+} // end dev_txt_init()
+
+
+//////////////////////////////////////////////////////////////////////////////////
+// This static function is called by dev_txt_read(), dev_txt_write(), and
+// dev_txt_sync_write() functions. 
+// For the TXT_READ and TXT_WRITE operation types:
+//  - it build the command, and registers it in the calling thread descriptor. 
+//  - then, it registers the calling thead in device waiting queue.
+//  - finally, it blocks on the THREAD_BLOCKED_DEV condition and deschedule.
+// For the TXT_SYNC_WRITE operation type:
+//  - it directly call the relevant driver, using a busy waiting policy.
+////////////////////////////////////i/////////////////////////////////////////////
+static error_t dev_txt_access( uint32_t   type,
+                               uint32_t   channel,
+                               char     * buffer,
+                               uint32_t   count )
+{
+    thread_t * this = CURRENT_THREAD;
+
+    txt_dmsg("\n[INFO] in %s : thread %x in process %x enters\n", 
+                 __FUNCTION__ , this->trdid , this->process->pid );
+
+    // check channel argument
+    if( channel >= CONFIG_MAX_TXT_CHANNELS )
+    {
+        printk("\n[PANIC] in %s : illegal channel index = %d\n", __FUNCTION__ , channel );
+        hal_core_sleep();
+    }
+
+    // get extended pointer on remote TXT device descriptor
+    xptr_t  dev_xp = devices_dir.txt[channel];
+
+    if ( dev_xp == XPTR_NULL )
+    {
+        printk("\n[PANIC] in %s : undefined TXT device descriptor for channel %d\n",
+               __FUNCTION__ , channel );
+        hal_core_sleep();
+    }
+
+    // register command in calling thread descriptor
+    this->dev.txt.dev_xp  = dev_xp;
+    this->dev.txt.type    = type;
+    this->dev.txt.buf_xp  = XPTR( local_cxy , buffer );
+    this->dev.txt.count   = count;
+
+
+    if( (type == TXT_READ) || (type == TXT_WRITE) )  // descheduling policy
+    {
+        // get a free WTI mailbox  
+        uint32_t wti_id;
+        while( 1 )
         {
-        // bind IRQ to the selected core
-        dev_pic_bind_irq( lid , txt );
-
-        // enable IRQ
-        dev_pic_enable_irq( lid , XPTR( local_cxy , txt ) );
+            wti_id = dev_icu_wti_alloc();
+            if( wti_id == -1 )  sched_yield();
+            else                break;
         }
 
-        // create server thread
-        thread_t * new_thread;
-        error_t    error;
-
-        error = thread_kernel_create( &new_thread,
-                                      THREAD_DEV,
-                                      &chdev_server_func,
-                                      txt,
-                                      lid );
-
-        assert( __FUNCTION__, (error == 0) , "cannot create server thread" );
-
-        // set "server" field in chdev descriptor
-        txt->server = new_thread;
-
-        // set "chdev" field in thread descriptor
-        new_thread->chdev = txt;
-
-        // unblock server thread
-        thread_unblock( XPTR( local_cxy , new_thread ) , THREAD_BLOCKED_GLOBAL );
-    }
-}  // end dev_txt_init()
+        // enable WTI IRQ in local ICU and update WTI interrupt vector
+        dev_icu_enable_irq( local_cxy , CURRENT_CORE->lid , WTI_TYPE , wti_id , dev_xp );
+
+        // link TXT IRQ to WTI mailbox in PIC component 
+        uint32_t irq_id = devices_input_irq.txt[channel];
+        dev_pic_bind_irq( irq_id , local_cxy , wti_id );
+
+        // register client thread in waiting queue, activate server thread
+        // block client thread on THREAD_BLOCKED_IO and deschedule.
+        // it is re-activated by the ISR signaling IO operation completion.
+        device_register_command( dev_xp , this );
+
+        // access PIC to unlink the IOC IRQ
+        dev_pic_unbind_irq( irq_id );
+
+        // disable WTI IRQ in ICU and update interrupt vector
+        dev_icu_disable_irq( local_cxy , CURRENT_CORE->lid , WTI_TYPE , wti_id );
+
+        // release  WTI mailbox
+        dev_icu_wti_release( wti_id );
+
+        txt_dmsg("\n[INFO] in %s : thread %x in process %x completes / error = %d\n", 
+                     __FUNCTION__ , this->trdid , this->process->pid , this->dev.txt.error );
+    }
+    else if( type == TXT_SYNC_WRITE )            // busy waiting policy
+    {
+        // get driver command function pointer from remote TXT device descriptor
+        cxy_t       dev_cxy = GET_CXY( dev_xp );
+        device_t  * dev_ptr = (device_t *)GET_PTR( dev_xp );
+        dev_cmd_t * cmd = (dev_cmd_t *)hal_remote_lpt( XPTR( dev_cxy , &dev_ptr->cmd ) );
+
+        // take the TXT device lock, because the calling thread does NOT
+        // register in device waiting queue for this synchronous command,
+        // and has exclusive access to the terminal...
+        remote_spinlock_lock( XPTR( dev_cxy , &dev_ptr->wait_lock ) );
+ 
+        // call directly driver command 
+        cmd( XPTR( local_cxy , this ) );
+
+        // release the TXT device lock
+        remote_spinlock_unlock( XPTR( dev_cxy , &dev_ptr->wait_lock ) );
+    }    
+
+    // return I/O operation status from calling thread descriptor
+    return this->dev.txt.error;  
+
+}  // end dev_txt_access()
 
 /////////////////////////////////////////
@@ -138,182 +222,71 @@
                        uint32_t   count )
 {
-    error_t error;
-
-#if (DEBUG_SYS_WRITE & 1)
-enter_txt_write = hal_time_stamp();
-#endif
-
-    thread_t * this = CURRENT_THREAD;
-
-#if DEBUG_DEV_TXT_TX
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_DEV_TXT_TX < cycle )
-printk("\n[%s] thread[%x,%x] enters for <%s> / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, buffer, cycle );
-#endif
-
-// check channel argument
-assert( __FUNCTION__, (channel < CONFIG_MAX_TXT_CHANNELS) , "illegal channel index" );
-
-    // get pointers on chdev
-    xptr_t    dev_xp  = chdev_dir.txt_tx[channel];
-    cxy_t     dev_cxy = GET_CXY( dev_xp );
-    chdev_t * dev_ptr = GET_PTR( dev_xp );
-
-// check dev_xp 
-assert( __FUNCTION__, (dev_xp != XPTR_NULL) , "undefined TXT chdev descriptor" );
-
-    // If we use MTTY (vci_multi_tty), we do a synchronous write on TXT[0]
-    // If we use TTY  (vci_tty_tsar), we do a standard asynchronous write
-    // TODO this is not very clean ... [AG]
-
-    if( dev_ptr->impl == IMPL_TXT_MTY )  
-    {
-        // get driver command function
-        dev_aux_t * aux = (dev_aux_t *)hal_remote_lpt( XPTR( dev_cxy , &dev_ptr->aux ) );
-
-        // build arguments structure
-        txt_sync_args_t  args;
-        args.dev_xp = dev_xp;
-        args.buffer = buffer;
-        args.count  = count;
-        args.channel = channel;
-
-        // call directly the driver function
-        aux( &args );
-
-        error = 0;
-    }
-    else
-    {
-        // register command in calling thread descriptor
-        this->txt_cmd.dev_xp  = dev_xp;
-        this->txt_cmd.type    = TXT_WRITE;
-        this->txt_cmd.buf_xp  = XPTR( local_cxy , buffer );
-        this->txt_cmd.count   = count;
-
-        // register client thread in waiting queue, activate server thread
-        // block client thread on THREAD_BLOCKED_IO and deschedule.
-        // it is re-activated by the ISR signaling IO operation completion.
-        chdev_register_command( dev_xp );
-
-        // get I/O operation status from calling thread descriptor
-        error = this->txt_cmd.error;
-
-        if( error )
-        {
-            printk("\n[ERROR] in %s : cannot write string %s / cycle %d\n",
-            __FUNCTION__, buffer, (uint32_t)hal_get_cycles() );
-        }
-    }
-
-#if DEBUG_DEV_TXT_TX
-cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_DEV_TXT_TX < cycle )
-printk("\n[%s] thread[%x,%x] exit /  cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, cycle );
-#endif
-
-#if (DEBUG_SYS_WRITE & 1)
-exit_txt_write = hal_time_stamp();
-#endif
-
-    return error;
-
-}  // end dev_txt_write()
-
+    return dev_txt_access( TXT_WRITE , channel , buffer , count );
+}
+ 
+///////////////////////////////////////////////
+error_t dev_txt_sync_write( uint32_t   channel,
+                            char     * buffer,
+                            uint32_t   count )
+{
+    return dev_txt_access( TXT_SYNC_WRITE , channel , buffer , count );
+}
+ 
 /////////////////////////////////////////
 error_t dev_txt_read( uint32_t   channel,
                       char     * buffer )
 {
-    error_t error;
-
-#if (DEBUG_SYS_READ & 1)
-enter_txt_read = hal_time_stamp();
-#endif
-
-    thread_t * this = CURRENT_THREAD;
-
-#if DEBUG_DEV_TXT_RX
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_DEV_TXT_RX < cycle )
-printk("\n[%s] thread[%x,%x] enters / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, cycle );
-#endif
-
-// check channel argument
-assert( __FUNCTION__, (channel < CONFIG_MAX_TXT_CHANNELS) , "illegal channel index" );
-
-    // get pointers on chdev
-    xptr_t    dev_xp  = chdev_dir.txt_rx[channel];
-
-// check dev_xp 
-assert( __FUNCTION__, (dev_xp != XPTR_NULL) , "undefined TXT chdev descriptor" );
-
-    // register command in calling thread descriptor
-    this->txt_cmd.dev_xp  = dev_xp;
-    this->txt_cmd.type    = TXT_READ;
-    this->txt_cmd.buf_xp  = XPTR( local_cxy , buffer );
-    this->txt_cmd.count   = 1;
-
-    // register client thread in waiting queue, activate server thread
-    // block client thread on THREAD_BLOCKED_IO and deschedule.
-    // it is re-activated by the ISR signaling IO operation completion.
-    chdev_register_command( dev_xp );
-
-    // get I/O operation status from calling thread descriptor
-    error = this->txt_cmd.error;
-
-    if( error )
-    {
-         printk("\n[ERROR] in %s : cannot write string %s / cycle %d\n",
-         __FUNCTION__, buffer, (uint32_t)hal_get_cycles() );
-    }
-
-#if DEBUG_DEV_TXT_RX
-cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_DEV_TXT_RX < cycle )
-printk("\n[%s] thread[%x,%x] get character <%c> / cycle %d\n",
-__FUNCTION__, this->process->pid, this->trdid, *buffer, cycle );
-#endif
-
-#if (DEBUG_SYS_READ & 1)
-exit_txt_read = hal_time_stamp();
-#endif
-
-    return error;
-
-}  // end dev_txt_read()
-
-////////////////////////////////////////////////
-error_t dev_txt_sync_write( const char * buffer,
-                            uint32_t     count )
-{
-    // get extended pointers on TXT[0] chdev
-    xptr_t    dev_xp  = chdev_dir.txt_tx[0];
-    cxy_t     dev_cxy = GET_CXY( dev_xp );
-    chdev_t * dev_ptr = GET_PTR( dev_xp );
-
-    if( dev_xp != XPTR_NULL) 
-    {
-        // get driver command function
-        dev_aux_t * aux = (dev_aux_t *)hal_remote_lpt( XPTR( dev_cxy , &dev_ptr->aux ) );
-
-        // build arguments structure
-        txt_sync_args_t  args;
-        args.dev_xp = dev_xp;
-        args.buffer = buffer;
-        args.count  = count;
-        args.channel = 0;
-
-        // call driver function
-        aux( &args );
-
-        return 0;
-    }
-    else
-    {
-        return -1;
-    }
-}   // end dev_txt_sync_write() 
-
+    return dev_txt_access( TXT_READ , channel , buffer , 1 );
+}
+
+/////////////////////////////////////
+void dev_txt_server( device_t * dev )
+{
+    xptr_t          client_xp;    // extended pointer on waiting thread
+    cxy_t           client_cxy;   // cluster of client thread
+    thread_t      * client_ptr;   // local pointer on client thread
+    thread_t      * server;       // local pointer on server thread
+    xptr_t          root_xp;      // extended pointer on device waiting queue root
+
+    server = CURRENT_THREAD;
+
+    root_xp = XPTR( local_cxy , &dev->wait_root );
+
+	// infinite loop handling commands of threads 
+    // registered in the TXT waiting queue
+    while( 1 )
+    {
+        // get lock protecting queue
+        remote_spinlock_lock( XPTR( local_cxy , &dev->wait_lock ) );
+
+        // block and deschedule server thread if waiting queue empty 
+        if( xlist_is_empty( root_xp ) )
+        {
+            thread_block( server , THREAD_BLOCKED_DEV_QUEUE );
+            remote_spinlock_unlock( XPTR( local_cxy , &dev->wait_lock ) );
+            sched_yield();
+        }
+        else
+        {
+            remote_spinlock_unlock( XPTR( local_cxy , &dev->wait_lock ) );
+        } 
+
+        // get extended pointer on first client thread
+        client_xp = XLIST_FIRST_ELEMENT( root_xp , thread_t , wait_list );
+
+        // call driver command function to start I/O operation
+        dev->cmd( client_xp );
+        
+        // get client thread cluster and local pointer
+        client_cxy = GET_CXY( client_xp );
+        client_ptr = (thread_t *)GET_PTR( client_xp );
+
+        // remove the client thread from waiting queue
+        remote_spinlock_lock( XPTR( local_cxy , &dev->wait_lock ) );
+        xlist_unlink( XPTR( client_cxy , &client_ptr->wait_list ) );
+        remote_spinlock_unlock( XPTR( local_cxy , &dev->wait_lock ) );
+
+    }  // end while
+
+}  // end dev_txt_server()
+
