From 7fe60435bce6595a9c58a9bfd8244d74b5320e96 Mon Sep 17 00:00:00 2001 From: Benjamin Franzke Date: Tue, 15 Jan 2013 08:46:13 +0100 Subject: Import DirectFB141_2k11R3_beta5 --- Source/DirectFB/systems/fbdev/vt.c | 675 +++++++++++++++++++++++++++++++++++++ 1 file changed, 675 insertions(+) create mode 100755 Source/DirectFB/systems/fbdev/vt.c (limited to 'Source/DirectFB/systems/fbdev/vt.c') diff --git a/Source/DirectFB/systems/fbdev/vt.c b/Source/DirectFB/systems/fbdev/vt.c new file mode 100755 index 0000000..02bad77 --- /dev/null +++ b/Source/DirectFB/systems/fbdev/vt.c @@ -0,0 +1,675 @@ +/* + (c) Copyright 2001-2009 The world wide DirectFB Open Source Community (directfb.org) + (c) Copyright 2000-2004 Convergence (integrated media) GmbH + + All rights reserved. + + Written by Denis Oliver Kropp , + Andreas Hundt , + Sven Neumann , + Ville Syrjälä and + Claudio Ciccani . + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include + +#include /* Needs to be included before dfb_types.h */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "fbdev.h" +#include "fb.h" +#include "vt.h" + +D_DEBUG_DOMAIN( VT, "FBDev/VT", "FBDev System Module VT Handling" ); + +/* + * FIXME: the following looks like a bad hack. + * + * SIGUNUSED is no longer unused, but is defined for backwards compatibility. + * sparc, mips and alpha signal.h however do not define SIGUNUSED. + */ + +#ifdef SIGUNUSED + #define SIG_SWITCH_FROM (SIGUNUSED + 10) + #define SIG_SWITCH_TO (SIGUNUSED + 11) +#else + #define SIG_SWITCH_FROM (31 + 10) + #define SIG_SWITCH_TO (31 + 11) +#endif + +#ifndef SI_KERNEL +/* glibc 2.1.x doesn't have this in /usr/include/bits/siginfo.h */ + #define SI_KERNEL 0x80 +#endif + + +extern FBDev *dfb_fbdev; + +static VirtualTerminal *dfb_vt = NULL; + +static DFBResult vt_init_switching( void ); +static int vt_get_fb( int vt ); +static void vt_set_fb( int vt, int fb ); +static void *vt_thread( DirectThread *thread, void *arg ); + +static void vt_start_flushing( void ); +static void vt_stop_flushing( void ); +static void *vt_flush_thread( DirectThread *thread, void *arg ); + +DFBResult +dfb_vt_initialize( void ) +{ + DFBResult ret; + struct vt_stat vs; + + D_DEBUG_AT( VT, "%s()\n", __FUNCTION__ ); + + dfb_vt = D_CALLOC( 1, sizeof(VirtualTerminal) ); + if (!dfb_vt) + return D_OOM(); + + setsid(); + dfb_vt->fd0 = open( "/dev/tty0", O_RDONLY | O_NOCTTY ); + if (dfb_vt->fd0 < 0) { + if (errno == ENOENT) { + dfb_vt->fd0 = open( "/dev/vc/0", O_RDONLY | O_NOCTTY ); + if (dfb_vt->fd0 < 0) { + if (errno == ENOENT) { + D_PERROR( "DirectFB/core/vt: Couldn't open " + "neither `/dev/tty0' nor `/dev/vc/0'!\n" ); + } + else { + D_PERROR( "DirectFB/core/vt: " + "Error opening `/dev/vc/0'!\n" ); + } + + D_FREE( dfb_vt ); + dfb_vt = NULL; + + return DFB_INIT; + } + } + else { + D_PERROR( "DirectFB/core/vt: Error opening `/dev/tty0'!\n"); + + D_FREE( dfb_vt ); + dfb_vt = NULL; + + return DFB_INIT; + } + } + + if (ioctl( dfb_vt->fd0, VT_GETSTATE, &vs ) < 0) { + D_PERROR( "DirectFB/core/vt: VT_GETSTATE failed!\n" ); + close( dfb_vt->fd0 ); + D_FREE( dfb_vt ); + dfb_vt = NULL; + return DFB_INIT; + } + + dfb_vt->prev = vs.v_active; + + + if (!dfb_config->vt_switch) { + if (dfb_config->vt_num != -1) + dfb_vt->num = dfb_config->vt_num; + else + dfb_vt->num = dfb_vt->prev; + + /* move vt to framebuffer */ + dfb_vt->old_fb = vt_get_fb( dfb_vt->num ); + vt_set_fb( dfb_vt->num, -1 ); + } + else { + if (dfb_config->vt_num == -1) { + int n; + + n = ioctl( dfb_vt->fd0, VT_OPENQRY, &dfb_vt->num ); + if (n < 0 || dfb_vt->num == -1) { + D_PERROR( "DirectFB/core/vt: Cannot allocate VT!\n" ); + close( dfb_vt->fd0 ); + D_FREE( dfb_vt ); + dfb_vt = NULL; + return DFB_INIT; + } + } + else { + dfb_vt->num = dfb_config->vt_num; + } + + /* move vt to framebuffer */ + dfb_vt->old_fb = vt_get_fb( dfb_vt->num ); + vt_set_fb( dfb_vt->num, -1 ); + + /* switch to vt */ + while (ioctl( dfb_vt->fd0, VT_ACTIVATE, dfb_vt->num ) < 0) { + if (errno == EINTR) + continue; + D_PERROR( "DirectFB/core/vt: VT_ACTIVATE failed!\n" ); + close( dfb_vt->fd0 ); + D_FREE( dfb_vt ); + dfb_vt = NULL; + return DFB_INIT; + } + + while (ioctl( dfb_vt->fd0, VT_WAITACTIVE, dfb_vt->num ) < 0) { + if (errno == EINTR) + continue; + D_PERROR( "DirectFB/core/vt: VT_WAITACTIVE failed!\n" ); + close( dfb_vt->fd0 ); + D_FREE( dfb_vt ); + dfb_vt = NULL; + return DFB_INIT; + } + + usleep( 40000 ); + } + + ret = vt_init_switching(); + if (ret) { + if (dfb_config->vt_switch) { + D_DEBUG_AT( VT, " -> switching back...\n" ); + ioctl( dfb_vt->fd0, VT_ACTIVATE, dfb_vt->prev ); + ioctl( dfb_vt->fd0, VT_WAITACTIVE, dfb_vt->prev ); + D_DEBUG_AT( VT, " -> ...switched back\n" ); + ioctl( dfb_vt->fd0, VT_DISALLOCATE, dfb_vt->num ); + } + + close( dfb_vt->fd0 ); + D_FREE( dfb_vt ); + dfb_vt = NULL; + return ret; + } + + vt_start_flushing(); + + dfb_fbdev->vt = dfb_vt; + + return DFB_OK; +} + +DFBResult +dfb_vt_join( void ) +{ + D_DEBUG_AT( VT, "%s()\n", __FUNCTION__ ); + + dfb_vt_detach( true ); + + return DFB_OK; +} + +DFBResult +dfb_vt_shutdown( bool emergency ) +{ + const char cursoron_str[] = "\033[?0;0;0c"; + const char blankon_str[] = "\033[9;10]"; + + D_DEBUG_AT( VT, "%s()\n", __FUNCTION__ ); + + if (!dfb_vt) + return DFB_OK; + + vt_stop_flushing(); + + if (dfb_config->vt_switching) { + if (ioctl( dfb_vt->fd, VT_SETMODE, &dfb_vt->vt_mode ) < 0) + D_PERROR( "DirectFB/fbdev/vt: Unable to restore VT mode!!!\n" ); + + sigaction( SIG_SWITCH_FROM, &dfb_vt->sig_usr1, NULL ); + sigaction( SIG_SWITCH_TO, &dfb_vt->sig_usr2, NULL ); + + direct_thread_cancel( dfb_vt->thread ); + direct_thread_join( dfb_vt->thread ); + direct_thread_destroy( dfb_vt->thread ); + + pthread_mutex_destroy( &dfb_vt->lock ); + pthread_cond_destroy( &dfb_vt->wait ); + } + + if (dfb_config->kd_graphics) { + if (ioctl( dfb_vt->fd, KDSETMODE, KD_TEXT ) < 0) + D_PERROR( "DirectFB/fbdev/vt: KD_TEXT failed!\n" ); + } + else { + write( dfb_vt->fd, blankon_str, sizeof(blankon_str) ); + } + write( dfb_vt->fd, cursoron_str, sizeof(cursoron_str) ); + + if (tcsetattr( dfb_vt->fd, TCSAFLUSH, &dfb_vt->old_ts ) < 0) + D_PERROR("DirectFB/fbdev/vt: tcsetattr for original values failed!\n"); + + if (ioctl( dfb_vt->fd, KDSKBMODE, K_XLATE ) < 0) + D_PERROR( "DirectFB/fbdev/vt: K_XLATE failed!\n" ); + + if (dfb_config->vt_switch) { + D_DEBUG_AT( VT, " -> switching back...\n" ); + + if (ioctl( dfb_vt->fd0, VT_ACTIVATE, dfb_vt->prev ) < 0) + D_PERROR( "DirectFB/core/vt: VT_ACTIVATE" ); + + if (ioctl( dfb_vt->fd0, VT_WAITACTIVE, dfb_vt->prev ) < 0) + D_PERROR( "DirectFB/core/vt: VT_WAITACTIVE" ); + + D_DEBUG_AT( VT, " -> switched back...\n" ); + + usleep( 40000 ); + + /* restore con2fbmap */ + vt_set_fb( dfb_vt->num, dfb_vt->old_fb ); + + if (close( dfb_vt->fd ) < 0) + D_PERROR( "DirectFB/core/vt: Unable to " + "close file descriptor of allocated VT!\n" ); + + if (ioctl( dfb_vt->fd0, VT_DISALLOCATE, dfb_vt->num ) < 0) + D_PERROR( "DirectFB/core/vt: Unable to disallocate VT!\n" ); + } + else { + /* restore con2fbmap */ + vt_set_fb( dfb_vt->num, dfb_vt->old_fb ); + + if (close( dfb_vt->fd ) < 0) + D_PERROR( "DirectFB/core/vt: Unable to " + "close file descriptor of current VT!\n" ); + } + + if (close( dfb_vt->fd0 ) < 0) + D_PERROR( "DirectFB/core/vt: Unable to " + "close file descriptor of tty0!\n" ); + + D_FREE( dfb_vt ); + dfb_vt = dfb_fbdev->vt = NULL; + + return DFB_OK; +} + +DFBResult +dfb_vt_leave( bool emergency ) +{ + D_DEBUG_AT( VT, "%s()\n", __FUNCTION__ ); + + return DFB_OK; +} + +DFBResult +dfb_vt_detach( bool force ) +{ + D_DEBUG_AT( VT, "%s()\n", __FUNCTION__ ); + + if (dfb_config->vt_switch || force) { + int fd; + struct vt_stat vt_state; + + fd = open( "/dev/tty", O_RDONLY | O_NOCTTY ); + if (fd < 0) { + if (errno == ENXIO) + return DFB_OK; + + D_PERROR( "DirectFB/VT: Opening /dev/tty failed!\n" ); + return errno2result( errno ); + } + + if (ioctl( fd, VT_GETSTATE, &vt_state )) { + close( fd ); + return DFB_OK; + } + + if (ioctl( fd, TIOCNOTTY )) { + D_PERROR( "DirectFB/VT: TIOCNOTTY on /dev/tty failed\n" ); + close( fd ); + return errno2result( errno ); + } + + close( fd ); + } + + return DFB_OK; +} + +bool +dfb_vt_switch( int num ) +{ + D_DEBUG_AT( VT, "%s( %d )\n", __FUNCTION__, num ); + + if (!dfb_config->vt_switching) + return false; + + D_DEBUG_AT( VT, " -> switching to vt %d...\n", num ); + + if (ioctl( dfb_vt->fd0, VT_ACTIVATE, num ) < 0) + D_PERROR( "DirectFB/fbdev/vt: VT_ACTIVATE failed\n" ); + + return true; +} + +static void * +vt_thread( DirectThread *thread, void *arg ) +{ + D_DEBUG_AT( VT, "%s( %p, %p )\n", __FUNCTION__, thread, arg ); + + pthread_mutex_lock( &dfb_vt->lock ); + + while (true) { + direct_thread_testcancel( thread ); + + D_DEBUG_AT( VT, "...%s (signal %d)\n", __FUNCTION__, dfb_vt->vt_sig); + + switch (dfb_vt->vt_sig) { + default: + D_BUG( "unexpected vt_sig" ); + /* fall through */ + + case -1: + pthread_cond_wait( &dfb_vt->wait, &dfb_vt->lock ); + continue; + + case SIG_SWITCH_FROM: + if (dfb_core_suspend( dfb_fbdev->core ) == DFB_OK) { + if (ioctl( dfb_vt->fd, VT_RELDISP, VT_ACKACQ ) < 0) + D_PERROR( "DirectFB/fbdev/vt: VT_RELDISP failed\n" ); + } + + break; + + case SIG_SWITCH_TO: + if (dfb_core_resume( dfb_fbdev->core ) == DFB_OK) { + if (ioctl( dfb_vt->fd, VT_RELDISP, VT_ACKACQ ) < 0) + D_PERROR( "DirectFB/fbdev/vt: VT_RELDISP failed\n" ); + + if (dfb_config->kd_graphics) { + if (ioctl( dfb_vt->fd, KDSETMODE, KD_GRAPHICS ) < 0) + D_PERROR( "DirectFB/fbdev/vt: KD_GRAPHICS failed!\n" ); + } + } + + break; + } + + dfb_vt->vt_sig = -1; + + pthread_cond_signal( &dfb_vt->wait ); + } + + return NULL; +} + +static void +vt_switch_handler( int signum ) +{ + D_DEBUG_AT( VT, "%s( %d )\n", __FUNCTION__, signum ); + + pthread_mutex_lock( &dfb_vt->lock ); + + while (dfb_vt->vt_sig != -1) + pthread_cond_wait( &dfb_vt->wait, &dfb_vt->lock ); + + dfb_vt->vt_sig = signum; + + pthread_cond_signal( &dfb_vt->wait ); + + pthread_mutex_unlock( &dfb_vt->lock ); +} + +static DFBResult +vt_init_switching( void ) +{ + struct termios ts; + const char cursoroff_str[] = "\033[?1;0;0c"; + const char blankoff_str[] = "\033[9;0]"; + char buf[32]; + + D_DEBUG_AT( VT, "%s()\n", __FUNCTION__ ); + + /* FIXME: Opening the device should be moved out of this function. */ + + snprintf(buf, 32, "/dev/tty%d", dfb_vt->num); + dfb_vt->fd = open( buf, O_RDWR | O_NOCTTY ); + if (dfb_vt->fd < 0) { + if (errno == ENOENT) { + snprintf(buf, 32, "/dev/vc/%d", dfb_vt->num); + dfb_vt->fd = open( buf, O_RDWR | O_NOCTTY ); + if (dfb_vt->fd < 0) { + if (errno == ENOENT) { + D_PERROR( "DirectFB/core/vt: Couldn't open " + "neither `/dev/tty%d' nor `/dev/vc/%d'!\n", + dfb_vt->num, dfb_vt->num ); + } + else { + D_PERROR( "DirectFB/core/vt: " + "Error opening `%s'!\n", buf ); + } + + return errno2result( errno ); + } + } + else { + D_PERROR( "DirectFB/core/vt: Error opening `%s'!\n", buf ); + return errno2result( errno ); + } + } + + /* attach to the new TTY before doing anything like KDSETMODE with it, + otherwise we'd get access denied error: */ + ioctl( dfb_vt->fd, TIOCSCTTY, 0 ); + + if (ioctl( dfb_vt->fd, KDSKBMODE, K_MEDIUMRAW ) < 0) { + D_PERROR( "DirectFB/fbdev/vt: K_MEDIUMRAW failed!\n" ); + close( dfb_vt->fd ); + return DFB_INIT; + } + + if (tcgetattr( dfb_vt->fd, &dfb_vt->old_ts ) < 0) { + D_PERROR( "DirectFB/fbdev/vt: tcgetattr failed!\n" ); + ioctl( dfb_vt->fd, KDSKBMODE, K_XLATE ); + close( dfb_vt->fd ); + return DFB_INIT; + } + ts = dfb_vt->old_ts; + ts.c_cc[VTIME] = 0; + ts.c_cc[VMIN] = 1; + ts.c_lflag &= ~(ICANON|ECHO|ISIG); + ts.c_iflag = 0; + if (tcsetattr( dfb_vt->fd, TCSAFLUSH, &ts ) < 0) { + D_PERROR( "DirectFB/fbdev/vt: tcsetattr for new values failed!\n" ); + ioctl( dfb_vt->fd, KDSKBMODE, K_XLATE ); + close( dfb_vt->fd ); + return DFB_INIT; + } + + write( dfb_vt->fd, cursoroff_str, sizeof(cursoroff_str) ); + if (dfb_config->kd_graphics) { + if (ioctl( dfb_vt->fd, KDSETMODE, KD_GRAPHICS ) < 0) { + D_PERROR( "DirectFB/fbdev/vt: KD_GRAPHICS failed!\n" ); + tcsetattr( dfb_vt->fd, TCSAFLUSH, &dfb_vt->old_ts ); + ioctl( dfb_vt->fd, KDSKBMODE, K_XLATE ); + close( dfb_vt->fd ); + return DFB_INIT; + } + } + else { + write( dfb_vt->fd, blankoff_str, sizeof(blankoff_str) ); + } + + if (dfb_config->vt_switching) { + struct vt_mode vt; + struct sigaction sig_tty; + + memset( &sig_tty, 0, sizeof( sig_tty ) ); + sig_tty.sa_handler = vt_switch_handler; + sigfillset( &sig_tty.sa_mask ); + + if (sigaction( SIG_SWITCH_FROM, &sig_tty, &dfb_vt->sig_usr1 ) || + sigaction( SIG_SWITCH_TO, &sig_tty, &dfb_vt->sig_usr2 )) { + D_PERROR( "DirectFB/fbdev/vt: sigaction failed!\n" ); + tcsetattr( dfb_vt->fd, TCSAFLUSH, &dfb_vt->old_ts ); + ioctl( dfb_vt->fd, KDSKBMODE, K_XLATE ); + close( dfb_vt->fd ); + return DFB_INIT; + } + + + vt.mode = VT_PROCESS; + vt.waitv = 0; + vt.relsig = SIG_SWITCH_FROM; + vt.acqsig = SIG_SWITCH_TO; + + if (ioctl( dfb_vt->fd, VT_SETMODE, &vt ) < 0) { + D_PERROR( "DirectFB/fbdev/vt: VT_SETMODE failed!\n" ); + + sigaction( SIG_SWITCH_FROM, &dfb_vt->sig_usr1, NULL ); + sigaction( SIG_SWITCH_TO, &dfb_vt->sig_usr2, NULL ); + + tcsetattr( dfb_vt->fd, TCSAFLUSH, &dfb_vt->old_ts ); + ioctl( dfb_vt->fd, KDSKBMODE, K_XLATE ); + close( dfb_vt->fd ); + + return DFB_INIT; + } + + direct_util_recursive_pthread_mutex_init( &dfb_vt->lock ); + + pthread_cond_init( &dfb_vt->wait, NULL ); + + dfb_vt->vt_sig = -1; + + dfb_vt->thread = direct_thread_create( DTT_CRITICAL, vt_thread, NULL, "VT Switcher" ); + } + + return DFB_OK; +} + +static int +vt_get_fb( int vt ) +{ + struct fb_con2fbmap c2m; + + D_DEBUG_AT( VT, "%s( %d )\n", __FUNCTION__, vt ); + + c2m.console = vt; + + if (ioctl( dfb_fbdev->fd, FBIOGET_CON2FBMAP, &c2m )) { + D_PERROR( "DirectFB/FBDev/vt: " + "FBIOGET_CON2FBMAP failed!\n" ); + return 0; + } + + D_DEBUG_AT( VT, " -> %d\n", c2m.framebuffer ); + + return c2m.framebuffer; +} + +static void +vt_set_fb( int vt, int fb ) +{ + struct fb_con2fbmap c2m; + struct stat sbf; + + D_DEBUG_AT( VT, "%s( %d, %d )\n", __FUNCTION__, vt, fb ); + + if (fstat( dfb_fbdev->fd, &sbf )) { + D_PERROR( "DirectFB/FBDev/vt: Could not fstat fb device!\n" ); + return; + } + + if (fb >= 0) + c2m.framebuffer = fb; + else + c2m.framebuffer = (sbf.st_rdev & 0xFF) >> 5; + + c2m.console = vt; + + if (ioctl( dfb_fbdev->fd, FBIOPUT_CON2FBMAP, &c2m ) < 0) { + D_PERROR( "DirectFB/FBDev/vt: " + "FBIOPUT_CON2FBMAP failed!\n" ); + } +} + +static void +vt_start_flushing( void ) +{ + dfb_vt->flush = true; + dfb_vt->flush_thread = direct_thread_create( DTT_DEFAULT, vt_flush_thread, NULL, "VT Flusher" ); +} + +static void +vt_stop_flushing( void ) +{ + dfb_vt->flush = false; + direct_thread_cancel( dfb_vt->flush_thread ); + direct_thread_join( dfb_vt->flush_thread ); + direct_thread_destroy( dfb_vt->flush_thread ); + dfb_vt->flush_thread = NULL; +} + +/* + * If the vt buffer in not kept clean the kernel may stop sleeping. + */ +static void * +vt_flush_thread( DirectThread *thread, void *arg ) +{ + D_DEBUG_AT( VT, "%s( %p, %p )\n", __FUNCTION__, thread, arg ); + + while (dfb_vt->flush) { + fd_set set; + int ret; + + FD_ZERO( &set ); + FD_SET( dfb_vt->fd, &set ); + + ret = select( dfb_vt->fd + 1, &set, NULL, NULL, NULL ); + + if (ret < 0 && errno == EINTR) + continue; + + if (ret < 0) + break; + + tcflush( dfb_vt->fd, TCIFLUSH ); + } + + return NULL; +} -- cgit