/////////////////////////////////////////////////////////////////////////////////// // File : tty_driver.c // Date : 23/05/2013 // Author : alain greiner // Copyright (c) UPMC-LIP6 /////////////////////////////////////////////////////////////////////////////////// // The tty_driver.c and tty_drivers.h files are part ot the GIET-VM kernel. // This driver supports the SocLib vci_multi_tty component. // // The total number of TTY terminals must be defined by the configuration // parameter NB_TTY_CHANNELS in the hard_config.h file. // // The "system" terminal is TTY[0]. // The "user" TTYs are allocated to applications by the GIET in the boot phase, // as defined in the mapping_info data structure. The corresponding tty_id must // be stored in the context of the task by the boot code. // // The seg_tty_base must be defined in giet_vsegs.ld file. /////////////////////////////////////////////////////////////////////////////////// // Implementation note: // // All physical accesses to device registers are done by the two // _tty_get_register(), _tty_set_register() low-level functions, // that are handling virtual / physical addressing. /////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #if !defined(NB_TTY_CHANNELS) # error: You must define NB_TTY_CHANNELS in the hard_config.h file #endif #if (NB_TTY_CHANNELS < 1) # error: NB_TTY_CHANNELS cannot be smaller than 1! #endif #define in_unckdata __attribute__((section (".unckdata"))) #define in_kdata __attribute__((section (".kdata"))) ////////////////////////////////////////////////////////////////////////////// // TTY global variables ////////////////////////////////////////////////////////////////////////////// in_unckdata volatile unsigned int _tty_rx_buf[NB_TTY_CHANNELS]; in_unckdata volatile unsigned int _tty_rx_full[NB_TTY_CHANNELS] = { [0 ... NB_TTY_CHANNELS - 1] = 0 }; in_kdata unsigned int _tty_lock[NB_TTY_CHANNELS] = { [0 ... NB_TTY_CHANNELS - 1] = 0 }; ////////////////////////////////////////////////////////////////////////////// // This low level function returns the value of register (channel / index) ////////////////////////////////////////////////////////////////////////////// unsigned int _tty_get_register( unsigned int channel, unsigned int index ) { unsigned int* vaddr = (unsigned int*)&seg_tty_base + channel*TTY_SPAN + index; return _io_extended_read( vaddr ); } ////////////////////////////////////////////////////////////////////////////// // This low level function set a new value in register (channel / index) ////////////////////////////////////////////////////////////////////////////// void _tty_set_register( unsigned int channel, unsigned int index, unsigned int value ) { unsigned int* vaddr = (unsigned int*)&seg_tty_base + channel*TTY_SPAN + index; _io_extended_write( vaddr, value ); } ///////////////////////////////////////////////////////////////////////////////// // This non-blocking function writes a character string from a fixed-length // buffer to a TTY terminal identified by the channel argument. // This function is intended to be used to handle a system call, and should // not be used by the kernel for log messages on TTY 0. // protecting exclusive access to the selected terminal. // If channel argument is 0xFFFFFFFF, the TTY index is found in the task context. // This is a non blocking call: it tests the TTY_STATUS register, and stops // the transfer as soon as the TTY_STATUS[WRITE] bit is set. ///////////////////////////////////////////////////////////////////////////////// // Returns the number of characters that have been written. ///////////////////////////////////////////////////////////////////////////////// unsigned int _tty_write( const char* buffer, unsigned int length, // number of characters unsigned int channel) // channel index { unsigned int nwritten; // compute and check tty channel if( channel == 0xFFFFFFFF ) channel = _get_context_slot(CTX_TTY_ID); if( channel >= NB_TTY_CHANNELS ) return -1; // write string to TTY channel for (nwritten = 0; nwritten < length; nwritten++) { // check tty's status if ( _tty_get_register( channel, TTY_STATUS ) & 0x2 ) break; // write one byte if (buffer[nwritten] == '\n') { _tty_set_register( channel, TTY_WRITE, (unsigned int)'\r' ); } _tty_set_register( channel, TTY_WRITE, (unsigned int)buffer[nwritten] ); } return nwritten; } ////////////////////////////////////////////////////////////////////////////// // This non-blocking function fetches one character from the // terminal identified by the channel argument. If the channel argument // is 0xFFFFFFFF, the channel index is obtained from the current task context. // It uses the TTY_GET_IRQ[tty_id] interrupt and the buffer must have been // filled by the TTY_ISR. // It test the _tty_rx_full[tty_id] variable, read the _tty_rx_buf[tty_id] // buffer, writes this character to the target buffer, and resets the // _tty_rx_full[tty_id] register. // The length argument is not used. ////////////////////////////////////////////////////////////////////////////// // Returns the number of characters that have been read (0/1). ////////////////////////////////////////////////////////////////////////////// unsigned int _tty_read( char* buffer, unsigned int length, // unused unsigned int channel) // channel index { // compute and check tty channel if( channel == 0xFFFFFFFF ) channel = _get_context_slot(CTX_TTY_ID); if( channel >= NB_TTY_CHANNELS ) return -1; // read one character from TTY channel if (_tty_rx_full[channel] == 0) { return 0; } else { *buffer = _tty_rx_buf[channel]; _tty_rx_full[channel] = 0; return 1; } } ////////////////////////////////////////////////////////////////////////////// // This function try to take the hardwired lock protecting // exclusive access to TTY terminal identified by the "channel" argument. // It enters a critical section before taking the lock, and save the SR value // at address defined by the "save_sr_ptr" argument. // It returns only when the lock has been successfully taken. ////////////////////////////////////////////////////////////////////////////// void _tty_get_lock( unsigned int channel, unsigned int * save_sr_ptr ) { if( channel >= NB_TTY_CHANNELS ) _exit(); _it_disable( save_sr_ptr ); // while ( _tty_get_register( channel, TTY_CONFIG ) ); // busy waiting _get_lock( &_tty_lock[channel] ); } ////////////////////////////////////////////////////////////////////////////// // This function releases the hardwired lock protecting // exclusive access to TTY terminal identified by the channel argument. // It exit the critical section after lock release, and restore SR value // from address defined by the "save_sr_ptr" argument. ////////////////////////////////////////////////////////////////////////////// void _tty_release_lock( unsigned int channel, unsigned int * save_sr_ptr ) { if( channel >= NB_TTY_CHANNELS ) _exit(); // _tty_set_register( channel, TTY_CONFIG, 0 ); _release_lock( &_tty_lock[channel] ); _it_restore( save_sr_ptr ); } /////////////////////////////////////////////////////////////////////////////////// // This ISR handles the IRQ signaling that the RX buffer is not empty. // IT can be an HWI or an SWI. // There is one communication buffer _tty_rx_buf[i] and one synchronisation // variable _tty_rx_full[i] per channel. // Does nothing if the TTY_RX buffer is empty, or if the kernel buffer is full // when the ISR is called. /////////////////////////////////////////////////////////////////////////////////// void _tty_rx_isr( unsigned int irq_type, // HWI / WTI unsigned int irq_id, // index returned by XCU unsigned int channel ) // TTY channel { unsigned int cluster_xy = _get_procid() / NB_PROCS_MAX; // get TTY status unsigned int status = _tty_get_register( channel, TTY_STATUS ); // check both TTY status and kernel buffer status: // does nothing if kernel buffer full or tty_buffer empty if ( ((status & 0x1) == 0) || (_tty_rx_full[channel] != 0) ) return; // reset WTI in XCU if WTI type if ( irq_type == IRQ_TYPE_WTI ) { unsigned int value; _xcu_get_wti_value( cluster_xy, irq_id, &value ); } // transfer character to kernel buffer and acknowledge TTY IRQ _tty_rx_buf[channel] = _tty_get_register( channel, TTY_READ ); // set kernel buffer status asm volatile( "sync" ); _tty_rx_full[channel] = 1; #if GIET_DEBUG_IRQS // we don't take the TTY lock to avoid deadlock unsigned int x = cluster_xy >> Y_WIDTH; unsigned int y = cluster_xy & ((1<