[PATCH 8/8] i386/pc386: VESA based frame buffer utilizing real mode interrupt 10h

Jan Dolezal dolezj21 at fel.cvut.cz
Thu Nov 20 14:00:33 UTC 2014


---
 c/src/lib/libbsp/i386/pc386/Makefile.am          |   5 +
 c/src/lib/libbsp/i386/pc386/configure.ac         |  13 +
 c/src/lib/libbsp/i386/pc386/console/fb_vesa_rm.c | 858 +++++++++++++++++++++++
 c/src/lib/libbsp/i386/pc386/include/fb_vesa.h    | 131 ++++
 c/src/lib/libbsp/i386/pc386/preinstall.am        |   6 +
 c/src/lib/libbsp/i386/pc386/start/start.S        |   8 +
 6 files changed, 1021 insertions(+)
 create mode 100644 c/src/lib/libbsp/i386/pc386/console/fb_vesa_rm.c
 create mode 100644 c/src/lib/libbsp/i386/pc386/include/fb_vesa.h

diff --git a/c/src/lib/libbsp/i386/pc386/Makefile.am b/c/src/lib/libbsp/i386/pc386/Makefile.am
index de970a3..391bfa9 100644
--- a/c/src/lib/libbsp/i386/pc386/Makefile.am
+++ b/c/src/lib/libbsp/i386/pc386/Makefile.am
@@ -106,13 +106,18 @@ libbsp_a_SOURCES += console/printk_support.c
 libbsp_a_SOURCES += console/vgacons.c
 libbsp_a_SOURCES += console/exar17d15x.c
 libbsp_a_SOURCES += console/rtd316.c
+if USE_VBE_RM
 include_bsp_HEADERS += include/vbe3.h
 include_HEADERS += include/edid.h
+include_bsp_HEADERS += include/fb_vesa.h
+libbsp_a_SOURCES += console/fb_vesa_rm.c
+else
 if USE_CIRRUS_GD5446
 libbsp_a_SOURCES += console/fb_cirrus.c
 else
 libbsp_a_SOURCES += console/fb_vga.c
 endif
+endif
 
 # gdb
 libbsp_a_SOURCES += ../../i386/shared/comm/i386-stub.c
diff --git a/c/src/lib/libbsp/i386/pc386/configure.ac b/c/src/lib/libbsp/i386/pc386/configure.ac
index ecec056..a74d6cb 100644
--- a/c/src/lib/libbsp/i386/pc386/configure.ac
+++ b/c/src/lib/libbsp/i386/pc386/configure.ac
@@ -82,6 +82,19 @@ RTEMS_BSPOPTS_HELP([USE_CIRRUS_GD5446],
  NOTE: This has only been tested on Qemu.])
 AM_CONDITIONAL(USE_CIRRUS_GD5446,test "$USE_CIRRUS_GD5446" = "1")
 
