This commit was manufactured by cvs2svn to create branch 'captive'.
[reactos.git] / drivers / dd / bootvid / bootvid.c
1 /* $Id$
2  *
3  * COPYRIGHT:      See COPYING in the top level directory
4  * PROJECT:        ReactOS kernel
5  * FILE:           ntoskrnl/inbv/bootvid.c
6  * PURPOSE:        Boot video support
7  * PROGRAMMER:     Casper S. Hornstrup (chorns@users.sourceforge.net)
8  * UPDATE HISTORY:
9  *  12-07-2003 CSH Created
10  */
11
12 /* INCLUDES ******************************************************************/
13
14 #include <ddk/ntddk.h>
15 #include <ddk/ntbootvid.h>
16 #include <reactos/resource.h>
17
18 #include "../../../ntoskrnl/include/internal/v86m.h"
19
20 /*#define NDEBUG*/
21 #include <debug.h>
22
23 #define RT_BITMAP   2
24
25 typedef struct tagRGBQUAD {
26   unsigned char    rgbBlue;
27   unsigned char    rgbGreen;
28   unsigned char    rgbRed;
29   unsigned char    rgbReserved;
30 } RGBQUAD, *PRGBQUAD;
31
32 typedef long FXPT2DOT30;
33
34 typedef struct tagCIEXYZ {
35   FXPT2DOT30 ciexyzX; 
36   FXPT2DOT30 ciexyzY; 
37   FXPT2DOT30 ciexyzZ; 
38 } CIEXYZ;
39 typedef CIEXYZ * LPCIEXYZ; 
40
41 typedef struct tagCIEXYZTRIPLE {
42   CIEXYZ  ciexyzRed; 
43   CIEXYZ  ciexyzGreen; 
44   CIEXYZ  ciexyzBlue; 
45 } CIEXYZTRIPLE;
46 typedef CIEXYZTRIPLE *LPCIEXYZTRIPLE;
47
48 typedef struct { 
49   DWORD        bV5Size; 
50   LONG         bV5Width; 
51   LONG         bV5Height; 
52   WORD         bV5Planes; 
53   WORD         bV5BitCount; 
54   DWORD        bV5Compression; 
55   DWORD        bV5SizeImage; 
56   LONG         bV5XPelsPerMeter; 
57   LONG         bV5YPelsPerMeter; 
58   DWORD        bV5ClrUsed; 
59   DWORD        bV5ClrImportant; 
60   DWORD        bV5RedMask; 
61   DWORD        bV5GreenMask; 
62   DWORD        bV5BlueMask; 
63   DWORD        bV5AlphaMask; 
64   DWORD        bV5CSType; 
65   CIEXYZTRIPLE bV5Endpoints; 
66   DWORD        bV5GammaRed; 
67   DWORD        bV5GammaGreen; 
68   DWORD        bV5GammaBlue; 
69   DWORD        bV5Intent; 
70   DWORD        bV5ProfileData; 
71   DWORD        bV5ProfileSize; 
72   DWORD        bV5Reserved; 
73 } BITMAPV5HEADER, *PBITMAPV5HEADER; 
74
75
76 #define MISC     0x3c2
77 #define SEQ      0x3c4
78 #define CRTC     0x3d4
79 #define GRAPHICS 0x3ce
80 #define FEATURE  0x3da
81 #define ATTRIB   0x3c0
82 #define STATUS   0x3da
83
84 typedef struct {
85   ULONG r;
86   ULONG g;
87   ULONG b;
88 } FADER_PALETTE_ENTRY;
89
90 /* In pixelsups.S */
91 extern VOID
92 InbvPutPixels(int x, int y, unsigned long c);
93
94 /* GLOBALS *******************************************************************/
95
96 char *vidmem;
97
98 /* Must be 4 bytes per entry */
99 long maskbit[640];
100 long y80[480];
101
102 static HANDLE BitmapThreadHandle;
103 static CLIENT_ID BitmapThreadId;
104 static BOOLEAN BitmapIsDrawn;
105 static PUCHAR BootimageBitmap;
106 static BOOLEAN InGraphicsMode = FALSE;
107
108 /* DATA **********************************************************************/
109
110 static BOOLEAN VideoAddressSpaceInitialized = FALSE;
111 static PVOID NonBiosBaseAddress;
112 static PDRIVER_OBJECT BootVidDriverObject = NULL;
113
114 /* FUNCTIONS *****************************************************************/
115
116 static BOOLEAN
117 InbvFindBootimage()
118 {
119   PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
120   LDR_RESOURCE_INFO ResourceInfo;
121   NTSTATUS Status;
122   PVOID BaseAddress = BootVidDriverObject->DriverStart;
123   ULONG Size;
124
125   ResourceInfo.Type = RT_BITMAP;
126   ResourceInfo.Name = IDB_BOOTIMAGE;
127   ResourceInfo.Language = 0x09;
128
129   Status = LdrFindResource_U(BaseAddress,
130     &ResourceInfo,
131     RESOURCE_DATA_LEVEL,
132     &ResourceDataEntry);
133   if (!NT_SUCCESS(Status))
134     {
135       DPRINT("LdrFindResource_U() failed with status 0x%.08x\n", Status);
136       return FALSE;
137     }
138
139   Status = LdrAccessResource(BaseAddress,
140     ResourceDataEntry,
141     (PVOID*)&BootimageBitmap,
142     &Size);
143   if (!NT_SUCCESS(Status))
144     {
145       DPRINT("LdrAccessResource() failed with status 0x%.08x\n", Status);
146       return FALSE;
147     }
148
149   return TRUE;
150 }
151
152
153 static BOOLEAN
154 InbvInitializeVideoAddressSpace(VOID)
155 {
156    OBJECT_ATTRIBUTES ObjectAttributes;
157    UNICODE_STRING PhysMemName;
158    NTSTATUS Status;
159    HANDLE PhysMemHandle;
160    PVOID BaseAddress;
161    LARGE_INTEGER Offset;
162    ULONG ViewSize;
163    CHAR IVT[1024];
164    CHAR BDA[256];
165    PVOID start = (PVOID)0x0;
166
167    /*
168     * Open the physical memory section
169     */
170    RtlInitUnicodeStringFromLiteral(&PhysMemName, L"\\Device\\PhysicalMemory");
171    InitializeObjectAttributes(&ObjectAttributes,
172                               &PhysMemName,
173                               0,
174                               NULL,
175                               NULL);
176    Status = ZwOpenSection(&PhysMemHandle, SECTION_ALL_ACCESS, 
177                           &ObjectAttributes);
178    if (!NT_SUCCESS(Status))
179      {
180         DPRINT("Couldn't open \\Device\\PhysicalMemory\n");
181         return FALSE;
182      }
183
184    /*
185     * Map the BIOS and device registers into the address space
186     */
187    Offset.QuadPart = 0xa0000;
188    ViewSize = 0x100000 - 0xa0000;
189    BaseAddress = (PVOID)0xa0000;
190    Status = NtMapViewOfSection(PhysMemHandle,
191                                NtCurrentProcess(),
192                                &BaseAddress,
193                                0,
194                                8192,
195                                &Offset,
196                                &ViewSize,
197                                ViewUnmap,
198                                0,
199                                PAGE_EXECUTE_READWRITE);
200    if (!NT_SUCCESS(Status))
201      {
202         DPRINT("Couldn't map physical memory (%x)\n", Status);
203         NtClose(PhysMemHandle);
204         return FALSE;
205      }
206    NtClose(PhysMemHandle);
207    if (BaseAddress != (PVOID)0xa0000)
208      {
209        DPRINT("Couldn't map physical memory at the right address "
210                 "(was %x)\n", BaseAddress);
211        return FALSE;
212      }
213
214    /*
215     * Map some memory to use for the non-BIOS parts of the v86 mode address
216     * space
217     */
218    NonBiosBaseAddress = (PVOID)0x1;
219    ViewSize = 0xa0000 - 0x1000;
220    Status = NtAllocateVirtualMemory(NtCurrentProcess(),
221                                     &NonBiosBaseAddress,
222                                     0,
223                                     &ViewSize,
224                                     MEM_COMMIT,
225                                     PAGE_EXECUTE_READWRITE);
226    if (!NT_SUCCESS(Status))
227      {
228        DPRINT("Failed to allocate virtual memory (Status %x)\n", Status);
229        return FALSE;
230      }
231    if (NonBiosBaseAddress != (PVOID)0x0)
232      {
233        DPRINT("Failed to allocate virtual memory at right address "
234                 "(was %x)\n", NonBiosBaseAddress);
235        return FALSE;
236      }
237
238    /*
239     * Get the real mode IVT from the kernel
240     */
241    Status = NtVdmControl(0, IVT);
242    if (!NT_SUCCESS(Status))
243      {
244        DPRINT("NtVdmControl failed (status %x)\n", Status);
245        return FALSE;
246      }
247    
248    /*
249     * Copy the real mode IVT into the right place
250     */
251    memcpy(start, IVT, 1024);
252    
253    /*
254     * Get the BDA from the kernel
255     */
256    Status = NtVdmControl(1, BDA);
257    if (!NT_SUCCESS(Status))
258      {
259        DPRINT("NtVdmControl failed (status %x)\n", Status);
260        return FALSE;
261      }
262    
263    /*
264     * Copy the BDA into the right place
265     */
266    memcpy((PVOID)0x400, BDA, 256);
267
268    return TRUE;
269 }
270
271
272 static BOOLEAN
273 InbvDeinitializeVideoAddressSpace(VOID)
274 {
275   ULONG RegionSize;
276   PUCHAR ViewBase;
277
278   RegionSize = 0xa0000 - 0x1000;
279   NtFreeVirtualMemory(NtCurrentProcess(),
280     &NonBiosBaseAddress,
281     &RegionSize,
282     MEM_RELEASE);
283
284   ViewBase = (PUCHAR) 0xa0000;
285   ZwUnmapViewOfSection(NtCurrentProcess(), ViewBase);
286
287  return TRUE;
288 }
289
290
291 static VOID
292 vgaPreCalc()
293 {
294   ULONG j;
295
296   for(j = 0; j < 80; j++)
297   {
298     maskbit[j * 8 + 0] = 128;
299     maskbit[j * 8 + 1] = 64;
300     maskbit[j * 8 + 2] = 32;
301     maskbit[j * 8 + 3] = 16;
302     maskbit[j * 8 + 4] = 8;
303     maskbit[j * 8 + 5] = 4;
304     maskbit[j * 8 + 6] = 2;
305     maskbit[j * 8 + 7] = 1;
306   }
307   for(j = 0; j < 480; j++)
308   {
309     y80[j] = j * 80; /* 80 = 640 / 8 = Number of bytes per scanline */
310   }
311 }
312
313
314 static VOID
315 InbvInitVGAMode(VOID)
316 {
317   KV86M_REGISTERS Regs;
318   NTSTATUS Status;
319   ULONG i;
320
321   vidmem = (char *)(0xd0000000 + 0xa0000);
322   memset(&Regs, 0, sizeof(Regs));
323   Regs.Eax = 0x0012;
324   Status = Ke386CallBios(0x10, &Regs);
325   assert(NT_SUCCESS(Status));
326
327   /* Get VGA registers into the correct state */
328   /* Reset the internal flip-flop. */
329   (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
330   /* Write the 16 palette registers. */
331   for (i = 0; i < 16; i++)
332     {
333       WRITE_PORT_UCHAR((PUCHAR)ATTRIB, i);
334       WRITE_PORT_UCHAR((PUCHAR)ATTRIB, i);
335     }
336   /* Write the mode control register - graphics mode; 16 color DAC. */
337   WRITE_PORT_UCHAR((PUCHAR)ATTRIB, 0x10);
338   WRITE_PORT_UCHAR((PUCHAR)ATTRIB, 0x81);
339   /* Write the color select register - select first 16 DAC registers. */
340   WRITE_PORT_UCHAR((PUCHAR)ATTRIB, 0x14);
341   WRITE_PORT_UCHAR((PUCHAR)ATTRIB, 0x00);
342
343   /* Get the VGA into the mode we want to work with */
344   WRITE_PORT_UCHAR((PUCHAR)0x3ce,0x08);     /* Set */
345   WRITE_PORT_UCHAR((PUCHAR)0x3cf,0);        /* the MASK */
346   WRITE_PORT_USHORT((PUSHORT)0x3ce,0x0205); /* write mode = 2 (bits 0,1) read mode = 0  (bit 3) */
347   (UCHAR) READ_REGISTER_UCHAR(vidmem);      /* Update bit buffer */
348   WRITE_REGISTER_UCHAR(vidmem, 0);          /* Write the pixel */
349   WRITE_PORT_UCHAR((PUCHAR)0x3ce,0x08);
350   WRITE_PORT_UCHAR((PUCHAR)0x3cf,0xff);
351
352   /* Set the PEL mask. */
353   WRITE_PORT_UCHAR((PUCHAR)0x3c6, 0xff);
354
355   /* Zero out video memory (clear a possibly trashed screen) */
356   RtlZeroMemory(vidmem, 64000);
357
358   vgaPreCalc();
359 }
360
361
362 BOOLEAN
363 STDCALL
364 VidResetDisplay(VOID)
365 {
366   /* 
367      We are only using standard VGA facilities so we can rely on the HAL 'int10mode3'
368      reset to cleanup the hardware state.
369   */
370   InGraphicsMode = FALSE;
371
372   return FALSE;
373 }
374
375
376 VOID
377 STDCALL
378 VidCleanUp(VOID)
379 {
380   /* 
381      We are only using standard VGA facilities so we can rely on the HAL 'int10mode3'
382      reset to cleanup the hardware state.
383   */
384   InGraphicsMode = FALSE;
385 }
386
387
388 static __inline__ VOID
389 InbvSetColor(int cindex, unsigned char red, unsigned char green, unsigned char blue)
390 {
391   red = (red * 63) / 255;
392   green = (green * 63) / 255;
393   blue = (blue * 63) / 255;
394
395   WRITE_PORT_UCHAR((PUCHAR)0x03c8, cindex);
396   (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
397   (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
398   WRITE_PORT_UCHAR((PUCHAR)0x03c9, red);
399   (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
400   (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
401   WRITE_PORT_UCHAR((PUCHAR)0x03c9, green);
402   (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
403   (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
404   WRITE_PORT_UCHAR((PUCHAR)0x03c9, blue);
405   (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
406   (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
407 }
408
409
410 static __inline__ VOID
411 InbvSetBlackPalette()
412 {
413   register ULONG r = 0;
414
415   /* Disable screen and enable palette access. */
416   (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
417   WRITE_PORT_UCHAR((PUCHAR)0x3c0, 0x00);
418   for (r = 0; r < 16; r++)
419     {
420       InbvSetColor(r, 0, 0, 0);
421     }
422   /* Enable screen and enable palette access. */
423   (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
424   WRITE_PORT_UCHAR((PUCHAR)0x3c0, 0x20);
425 }
426
427
428 static VOID
429 InbvDisplayBitmap(ULONG Width, ULONG Height, PCHAR ImageData)
430 {
431   ULONG j,k,y;
432   register ULONG i;
433   register ULONG x;
434   register ULONG c;
435
436   k = 0;
437   for (y = 0; y < Height; y++)
438     {
439       for (j = 0; j < 8; j++)
440         {
441           x = j;
442
443           /*
444            * Loop through the line and process every 8th pixel.
445            * This way we can get a way with using the same bit mask
446            * for several pixels and thus not need to do as much I/O
447            * communication.
448            */
449           while (x < 640)
450             {
451               c = 0;
452
453               if (x < Width)
454                 {
455                   c = ImageData[k + x];
456                   for (i = 1; i < 4; i++)
457                     {
458                       if (x + i*8 < Width)
459                         {
460                           c |= (ImageData[k + x + i * 8] << i * 8);
461                         }
462                     }
463                 }
464
465               InbvPutPixels(x, 479 - y, c);
466               x += 8*4;
467             }
468         }
469       k += Width;
470     }
471 }
472
473
474 static VOID
475 InbvDisplayCompressedBitmap()
476 {
477   PBITMAPV5HEADER bminfo;
478   ULONG i,j,k;
479   ULONG x,y;
480   ULONG curx,cury;
481   ULONG bfOffBits;
482   ULONG clen;
483   PCHAR ImageData;
484
485   bminfo = (PBITMAPV5HEADER) &BootimageBitmap[0];
486   DPRINT("bV5Size = %d\n", bminfo->bV5Size);
487   DPRINT("bV5Width = %d\n", bminfo->bV5Width);
488   DPRINT("bV5Height = %d\n", bminfo->bV5Height);
489   DPRINT("bV5Planes = %d\n", bminfo->bV5Planes);
490   DPRINT("bV5BitCount = %d\n", bminfo->bV5BitCount);
491   DPRINT("bV5Compression = %d\n", bminfo->bV5Compression);
492   DPRINT("bV5SizeImage = %d\n", bminfo->bV5SizeImage);
493   DPRINT("bV5XPelsPerMeter = %d\n", bminfo->bV5XPelsPerMeter);
494   DPRINT("bV5YPelsPerMeter = %d\n", bminfo->bV5YPelsPerMeter);
495   DPRINT("bV5ClrUsed = %d\n", bminfo->bV5ClrUsed);
496   DPRINT("bV5ClrImportant = %d\n", bminfo->bV5ClrImportant);
497
498   bfOffBits = bminfo->bV5Size + bminfo->bV5ClrUsed * sizeof(RGBQUAD);
499   DPRINT("bfOffBits = %d\n", bfOffBits);
500   DPRINT("size of color indices = %d\n", bminfo->bV5ClrUsed * sizeof(RGBQUAD));
501   DPRINT("first byte of data = %d\n", BootimageBitmap[bfOffBits]);
502
503   InbvSetBlackPalette();
504
505   ImageData = ExAllocatePool(NonPagedPool, bminfo->bV5Width * bminfo->bV5Height);
506   RtlZeroMemory(ImageData, bminfo->bV5Width * bminfo->bV5Height);
507
508   /*
509    * ImageData has 1 pixel per byte.
510    * bootimage has 2 pixels per byte.
511    */
512
513   if (bminfo->bV5Compression == 2)
514     {
515       k = 0;
516       j = 0;
517       while ((j < bminfo->bV5SizeImage) && (k < (ULONG) (bminfo->bV5Width * bminfo->bV5Height)))
518         {
519           unsigned char b;
520     
521           clen = BootimageBitmap[bfOffBits + j];
522           j++;
523     
524           if (clen > 0)
525             {
526               /* Encoded mode */
527     
528               b = BootimageBitmap[bfOffBits + j];
529               j++;
530     
531               for (i = 0; i < (clen / 2); i++)
532                 {
533                   ImageData[k] = (b & 0xf0) >> 4;
534                   k++;
535                   ImageData[k] = b & 0xf;
536                   k++;
537                 }
538               if ((clen & 1) > 0)
539               {
540                 ImageData[k] = (b & 0xf0) >> 4;
541                 k++;
542               }
543             }
544           else
545             {
546               /* Absolute mode */
547               b = BootimageBitmap[bfOffBits + j];
548               j++;
549     
550               if (b == 0)
551                 {
552                   /* End of line */
553                 }
554               else if (b == 1)
555                 {
556                   /* End of image */
557                   break;
558                 }
559               else if (b == 2)
560                 {
561                   x = BootimageBitmap[bfOffBits + j];
562                   j++;
563                   y = BootimageBitmap[bfOffBits + j];
564                   j++;
565                   curx = k % bminfo->bV5Width;
566                   cury = k / bminfo->bV5Width;
567                   k = (cury + y) * bminfo->bV5Width + (curx + x);
568                 }
569               else
570                 {
571                   if ((j & 1) > 0)
572                     {
573                       DPRINT("Unaligned copy!\n");
574                     }
575     
576                   clen = b;
577                   for (i = 0; i < (clen / 2); i++)
578                     {
579                       b = BootimageBitmap[bfOffBits + j];
580                       j++;
581         
582                       ImageData[k] = (b & 0xf0) >> 4;
583                       k++;
584                       ImageData[k] = b & 0xf;
585                       k++;
586                     }
587                   if ((clen & 1) > 0)
588                   {
589                     b = BootimageBitmap[bfOffBits + j];
590                     j++;
591                     ImageData[k] = (b & 0xf0) >> 4;
592                     k++;
593                   }
594                   /* Word align */
595                   j += (j & 1);
596                 }
597             }
598         }
599
600       InbvDisplayBitmap(bminfo->bV5Width, bminfo->bV5Height, ImageData);
601     }
602   else
603     {
604       DbgPrint("Warning boot image need to be compressed using RLE4\n");
605     }
606
607   ExFreePool(ImageData);
608 }
609
610
611 #define PALETTE_FADE_STEPS  20
612 #define PALETTE_FADE_TIME   20 * 10000 /* 20ms */
613
614 static VOID
615 InbvFadeUpPalette()
616 {
617   PBITMAPV5HEADER bminfo;
618   PRGBQUAD Palette;
619   ULONG i;
620   unsigned char r,g,b;
621   register ULONG c;
622   LARGE_INTEGER Interval;
623   FADER_PALETTE_ENTRY FaderPalette[16];
624   FADER_PALETTE_ENTRY FaderPaletteDelta[16];
625
626   RtlZeroMemory(&FaderPalette, sizeof(FaderPalette));
627   RtlZeroMemory(&FaderPaletteDelta, sizeof(FaderPaletteDelta));
628
629   bminfo = (PBITMAPV5HEADER) &BootimageBitmap[0]; //sizeof(BITMAPFILEHEADER)];
630   Palette = (PRGBQUAD) &BootimageBitmap[/* sizeof(BITMAPFILEHEADER) + */ bminfo->bV5Size];
631
632   for (i = 0; i < 16; i++)
633     {
634       if (i < bminfo->bV5ClrUsed)
635         {
636           FaderPaletteDelta[i].r = ((Palette[i].rgbRed << 8) / PALETTE_FADE_STEPS);
637           FaderPaletteDelta[i].g = ((Palette[i].rgbGreen << 8) / PALETTE_FADE_STEPS);
638           FaderPaletteDelta[i].b = ((Palette[i].rgbBlue << 8) / PALETTE_FADE_STEPS);
639         }
640     }
641
642   for (i = 0; i < PALETTE_FADE_STEPS; i++)
643     {
644       /* Disable screen and enable palette access. */
645       (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
646       WRITE_PORT_UCHAR((PUCHAR)0x3c0, 0x00);
647       for (c = 0; c < bminfo->bV5ClrUsed; c++)
648         {
649           /* Add the delta */
650           FaderPalette[c].r += FaderPaletteDelta[c].r;
651           FaderPalette[c].g += FaderPaletteDelta[c].g;
652           FaderPalette[c].b += FaderPaletteDelta[c].b;
653
654           /* Get the integer values */
655           r = FaderPalette[c].r >> 8;
656           g = FaderPalette[c].g >> 8;
657           b = FaderPalette[c].b >> 8;
658
659           /* Don't go too far */
660           if (r > Palette[c].rgbRed)
661             r = Palette[c].rgbRed;
662           if (g > Palette[c].rgbGreen)
663             g = Palette[c].rgbGreen;
664           if (b > Palette[c].rgbBlue)
665             b = Palette[c].rgbBlue;
666
667           /* Update the hardware */
668           InbvSetColor(c, r, g, b);
669         }
670       /* Enable screen and disable palette access. */
671       (VOID)READ_PORT_UCHAR((PUCHAR)FEATURE);
672       WRITE_PORT_UCHAR((PUCHAR)0x3c0, 0x20);
673       /* Wait for a bit. */
674       Interval.QuadPart = -PALETTE_FADE_TIME;
675       KeDelayExecutionThread(KernelMode, FALSE, &Interval);
676     }
677 }
678
679 static VOID STDCALL
680 InbvBitmapThreadMain(PVOID Ignored)
681 {
682   if (InbvFindBootimage())
683     {
684       InbvDisplayCompressedBitmap();
685       InbvFadeUpPalette();
686     }
687   else
688     {
689       DbgPrint("Warning: Cannot find boot image\n");
690     }
691
692   BitmapIsDrawn = TRUE;
693 }
694
695
696 BOOLEAN
697 STDCALL
698 VidIsBootDriverInstalled(VOID)
699 {
700   return InGraphicsMode;
701 }
702
703
704 BOOLEAN
705 STDCALL
706 VidInitialize(VOID)
707 {
708   NTSTATUS Status;
709
710   if (!VideoAddressSpaceInitialized)
711     {
712       InbvInitializeVideoAddressSpace();
713     }
714
715   InbvInitVGAMode();
716
717   InGraphicsMode = TRUE;
718
719   BitmapIsDrawn = FALSE;
720   
721   Status = PsCreateSystemThread(&BitmapThreadHandle,
722     THREAD_ALL_ACCESS,
723     NULL,
724     NULL,
725     &BitmapThreadId,
726     InbvBitmapThreadMain,
727     NULL);
728   if (!NT_SUCCESS(Status))
729     {
730       return FALSE;
731     }
732   NtClose(BitmapThreadHandle);
733
734   InbvDeinitializeVideoAddressSpace();
735   VideoAddressSpaceInitialized = FALSE;
736
737   return TRUE;
738 }
739
740 NTSTATUS STDCALL_FUNC
741 VidDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
742 {
743   PIO_STACK_LOCATION piosStack = IoGetCurrentIrpStackLocation(Irp);
744   NTSTATUS nErrCode;
745   NTBOOTVID_FUNCTION_TABLE* FunctionTable;
746  
747   nErrCode = STATUS_SUCCESS;
748
749   switch(piosStack->MajorFunction)
750     {
751       /* opening and closing handles to the device */
752       case IRP_MJ_CREATE:
753       case IRP_MJ_CLOSE:
754         break;
755
756     case IRP_MJ_DEVICE_CONTROL:
757       switch (piosStack->Parameters.DeviceIoControl.IoControlCode)
758         {
759         case IOCTL_BOOTVID_INITIALIZE:
760           VidInitialize();
761           FunctionTable = (NTBOOTVID_FUNCTION_TABLE*)
762             Irp->AssociatedIrp.SystemBuffer;
763           FunctionTable->ResetDisplay = VidResetDisplay;
764           break;
765         case IOCTL_BOOTVID_CLEANUP:
766           VidCleanUp();   
767           break;
768         default:
769           nErrCode = STATUS_NOT_IMPLEMENTED;
770           break;
771         }
772         break;
773
774       /* unsupported operations */
775       default:
776         nErrCode = STATUS_NOT_IMPLEMENTED;
777     }
778
779   Irp->IoStatus.Status = nErrCode;
780   IoCompleteRequest(Irp, IO_NO_INCREMENT);
781
782   return nErrCode;
783 }
784
785
786 NTSTATUS STDCALL
787 DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
788 {
789   PDEVICE_OBJECT BootVidDevice;
790   UNICODE_STRING DeviceName;
791   UNICODE_STRING DosName;
792   NTSTATUS Status;
793
794   BootVidDriverObject = DriverObject;
795
796   /* register driver routines */
797   DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)VidDispatch;
798   DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)VidDispatch;
799   DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = 
800     (PDRIVER_DISPATCH)VidDispatch;
801   DriverObject->DriverUnload = NULL;
802
803   /* create device */
804   RtlInitUnicodeStringFromLiteral(&DeviceName, L"\\Device\\BootVid");
805
806   Status = IoCreateDevice(DriverObject, 0, &DeviceName, FILE_DEVICE_BOOTVID,
807                             0, FALSE, &BootVidDevice);
808   if (! NT_SUCCESS(Status))
809     {
810       return Status;
811     }
812
813   BootVidDevice->Flags |= DO_BUFFERED_IO;
814
815   return Status;
816 }