aboutsummaryrefslogtreecommitdiff
path: root/src/display33.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/display33.c')
-rw-r--r--src/display33.c575
1 files changed, 575 insertions, 0 deletions
diff --git a/src/display33.c b/src/display33.c
new file mode 100644
index 0000000..928d4e0
--- /dev/null
+++ b/src/display33.c
@@ -0,0 +1,575 @@
+#include "string.h"
+#include "stdlib.h"
+#include "signal.h"
+#include "fcntl.h"
+#include "errno.h"
+#include "termios.h"
+#include "unistd.h"
+#include "poll.h"
+#include "dirent.h"
+#include "termio.h"
+#include "stdio.h"
+#include "libdrm/drm_mode.h"
+#include "sys/vt.h"
+#include "sys/kd.h"
+#include "sys/ioctl.h"
+#include "sys/mman.h"
+
+#include "display33.h"
+#include "system33.h"
+#include "logger33.h"
+#include "options33.h"
+
+
+#define REL_SIGNAL SIGUSR1
+#define ACQ_SIGNAL SIGUSR2
+#define REL_EVENT 1
+#define ACQ_EVENT 2
+
+
+Display33 __display = {0,};
+
+
+typedef struct {
+ struct drm_mode_card_res drmRes;
+ __u64 *drmRes_fbs;
+ __u32 *drmRes_conns;
+ __u64 *drmRes_encs;
+ __u64 *drmRes_crtcs;
+
+ __u64 connProps[ 16 ];
+ __u64 connPropVals[ 16 ];
+ __u64 connEncs[ 16 ];
+ struct drm_mode_modeinfo connModes[ 32 ];
+ struct drm_mode_get_encoder *encoder;
+
+ struct drm_mode_create_dumb drmCreateDumb[2];
+ struct drm_mode_map_dumb drmMapDumb[2];
+ struct drm_mode_fb_cmd drmFBCmd[2];
+} DRMData_t;
+
+static DRMData_t drmData = {0,};
+
+static int vtPipe[2] = { -1, -1 };
+
+static struct termios termOldConfig;
+static struct vt_mode vtModeOld = {0,};
+
+static Error _open_graphics_device( void );
+static Error _get_drm_resources( void );
+static Error _get_drm_connector( void );
+static Error _get_drm_crtc( void );
+static Error _get_drm_framebuffer(void );
+
+static Size _init_vt_switch( void );
+static void _vt_release( void );
+static void _vt_acquire( void );
+static void _vt_switch_sighandler(int sig);
+
+
+Error display33_init( void )
+{
+ struct termios termConfig;
+
+
+ if(!isatty(STDIN_FILENO)) {
+ LOGF( "stdin is not a terminal." );
+ return E33_EXIT_FAILURE;
+ }
+
+ if( tcgetattr(STDIN_FILENO, &termOldConfig) == -1 ) {
+ LOGF( "Could not get terminal attributes." );
+ return E33_EXIT_FAILURE;
+ }
+
+ termConfig = termOldConfig;
+ termConfig.c_iflag &= ~(IGNBRK | BRKINT | PARMRK);
+ termConfig.c_lflag &= ~(ICANON | ECHO | IEXTEN | TOSTOP);
+ termConfig.c_lflag |= ISIG;
+ termConfig.c_cc[VTIME] = 0;
+ termConfig.c_cc[VMIN] = 0;
+ termConfig.c_cc[VSTART] = 0;
+ termConfig.c_cc[VSTOP] = 0;
+
+ if( tcsetattr(STDIN_FILENO, TCSANOW, &termConfig) == -1 ) {
+ LOGF( "Could not set terminal attributes." );
+ return E33_EXIT_FAILURE;
+ }
+
+ if( _init_vt_switch() ) {
+ LOGW( "Failed to initialize VT switcher. VT switching will not be available." );
+ }
+
+
+ if( _open_graphics_device() ) {
+ LOGF( "Failed to find a suitable graphics device." );
+ return E33_EXIT_FAILURE;
+ }
+
+ if( e33_ioctl( __display.devFd, DRM_IOCTL_SET_MASTER, 0 ) == -1 )
+ {
+ LOGF( "Failed to set DRM master." );
+ return E33_EXIT_FAILURE;
+ }
+
+ if( _get_drm_resources() ) {
+ LOGF( "Failed to get resources." );
+ return E33_EXIT_FAILURE;
+ }
+ if( _get_drm_connector() ) {
+ LOGF( "Failed to set connector." );
+ return E33_EXIT_FAILURE;
+ }
+ if( _get_drm_framebuffer() ) {
+ LOGF( "Failed to set framebuffer." );
+ return E33_EXIT_FAILURE;
+ }
+ if( _get_drm_crtc() ) {
+ LOGF( "Failed to set crtc." );
+ return E33_EXIT_FAILURE;
+ }
+
+
+ __display.surface.w = __display.mode.hdisplay;
+ __display.surface.h = __display.mode.vdisplay;
+ __display.fb.size = (Size)drmData.drmCreateDumb[0].size;
+ __display.surface.data = __display.fb.map[0];
+ __display.active = E33_TRUE;
+
+ free( drmData.drmRes_fbs );
+ free( drmData.drmRes_encs );
+ free( drmData.drmRes_conns );
+
+ return E33_EXIT_SUCCESS;
+}
+
+void display33_flip( void )
+{
+ static u8 i_s = 1;
+
+ struct drm_mode_crtc_page_flip flip = { 0, };
+
+ __display.surface.data = __display.fb.map[i_s];
+ i_s ^= 1;
+ __display.crtc.fb_id = __display.fb.id[i_s];
+
+ if( e33_ioctl( __display.devFd, (int)DRM_IOCTL_MODE_SETCRTC, &__display.crtc ) == -1 ) {
+ LOGW( "Failed to set CRTC for page flip prep. Skipping." );
+ return;
+ }
+
+ flip.fb_id = __display.fb.id[i_s];
+ flip.crtc_id = __display.crtc.crtc_id;
+ flip.user_data = ((__u64)(&__display.crtc.crtc_id));
+ flip.flags = DRM_MODE_PAGE_FLIP_EVENT;
+
+ e33_ioctl( __display.devFd, (int)DRM_IOCTL_MODE_PAGE_FLIP, &flip );
+
+ /* TODO remove later */
+ memset( __display.surface.data, 0, (uSize)__display.fb.size );
+}
+
+void display33_term( void )
+{
+ if( e33_ioctl( __display.devFd, DRM_IOCTL_DROP_MASTER, 0 ) == -1 ) {
+ LOGW( "Failed to drop drm master." );
+ }
+
+ if( munmap( __display.fb.map[0], (uSize)__display.fb.size ) == -1 ) {
+ LOGW( "Failed to unmap framebuffer[0]." );
+ }
+ if( munmap( __display.fb.map[1], (uSize)__display.fb.size ) == -1 ) {
+ LOGW( "Failed to unmap framebuffer[1]." );
+ }
+
+ if( close( __display.devFd ) ) {
+ LOGW( "Failed to close graphics device." );
+ }
+
+ free( drmData.drmRes_crtcs );
+
+ if( ioctl(__display.ttyFd, VT_SETMODE, &vtModeOld) < 0 ) {
+ LOGW( "Could not reset VT mode." );
+ }
+
+ if( tcsetattr(STDIN_FILENO, TCSAFLUSH, &termOldConfig) == -1 ) {
+ LOGW( "Failed to set stdin attributes." );
+ }
+
+ if( close( __display.ttyFd ) == -1 ) {
+ LOGW( "Failed to close TTY." );
+ }
+}
+
+
+void display33_vtswitcher_poll( int timeout )
+{
+ struct pollfd fds[1];
+ unsigned char event;
+
+ fds[0].fd = vtPipe[0];
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+
+ poll( fds, 1, timeout );
+ if( !fds[0].revents ) {
+ return;
+ }
+
+ if( read(fds[0].fd, &event, sizeof(event)) != sizeof(event) ) {
+ LOGW( "Invalid VT switch events read.");
+ return;
+ }
+
+ switch(event) {
+ case REL_EVENT: _vt_release();
+ break;
+ case ACQ_EVENT: _vt_acquire();
+ break;
+ }
+}
+
+
+
+
+
+static Error _open_graphics_device( void )
+{
+ struct drm_get_cap cap = {0,};
+
+
+ if( (__display.devFd = open( E33_DRM_DEVICE, O_RDWR )) == -1 ) {
+ LOGE( "Failed to open DRM device '%s'.", E33_DRM_DEVICE );
+ return E33_EXIT_FAILURE;
+ }
+
+ cap.capability = DRM_CAP_DUMB_BUFFER;
+
+ if( e33_ioctl( __display.devFd, DRM_IOCTL_GET_CAP, &cap ) == -1 ) {
+ LOGW( "Failed to get DRM device capabilities." );
+ return E33_EXIT_FAILURE;
+ }
+
+ if( cap.value ) {
+ return E33_EXIT_SUCCESS;
+ }
+
+ return E33_EXIT_FAILURE;
+}
+
+static Size _get_drm_resources( void )
+{
+ if( e33_ioctl(__display.devFd, (int)DRM_IOCTL_MODE_GETRESOURCES, &drmData.drmRes) == -1 ) {
+ LOGE( "Failed to init drm resources." );
+ return E33_EXIT_FAILURE;
+ }
+
+ drmData.drmRes_fbs = malloc( sizeof(__u64) * drmData.drmRes.count_fbs );
+ drmData.drmRes_crtcs = malloc( sizeof(__u64) * drmData.drmRes.count_crtcs );
+ drmData.drmRes_encs = malloc( sizeof(__u64) * drmData.drmRes.count_encoders );
+ drmData.drmRes_conns = malloc( sizeof(__u32) * drmData.drmRes.count_connectors );
+
+ /* DRM resource objects must also be zero'd before second call */
+ memset( drmData.drmRes_fbs, 0, sizeof(__u64) * drmData.drmRes.count_fbs );
+ memset( drmData.drmRes_crtcs, 0, sizeof(__u64) * drmData.drmRes.count_crtcs );
+ memset( drmData.drmRes_encs, 0, sizeof(__u64) * drmData.drmRes.count_encoders );
+ memset( drmData.drmRes_conns, 0, sizeof(__u32) * drmData.drmRes.count_connectors );
+
+ drmData.drmRes.fb_id_ptr = (__u64)drmData.drmRes_fbs;
+ drmData.drmRes.crtc_id_ptr = (__u64)drmData.drmRes_crtcs;
+ drmData.drmRes.connector_id_ptr = (__u64)drmData.drmRes_conns;
+ drmData.drmRes.encoder_id_ptr = (__u64)drmData.drmRes_encs;
+
+ /* Second call writes drm resource data using the given pointers */
+ if( e33_ioctl( __display.devFd, (int)DRM_IOCTL_MODE_GETRESOURCES, &drmData.drmRes ) == -1 ) {
+ LOGE( "Failed to get drm resources." );
+ return E33_EXIT_FAILURE;
+ }
+
+ return E33_EXIT_SUCCESS;
+}
+
+
+static Size _get_drm_connector( void )
+{
+ u16 i, j;
+
+ for( i = 0; i < drmData.drmRes.count_connectors; ++i )
+ {
+ struct drm_mode_get_connector *connector = &__display.connector;
+
+
+ connector->connector_id = drmData.drmRes_conns[i];
+
+ if( connector->connector_id <= 0 ) {
+ continue;
+ }
+
+ if( e33_ioctl( __display.devFd, (int)DRM_IOCTL_MODE_GETCONNECTOR, connector ) == -1 ) {
+ continue;
+ }
+
+ if( connector->connection == 1 &&
+ connector->count_modes > 0 &&
+ connector->count_encoders > 0
+ ) {
+ connector->modes_ptr = (__u64)drmData.connModes;
+ connector->props_ptr = (__u64)drmData.connProps;
+ connector->prop_values_ptr = (__u64)drmData.connPropVals;
+ connector->encoders_ptr = (__u64)drmData.connEncs;
+
+ if( e33_ioctl( __display.devFd, (int)DRM_IOCTL_MODE_GETCONNECTOR, connector) == -1 ) {
+ continue;
+ }
+
+ for( j = 0; j < connector->count_modes; ++j )
+ {
+ struct drm_mode_modeinfo *mode = &drmData.connModes[j];
+
+ if( e33_strcmp( mode->name, E33_DRM_MODE ) && (mode->vrefresh == E33_DRM_RATE) )
+ {
+ /* TODO there has to be a better way */
+ if( mode->hdisplay == 1366 ) {
+ mode->hdisplay += 10;
+ }
+
+ __display.mode = *mode;
+ return E33_EXIT_SUCCESS;
+ }
+ }
+ }
+ }
+
+ LOGE( "Failed to find a valid connector." );
+ return E33_EXIT_FAILURE;
+}
+
+static Size _get_drm_crtc( void )
+{
+ struct drm_mode_get_encoder encoder = {0,};
+ struct drm_mode_crtc *crtc = &__display.crtc;
+
+ encoder.encoder_id = __display.connector.encoder_id;
+
+ if( e33_ioctl( __display.devFd, (int)DRM_IOCTL_MODE_GETENCODER, &encoder ) == -1 ) {
+ LOGE( "Failed to get encoder." );
+ return E33_EXIT_FAILURE;
+ }
+
+ crtc->crtc_id = encoder.crtc_id;
+
+ if( e33_ioctl( __display.devFd, (int)DRM_IOCTL_MODE_GETCRTC, crtc ) == -1 ) {
+ LOGE( "Failed to get CRTC." );
+ return E33_EXIT_FAILURE;
+ }
+
+ crtc->fb_id = drmData.drmFBCmd[0].fb_id;
+ crtc->set_connectors_ptr = (__u64)drmData.drmRes_conns;
+ crtc->count_connectors = 1;
+ crtc->mode = __display.mode;
+ crtc->mode_valid = 1;
+
+ if( e33_ioctl( __display.devFd, (int)DRM_IOCTL_MODE_SETCRTC, crtc ) == -1 ) {
+ LOGE( "Could not set CRTC." );
+ return E33_EXIT_FAILURE;
+ }
+
+ return E33_EXIT_SUCCESS;
+}
+
+static Size _get_drm_framebuffer( void )
+{
+ struct drm_mode_create_dumb *drmCreateDumb = drmData.drmCreateDumb;
+ struct drm_mode_map_dumb *drmMapDumb = drmData.drmMapDumb;
+ struct drm_mode_fb_cmd *drmFBCmd = drmData.drmFBCmd;
+ struct drm_mode_modeinfo *mode = &__display.mode;
+ u8 i;
+
+ for( i = 0; i < 2; ++i )
+ {
+ drmCreateDumb[i].width = mode->hdisplay;
+ drmCreateDumb[i].height = mode->vdisplay;
+ drmCreateDumb[i].bpp = 32;
+
+ if( e33_ioctl( __display.devFd, (int)DRM_IOCTL_MODE_CREATE_DUMB,
+ drmCreateDumb + i ) == -1 )
+ {
+ LOGE( "Failed to create dumb buffer." );
+ return E33_EXIT_FAILURE;
+ }
+
+ drmFBCmd[i].width = drmCreateDumb[i].width;
+ drmFBCmd[i].height = drmCreateDumb[i].height;
+ drmFBCmd[i].bpp = drmCreateDumb[i].bpp;
+ drmFBCmd[i].pitch = drmCreateDumb[i].pitch;
+ drmFBCmd[i].depth = 24;
+ drmFBCmd[i].handle = drmCreateDumb[i].handle;
+
+ if( e33_ioctl( __display.devFd, (int)DRM_IOCTL_MODE_ADDFB, drmFBCmd + i ) == -1 ) {
+ LOGE( "Failed to add framebuffer." );
+ return E33_EXIT_FAILURE;
+ }
+
+ __display.fb.id[i] = drmFBCmd[i].fb_id;
+
+ drmMapDumb[i].handle = drmCreateDumb[i].handle;
+
+ if( e33_ioctl( __display.devFd, (int)DRM_IOCTL_MODE_MAP_DUMB, drmMapDumb + i ) == -1 )
+ {
+ LOGE( "Failed to map dumb buffer." );
+ return E33_EXIT_FAILURE;
+ }
+
+ __display.fb.map[i] =
+ mmap( 0, drmCreateDumb[i].size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, __display.devFd, drmMapDumb[i].offset );
+
+ if( __display.fb.map[i] == MAP_FAILED ) {
+ LOGE( "Failed to map framebuffer." );
+ return E33_EXIT_FAILURE;
+ }
+ }
+
+ return E33_EXIT_SUCCESS;
+}
+
+static void _vt_release( void )
+{
+ LOGI( "VT release signal.");
+
+ if( ioctl( __display.devFd, DRM_IOCTL_DROP_MASTER, 0 ) ) {
+ LOGW( "Could not drop DRM master." );
+ }
+
+ if( ioctl(__display.ttyFd, VT_RELDISP, 1) < 0 ) {
+ LOGW( "Failed to release VT.");
+ }
+
+ __display.active = 0;
+}
+
+static void _vt_acquire( void )
+{
+ LOGI( "VT acquire signal");
+
+ if( ioctl( __display.devFd, DRM_IOCTL_SET_MASTER, 0 ) == -1 ) {
+ LOGW( "Could not become DRM master." );
+ }
+
+ if( ioctl(__display.ttyFd, VT_RELDISP, VT_ACKACQ) < 0 ) {
+ LOGW( "(vt_acquire) Failed to acquire VT.");
+ }
+
+ if( ioctl( __display.devFd, (int)DRM_IOCTL_MODE_SETCRTC, &__display.crtc ) == -1 ) {
+ LOGW( "Failed to set crtc on VT switch." );
+ }
+
+ __display.active = 1;
+}
+
+
+static int _set_pipe(int fd)
+{
+ if (fd >= 0)
+ {
+ int flags = fcntl(fd, F_GETFD);
+
+ if (flags == -1) {
+ LOGW( "Could not get flags for VT switcher pipe.");
+ return E33_EXIT_FAILURE;
+ }
+
+ if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
+ LOGW( "Could not set flags for VT switcher pipe.");
+ return E33_EXIT_FAILURE;
+ }
+ }
+
+ return E33_EXIT_SUCCESS;
+}
+
+
+static void _vt_switch_sighandler(int sig)
+{
+ unsigned char event = REL_EVENT;
+
+ if( sig == ACQ_SIGNAL ) {
+ event = ACQ_EVENT;
+ }
+
+ (void)write( vtPipe[1], &event, sizeof(event) );
+}
+
+static Size _init_vt_switch( void )
+{
+ struct vt_mode vtMode = {0,};
+ sigset_t set;
+
+
+ __display.ttyFd = -1;
+
+ if( pipe2(vtPipe, 0) == -1 )
+ {
+ vtPipe[0] = vtPipe[1] = -1;
+ LOGW( "Could not set VT pipes.");
+ return E33_EXIT_FAILURE;
+ }
+
+ if( _set_pipe(vtPipe[0]) ) {
+ LOGW( "Could not set VT read pipe." );
+ return E33_EXIT_FAILURE;
+ }
+ if( _set_pipe(vtPipe[1]) ) {
+ LOGW( "Could not set VT write pipe." );
+ return E33_EXIT_FAILURE;
+ }
+
+
+ if( __display.ttyFd = open("/dev/tty", O_RDWR ) < 0 ) {
+ LOGW( "Could not open TTY for VT control." );
+ return E33_EXIT_FAILURE;
+ }
+
+
+ if( e33_has_signal(REL_SIGNAL) ) {
+ LOGW( "VT release signal is already in use.");
+ return E33_EXIT_FAILURE;
+ }
+ if( e33_has_signal(ACQ_SIGNAL) ) {
+ LOGW( "VT acquire signal is already in use.");
+ return E33_EXIT_FAILURE;
+ }
+
+ if( e33_set_signal( REL_SIGNAL, _vt_switch_sighandler ) ) {
+ LOGW( "Could not set relese signal handler.");
+ return E33_EXIT_FAILURE;
+ }
+ if( e33_set_signal( ACQ_SIGNAL, _vt_switch_sighandler ) ) {
+ LOGW( "Could not set acquire signal handler.");
+ return E33_EXIT_FAILURE;
+ }
+
+
+ if( ioctl(__display.ttyFd, VT_GETMODE, &vtModeOld) < 0 ) {
+ LOGW( "Could not get VT mode." );
+ return E33_EXIT_FAILURE;
+ }
+
+ vtMode = vtModeOld;
+ vtMode.mode = VT_PROCESS;
+ vtMode.relsig = REL_SIGNAL;
+ vtMode.acqsig = ACQ_SIGNAL;
+ vtMode.frsig = SIGIO;
+
+ if( ioctl(__display.ttyFd, VT_SETMODE, &vtMode) < 0 ) {
+ LOGW( "Could not set VT mode." );
+ return E33_EXIT_FAILURE;
+ }
+
+ return E33_EXIT_SUCCESS;
+}
+
+
+
+
+