+RTEMS_BSPOPTS_SET([USE_VBE_RM],[*],[0])
+RTEMS_BSPOPTS_HELP([USE_VBE_RM],
+[If defined, enables use of the Vesa Bios Extensions - real mode interface,
+ which enables graphical mode and introduce it upon bootup.])
+AM_CONDITIONAL(USE_VBE_RM,test "$USE_VBE_RM" = "1")
+
+if test "${USE_VBE_RM}" = "1" ; then
+  if test -z "${NUM_APP_DRV_GDT_DESCRIPTORS}"; then
+      NUM_APP_DRV_GDT_DESCRIPTORS=2 ;
+  else
+      NUM_APP_DRV_GDT_DESCRIPTORS+=2 ;
+  fi
+fi
 RTEMS_BSPOPTS_SET([NUM_APP_DRV_GDT_DESCRIPTORS],[*],[0])
 RTEMS_BSPOPTS_HELP([NUM_APP_DRV_GDT_DESCRIPTORS],
 [Defines how many descriptors in GDT may be allocated for application or
diff --git a/c/src/lib/libbsp/i386/pc386/console/fb_vesa_rm.c b/c/src/lib/libbsp/i386/pc386/console/fb_vesa_rm.c
new file mode 100644
index 0000000..6b26d35
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/console/fb_vesa_rm.c
@@ -0,0 +1,858 @@
+/*
+ *  FB driver for graphic hardware compatible with VESA Bios Extension
+ *  Real mode interface utilized
+ *  Tested on real HW.
+ *
+ *  Copyright (c) 2014 - CTU in Prague
+ *                       Jan Doležal ( dolezj21 at fel.cvut.cz )
+ *
+ *  The license and distribution terms for this file may be
+ *  found in the file LICENSE in this distribution or at
+ *  http://www.rtems.org/license/LICENSE.
+ *
+ *  The code for rtems_buffer_* functions were greatly
+ *  inspired or coppied from:
+ *    - RTEMS fb_cirrus.c - Alexandru-Sever Horin (alex.sever.h at gmail.com)
+ *
+ *  Public sources related:
+ *    - VESA BIOS EXTENSION (VBE) Core Function Standard, Ver: 3.0, Sep 16, 1998
+ *    - VESA Enhanced Extended Display Identification Data (E-EDID) Standard
+ *      Release A, Revision 2, September 25, 2006
+ */
+
+/*
+ *  Hardware is completely initialized upon boot of the system.
+ *  Therefore there is no way to change graphics mode later.
+ *
+ *  Interrupt 0x10 is used for entering graphics BIOS.
+ *
+ *  Driver reads parameter from multiboot command line to setup video:
+ *  "--video=<resX>x<resY>[-<bpp>]"
+ *  If cmdline parameter is not specified an attempt for obtaining
+ *  resolution from display attached is made.
+ */
+
+#include <bsp.h>
+
+#include <bsp/fb_vesa.h>
+#include <bsp/realmode_int.h>
+
+#include <pthread.h>
+
+#include <rtems/libio.h>
+
+#include <rtems/fb.h>
+#include <rtems/framebuffer.h>
+
+#include <stdlib.h>
+
+#define FB_VESA_NAME    "FB_VESA_RM"
+
+void vesa_realmode_bootup_init(void);
+
+/* mutex for protection against multiple opens, when called frame_buffer_open */
+static pthread_mutex_t vesa_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* screen information for the VGA driver
+ * standard structures - from RTEMS fb interface
+ */
+static struct fb_var_screeninfo fb_var;
+static struct fb_fix_screeninfo fb_fix;
+
+static uint16_t vbe_usedMode;
+
+inline uint32_t VBEControllerInformation(   struct VBE_VbeInfoBlock *infoBlock,
+                                            uint16_t queriedVBEVersion)
+{
+    uint16_t size;
+    struct VBE_VbeInfoBlock *VBE_buffer =
+        (struct VBE_VbeInfoBlock *)i386_get_default_rm_buffer(&size);
+    i386_realmode_interrupt_registers parret;
+    parret.reg_eax = VBE_RetVBEConInf;
+    uint16_t seg, off;
+    i386_Physical_to_real(VBE_buffer, &seg, &off);
+    parret.reg_edi = (uint32_t)off;
+    parret.reg_es = seg;
+    /* indicate to graphic's bios that VBE2.0 extended information is desired */
+    if (queriedVBEVersion >= 0x200)
+    {
+        strncpy(
+            (char *)&VBE_buffer->VbeSignature,
+            VBE20plus_SIGNATURE,
+            4*sizeof(size_t)
+        );
+    }
+    if (i386_real_interrupt_call(INTERRUPT_NO_VIDEO_SERVICES, &parret) == 0)
+        return -1;
+    if ((parret.reg_eax & 0xFFFF) ==
+        (VBE_callSuccessful<<8 | VBE_functionSupported))
+    {
+        *infoBlock = *VBE_buffer;
+    }
+    return (parret.reg_eax & 0xFFFF);
+}
+
+inline uint32_t VBEModeInformation( struct VBE_ModeInfoBlock *infoBlock,
+                                    uint16_t modeNumber)
+{
+    uint16_t size;
+    struct VBE_ModeInfoBlock *VBE_buffer =
+        (struct VBE_ModeInfoBlock *)i386_get_default_rm_buffer(&size);
+    i386_realmode_interrupt_registers parret;
+    parret.reg_eax = VBE_RetVBEModInf;
+    parret.reg_ecx = modeNumber;
+    uint16_t seg, off;
+    i386_Physical_to_real(VBE_buffer, &seg, &off);
+    parret.reg_edi = (uint32_t)off;
+    parret.reg_es = seg;
+    if (i386_real_interrupt_call(INTERRUPT_NO_VIDEO_SERVICES, &parret) == 0)
+        return -1;
+    if ((parret.reg_eax & 0xFFFF) ==
+        (VBE_callSuccessful<<8 | VBE_functionSupported))
+    {
+        *infoBlock = *VBE_buffer;
+    }
+    return (parret.reg_eax & 0xFFFF);
+}
+
+inline uint32_t VBESetMode( uint16_t modeNumber,
+                            struct VBE_CRTCInfoBlock *infoBlock)
+{
+    uint16_t size;
+    struct VBE_CRTCInfoBlock *VBE_buffer =
+        (struct VBE_CRTCInfoBlock *)i386_get_default_rm_buffer(&size);
+    i386_realmode_interrupt_registers parret;
+    /* copy CRTC */
+    *VBE_buffer = *infoBlock;
+    parret.reg_eax = VBE_SetVBEMod;
+    parret.reg_ebx = modeNumber;
+    uint16_t seg, off;
+    i386_Physical_to_real(VBE_buffer, &seg, &off);
+    parret.reg_edi = (uint32_t)off;
+    parret.reg_es = seg;
+    if (i386_real_interrupt_call(INTERRUPT_NO_VIDEO_SERVICES, &parret) == 0)
+        return -1;
+    return (parret.reg_eax & 0xFFFF);
+}
+
+inline uint32_t VBECurrentMode(uint16_t *modeNumber)
+{
+    i386_realmode_interrupt_registers parret;
+    parret.reg_eax = VBE_RetCurVBEMod;
+    if (i386_real_interrupt_call(INTERRUPT_NO_VIDEO_SERVICES, &parret) == 0)
+        return -1;
+    *modeNumber = (uint16_t)parret.reg_ebx;
+    return (parret.reg_eax & 0xFFFF);
+}
+
+inline uint32_t VBEReportDDCCapabilities(   uint16_t controllerUnitNumber,
+                                            uint8_t *secondsToTransferEDIDBlock,
+                                            uint8_t *DDCLevelSupported)
+{
+    i386_realmode_interrupt_registers parret;
+    parret.reg_eax = VBE_DisDatCha;
+    parret.reg_ebx = VBEDDC_Capabilities;
+    parret.reg_ecx = controllerUnitNumber;
+    parret.reg_edi = 0;
+    parret.reg_es = 0;
+    if (i386_real_interrupt_call(INTERRUPT_NO_VIDEO_SERVICES, &parret) == 0)
+        return -1;
+    *secondsToTransferEDIDBlock = (uint8_t)parret.reg_ebx >> 8;
+    *DDCLevelSupported = (uint8_t)parret.reg_ebx;
+    return (parret.reg_eax & 0xFFFF);
+}
+
+inline uint32_t VBEReadEDID(uint16_t controllerUnitNumber,
+                            uint16_t EDIDBlockNumber,
+                            struct edid1 *buffer)
+{
+    uint16_t size;
+    struct edid1 *VBE_buffer = (struct edid1 *)i386_get_default_rm_buffer(&size);
+    i386_realmode_interrupt_registers parret;
+    parret.reg_eax = VBE_DisDatCha;
+    parret.reg_ebx = VBEDDC_ReadEDID;
+    parret.reg_ecx = controllerUnitNumber;
+    parret.reg_edx = EDIDBlockNumber;
+    uint16_t seg, off;
+    i386_Physical_to_real(VBE_buffer, &seg, &off);
+    parret.reg_edi = (uint32_t)off;
+    parret.reg_es = seg;
+    if (i386_real_interrupt_call(INTERRUPT_NO_VIDEO_SERVICES, &parret) == 0)
+        return -1;
+    if ((parret.reg_eax & 0xFFFF) ==
+        (VBE_callSuccessful<<8 | VBE_functionSupported))
+    {
+        *buffer = *VBE_buffer;
+    }
+    return (parret.reg_eax & 0xFFFF);
+}
+
+typedef struct {
+    uint16_t modeNumber;
+    uint16_t resX;
+    uint16_t resY;
+    uint8_t bpp;
+} modeParams;
+
+/* finds mode in 'modeList' of 'listLength' length according to resolution
+    given in 'searchedResolution'. If bpp is given in that struct as well
+    mode with such color depth and resolution is searched for. Otherwise bpp
+    has to be zero. Mode number found is returned and also filled into
+    'searchedResolution'. bpp is also filled into 'searchedResolution' if it
+    was 0 before call. */
+static uint16_t findModeByResolution(   modeParams *modeList,
+                                        uint8_t listLength,
+                                        modeParams *searchedResolution)
+{
+    uint8_t i = 0;
+    while (i < listLength)
+    {
+        if (searchedResolution->resX == modeList[i].resX &&
+            searchedResolution->resY == modeList[i].resY)
+        {
+            if (searchedResolution->bpp==0 ||
+                searchedResolution->bpp==modeList[i].bpp)
+            {
+                searchedResolution->bpp = modeList[i].bpp;
+                searchedResolution->modeNumber = modeList[i].modeNumber;
+                return modeList[i].modeNumber;
+            }
+        }
+        i++;
+    }
+    return -1;
+}
+
+/*
+ * Parse comandline option "--video=" if available.
+ *  expected format
+ *  --video=<resX>x<resY>[-<bpp>]
+ *  numbers <resX>, <resY> and <bpp> are decadic
+ *
+ * @retval video mode number to be set
+ *         -1 on parsing error or when no suitable mode found
+ */
+static uint16_t findModeUsingCmdline(   modeParams *modeList,
+                                        uint8_t listLength)
+{
+    const char* opt;
+    modeParams cmdlineMode;
+    char* endptr;
+    cmdlineMode.bpp = 0;
+    opt = bsp_cmdline_arg("--video=");
+    if (opt)
+    {
+        opt += sizeof("--video=")-1;
+        cmdlineMode.resX = strtol(opt, &endptr, 10);
+        if (*endptr != 'x')
+        {
+            return -1;
+        }
+        opt = endptr+1;
+        cmdlineMode.resY = strtol(opt, &endptr, 10);
+        switch (*endptr)
+        {
+            case '-':
+                opt = endptr+1;
+                if (strlen(opt) <= 2)
+                    cmdlineMode.bpp = strtol(opt, &endptr, 10);
+                else
+                {
+                    cmdlineMode.bpp = strtol(opt, &endptr, 10);
+                    if (*endptr != ' ')
+                    {
+                        return -1;
+                    }
+                }
+            case ' ':
+            case 0:
+                break;
+            default:
+                return -1;
+        }
+
+        if (findModeByResolution(modeList, listLength, &cmdlineMode) !=
+            (uint16_t)-1)
+            return cmdlineMode.modeNumber;
+    }
+    return -1;
+}
+
+/*
+ * returns mode number best fitting to monitor attached
+ *
+ * @retval video mode number to be set
+ *         -1 on parsing error or when no suitable mode found
+ */
+static uint16_t findModeUsingEDID(  modeParams *modeList,
+                                    uint8_t listLength)
+{
+    struct edid1 edid;
+    uint8_t checksum, iterator;
+    uint8_t index, j;
+    modeParams EDIDmode;
+    checksum = 0;
+    iterator = 0;
+    EDIDmode.bpp = 0;
+    if (VBEReadEDID(0, 0, &edid) !=
+        (VBE_callSuccessful<<8 | VBE_functionSupported))
+    {
+        printk(FB_VESA_NAME " Function 15h (read EDID) not supported.\n");
+        return -1;
+    }
+/* version of EDID structure */
+    if (edid.Version == 1)
+    { /* EDID version 1 */
+        while (iterator < sizeof(struct edid1))
+        {
+            checksum += *((uint8_t *)&edid+iterator);
+            iterator++;
+        }
+        if (checksum)
+            /* not implemented: try to read EDID again */
+            printk(FB_VESA_NAME " EDID v1 checksum failed\n");
+
+        /* try to find Detailed Timing Descriptor (defined in BASE EDID)
+           in controller mode list; first should be preffered mode */
+        index = 0;
+        while (index < 4)
+        {
+            /* skip if it is monitor descriptor */
+            if (edid.dtd_md[index].md.Flag0[0] == 0 &&
+                edid.dtd_md[index].md.Flag0[1] == 0 &&
+                edid.dtd_md[index].md.Flag1 == 0)
+            {
+                index++;
+                continue;
+            }
+            EDIDmode.resX = DTD_HorizontalActive(&edid.dtd_md[0].dtd);
+            EDIDmode.resY = DTD_VerticalActive(&edid.dtd_md[0].dtd);
+            if (findModeByResolution(modeList, listLength, &EDIDmode) !=
+                (uint16_t)-1)
+                return EDIDmode.modeNumber;
+
+            index++;
+        }
+        /* try to find Detailed Timing Descriptor (defined in optional EXTENSION
+        Blocks) in controller mode list */
+        if (edid.ExtensionFlag > 0)
+        {
+            /* not implemented */
+        }
+        /* try to find CVT (defined in BASE EDID) in controller mode list */
+        index = 1;
+        while (index < 4)
+        {
+            if (edid.dtd_md[index].md.DataTypeTag ==
+                    EDID_DTT_CVT3ByteTimingCodes     &&
+                edid.dtd_md[index].md.Flag0[0] == 0  &&
+                edid.dtd_md[index].md.Flag0[1] == 0  &&
+                edid.dtd_md[index].md.Flag1 == 0     &&
+                edid.dtd_md[index].md.Flag2 == 0)
+            {
+                struct CVTTimingCodes3B *cvt = (struct CVTTimingCodes3B *)
+                    &edid.dtd_md[index].md.DescriptorData[0];
+                j = 0;
+                while (j < 4)
+                {
+                    EDIDmode.resY = edid1_CVT_AddressableLinesHigh(&cvt->cvt[j]);
+                    switch (edid1_CVT_AspectRatio(&cvt->cvt[j]))
+                    {
+                        case EDID_CVT_AspectRatio_4_3:
+                            EDIDmode.resX = (EDIDmode.resY*4)/3;
+                            break;
+                        case EDID_CVT_AspectRatio_16_9:
+                            EDIDmode.resX = (EDIDmode.resY*16)/9;
+                            break;
+                        case EDID_CVT_AspectRatio_16_10:
+                            EDIDmode.resX = (EDIDmode.resY*16)/10;
+                            break;
+                        case EDID_CVT_AspectRatio_15_9:
+                            EDIDmode.resX = (EDIDmode.resY*15)/9;
+                            break;
+                    }
+                    EDIDmode.resX = (EDIDmode.resX/8)*8;
+                    if (findModeByResolution(modeList, listLength, &EDIDmode) !=
+                        (uint16_t)-1)
+                        return EDIDmode.modeNumber;
+
+                    j++;
+                }
+            }
+            index++;
+        }
+        /* try to find CVT (defined in optional EXTENSION Blocks)
+        in controller mode list */
+        /* not implemented */
+        /* try to find Standard Timings (listed in BASE EDID)
+        in controller mode list */
+        index = 0;
+        while (index < 8)
+        {
+            /* check if descriptor is unused */
+            if (*(uint16_t*)&edid.STI[index] == EDID_STI_DescriptorUnused)
+            {
+                index++;
+                continue;
+            }
+            EDIDmode.resX = (edid.STI[index].HorizontalActivePixels+31)*8;
+            switch (edid.STI[index].ImageAspectRatio_RefreshRate & EDID1_STI_ImageAspectRatioMask)
+            {
+                case EDID_STI_AspectRatio_16_10:
+                    EDIDmode.resY = (EDIDmode.resX*10)/16;
+                    break;
+                case EDID_STI_AspectRatio_4_3:
+                    EDIDmode.resY = (EDIDmode.resX*3)/4;
+                    break;
+                case EDID_STI_AspectRatio_5_4:
+                    EDIDmode.resY = (EDIDmode.resX*4)/5;
+                    break;
+                case EDID_STI_AspectRatio_16_9:
+                    EDIDmode.resY = (EDIDmode.resX*9)/16;
+                    break;
+            }
+            if (findModeByResolution(modeList, listLength, &EDIDmode) !=
+                (uint16_t)-1)
+                return EDIDmode.modeNumber;
+
+            index++;
+        }
+        /* try to find Standard Timings (listed in optional EXTENSION Blocks)
+        in controller mode list */
+        /* not implemented */
+        /* use Established Timings */
+        if (edid1_EstablishedTim(&edid, EST_1280x1024_75Hz))
+        {
+            EDIDmode.resX = 1280;
+            EDIDmode.resY = 1024;
+            EDIDmode.bpp = 0;
+            if (findModeByResolution(modeList, listLength, &EDIDmode) !=
+                (uint16_t)-1)
+                return EDIDmode.modeNumber;
+        }
+        if (edid1_EstablishedTim(&edid, EST_1152x870_75Hz))
+        {
+            EDIDmode.resX = 1152;
+            EDIDmode.resY = 870;
+            EDIDmode.bpp = 0;
+            if (findModeByResolution(modeList, listLength, &EDIDmode) !=
+                (uint16_t)-1)
+                return EDIDmode.modeNumber;
+        }
+        if (edid1_EstablishedTim(&edid, EST_1024x768_75Hz) ||
+            edid1_EstablishedTim(&edid, EST_1024x768_70Hz) ||
+            edid1_EstablishedTim(&edid, EST_1024x768_60Hz) ||
+            edid1_EstablishedTim(&edid, EST_1024x768_87Hz))
+        {
+            EDIDmode.resX = 1024;
+            EDIDmode.resY = 768;
+            EDIDmode.bpp = 0;
+            if (findModeByResolution(modeList, listLength, &EDIDmode) !=
+                (uint16_t)-1)
+                return EDIDmode.modeNumber;
+        }
+        if (edid1_EstablishedTim(&edid, EST_832x624_75Hz))
+        {
+            EDIDmode.resX = 832;
+            EDIDmode.resY = 624;
+            EDIDmode.bpp = 0;
+            if (findModeByResolution(modeList, listLength, &EDIDmode) !=
+                (uint16_t)-1)
+                return EDIDmode.modeNumber;
+        }
+        if (edid1_EstablishedTim(&edid, EST_800x600_60Hz) ||
+            edid1_EstablishedTim(&edid, EST_800x600_56Hz) ||
+            edid1_EstablishedTim(&edid, EST_800x600_75Hz) ||
+            edid1_EstablishedTim(&edid, EST_800x600_72Hz))
+        {
+            EDIDmode.resX = 800;
+            EDIDmode.resY = 600;
+            EDIDmode.bpp = 0;
+            if (findModeByResolution(modeList, listLength, &EDIDmode) !=
+                (uint16_t)-1)
+                return EDIDmode.modeNumber;
+        }
+        if (edid1_EstablishedTim(&edid, EST_720x400_88Hz) ||
+            edid1_EstablishedTim(&edid, EST_720x400_70Hz))
+        {
+            EDIDmode.resX = 720;
+            EDIDmode.resY = 400;
+            EDIDmode.bpp = 0;
+            if (findModeByResolution(modeList, listLength, &EDIDmode) !=
+                (uint16_t)-1)
+                return EDIDmode.modeNumber;
+        }
+        if (edid1_EstablishedTim(&edid, EST_640x480_75Hz) ||
+            edid1_EstablishedTim(&edid, EST_640x480_72Hz) ||
+            edid1_EstablishedTim(&edid, EST_640x480_67Hz) ||
+            edid1_EstablishedTim(&edid, EST_640x480_60Hz))
+        {
+            EDIDmode.resX = 640;
+            EDIDmode.resY = 480;
+            EDIDmode.bpp = 0;
+            if (findModeByResolution(modeList, listLength, &EDIDmode) !=
+                (uint16_t)-1)
+                return EDIDmode.modeNumber;
+        }
+    }
+    else
+        printk(FB_VESA_NAME " error reading EDID: unsupported version\n");
+    return (uint16_t)-1;
+}
+
+void vesa_realmode_bootup_init(void)
+{
+    uint32_t vbe_ret_val;
+    uint16_t size;
+    struct VBE_VbeInfoBlock *vib = (struct VBE_VbeInfoBlock *)
+        i386_get_default_rm_buffer(&size);
+    vbe_ret_val = VBEControllerInformation(vib, 0x300);
+    if (vbe_ret_val == -1)
+    {
+        printk(FB_VESA_NAME " error calling real mode interrupt.\n");
+        return;
+    }
+    if (vbe_ret_val != (VBE_callSuccessful<<8 | VBE_functionSupported))
+    {
+        printk(FB_VESA_NAME " Function 00h (read VBE info block)"
+            "not supported.\n");
+    }
+/*  Helper array is later filled with mode numbers and their parameters
+    sorted from the biggest values to the smalest where priorities of
+    parameters are from the highest to the lowest: resolution X,
+    resolution Y, bits per pixel.
+    The array is used for search the monitor provided parameters in EDID
+    structure and if found we set such mode using corresponding
+    VESA function. */
+#define MAX_NO_OF_SORTED_MODES 100
+    modeParams sortModeParams[MAX_NO_OF_SORTED_MODES];
+
+    uint16_t *vmpSegOff = (uint16_t *)&vib->VideoModePtr;
+    uint16_t *modeNOPtr = (uint16_t*)
+        i386_Real_to_physical(*(vmpSegOff+1), *vmpSegOff);
+    uint16_t iterator = 0;
+    if (*(uint16_t*)vib->VideoModePtr == VBE_STUB_VideoModeList)
+    {
+        printk(FB_VESA_NAME " VBE Core not implemented!\n");
+    }
+    else
+    {
+        /* prepare list of modes */
+        while (*(modeNOPtr+iterator) != VBE_END_OF_VideoModeList &&
+            *(modeNOPtr+iterator) != 0)
+        { /* some bios implementations ends the list incorrectly with 0 */
+            if (iterator < MAX_NO_OF_SORTED_MODES)
+            {
+                sortModeParams[iterator].modeNumber = *(modeNOPtr+iterator);
+                iterator ++;
+            }
+            else
+                break;
+        }
+        if (iterator < MAX_NO_OF_SORTED_MODES)
+            sortModeParams[iterator].modeNumber = 0;
+    }
+
+    struct VBE_ModeInfoBlock *mib = (struct VBE_ModeInfoBlock *)
+        i386_get_default_rm_buffer(&size);
+    iterator = 0;
+    uint8_t nextFilteredMode = 0;
+    uint16_t required_mode_attributes = VBE_modSupInHWMask |
+        VBE_ColorModeMask | VBE_GraphicsModeMask | VBE_LinFraBufModeAvaiMask;
+    /* get parameters of modes and filter modes according to set
+        required parameters */
+    while (iterator < MAX_NO_OF_SORTED_MODES &&
+        sortModeParams[iterator].modeNumber!=0)
+    {
+        VBEModeInformation(mib, sortModeParams[iterator].modeNumber);
+        if ((mib->ModeAttributes&required_mode_attributes) ==
+            required_mode_attributes)
+        {
+            sortModeParams[nextFilteredMode].modeNumber =
+                sortModeParams[iterator].modeNumber;
+            sortModeParams[nextFilteredMode].resX = mib->XResolution;
+            sortModeParams[nextFilteredMode].resY = mib->YResolution;
+            sortModeParams[nextFilteredMode].bpp  = mib->BitsPerPixel;
+            nextFilteredMode ++;
+        }
+        iterator ++;
+    }
+    sortModeParams[nextFilteredMode].modeNumber = 0;
+
+    uint8_t numberOfModes = nextFilteredMode;
+    /* sort filtered modes */
+    modeParams modeXchgPlace;
+    iterator = 0;
+    uint8_t j;
+    uint8_t idxBestMode;
+    while (iterator < numberOfModes)
+    {
+        idxBestMode = iterator;
+        j = iterator+1;
+        while (j < numberOfModes)
+        {
+            if (sortModeParams[j].resX > sortModeParams[idxBestMode].resX)
+                idxBestMode = j;
+            else if (sortModeParams[j].resX == sortModeParams[idxBestMode].resX)
+            {
+                if (sortModeParams[j].resY > sortModeParams[idxBestMode].resY)
+                    idxBestMode = j;
+                else if (sortModeParams[j].resY ==
+                    sortModeParams[idxBestMode].resY)
+                {
+                    if (sortModeParams[j].bpp > sortModeParams[idxBestMode].bpp)
+                        idxBestMode = j;
+                }
+            }
+            j++;
+        }
+        if (idxBestMode != iterator)
+        {
+            modeXchgPlace = sortModeParams[iterator];
+            sortModeParams[iterator] = sortModeParams[idxBestMode];
+            sortModeParams[idxBestMode] = modeXchgPlace;
+        }
+        iterator++;
+    }
+
+    /* first search for video argument in multiboot options */
+    vbe_usedMode = findModeUsingCmdline(sortModeParams, numberOfModes);
+    if (vbe_usedMode == (uint16_t)-1)
+    {
+        printk(FB_VESA_NAME " video on command line not provided"
+            "\n\ttrying EDID ...\n");
+        /* second search monitor for good resolution */
+        vbe_usedMode = findModeUsingEDID(sortModeParams, numberOfModes);
+        if (vbe_usedMode == (uint16_t)-1)
+        {
+            printk(FB_VESA_NAME" monitor's EDID video parameters not supported"
+                               "\n\tusing mode with highest resolution, bpp\n");
+            /* third set highest values */
+            vbe_usedMode = sortModeParams[0].modeNumber;
+        }
+    }
+
+    /* fill framebuffer structs with info about selected mode */
+    vbe_ret_val = VBEModeInformation(mib, vbe_usedMode);
+    if ((vbe_ret_val&0xff)!=VBE_functionSupported ||
+        (vbe_ret_val>>8)!=VBE_callSuccessful)
+    {
+        printk(FB_VESA_NAME " Cannot get mode info anymore. ax=0x%x\n",
+            vbe_ret_val);
+    }
+
+    fb_var.xres = mib->XResolution;
+    fb_var.yres = mib->YResolution;
+    fb_var.bits_per_pixel = mib->BitsPerPixel;
+    fb_var.red.offset =      mib->LinRedFieldPosition;
+    fb_var.red.length =      mib->LinRedMaskSize;
+    fb_var.red.msb_right =   0;
+    fb_var.green.offset =    mib->LinGreenFieldPosition;
+    fb_var.green.length =    mib->LinGreenMaskSize;
+    fb_var.green.msb_right = 0;
+    fb_var.blue.offset =     mib->LinBlueFieldPosition;
+    fb_var.blue.length =     mib->LinBlueMaskSize;
+    fb_var.blue.msb_right =  0;
+    fb_var.transp.offset =   mib->LinRsvdFieldPosition;
+    fb_var.transp.length =   mib->LinRsvdMaskSize;
+    fb_var.transp.msb_right =0;
+
+    fb_fix.smem_start  = (char *)mib->PhysBasePtr;
+    fb_fix.line_length = mib->LinBytesPerScanLine;
+    fb_fix.smem_len    = fb_fix.line_length*fb_var.yres;
+    fb_fix.type        = FB_TYPE_PACKED_PIXELS;
+    if (fb_var.bits_per_pixel < 24)
+        fb_fix.visual  = FB_VISUAL_DIRECTCOLOR;
+    else
+        fb_fix.visual  = FB_VISUAL_TRUECOLOR;
+
+    /* set selected mode */
+    vbe_ret_val = VBESetMode(vbe_usedMode | VBE_linearFlatFrameBufMask,
+        (struct VBE_CRTCInfoBlock *)(i386_get_default_rm_buffer(&size)));
+    if (vbe_ret_val>>8 == VBE_callFailed)
+        printk(FB_VESA_NAME " VBE: Requested mode is not available.");
+
+    if ((vbe_ret_val&0xff)!= (VBE_functionSupported | VBE_callSuccessful<<8))
+        printk(FB_VESA_NAME " Call to function 2h (set VBE mode) failed. "
+            "ax=0x%x\n", vbe_ret_val);
+
+    vib = (void *) 0;
+    mib = (void *) 0;
+}
+
+/*
+ * fb_vesa device driver INITIALIZE entry point.
+ */
+rtems_device_driver
+frame_buffer_initialize(
+    rtems_device_major_number  major,
+    rtems_device_minor_number  minor,
+    void                      *arg
+)
+{
+    rtems_status_code status;
+
+    printk(FB_VESA_NAME " frame buffer -- driver initializing..\n" );
+
+/*
+ * Register the device.
+ */
+    status = rtems_io_register_name(FRAMEBUFFER_DEVICE_0_NAME, major, 0);
+    if (status != RTEMS_SUCCESSFUL)
+    {
+        printk("Error registering " FRAMEBUFFER_DEVICE_0_NAME
+        " - " FB_VESA_NAME " frame buffer device!\n");
+        rtems_fatal_error_occurred( status );
+    }
+
+    return RTEMS_SUCCESSFUL;
+}
+
+/*
+ * fb_vesa device driver OPEN entry point
+ */
+rtems_device_driver
+frame_buffer_open(
+    rtems_device_major_number  major,
+    rtems_device_minor_number  minor,
+    void                      *arg
+)
+{
+    printk( FB_VESA_NAME " open device\n" );
+
+    if (pthread_mutex_trylock(&vesa_mutex) != 0)
+    {
+        printk( FB_VESA_NAME " could not lock vesa_mutex\n" );
+
+        return RTEMS_UNSATISFIED;
+    }
+
+    return RTEMS_SUCCESSFUL;
+
+}
+
+/*
+ * fb_vesa device driver CLOSE entry point
+ */
+rtems_device_driver
+frame_buffer_close(
+    rtems_device_major_number  major,
+    rtems_device_minor_number  minor,
+    void                      *arg
+)
+{
+  printk( FB_VESA_NAME " close device\n" );
+  if (pthread_mutex_unlock(&vesa_mutex) == 0)
+  {
+      /* restore previous state.  for VGA this means return to text mode.
+       * leave out if graphics hardware has been initialized in
+       * frame_buffer_initialize() */
+
+      printk(FB_VESA_NAME ": close called.\n" );
+      return RTEMS_SUCCESSFUL;
+  }
+
+  return RTEMS_UNSATISFIED;
+}
+
+/*
+ * fb_vesa device driver READ entry point.
+ */
+rtems_device_driver
+frame_buffer_read(
+    rtems_device_major_number  major,
+    rtems_device_minor_number  minor,
+    void                      *arg
+)
+{
+  printk( FB_VESA_NAME " read device\n" );
+  rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg;
+  rw_args->bytes_moved =
+    ((rw_args->offset + rw_args->count) > fb_fix.smem_len ) ?
+        (fb_fix.smem_len - rw_args->offset) :
+        rw_args->count;
+  memcpy(rw_args->buffer, (const void *)
+    (fb_fix.smem_start + rw_args->offset), rw_args->bytes_moved);
+  return RTEMS_SUCCESSFUL;
+}
+
+/*
+ * frame_vesa device driver WRITE entry point.
+ */
+rtems_device_driver
+frame_buffer_write(
+    rtems_device_major_number  major,
+    rtems_device_minor_number  minor,
+    void                      *arg
+)
+{
+  printk( FB_VESA_NAME " write device\n" );
+  rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg;
+  rw_args->bytes_moved =
+    ((rw_args->offset + rw_args->count) > fb_fix.smem_len ) ?
+        (fb_fix.smem_len - rw_args->offset) :
+        rw_args->count;
+  memcpy( (void *) (fb_fix.smem_start + rw_args->offset),
+    rw_args->buffer, rw_args->bytes_moved);
+  return RTEMS_SUCCESSFUL;
+}
+
+static int get_fix_screen_info( struct fb_fix_screeninfo *info )
+{
+    printk("get_fix_screen_info\n");
+  *info = fb_fix;
+  return 0;
+}
+
+static int get_var_screen_info( struct fb_var_screeninfo *info )
+{
+    printk("get_var_screen_info\n");
+  *info =  fb_var;
+  return 0;
+}
+
+/*
+ * IOCTL entry point -- This method is called to carry
+ * all services of this interface.
+ */
+rtems_device_driver
+frame_buffer_control(
+    rtems_device_major_number  major,
+    rtems_device_minor_number  minor,
+    void                      *arg
+)
+{
+  rtems_libio_ioctl_args_t *args = arg;
+
+  printk( FB_VESA_NAME " ioctl called, cmd=%x\n", args->command  );
+    printk("fbxres %d, fbyres %d\n", fb_var.xres, fb_var.yres);
+    printk("fbbpp %d\n", fb_var.bits_per_pixel);
+
+  switch (args->command)
+  {
+  case FBIOGET_FSCREENINFO:
+      args->ioctl_return =
+        get_fix_screen_info( ( struct fb_fix_screeninfo * ) args->buffer );
+      break;
+  case FBIOGET_VSCREENINFO:
+      args->ioctl_return =
+        get_var_screen_info( ( struct fb_var_screeninfo * ) args->buffer );
+      break;
+  case FBIOPUT_VSCREENINFO:
+    /* not implemented yet */
+    args->ioctl_return = -1;
+    return RTEMS_UNSATISFIED;
+  case FBIOGETCMAP:
+    /* no palette - truecolor mode */
+    args->ioctl_return = -1;
+    return RTEMS_UNSATISFIED;
+  case FBIOPUTCMAP:
+    /* no palette - truecolor mode */
+    args->ioctl_return = -1;
+    return RTEMS_UNSATISFIED;
+  default:
+    args->ioctl_return = 0;
+    break;
+  }
+  return RTEMS_SUCCESSFUL;
+}
diff --git a/c/src/lib/libbsp/i386/pc386/include/fb_vesa.h b/c/src/lib/libbsp/i386/pc386/include/fb_vesa.h
new file mode 100644
index 0000000..5638b50
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/include/fb_vesa.h
@@ -0,0 +1,131 @@
+/**
+ * @file fb_vesa.h
+ *
+ * @ingroup i386_pc386
+ *
+ * @brief Definitioins for vesa based framebuffer drivers.
+ */
+
+/*
+ * Headers specific for framebuffer drivers utilizing VESA VBE.
+ *
+ * Copyright (C) 2014  Jan Doležal (dolezj21 at fel.cvut.cz)
+ *                     CTU in Prague.
+ *
+ *  The license and distribution terms for this file may be
+ *  found in the file LICENSE in this distribution or at
+ *  http://www.rtems.org/license/LICENSE.
+ */
+
+#include <bsp/vbe3.h>
+#include <edid.h>
+
+#ifndef _FB_VESA_H
+#define _FB_VESA_H
+
+#ifndef ASM /* ASM */
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/* ----- Prototypes ----- */
+
+/**
+ * Returns information about graphic's controller in the infoBlock structure.
+ *
+ * @param infoBlock pointer to the struct to be filled with
+                    controller information
+ * @param queriedVBEVersion if >0x200 then video bios is asked to fill in
+ *                          parameters which appeared with second version
+ *                          of VBE.
+ * @retval  register ax content as defined in VBE RETURN STATUS paragraph
+ *          -1 error calling graphical bios
+ */
+uint32_t VBEControllerInformation (
+    struct VBE_VbeInfoBlock *infoBlock,
+    uint16_t queriedVBEVersion
+);
+
+/**
+ * Fills structure infoBlock with informations about selected mode in
+ * modeNumber variable.
+ *
+ * @param infoBlock pointer to the struct to be filled with mode information
+ * @param modeNumber detailes of this mode to be filled
+ * @retval  register ax content as defined in VBE RETURN STATUS paragraph
+ *          -1 error calling graphical bios
+ */
+uint32_t VBEModeInformation (
+    struct VBE_ModeInfoBlock *infoBlock,
+    uint16_t modeNumber
+);
+
+/**
+ * Sets graphics mode selected. If mode has refreshRateCtrl bit set, than the
+ * infoBlock must be filled accordingly.
+ *
+ * @param modeNumber number of mode to be set
+ * @param infoBlock pointer to struct containing refresh rate control info
+ * @retval  register ax content as defined in VBE RETURN STATUS paragraph
+ *          -1 error calling graphical bios
+ */
+uint32_t VBESetMode (
+    uint16_t modeNumber,
+    struct VBE_CRTCInfoBlock *infoBlock
+);
+
+/**
+ * Get currently set mode number.
+ *
+ * @param modeNumber variable to be filled with current mode number
+ * @retval  register ax content as defined in VBE RETURN STATUS paragraph
+ *          -1 error calling graphical bios
+ */
+uint32_t VBECurrentMode (
+    uint16_t *modeNumber
+);
+
+/**
+ * Gets information about display data channel implemented in the
+ * graphic's controller.
+ *
+ * @param controllerUnitNumber
+ * @param secondsToTransferEDIDBlock approximate time to transfer one EDID block
+ *                                   rounded up to seconds
+ * @param DDCLevelSupported after call contains DDC version supported and
+ *                          screen blanking state during transfer
+ * @retval  register ax content as defined in VBE RETURN STATUS paragraph
+ *          -1 error calling graphical bios
+ */
+uint32_t VBEReportDDCCapabilities (
+    uint16_t controllerUnitNumber,
+    uint8_t *secondsToTransferEDIDBlock,
+    uint8_t *DDCLevelSupported
+);
+
+/**
+ * Reads selected EDID block from display attached to controller's interface.
+ *
+ * @param controllerUnitNumber
+ * @param EDIDBlockNumber block no. to be read from the display
+ * @param buffer place to store block fetched from the display
+ * @retval  register ax content as defined in VBE RETURN STATUS paragraph
+ *          -1 error calling graphical bios
+ */
+uint32_t VBEReadEDID (
+    uint16_t controllerUnitNumber,
+    uint16_t EDIDBlockNumber,
+    struct edid1 *buffer
+);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* ASM */
+
+#endif /* _FB_VESA_H */
diff --git a/c/src/lib/libbsp/i386/pc386/preinstall.am b/c/src/lib/libbsp/i386/pc386/preinstall.am
index b4ac86b..33a230c 100644
--- a/c/src/lib/libbsp/i386/pc386/preinstall.am
+++ b/c/src/lib/libbsp/i386/pc386/preinstall.am
@@ -147,6 +147,7 @@ $(PROJECT_INCLUDE)/i386_io.h: ../../i386/shared/comm/i386_io.h $(PROJECT_INCLUDE
 	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/i386_io.h
 PREINSTALL_FILES += $(PROJECT_INCLUDE)/i386_io.h
 
+if USE_VBE_RM
 $(PROJECT_INCLUDE)/bsp/vbe3.h: include/vbe3.h $(PROJECT_INCLUDE)/bsp/$(dirstamp)
 	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/vbe3.h
 PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/vbe3.h
@@ -155,6 +156,11 @@ $(PROJECT_INCLUDE)/edid.h: include/edid.h $(PROJECT_INCLUDE)/$(dirstamp)
 	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/edid.h
 PREINSTALL_FILES += $(PROJECT_INCLUDE)/edid.h
 
+$(PROJECT_INCLUDE)/bsp/fb_vesa.h: include/fb_vesa.h $(PROJECT_INCLUDE)/bsp/$(dirstamp)
+	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/fb_vesa.h
+PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/fb_vesa.h
+endif
+
 $(PROJECT_INCLUDE)/pcibios.h: ../../i386/shared/pci/pcibios.h $(PROJECT_INCLUDE)/$(dirstamp)
 	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/pcibios.h
 PREINSTALL_FILES += $(PROJECT_INCLUDE)/pcibios.h
diff --git a/c/src/lib/libbsp/i386/pc386/start/start.S b/c/src/lib/libbsp/i386/pc386/start/start.S
index 1cba124..ab92a4f 100644
--- a/c/src/lib/libbsp/i386/pc386/start/start.S
+++ b/c/src/lib/libbsp/i386/pc386/start/start.S
@@ -41,6 +41,7 @@
 
 #include <rtems/asm.h>
 #include <rtems/score/cpu.h>
+#include <bspopts.h>
 
 /*----------------------------------------------------------------------------+
 | Size of heap and stack:
@@ -61,6 +62,9 @@ BEGIN_CODE
 	PUBLIC (start)		# GNU default entry point
 
 	EXTERN (boot_card)
+#ifdef USE_VBE_RM
+        EXTERN (vesa_realmode_bootup_init)
+#endif
 	EXTERN (_load_segments)
 	EXTERN (_return_to_monitor)
 	EXTERN (_IBMPC_initVideo)
@@ -201,6 +205,10 @@ SYM (zero_bss):
 +-------------------------------------------------------------------*/
 	call _IBMPC_initVideo
 
+#ifdef USE_VBE_RM
+        call    vesa_realmode_bootup_init
+#endif
+
 /*---------------------------------------------------------------------+
 | Check CPU type. Enable Cache and init coprocessor if needed.
 +---------------------------------------------------------------------*/
-- 
1.9.1





More information about the devel mailing list