/* * dev_txt.c - TXT (Text Terminal) generic device API implementation. * * Author Alain Greiner (2016) * * Copyright (c) UPMC Sorbonne Universites * * This file is part of ALMOS-MK * * ALMOS-MKH.is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2.0 of the License. * * ALMOS-MKH.is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ALMOS-kernel; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include ///////////////////////////////////////////////////////////////////////////////////////// // Extern global variables ///////////////////////////////////////////////////////////////////////////////////////// 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( 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 ); // 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 ) { wti_id = dev_icu_wti_alloc(); if( wti_id == -1 ) sched_yield(); else break; } // 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() ///////////////////////////////////////// error_t dev_txt_write( uint32_t channel, char * buffer, uint32_t count ) { 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 ) { 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()