fixed difference in signedness warning
[reactos.git] / reactos / drivers / dd / bootvid / bootvid.c
1 /*
2 * ReactOS Boot video driver
3 *
4 * Copyright (C) 2003 Casper S. Hornstroup
5 * Copyright (C) 2004 Filip Navara
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 * $Id$
22 */
23
24 /* INCLUDES ******************************************************************/
25
26 #include <ntddk.h>
27 #include <ndk/ldrfuncs.h>
28 #include "bootvid.h"
29 #include "ntbootvid.h"
30 #include "resource.h"
31
32 #define NDEBUG
33 #include <debug.h>
34
35 /* GLOBALS *******************************************************************/
36
37 /*
38 * NOTE:
39 * This is based on SvgaLib 640x480x16 mode definition with the
40 * following changes:
41 * - Graphics: Data Rotate (Index 3)
42 * Set to zero to indicate that the data written to video memory by
43 * CPU should be processed unmodified.
44 * - Graphics: Mode Register (Index 5)
45 * Set to Write Mode 2 and Read Mode 0.
46 */
47
48 static const VGA_REGISTERS Mode12Regs =
49 {
50 /* CRT Controller Registers */
51 {0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00,
52 0x00, 0x00, 0x00, 0x00, 0xEA, 0x8C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xE3},
53 /* Attribute Controller Registers */
54 {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
55 0x0C, 0x0D, 0x0E, 0x0F, 0x81, 0x00, 0x0F, 0x00, 0x00},
56 /* Graphics Controller Registers */
57 {0x00, 0x0F, 0x00, 0x00, 0x00, 0x02, 0x05, 0x0F, 0xFF},
58 /* Sequencer Registers */
59 {0x03, 0x01, 0x0F, 0x00, 0x06},
60 /* Misc Output Register */
61 0xE3
62 };
63
64 PUCHAR VideoMemory;
65
66 /* Must be 4 bytes per entry */
67 long maskbit[640];
68
69 static CLIENT_ID BitmapThreadId;
70 static PUCHAR BootimageBitmap;
71
72 static LONG ShutdownNotify;
73 static KEVENT ShutdownCompleteEvent;
74
75 /* DATA **********************************************************************/
76
77 static PDRIVER_OBJECT BootVidDriverObject = NULL;
78
79 /* FUNCTIONS *****************************************************************/
80
81 STATIC BOOLEAN FASTCALL
82 InbvFindBootimage()
83 {
84 PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
85 LDR_RESOURCE_INFO ResourceInfo;
86 NTSTATUS Status;
87 PVOID BaseAddress = BootVidDriverObject->DriverStart;
88 ULONG Size;
89
90 ResourceInfo.Type = RT_BITMAP;
91 ResourceInfo.Name = IDB_BOOTIMAGE;
92 ResourceInfo.Language = 0x09;
93
94 Status = LdrFindResource_U(
95 BaseAddress,
96 &ResourceInfo,
97 RESOURCE_DATA_LEVEL,
98 &ResourceDataEntry);
99
100 if (!NT_SUCCESS(Status))
101 {
102 DPRINT("LdrFindResource_U() failed with status 0x%.08x\n", Status);
103 return FALSE;
104 }
105
106 Status = LdrAccessResource(
107 BaseAddress,
108 ResourceDataEntry,
109 (PVOID*)&BootimageBitmap,
110 &Size);
111
112 if (!NT_SUCCESS(Status))
113 {
114 DPRINT("LdrAccessResource() failed with status 0x%.08x\n", Status);
115 return FALSE;
116 }
117
118 return TRUE;
119 }
120
121
122 STATIC BOOLEAN FASTCALL
123 InbvMapVideoMemory(VOID)
124 {
125 PHYSICAL_ADDRESS PhysicalAddress;
126
127 PhysicalAddress.QuadPart = 0xA0000;
128 VideoMemory = MmMapIoSpace(PhysicalAddress, 0x10000, MmNonCached);
129
130 return VideoMemory != NULL;
131 }
132
133
134 STATIC BOOLEAN FASTCALL
135 InbvUnmapVideoMemory(VOID)
136 {
137 MmUnmapIoSpace(VideoMemory, 0x10000);
138 return TRUE;
139 }
140
141
142 STATIC VOID FASTCALL
143 vgaPreCalc()
144 {
145 ULONG j;
146
147 for (j = 0; j < 80; j++)
148 {
149 maskbit[j * 8 + 0] = 128;
150 maskbit[j * 8 + 1] = 64;
151 maskbit[j * 8 + 2] = 32;
152 maskbit[j * 8 + 3] = 16;
153 maskbit[j * 8 + 4] = 8;
154 maskbit[j * 8 + 5] = 4;
155 maskbit[j * 8 + 6] = 2;
156 maskbit[j * 8 + 7] = 1;
157 }
158 }
159
160
161 STATIC VOID FASTCALL
162 vgaSetRegisters(const VGA_REGISTERS *Registers)
163 {
164 UINT i;
165
166 /* Update misc output register */
167 WRITE_PORT_UCHAR(MISC, Registers->Misc);
168
169 /* Synchronous reset on */
170 WRITE_PORT_UCHAR(SEQ, 0x00);
171 WRITE_PORT_UCHAR(SEQDATA, 0x01);
172
173 /* Write sequencer registers */
174 for (i = 1; i < sizeof(Registers->Sequencer); i++)
175 {
176 WRITE_PORT_UCHAR(SEQ, i);
177 WRITE_PORT_UCHAR(SEQDATA, Registers->Sequencer[i]);
178 }
179
180 /* Synchronous reset off */
181 WRITE_PORT_UCHAR(SEQ, 0x00);
182 WRITE_PORT_UCHAR(SEQDATA, 0x03);
183
184 /* Deprotect CRT registers 0-7 */
185 WRITE_PORT_UCHAR(CRTC, 0x11);
186 WRITE_PORT_UCHAR(CRTCDATA, Registers->CRT[0x11] & 0x7f);
187
188 /* Write CRT registers */
189 for (i = 0; i < sizeof(Registers->CRT); i++)
190 {
191 WRITE_PORT_UCHAR(CRTC, i);
192 WRITE_PORT_UCHAR(CRTCDATA, Registers->CRT[i]);
193 }
194
195 /* Write graphics controller registers */
196 for (i = 0; i < sizeof(Registers->Graphics); i++)
197 {
198 WRITE_PORT_UCHAR(GRAPHICS, i);
199 WRITE_PORT_UCHAR(GRAPHICSDATA, Registers->Graphics[i]);
200 }
201
202 /* Write attribute controller registers */
203 for (i = 0; i < sizeof(Registers->Attribute); i++)
204 {
205 READ_PORT_UCHAR(STATUS);
206 WRITE_PORT_UCHAR(ATTRIB, i);
207 WRITE_PORT_UCHAR(ATTRIB, Registers->Attribute[i]);
208 }
209 }
210
211
212 static VOID
213 InbvInitVGAMode(VOID)
214 {
215 /* Zero out video memory (clear a possibly trashed screen) */
216 RtlZeroMemory(VideoMemory, 0x10000);
217
218 vgaSetRegisters(&Mode12Regs);
219
220 /* Set the PEL mask. */
221 WRITE_PORT_UCHAR(PELMASK, 0xff);
222
223 vgaPreCalc();
224 }
225
226
227 static BOOL STDCALL
228 VidResetDisplay(VOID)
229 {
230 /*
231 * We are only using standard VGA facilities so we can rely on the
232 * HAL 'int10mode3' reset to cleanup the hardware state.
233 */
234
235 return FALSE;
236 }
237
238
239 static VOID STDCALL
240 VidCleanUp(VOID)
241 {
242 InbvUnmapVideoMemory();
243 InterlockedIncrement(&ShutdownNotify);
244 KeWaitForSingleObject(&ShutdownCompleteEvent, Executive, KernelMode,
245 FALSE, NULL);
246 }
247
248
249 STATIC VOID FASTCALL
250 InbvSetColor(INT Index, UCHAR Red, UCHAR Green, UCHAR Blue)
251 {
252 WRITE_PORT_UCHAR(PELINDEX, Index);
253 WRITE_PORT_UCHAR(PELDATA, Red >> 2);
254 WRITE_PORT_UCHAR(PELDATA, Green >> 2);
255 WRITE_PORT_UCHAR(PELDATA, Blue >> 2);
256 }
257
258
259 STATIC VOID FASTCALL
260 InbvSetBlackPalette()
261 {
262 register ULONG r = 0;
263
264 /* Disable screen and enable palette access. */
265 READ_PORT_UCHAR(STATUS);
266 WRITE_PORT_UCHAR(ATTRIB, 0x00);
267
268 for (r = 0; r < 16; r++)
269 {
270 InbvSetColor(r, 0, 0, 0);
271 }
272
273 /* Enable screen and enable palette access. */
274 READ_PORT_UCHAR(STATUS);
275 WRITE_PORT_UCHAR(ATTRIB, 0x20);
276 }
277
278
279 STATIC VOID FASTCALL
280 InbvDisplayBitmap(ULONG Width, ULONG Height, PCHAR ImageData)
281 {
282 ULONG j, k, y;
283 register ULONG i;
284 register ULONG x;
285 register ULONG c;
286
287 k = 0;
288 for (y = 0; y < Height; y++)
289 {
290 for (j = 0; j < 8; j++)
291 {
292 x = j;
293
294 /*
295 * Loop through the line and process every 8th pixel.
296 * This way we can get a way with using the same bit mask
297 * for several pixels and thus not need to do as much I/O
298 * communication.
299 */
300 while (x < 640)
301 {
302 c = 0;
303
304 if (x < Width)
305 {
306 c = ImageData[k + x];
307 for (i = 1; i < 4; i++)
308 {
309 if (x + i * 8 < Width)
310 {
311 c |= (ImageData[k + x + i * 8] << i * 8);
312 }
313 }
314 }
315
316 InbvPutPixels(x, 479 - y, c);
317 x += 8 * 4;
318 }
319 }
320 k += Width;
321 }
322 }
323
324
325 STATIC VOID FASTCALL
326 InbvDisplayCompressedBitmap()
327 {
328 PBITMAPV5HEADER bminfo;
329 ULONG i,j,k;
330 ULONG x,y;
331 ULONG curx,cury;
332 ULONG bfOffBits;
333 ULONG clen;
334 PCHAR ImageData;
335 UCHAR ClrUsed;
336
337 bminfo = (PBITMAPV5HEADER) &BootimageBitmap[0];
338 DPRINT("bV5Size = %d\n", bminfo->bV5Size);
339 DPRINT("bV5Width = %d\n", bminfo->bV5Width);
340 DPRINT("bV5Height = %d\n", bminfo->bV5Height);
341 DPRINT("bV5Planes = %d\n", bminfo->bV5Planes);
342 DPRINT("bV5BitCount = %d\n", bminfo->bV5BitCount);
343 DPRINT("bV5Compression = %d\n", bminfo->bV5Compression);
344 DPRINT("bV5SizeImage = %d\n", bminfo->bV5SizeImage);
345 DPRINT("bV5XPelsPerMeter = %d\n", bminfo->bV5XPelsPerMeter);
346 DPRINT("bV5YPelsPerMeter = %d\n", bminfo->bV5YPelsPerMeter);
347 DPRINT("bV5ClrUsed = %d\n", bminfo->bV5ClrUsed);
348 DPRINT("bV5ClrImportant = %d\n", bminfo->bV5ClrImportant);
349
350 if (bminfo->bV5ClrUsed)
351 ClrUsed = bminfo->bV5ClrUsed;
352 else
353 ClrUsed = 1 << bminfo->bV5BitCount;
354
355 bfOffBits = bminfo->bV5Size + ClrUsed * sizeof(RGBQUAD);
356 DPRINT("bfOffBits = %d\n", bfOffBits);
357 DPRINT("size of color indices = %d\n", ClrUsed * sizeof(RGBQUAD));
358 DPRINT("first byte of data = %d\n", BootimageBitmap[bfOffBits]);
359
360 InbvSetBlackPalette();
361
362 ImageData = ExAllocatePool(NonPagedPool, bminfo->bV5Width * bminfo->bV5Height);
363 RtlZeroMemory(ImageData, bminfo->bV5Width * bminfo->bV5Height);
364
365 /*
366 * ImageData has 1 pixel per byte.
367 * bootimage has 2 pixels per byte.
368 */
369
370 if (bminfo->bV5Compression == 2)
371 {
372 k = 0;
373 j = 0;
374 while ((j < bminfo->bV5SizeImage) && (k < (ULONG) (bminfo->bV5Width * bminfo->bV5Height)))
375 {
376 unsigned char b;
377
378 clen = BootimageBitmap[bfOffBits + j];
379 j++;
380
381 if (clen > 0)
382 {
383 /* Encoded mode */
384
385 b = BootimageBitmap[bfOffBits + j];
386 j++;
387
388 for (i = 0; i < (clen / 2); i++)
389 {
390 ImageData[k] = (b & 0xf0) >> 4;
391 k++;
392 ImageData[k] = b & 0xf;
393 k++;
394 }
395 if ((clen & 1) > 0)
396 {
397 ImageData[k] = (b & 0xf0) >> 4;
398 k++;
399 }
400 }
401 else
402 {
403 /* Absolute mode */
404 b = BootimageBitmap[bfOffBits + j];
405 j++;
406
407 if (b == 0)
408 {
409 /* End of line */
410 if (k % bminfo->bV5Width)
411 {
412 cury = k / bminfo->bV5Width;
413 k = (cury + 1) * bminfo->bV5Width;
414 }
415 }
416 else if (b == 1)
417 {
418 /* End of image */
419 break;
420 }
421 else if (b == 2)
422 {
423 x = BootimageBitmap[bfOffBits + j];
424 j++;
425 y = BootimageBitmap[bfOffBits + j];
426 j++;
427 curx = k % bminfo->bV5Width;
428 cury = k / bminfo->bV5Width;
429 k = (cury + y) * bminfo->bV5Width + (curx + x);
430 }
431 else
432 {
433 if ((j & 1) > 0)
434 {
435 DPRINT("Unaligned copy!\n");
436 }
437
438 clen = b;
439 for (i = 0; i < (clen / 2); i++)
440 {
441 b = BootimageBitmap[bfOffBits + j];
442 j++;
443
444 ImageData[k] = (b & 0xf0) >> 4;
445 k++;
446 ImageData[k] = b & 0xf;
447 k++;
448 }
449 if ((clen & 1) > 0)
450 {
451 b = BootimageBitmap[bfOffBits + j];
452 j++;
453 ImageData[k] = (b & 0xf0) >> 4;
454 k++;
455 }
456 /* Word align */
457 j += (j & 1);
458 }
459 }
460 }
461
462 InbvDisplayBitmap(bminfo->bV5Width, bminfo->bV5Height, ImageData);
463 }
464 else
465 {
466 DbgPrint("Warning boot image need to be compressed using RLE4\n");
467 }
468
469 ExFreePool(ImageData);
470 }
471
472
473 STATIC VOID FASTCALL
474 InbvFadeUpPalette()
475 {
476 PBITMAPV5HEADER bminfo;
477 PRGBQUAD Palette;
478 ULONG i;
479 unsigned char r, g, b;
480 register ULONG c;
481 LARGE_INTEGER Interval;
482 FADER_PALETTE_ENTRY FaderPalette[16];
483 FADER_PALETTE_ENTRY FaderPaletteDelta[16];
484 UCHAR ClrUsed;
485
486 RtlZeroMemory(&FaderPalette, sizeof(FaderPalette));
487 RtlZeroMemory(&FaderPaletteDelta, sizeof(FaderPaletteDelta));
488
489 bminfo = (PBITMAPV5HEADER)&BootimageBitmap[0];
490 Palette = (PRGBQUAD)&BootimageBitmap[bminfo->bV5Size];
491
492 if (bminfo->bV5ClrUsed)
493 ClrUsed = bminfo->bV5ClrUsed;
494 else
495 ClrUsed = 1 << bminfo->bV5BitCount;
496
497 for (i = 0; i < 16 && i < ClrUsed; i++)
498 {
499 FaderPaletteDelta[i].r = ((Palette[i].rgbRed << 8) / PALETTE_FADE_STEPS);
500 FaderPaletteDelta[i].g = ((Palette[i].rgbGreen << 8) / PALETTE_FADE_STEPS);
501 FaderPaletteDelta[i].b = ((Palette[i].rgbBlue << 8) / PALETTE_FADE_STEPS);
502 }
503
504 for (i = 0; i < PALETTE_FADE_STEPS && !ShutdownNotify; i++)
505 {
506 /* Disable screen and enable palette access. */
507 READ_PORT_UCHAR(STATUS);
508 WRITE_PORT_UCHAR(ATTRIB, 0x00);
509
510 for (c = 0; c < ClrUsed; c++)
511 {
512 /* Add the delta */
513 FaderPalette[c].r += FaderPaletteDelta[c].r;
514 FaderPalette[c].g += FaderPaletteDelta[c].g;
515 FaderPalette[c].b += FaderPaletteDelta[c].b;
516
517 /* Get the integer values */
518 r = FaderPalette[c].r >> 8;
519 g = FaderPalette[c].g >> 8;
520 b = FaderPalette[c].b >> 8;
521
522 /* Don't go too far */
523 if (r > Palette[c].rgbRed)
524 r = Palette[c].rgbRed;
525 if (g > Palette[c].rgbGreen)
526 g = Palette[c].rgbGreen;
527 if (b > Palette[c].rgbBlue)
528 b = Palette[c].rgbBlue;
529
530 /* Update the hardware */
531 InbvSetColor(c, r, g, b);
532 }
533
534 /* Enable screen and disable palette access. */
535 READ_PORT_UCHAR(STATUS);
536 WRITE_PORT_UCHAR(ATTRIB, 0x20);
537
538 /* Wait for a bit. */
539 Interval.QuadPart = -PALETTE_FADE_TIME;
540 KeDelayExecutionThread(KernelMode, FALSE, &Interval);
541 }
542 }
543
544
545 STATIC VOID STDCALL
546 InbvBitmapThreadMain(PVOID Ignored)
547 {
548 if (InbvFindBootimage())
549 {
550 InbvDisplayCompressedBitmap();
551 InbvFadeUpPalette();
552 }
553 else
554 {
555 DbgPrint("Warning: Cannot find boot image\n");
556 }
557 KeSetEvent(&ShutdownCompleteEvent, 0, FALSE);
558 }
559
560
561 STATIC BOOLEAN STDCALL
562 VidInitialize(VOID)
563 {
564 NTSTATUS Status;
565 HANDLE BitmapThreadHandle;
566
567 InbvMapVideoMemory();
568 InbvInitVGAMode();
569
570 Status = PsCreateSystemThread(
571 &BitmapThreadHandle,
572 THREAD_ALL_ACCESS,
573 NULL,
574 NULL,
575 &BitmapThreadId,
576 InbvBitmapThreadMain,
577 NULL);
578
579 if (!NT_SUCCESS(Status))
580 {
581 return FALSE;
582 }
583
584 ZwClose(BitmapThreadHandle);
585
586 return TRUE;
587 }
588
589
590 static NTSTATUS STDCALL
591 VidDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
592 {
593 PIO_STACK_LOCATION IrpSp;
594 NTSTATUS Status;
595 NTBOOTVID_FUNCTION_TABLE* FunctionTable;
596
597 IrpSp = IoGetCurrentIrpStackLocation(Irp);
598 Status = STATUS_SUCCESS;
599
600 switch(IrpSp->MajorFunction)
601 {
602 /* Opening and closing handles to the device */
603 case IRP_MJ_CREATE:
604 case IRP_MJ_CLOSE:
605 break;
606
607 case IRP_MJ_DEVICE_CONTROL:
608 switch (IrpSp->Parameters.DeviceIoControl.IoControlCode)
609 {
610 case IOCTL_BOOTVID_INITIALIZE:
611 VidInitialize();
612 FunctionTable = (NTBOOTVID_FUNCTION_TABLE *)
613 Irp->AssociatedIrp.SystemBuffer;
614 FunctionTable->ResetDisplay = VidResetDisplay;
615 Irp->IoStatus.Information = sizeof(NTBOOTVID_FUNCTION_TABLE);
616 break;
617
618 case IOCTL_BOOTVID_CLEANUP:
619 VidCleanUp();
620 break;
621
622 default:
623 Status = STATUS_NOT_IMPLEMENTED;
624 break;
625 }
626 break;
627
628 /* Unsupported operations */
629 default:
630 Status = STATUS_NOT_IMPLEMENTED;
631 }
632
633 Irp->IoStatus.Status = Status;
634 IoCompleteRequest(Irp, IO_NO_INCREMENT);
635
636 return Status;
637 }
638
639
640 NTSTATUS STDCALL
641 DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
642 {
643 PDEVICE_OBJECT BootVidDevice;
644 UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\BootVid");
645 NTSTATUS Status;
646
647 BootVidDriverObject = DriverObject;
648
649 ShutdownNotify = 0;
650 KeInitializeEvent(&ShutdownCompleteEvent, NotificationEvent, FALSE);
651
652 /* Register driver routines */
653 DriverObject->MajorFunction[IRP_MJ_CLOSE] = VidDispatch;
654 DriverObject->MajorFunction[IRP_MJ_CREATE] = VidDispatch;
655 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VidDispatch;
656 DriverObject->DriverUnload = NULL;
657
658 DriverObject->Flags |= DO_BUFFERED_IO;
659
660 /* Create device */
661 Status = IoCreateDevice(
662 DriverObject,
663 0,
664 &DeviceName,
665 FILE_DEVICE_BOOTVID,
666 0,
667 FALSE,
668 &BootVidDevice);
669
670 return Status;
671 }