update for HEAD-2003091401
[reactos.git] / drivers / dd / bootvid / bootvid.c
diff --git a/drivers/dd/bootvid/bootvid.c b/drivers/dd/bootvid/bootvid.c
new file mode 100644 (file)
index 0000000..e06e110
--- /dev/null
@@ -0,0 +1,816 @@
+/* $Id$
+ *
+ * COPYRIGHT:      See COPYING in the top level directory
+ * PROJECT:        ReactOS kernel
+ * FILE:           ntoskrnl/inbv/bootvid.c
+ * PURPOSE:        Boot video support
+ * PROGRAMMER:     Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * UPDATE HISTORY:
+ *  12-07-2003 CSH Created
+ */
+
+/* INCLUDES ******************************************************************/
+
+#include <ddk/ntddk.h>
+#include <ddk/ntbootvid.h>
+#include <reactos/resource.h>
+
+#include "../../../ntoskrnl/include/internal/v86m.h"
+
+/*#define NDEBUG*/
+#include <debug.h>
+
+#define RT_BITMAP   2
+
+typedef struct tagRGBQUAD {
+  unsigned char    rgbBlue;
+  unsigned char    rgbGreen;
+  unsigned char    rgbRed;
+  unsigned char    rgbReserved;
+} RGBQUAD, *PRGBQUAD;
+
+typedef long FXPT2DOT30;
+
+typedef struct tagCIEXYZ {
+  FXPT2DOT30 ciexyzX; 
+  FXPT2DOT30 ciexyzY; 
+  FXPT2DOT30 ciexyzZ; 
+} CIEXYZ;
+typedef CIEXYZ * LPCIEXYZ; 
+
+typedef struct tagCIEXYZTRIPLE {
+  CIEXYZ  ciexyzRed; 
+  CIEXYZ  ciexyzGreen; 
+  CIEXYZ  ciexyzBlue; 
+} CIEXYZTRIPLE;
+typedef CIEXYZTRIPLE *LPCIEXYZTRIPLE;
+
+typedef struct { 
+  DWORD        bV5Size; 
+  LONG         bV5Width; 
+  LONG         bV5Height; 
+  WORD         bV5Planes; 
+  WORD         bV5BitCount; 
+  DWORD        bV5Compression; 
+  DWORD        bV5SizeImage; 
+  LONG         bV5XPelsPerMeter; 
+  LONG         bV5YPelsPerMeter; 
+  DWORD        bV5ClrUsed; 
+  DWORD        bV5ClrImportant; 
+  DWORD        bV5RedMask; 
+  DWORD        bV5GreenMask; 
+  DWORD        bV5BlueMask; 
+  DWORD        bV5AlphaMask; 
+  DWORD        bV5CSType; 
+  CIEXYZTRIPLE bV5Endpoints; 
+  DWORD        bV5GammaRed; 
+  DWORD        bV5GammaGreen; 
+  DWORD        bV5GammaBlue; 
+  DWORD        bV5Intent; 
+  DWORD        bV5ProfileData; 
+  DWORD        bV5ProfileSize; 
+  DWORD        bV5Reserved; 
+} BITMAPV5HEADER, *PBITMAPV5HEADER; 
+
+
+#define MISC     0x3c2
+#define SEQ      0x3c4
+#define CRTC     0x3d4
+#define GRAPHICS 0x3ce
+#define FEATURE  0x3da
+#define ATTRIB   0x3c0
+#define STATUS   0x3da
+
+typedef struct {
+  ULONG r;
+  ULONG g;
+  ULONG b;
+} FADER_PALETTE_ENTRY;
+
+/* In pixelsups.S */
+extern VOID
+InbvPutPixels(int x, int y, unsigned long c);
+
+/* GLOBALS *******************************************************************/
+
+char *vidmem;
+
+/* Must be 4 bytes per entry */
+long maskbit[640];
+long y80[480];
+
+static HANDLE BitmapThreadHandle;
+static CLIENT_ID BitmapThreadId;
+static BOOLEAN BitmapIsDrawn;
+static PUCHAR BootimageBitmap;
+static BOOLEAN InGraphicsMode = FALSE;
+
+/* DATA **********************************************************************/
+
+static BOOLEAN VideoAddressSpaceInitialized = FALSE;
+static PVOID NonBiosBaseAddress;
+static PDRIVER_OBJECT BootVidDriverObject = NULL;
+
+/* FUNCTIONS *****************************************************************/
+
+static BOOLEAN
+InbvFindBootimage()
+{
+  PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
+  LDR_RESOURCE_INFO ResourceInfo;
+  NTSTATUS Status;
+  PVOID BaseAddress = BootVidDriverObject->DriverStart;
+  ULONG Size;
+
+  ResourceInfo.Type = RT_BITMAP;
+  ResourceInfo.Name = IDB_BOOTIMAGE;
+  ResourceInfo.Language = 0x09;
+
+  Status = LdrFindResource_U(BaseAddress,
+    &ResourceInfo,
+    RESOURCE_DATA_LEVEL,
+    &ResourceDataEntry);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("LdrFindResource_U() failed with status 0x%.08x\n", Status);
+      return FALSE;
+    }
+
+  Status = LdrAccessResource(BaseAddress,
+    ResourceDataEntry,
+    (PVOID*)&BootimageBitmap,
+    &Size);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("LdrAccessResource() failed with status 0x%.08x\n", Status);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+
+static BOOLEAN
+InbvInitializeVideoAddressSpace(VOID)
+{
+   OBJECT_ATTRIBUTES ObjectAttributes;
+   UNICODE_STRING PhysMemName;
+   NTSTATUS Status;
+   HANDLE PhysMemHandle;
+   PVOID BaseAddress;
+   LARGE_INTEGER Offset;
+   ULONG ViewSize;
+   CHAR IVT[1024];
+   CHAR BDA[256];
+   PVOID start = (PVOID)0x0;
+
+   /*
+    * Open the physical memory section
+    */
+   RtlInitUnicodeStringFromLiteral(&PhysMemName, L"\\Device\\PhysicalMemory");
+   InitializeObjectAttributes(&ObjectAttributes,
+                             &PhysMemName,
+                             0,
+                             NULL,
+                             NULL);
+   Status = ZwOpenSection(&PhysMemHandle, SECTION_ALL_ACCESS, 
+                         &ObjectAttributes);
+   if (!NT_SUCCESS(Status))
+     {
+       DPRINT("Couldn't open \\Device\\PhysicalMemory\n");
+       return FALSE;
+     }
+
+   /*
+    * Map the BIOS and device registers into the address space
+    */
+   Offset.QuadPart = 0xa0000;
+   ViewSize = 0x100000 - 0xa0000;
+   BaseAddress = (PVOID)0xa0000;
+   Status = NtMapViewOfSection(PhysMemHandle,
+                              NtCurrentProcess(),
+                              &BaseAddress,
+                              0,
+                              8192,
+                              &Offset,
+                              &ViewSize,
+                              ViewUnmap,
+                              0,
+                              PAGE_EXECUTE_READWRITE);
+   if (!NT_SUCCESS(Status))
+     {
+       DPRINT("Couldn't map physical memory (%x)\n", Status);
+       NtClose(PhysMemHandle);
+       return FALSE;
+     }
+   NtClose(PhysMemHandle);
+   if (BaseAddress != (PVOID)0xa0000)
+     {
+       DPRINT("Couldn't map physical memory at the right address "
+               "(was %x)\n", BaseAddress);
+       return FALSE;
+     }
+
+   /*
+    * Map some memory to use for the non-BIOS parts of the v86 mode address
+    * space
+    */
+   NonBiosBaseAddress = (PVOID)0x1;
+   ViewSize = 0xa0000 - 0x1000;
+   Status = NtAllocateVirtualMemory(NtCurrentProcess(),
+                                   &NonBiosBaseAddress,
+                                   0,
+                                   &ViewSize,
+                                   MEM_COMMIT,
+                                   PAGE_EXECUTE_READWRITE);
+   if (!NT_SUCCESS(Status))
+     {
+       DPRINT("Failed to allocate virtual memory (Status %x)\n", Status);
+       return FALSE;
+     }
+   if (NonBiosBaseAddress != (PVOID)0x0)
+     {
+       DPRINT("Failed to allocate virtual memory at right address "
+               "(was %x)\n", NonBiosBaseAddress);
+       return FALSE;
+     }
+
+   /*
+    * Get the real mode IVT from the kernel
+    */
+   Status = NtVdmControl(0, IVT);
+   if (!NT_SUCCESS(Status))
+     {
+       DPRINT("NtVdmControl failed (status %x)\n", Status);
+       return FALSE;
+     }
+   
+   /*
+    * Copy the real mode IVT into the right place
+    */
+   memcpy(start, IVT, 1024);
+   
+   /*
+    * Get the BDA from the kernel
+    */
+   Status = NtVdmControl(1, BDA);
+   if (!NT_SUCCESS(Status))
+     {
+       DPRINT("NtVdmControl failed (status %x)\n", Status);
+       return FALSE;
+     }
+   
+   /*
+    * Copy the BDA into the right place
+    */
+   memcpy((PVOID)0x400, BDA, 256);
+
+   return TRUE;
+}
+
+
+static BOOLEAN
+InbvDeinitializeVideoAddressSpace(VOID)
+{
+  ULONG RegionSize;
+  PUCHAR ViewBase;
+
+  RegionSize = 0xa0000 - 0x1000;
+  NtFreeVirtualMemory(NtCurrentProcess(),
+    &NonBiosBaseAddress,
+    &RegionSize,
+    MEM_RELEASE);
+
+  ViewBase = (PUCHAR) 0xa0000;
+  ZwUnmapViewOfSection(NtCurrentProcess(), ViewBase);
+
+ return TRUE;
+}
+
+
+static VOID
+vgaPreCalc()
+{
+  ULONG j;
+
+  for(j = 0; j < 80; j++)
+  {
+    maskbit[j * 8 + 0] = 128;
+    maskbit[j * 8 + 1] = 64;
+    maskbit[j * 8 + 2] = 32;
+    maskbit[j * 8 + 3] = 16;
+    maskbit[j * 8 + 4] = 8;
+    maskbit[j * 8 + 5] = 4;
+    maskbit[j * 8 + 6] = 2;
+    maskbit[j * 8 + 7] = 1;
+  }
+  for(j = 0; j < 480; j++)
+  {
+    y80[j] = j * 80; /* 80 = 640 / 8 = Number of bytes per scanline */
+  }
+}
+
+
+static VOID
+InbvInitVGAMode(VOID)
+{
+  KV86M_REGISTERS Regs;
+  NTSTATUS Status;
+  ULONG i;
+
+  vidmem = (char *)(0xd0000000 + 0xa0000);
+  memset(&Regs, 0, sizeof(Regs));
+  Regs.Eax = 0x0012;
+  Status = Ke386CallBios(0x10, &Regs);
+  assert(NT_SUCCESS(Status));
+
+  /* Get VGA registers into the correct state */
+  /* Reset the internal flip-flop. */
+  (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
+  /* Write the 16 palette registers. */
+  for (i = 0; i < 16; i++)
+    {
+      WRITE_PORT_UCHAR((PUCHAR)ATTRIB, i);
+      WRITE_PORT_UCHAR((PUCHAR)ATTRIB, i);
+    }
+  /* Write the mode control register - graphics mode; 16 color DAC. */
+  WRITE_PORT_UCHAR((PUCHAR)ATTRIB, 0x10);
+  WRITE_PORT_UCHAR((PUCHAR)ATTRIB, 0x81);
+  /* Write the color select register - select first 16 DAC registers. */
+  WRITE_PORT_UCHAR((PUCHAR)ATTRIB, 0x14);
+  WRITE_PORT_UCHAR((PUCHAR)ATTRIB, 0x00);
+
+  /* Get the VGA into the mode we want to work with */
+  WRITE_PORT_UCHAR((PUCHAR)0x3ce,0x08);     /* Set */
+  WRITE_PORT_UCHAR((PUCHAR)0x3cf,0);        /* the MASK */
+  WRITE_PORT_USHORT((PUSHORT)0x3ce,0x0205); /* write mode = 2 (bits 0,1) read mode = 0  (bit 3) */
+  (UCHAR) READ_REGISTER_UCHAR(vidmem);      /* Update bit buffer */
+  WRITE_REGISTER_UCHAR(vidmem, 0);          /* Write the pixel */
+  WRITE_PORT_UCHAR((PUCHAR)0x3ce,0x08);
+  WRITE_PORT_UCHAR((PUCHAR)0x3cf,0xff);
+
+  /* Set the PEL mask. */
+  WRITE_PORT_UCHAR((PUCHAR)0x3c6, 0xff);
+
+  /* Zero out video memory (clear a possibly trashed screen) */
+  RtlZeroMemory(vidmem, 64000);
+
+  vgaPreCalc();
+}
+
+
+BOOLEAN
+STDCALL
+VidResetDisplay(VOID)
+{
+  /* 
+     We are only using standard VGA facilities so we can rely on the HAL 'int10mode3'
+     reset to cleanup the hardware state.
+  */
+  InGraphicsMode = FALSE;
+
+  return FALSE;
+}
+
+
+VOID
+STDCALL
+VidCleanUp(VOID)
+{
+  /* 
+     We are only using standard VGA facilities so we can rely on the HAL 'int10mode3'
+     reset to cleanup the hardware state.
+  */
+  InGraphicsMode = FALSE;
+}
+
+
+static __inline__ VOID
+InbvSetColor(int cindex, unsigned char red, unsigned char green, unsigned char blue)
+{
+  red = (red * 63) / 255;
+  green = (green * 63) / 255;
+  blue = (blue * 63) / 255;
+
+  WRITE_PORT_UCHAR((PUCHAR)0x03c8, cindex);
+  (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
+  (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
+  WRITE_PORT_UCHAR((PUCHAR)0x03c9, red);
+  (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
+  (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
+  WRITE_PORT_UCHAR((PUCHAR)0x03c9, green);
+  (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
+  (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
+  WRITE_PORT_UCHAR((PUCHAR)0x03c9, blue);
+  (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
+  (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
+}
+
+
+static __inline__ VOID
+InbvSetBlackPalette()
+{
+  register ULONG r = 0;
+
+  /* Disable screen and enable palette access. */
+  (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
+  WRITE_PORT_UCHAR((PUCHAR)0x3c0, 0x00);
+  for (r = 0; r < 16; r++)
+    {
+      InbvSetColor(r, 0, 0, 0);
+    }
+  /* Enable screen and enable palette access. */
+  (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
+  WRITE_PORT_UCHAR((PUCHAR)0x3c0, 0x20);
+}
+
+
+static VOID
+InbvDisplayBitmap(ULONG Width, ULONG Height, PCHAR ImageData)
+{
+  ULONG j,k,y;
+  register ULONG i;
+  register ULONG x;
+  register ULONG c;
+
+  k = 0;
+  for (y = 0; y < Height; y++)
+    {
+      for (j = 0; j < 8; j++)
+        {
+          x = j;
+
+          /*
+           * Loop through the line and process every 8th pixel.
+           * This way we can get a way with using the same bit mask
+           * for several pixels and thus not need to do as much I/O
+           * communication.
+           */
+          while (x < 640)
+            {
+              c = 0;
+
+              if (x < Width)
+                {
+                  c = ImageData[k + x];
+                  for (i = 1; i < 4; i++)
+                    {
+                      if (x + i*8 < Width)
+                        {
+                          c |= (ImageData[k + x + i * 8] << i * 8);
+                        }
+                    }
+                }
+
+             InbvPutPixels(x, 479 - y, c);
+              x += 8*4;
+            }
+        }
+      k += Width;
+    }
+}
+
+
+static VOID
+InbvDisplayCompressedBitmap()
+{
+  PBITMAPV5HEADER bminfo;
+  ULONG i,j,k;
+  ULONG x,y;
+  ULONG curx,cury;
+  ULONG bfOffBits;
+  ULONG clen;
+  PCHAR ImageData;
+
+  bminfo = (PBITMAPV5HEADER) &BootimageBitmap[0];
+  DPRINT("bV5Size = %d\n", bminfo->bV5Size);
+  DPRINT("bV5Width = %d\n", bminfo->bV5Width);
+  DPRINT("bV5Height = %d\n", bminfo->bV5Height);
+  DPRINT("bV5Planes = %d\n", bminfo->bV5Planes);
+  DPRINT("bV5BitCount = %d\n", bminfo->bV5BitCount);
+  DPRINT("bV5Compression = %d\n", bminfo->bV5Compression);
+  DPRINT("bV5SizeImage = %d\n", bminfo->bV5SizeImage);
+  DPRINT("bV5XPelsPerMeter = %d\n", bminfo->bV5XPelsPerMeter);
+  DPRINT("bV5YPelsPerMeter = %d\n", bminfo->bV5YPelsPerMeter);
+  DPRINT("bV5ClrUsed = %d\n", bminfo->bV5ClrUsed);
+  DPRINT("bV5ClrImportant = %d\n", bminfo->bV5ClrImportant);
+
+  bfOffBits = bminfo->bV5Size + bminfo->bV5ClrUsed * sizeof(RGBQUAD);
+  DPRINT("bfOffBits = %d\n", bfOffBits);
+  DPRINT("size of color indices = %d\n", bminfo->bV5ClrUsed * sizeof(RGBQUAD));
+  DPRINT("first byte of data = %d\n", BootimageBitmap[bfOffBits]);
+
+  InbvSetBlackPalette();
+
+  ImageData = ExAllocatePool(NonPagedPool, bminfo->bV5Width * bminfo->bV5Height);
+  RtlZeroMemory(ImageData, bminfo->bV5Width * bminfo->bV5Height);
+
+  /*
+   * ImageData has 1 pixel per byte.
+   * bootimage has 2 pixels per byte.
+   */
+
+  if (bminfo->bV5Compression == 2)
+    {
+      k = 0;
+      j = 0;
+      while ((j < bminfo->bV5SizeImage) && (k < (ULONG) (bminfo->bV5Width * bminfo->bV5Height)))
+        {
+          unsigned char b;
+    
+          clen = BootimageBitmap[bfOffBits + j];
+          j++;
+    
+          if (clen > 0)
+            {
+              /* Encoded mode */
+    
+              b = BootimageBitmap[bfOffBits + j];
+              j++;
+    
+              for (i = 0; i < (clen / 2); i++)
+                {
+                  ImageData[k] = (b & 0xf0) >> 4;
+                  k++;
+                  ImageData[k] = b & 0xf;
+                  k++;
+                }
+              if ((clen & 1) > 0)
+              {
+                ImageData[k] = (b & 0xf0) >> 4;
+                k++;
+              }
+            }
+          else
+            {
+              /* Absolute mode */
+              b = BootimageBitmap[bfOffBits + j];
+              j++;
+    
+              if (b == 0)
+                {
+                  /* End of line */
+                }
+              else if (b == 1)
+                {
+                  /* End of image */
+                  break;
+                }
+              else if (b == 2)
+                {
+                  x = BootimageBitmap[bfOffBits + j];
+                  j++;
+                  y = BootimageBitmap[bfOffBits + j];
+                  j++;
+                  curx = k % bminfo->bV5Width;
+                  cury = k / bminfo->bV5Width;
+                  k = (cury + y) * bminfo->bV5Width + (curx + x);
+                }
+              else
+                {
+                  if ((j & 1) > 0)
+                    {
+                      DPRINT("Unaligned copy!\n");
+                    }
+    
+                  clen = b;
+                  for (i = 0; i < (clen / 2); i++)
+                    {
+                      b = BootimageBitmap[bfOffBits + j];
+                      j++;
+        
+                      ImageData[k] = (b & 0xf0) >> 4;
+                      k++;
+                      ImageData[k] = b & 0xf;
+                      k++;
+                    }
+                  if ((clen & 1) > 0)
+                  {
+                    b = BootimageBitmap[bfOffBits + j];
+                    j++;
+                    ImageData[k] = (b & 0xf0) >> 4;
+                    k++;
+                  }
+                  /* Word align */
+                  j += (j & 1);
+                }
+            }
+        }
+
+      InbvDisplayBitmap(bminfo->bV5Width, bminfo->bV5Height, ImageData);
+    }
+  else
+    {
+      DbgPrint("Warning boot image need to be compressed using RLE4\n");
+    }
+
+  ExFreePool(ImageData);
+}
+
+
+#define PALETTE_FADE_STEPS  20
+#define PALETTE_FADE_TIME   20 * 10000 /* 20ms */
+
+static VOID
+InbvFadeUpPalette()
+{
+  PBITMAPV5HEADER bminfo;
+  PRGBQUAD Palette;
+  ULONG i;
+  unsigned char r,g,b;
+  register ULONG c;
+  LARGE_INTEGER        Interval;
+  FADER_PALETTE_ENTRY FaderPalette[16];
+  FADER_PALETTE_ENTRY FaderPaletteDelta[16];
+
+  RtlZeroMemory(&FaderPalette, sizeof(FaderPalette));
+  RtlZeroMemory(&FaderPaletteDelta, sizeof(FaderPaletteDelta));
+
+  bminfo = (PBITMAPV5HEADER) &BootimageBitmap[0]; //sizeof(BITMAPFILEHEADER)];
+  Palette = (PRGBQUAD) &BootimageBitmap[/* sizeof(BITMAPFILEHEADER) + */ bminfo->bV5Size];
+
+  for (i = 0; i < 16; i++)
+    {
+      if (i < bminfo->bV5ClrUsed)
+        {
+          FaderPaletteDelta[i].r = ((Palette[i].rgbRed << 8) / PALETTE_FADE_STEPS);
+          FaderPaletteDelta[i].g = ((Palette[i].rgbGreen << 8) / PALETTE_FADE_STEPS);
+          FaderPaletteDelta[i].b = ((Palette[i].rgbBlue << 8) / PALETTE_FADE_STEPS);
+        }
+    }
+
+  for (i = 0; i < PALETTE_FADE_STEPS; i++)
+    {
+      /* Disable screen and enable palette access. */
+      (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
+      WRITE_PORT_UCHAR((PUCHAR)0x3c0, 0x00);
+      for (c = 0; c < bminfo->bV5ClrUsed; c++)
+        {
+          /* Add the delta */
+          FaderPalette[c].r += FaderPaletteDelta[c].r;
+          FaderPalette[c].g += FaderPaletteDelta[c].g;
+          FaderPalette[c].b += FaderPaletteDelta[c].b;
+
+          /* Get the integer values */
+          r = FaderPalette[c].r >> 8;
+          g = FaderPalette[c].g >> 8;
+          b = FaderPalette[c].b >> 8;
+
+          /* Don't go too far */
+          if (r > Palette[c].rgbRed)
+            r = Palette[c].rgbRed;
+          if (g > Palette[c].rgbGreen)
+            g = Palette[c].rgbGreen;
+          if (b > Palette[c].rgbBlue)
+            b = Palette[c].rgbBlue;
+
+          /* Update the hardware */
+          InbvSetColor(c, r, g, b);
+        }
+      /* Enable screen and disable palette access. */
+      (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
+      WRITE_PORT_UCHAR((PUCHAR)0x3c0, 0x20);
+      /* Wait for a bit. */
+      Interval.QuadPart = -PALETTE_FADE_TIME;
+      KeDelayExecutionThread(KernelMode, FALSE, &Interval);
+    }
+}
+
+static VOID STDCALL
+InbvBitmapThreadMain(PVOID Ignored)
+{
+  if (InbvFindBootimage())
+    {
+      InbvDisplayCompressedBitmap();
+      InbvFadeUpPalette();
+    }
+  else
+    {
+      DbgPrint("Warning: Cannot find boot image\n");
+    }
+
+  BitmapIsDrawn = TRUE;
+}
+
+
+BOOLEAN
+STDCALL
+VidIsBootDriverInstalled(VOID)
+{
+  return InGraphicsMode;
+}
+
+
+BOOLEAN
+STDCALL
+VidInitialize(VOID)
+{
+  NTSTATUS Status;
+
+  if (!VideoAddressSpaceInitialized)
+    {
+      InbvInitializeVideoAddressSpace();
+    }
+
+  InbvInitVGAMode();
+
+  InGraphicsMode = TRUE;
+
+  BitmapIsDrawn = FALSE;
+  
+  Status = PsCreateSystemThread(&BitmapThreadHandle,
+    THREAD_ALL_ACCESS,
+    NULL,
+    NULL,
+    &BitmapThreadId,
+    InbvBitmapThreadMain,
+    NULL);
+  if (!NT_SUCCESS(Status))
+    {
+      return FALSE;
+    }
+  NtClose(BitmapThreadHandle);
+
+  InbvDeinitializeVideoAddressSpace();
+  VideoAddressSpaceInitialized = FALSE;
+
+  return TRUE;
+}
+
+NTSTATUS STDCALL_FUNC
+VidDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+{
+  PIO_STACK_LOCATION piosStack = IoGetCurrentIrpStackLocation(Irp);
+  NTSTATUS nErrCode;
+  NTBOOTVID_FUNCTION_TABLE* FunctionTable;
+  nErrCode = STATUS_SUCCESS;
+
+  switch(piosStack->MajorFunction)
+    {
+      /* opening and closing handles to the device */
+      case IRP_MJ_CREATE:
+      case IRP_MJ_CLOSE:
+        break;
+
+    case IRP_MJ_DEVICE_CONTROL:
+      switch (piosStack->Parameters.DeviceIoControl.IoControlCode)
+       {
+       case IOCTL_BOOTVID_INITIALIZE:
+         VidInitialize();
+         FunctionTable = (NTBOOTVID_FUNCTION_TABLE*)
+           Irp->AssociatedIrp.SystemBuffer;
+         FunctionTable->ResetDisplay = VidResetDisplay;
+         break;
+       case IOCTL_BOOTVID_CLEANUP:
+         VidCleanUp();   
+         break;
+       default:
+         nErrCode = STATUS_NOT_IMPLEMENTED;
+         break;
+       }
+        break;
+
+      /* unsupported operations */
+      default:
+        nErrCode = STATUS_NOT_IMPLEMENTED;
+    }
+
+  Irp->IoStatus.Status = nErrCode;
+  IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+  return nErrCode;
+}
+
+
+NTSTATUS STDCALL
+DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
+{
+  PDEVICE_OBJECT BootVidDevice;
+  UNICODE_STRING DeviceName;
+  UNICODE_STRING DosName;
+  NTSTATUS Status;
+
+  BootVidDriverObject = DriverObject;
+
+  /* register driver routines */
+  DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)VidDispatch;
+  DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)VidDispatch;
+  DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = 
+    (PDRIVER_DISPATCH)VidDispatch;
+  DriverObject->DriverUnload = NULL;
+
+  /* create device */
+  RtlInitUnicodeStringFromLiteral(&DeviceName, L"\\Device\\BootVid");
+
+  Status = IoCreateDevice(DriverObject, 0, &DeviceName, FILE_DEVICE_BOOTVID,
+                            0, FALSE, &BootVidDevice);
+  if (! NT_SUCCESS(Status))
+    {
+      return Status;
+    }
+
+  BootVidDevice->Flags |= DO_BUFFERED_IO;
+
+  return Status;
+}