xref: /reactos/drivers/setup/blue/blue.c (revision 84344399)
1 /*
2  * PROJECT:     ReactOS Console Text-Mode Device Driver
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Driver Management Functions.
5  * COPYRIGHT:   Copyright 1999 Boudewijn Dekker
6  *              Copyright 1999-2019 Eric Kohl
7  *              Copyright 2006 Filip Navara
8  *              Copyright 2019 Hermes Belusca-Maito
9  */
10 
11 /* INCLUDES ******************************************************************/
12 
13 #include "blue.h"
14 #include <ndk/inbvfuncs.h>
15 
16 #define NDEBUG
17 #include <debug.h>
18 
19 /* NOTES ******************************************************************/
20 /*
21  *  [[character][attribute]][[character][attribute]]....
22  */
23 
24 /* TYPEDEFS ***************************************************************/
25 
26 typedef struct _DEVICE_EXTENSION
27 {
28     PUCHAR  VideoMemory;    /* Pointer to video memory */
29     SIZE_T  VideoMemorySize;
30     BOOLEAN Enabled;
31     PUCHAR  ScreenBuffer;   /* Pointer to screenbuffer */
32     SIZE_T  ScreenBufferSize;
33     ULONG   CursorSize;
34     INT     CursorVisible;
35     USHORT  CharAttribute;
36     ULONG   Mode;
37     UCHAR   ScanLines;  /* Height of a text line */
38     USHORT  Rows;       /* Number of rows        */
39     USHORT  Columns;    /* Number of columns     */
40     USHORT  CursorX, CursorY; /* Cursor position */
41     PUCHAR  FontBitfield; /* Specifies the font  */
42 } DEVICE_EXTENSION, *PDEVICE_EXTENSION;
43 
44 typedef struct _VGA_REGISTERS
45 {
46     UCHAR CRT[24];
47     UCHAR Attribute[21];
48     UCHAR Graphics[9];
49     UCHAR Sequencer[5];
50     UCHAR Misc;
51 } VGA_REGISTERS, *PVGA_REGISTERS;
52 
53 static const VGA_REGISTERS VidpMode3Regs =
54 {
55     /* CRT Controller Registers */
56     {0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F, 0x00, 0x47, 0x1E, 0x00,
57     0x00, 0x00, 0x05, 0xF0, 0x9C, 0x8E, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3},
58     /* Attribute Controller Registers */
59     {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
60     0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00},
61     /* Graphics Controller Registers */
62     {0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x00, 0xFF},
63     /* Sequencer Registers */
64     {0x03, 0x00, 0x03, 0x00, 0x02},
65     /* Misc Output Register */
66     0x67
67 };
68 
69 static const UCHAR DefaultPalette[] =
70 {
71     0, 0, 0,
72     0, 0, 0xC0,
73     0, 0xC0, 0,
74     0, 0xC0, 0xC0,
75     0xC0, 0, 0,
76     0xC0, 0, 0xC0,
77     0xC0, 0xC0, 0,
78     0xC0, 0xC0, 0xC0,
79     0x80, 0x80, 0x80,
80     0, 0, 0xFF,
81     0, 0xFF, 0,
82     0, 0xFF, 0xFF,
83     0xFF, 0, 0,
84     0xFF, 0, 0xFF,
85     0xFF, 0xFF, 0,
86     0xFF, 0xFF, 0xFF
87 };
88 
89 /* INBV MANAGEMENT FUNCTIONS **************************************************/
90 
91 static BOOLEAN
92 ScrResetScreen(
93     _In_ PDEVICE_EXTENSION DeviceExtension,
94     _In_ BOOLEAN FullReset,
95     _In_ BOOLEAN Enable);
96 
97 static PDEVICE_EXTENSION ResetDisplayParametersDeviceExtension = NULL;
98 static HANDLE InbvThreadHandle = NULL;
99 static BOOLEAN InbvMonitoring = FALSE;
100 
101 /*
102  * Reinitialize the display to base VGA mode.
103  *
104  * Returns TRUE if it completely resets the adapter to the given character mode.
105  * Returns FALSE otherwise, indicating that the HAL should perform the VGA mode
106  * reset itself after HwVidResetHw() returns control.
107  *
108  * This callback has been registered with InbvNotifyDisplayOwnershipLost()
109  * and is called by InbvAcquireDisplayOwnership(), typically when the bugcheck
110  * code regains display access. Therefore this routine can be called at any
111  * IRQL, and in particular at IRQL = HIGH_LEVEL. This routine must also reside
112  * completely in non-paged pool, and cannot perform the following actions:
113  * Allocate memory, access pageable memory, use any synchronization mechanisms
114  * or call any routine that must execute at IRQL = DISPATCH_LEVEL or below.
115  */
116 static BOOLEAN
117 NTAPI
118 ScrResetDisplayParametersEx(
119     _In_ ULONG Columns,
120     _In_ ULONG Rows,
121     _In_ BOOLEAN CalledByInbv)
122 {
123     PDEVICE_EXTENSION DeviceExtension;
124 
125     /* Bail out early if we don't have any resettable adapter */
126     if (!ResetDisplayParametersDeviceExtension)
127         return FALSE; // No adapter found: request HAL to perform a full reset.
128 
129     /*
130      * If we have been unexpectedly called via a callback from
131      * InbvAcquireDisplayOwnership(), start monitoring INBV.
132      */
133     if (CalledByInbv)
134         InbvMonitoring = TRUE;
135 
136     DeviceExtension = ResetDisplayParametersDeviceExtension;
137     ASSERT(DeviceExtension);
138 
139     /* Disable the screen but don't reset all screen settings (OK at high IRQL) */
140     return ScrResetScreen(DeviceExtension, FALSE, FALSE);
141 }
142 
143 /* This callback is registered with InbvNotifyDisplayOwnershipLost() */
144 static BOOLEAN
145 NTAPI
146 ScrResetDisplayParameters(
147     _In_ ULONG Columns,
148     _In_ ULONG Rows)
149 {
150     /* Call the extended function, specifying we were called by INBV */
151     return ScrResetDisplayParametersEx(Columns, Rows, TRUE);
152 }
153 
154 /*
155  * (Adapted for ReactOS/Win2k3 from an original comment
156  *  by Gé van Geldorp, June 2003, r4937)
157  *
158  * DISPLAY OWNERSHIP
159  *
160  * So, who owns the physical display and is allowed to write to it?
161  *
162  * In NT 5.x (Win2k/Win2k3), upon boot INBV/BootVid owns the display, unless
163  * /NOGUIBOOT has been specified in the boot command line. Later in the boot
164  * sequence, WIN32K.SYS opens the DISPLAY device. This open call ends up in
165  * VIDEOPRT.SYS. This component takes ownership of the display by calling
166  * InbvNotifyDisplayOwnershipLost() -- effectively telling INBV to release
167  * ownership of the display it previously had. From that moment on, the display
168  * is owned by that component and can be switched to graphics mode. The display
169  * is not supposed to return to text mode, except in case of a bugcheck.
170  * The bugcheck code calls InbvAcquireDisplayOwnership() so as to make INBV
171  * re-take display ownership, and calls back the function previously registered
172  * by VIDEOPRT.SYS with InbvNotifyDisplayOwnershipLost(). After the bugcheck,
173  * execution is halted. So, under NT, the only possible sequence of display
174  * modes is text mode -> graphics mode -> text mode (the latter hopefully
175  * happening very infrequently).
176  *
177  * In ReactOS things are a little bit different. We want to have a functional
178  * interactive text mode. We should be able to switch back and forth from
179  * text mode to graphics mode when a GUI app is started and then finished.
180  * Also, when the system bugchecks in graphics mode we want to switch back to
181  * text mode and show the bugcheck information. Last but not least, when using
182  * KDBG in /DEBUGPORT=SCREEN mode, breaking into the debugger would trigger a
183  * switch to text mode, and the user would expect that by continuing execution
184  * a switch back to graphics mode is done.
185  */
186 static VOID
187 NTAPI
188 InbvMonitorThread(
189     _In_ PVOID Context)
190 {
191     LARGE_INTEGER Delay;
192     USHORT i;
193 
194     KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);
195 
196     while (TRUE)
197     {
198         /*
199          * During one second, check the INBV status each 100 milliseconds,
200          * then revert to 1 second delay.
201          */
202         i = 10;
203         Delay.QuadPart = (LONGLONG)-100*1000*10; // 100 millisecond delay
204         while (!InbvMonitoring)
205         {
206             KeDelayExecutionThread(KernelMode, FALSE, &Delay);
207 
208             if ((i > 0) && (--i == 0))
209                 Delay.QuadPart = (LONGLONG)-1*1000*1000*10; // 1 second delay
210         }
211 
212         /*
213          * Loop while the display is owned by INBV. We cannot do anything else
214          * than polling since INBV does not offer a proper notification system.
215          *
216          * During one second, check the INBV status each 100 milliseconds,
217          * then revert to 1 second delay.
218          */
219         i = 10;
220         Delay.QuadPart = (LONGLONG)-100*1000*10; // 100 millisecond delay
221         while (InbvCheckDisplayOwnership())
222         {
223             KeDelayExecutionThread(KernelMode, FALSE, &Delay);
224 
225             if ((i > 0) && (--i == 0))
226                 Delay.QuadPart = (LONGLONG)-1*1000*1000*10; // 1 second delay
227         }
228 
229         /* Reset the monitoring */
230         InbvMonitoring = FALSE;
231 
232         /*
233          * Somebody released INBV display ownership, usually by invoking
234          * InbvNotifyDisplayOwnershipLost(). However the caller of this
235          * function certainly specified a different callback than ours.
236          * As we are going to be the only owner of the active display,
237          * we need to re-register our own display reset callback.
238          */
239         InbvNotifyDisplayOwnershipLost(ScrResetDisplayParameters);
240 
241         /* Re-enable the screen, keeping the original screen settings */
242         if (ResetDisplayParametersDeviceExtension)
243             ScrResetScreen(ResetDisplayParametersDeviceExtension, FALSE, TRUE);
244     }
245 
246     // FIXME: See ScrInbvCleanup().
247     // PsTerminateSystemThread(STATUS_SUCCESS);
248 }
249 
250 static NTSTATUS
251 ScrInbvInitialize(VOID)
252 {
253     /* Create the INBV monitoring thread if needed */
254     if (!InbvThreadHandle)
255     {
256         NTSTATUS Status;
257         OBJECT_ATTRIBUTES ObjectAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(NULL, OBJ_KERNEL_HANDLE);
258 
259         Status = PsCreateSystemThread(&InbvThreadHandle,
260                                       0,
261                                       &ObjectAttributes,
262                                       NULL,
263                                       NULL,
264                                       InbvMonitorThread,
265                                       NULL);
266         if (!NT_SUCCESS(Status))
267             InbvThreadHandle = NULL;
268     }
269 
270     /* Re-register the display reset callback with INBV */
271     InbvNotifyDisplayOwnershipLost(ScrResetDisplayParameters);
272 
273     return STATUS_SUCCESS;
274 }
275 
276 static NTSTATUS
277 ScrInbvCleanup(VOID)
278 {
279     // HANDLE ThreadHandle;
280 
281     // ResetDisplayParametersDeviceExtension = NULL;
282     if (ResetDisplayParametersDeviceExtension)
283     {
284         InbvNotifyDisplayOwnershipLost(NULL);
285         ScrResetDisplayParametersEx(80, 50, FALSE);
286         // or InbvAcquireDisplayOwnership(); ?
287     }
288 
289 #if 0
290     // TODO: Find the best way to communicate the request.
291     /* Signal the INBV monitoring thread and wait for it to terminate */
292     ThreadHandle = InterlockedExchangePointer((PVOID*)&InbvThreadHandle, NULL);
293     if (ThreadHandle)
294     {
295         ZwWaitForSingleObject(ThreadHandle, Executive, KernelMode, FALSE, NULL);
296         /* Close its handle */
297         ObCloseHandle(ThreadHandle, KernelMode);
298     }
299 #endif
300 
301     return STATUS_SUCCESS;
302 }
303 
304 /* FUNCTIONS **************************************************************/
305 
306 static VOID
307 FASTCALL
308 ScrSetRegisters(const VGA_REGISTERS *Registers)
309 {
310     UINT32 i;
311 
312     /* Update misc output register */
313     WRITE_PORT_UCHAR(MISC, Registers->Misc);
314 
315     /* Synchronous reset on */
316     WRITE_PORT_UCHAR(SEQ, 0x00);
317     WRITE_PORT_UCHAR(SEQDATA, 0x01);
318 
319     /* Write sequencer registers */
320     for (i = 1; i < sizeof(Registers->Sequencer); i++)
321     {
322         WRITE_PORT_UCHAR(SEQ, i);
323         WRITE_PORT_UCHAR(SEQDATA, Registers->Sequencer[i]);
324     }
325 
326     /* Synchronous reset off */
327     WRITE_PORT_UCHAR(SEQ, 0x00);
328     WRITE_PORT_UCHAR(SEQDATA, 0x03);
329 
330     /* Deprotect CRT registers 0-7 */
331     WRITE_PORT_UCHAR(CRTC, 0x11);
332     WRITE_PORT_UCHAR(CRTCDATA, Registers->CRT[0x11] & 0x7f);
333 
334     /* Write CRT registers */
335     for (i = 0; i < sizeof(Registers->CRT); i++)
336     {
337         WRITE_PORT_UCHAR(CRTC, i);
338         WRITE_PORT_UCHAR(CRTCDATA, Registers->CRT[i]);
339     }
340 
341     /* Write graphics controller registers */
342     for (i = 0; i < sizeof(Registers->Graphics); i++)
343     {
344         WRITE_PORT_UCHAR(GRAPHICS, i);
345         WRITE_PORT_UCHAR(GRAPHICSDATA, Registers->Graphics[i]);
346     }
347 
348     /* Write attribute controller registers */
349     for (i = 0; i < sizeof(Registers->Attribute); i++)
350     {
351         READ_PORT_UCHAR(STATUS);
352         WRITE_PORT_UCHAR(ATTRIB, i);
353         WRITE_PORT_UCHAR(ATTRIB, Registers->Attribute[i]);
354     }
355 
356     /* Set the PEL mask */
357     WRITE_PORT_UCHAR(PELMASK, 0xff);
358 }
359 
360 static VOID
361 FASTCALL
362 ScrSetCursor(
363     _In_ PDEVICE_EXTENSION DeviceExtension)
364 {
365     ULONG Offset;
366 
367     if (!DeviceExtension->VideoMemory)
368         return;
369 
370     Offset = (DeviceExtension->CursorY * DeviceExtension->Columns) + DeviceExtension->CursorX;
371 
372     _disable();
373     WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_CURSORPOSLO);
374     WRITE_PORT_UCHAR(CRTC_DATA, Offset);
375     WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_CURSORPOSHI);
376     WRITE_PORT_UCHAR(CRTC_DATA, Offset >> 8);
377     _enable();
378 }
379 
380 static VOID
381 FASTCALL
382 ScrSetCursorShape(
383     _In_ PDEVICE_EXTENSION DeviceExtension)
384 {
385     ULONG size, height;
386     UCHAR data, value;
387 
388     if (!DeviceExtension->VideoMemory)
389         return;
390 
391     height = DeviceExtension->ScanLines;
392     data = (DeviceExtension->CursorVisible) ? 0x00 : 0x20;
393 
394     size = (DeviceExtension->CursorSize * height) / 100;
395     if (size < 1)
396         size = 1;
397 
398     data |= (UCHAR)(height - size);
399 
400     _disable();
401     WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_CURSORSTART);
402     WRITE_PORT_UCHAR(CRTC_DATA, data);
403     WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_CURSOREND);
404     value = READ_PORT_UCHAR(CRTC_DATA) & 0xE0;
405     WRITE_PORT_UCHAR(CRTC_DATA, value | (height - 1));
406     _enable();
407 }
408 
409 static VOID
410 FASTCALL
411 ScrAcquireOwnership(
412     _In_ PDEVICE_EXTENSION DeviceExtension)
413 {
414     UCHAR data, value;
415     ULONG offset;
416     ULONG Index;
417 
418     _disable();
419 
420     ScrSetRegisters(&VidpMode3Regs);
421 
422     /* Disable screen and enable palette access */
423     READ_PORT_UCHAR(STATUS);
424     WRITE_PORT_UCHAR(ATTRIB, 0x00);
425 
426     for (Index = 0; Index < sizeof(DefaultPalette) / 3; Index++)
427     {
428         WRITE_PORT_UCHAR(PELINDEX, Index);
429         WRITE_PORT_UCHAR(PELDATA, DefaultPalette[Index * 3] >> 2);
430         WRITE_PORT_UCHAR(PELDATA, DefaultPalette[Index * 3 + 1] >> 2);
431         WRITE_PORT_UCHAR(PELDATA, DefaultPalette[Index * 3 + 2] >> 2);
432     }
433 
434     /* Enable screen and disable palette access */
435     READ_PORT_UCHAR(STATUS);
436     WRITE_PORT_UCHAR(ATTRIB, 0x20);
437 
438     /* Switch blinking characters off */
439     READ_PORT_UCHAR(ATTRC_INPST1);
440     value = READ_PORT_UCHAR(ATTRC_WRITEREG);
441     WRITE_PORT_UCHAR(ATTRC_WRITEREG, 0x10);
442     data  = READ_PORT_UCHAR(ATTRC_READREG);
443     data  = data & ~0x08;
444     WRITE_PORT_UCHAR(ATTRC_WRITEREG, data);
445     WRITE_PORT_UCHAR(ATTRC_WRITEREG, value);
446     READ_PORT_UCHAR(ATTRC_INPST1);
447 
448     /* Read screen information from CRT controller */
449     WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_COLUMNS);
450     DeviceExtension->Columns = READ_PORT_UCHAR(CRTC_DATA) + 1;
451     WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_ROWS);
452     DeviceExtension->Rows = READ_PORT_UCHAR(CRTC_DATA);
453     WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_OVERFLOW);
454     data = READ_PORT_UCHAR(CRTC_DATA);
455     DeviceExtension->Rows |= (((data & 0x02) << 7) | ((data & 0x40) << 3));
456     DeviceExtension->Rows++;
457     WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_SCANLINES);
458     DeviceExtension->ScanLines = (READ_PORT_UCHAR(CRTC_DATA) & 0x1F) + 1;
459 
460     /* Retrieve the current output cursor position */
461     WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_CURSORPOSLO);
462     offset = READ_PORT_UCHAR(CRTC_DATA);
463     WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_CURSORPOSHI);
464     offset += (READ_PORT_UCHAR(CRTC_DATA) << 8);
465 
466     /* Show blinking cursor */
467     // FIXME: cursor block? Call ScrSetCursorShape() instead?
468     WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_CURSORSTART);
469     WRITE_PORT_UCHAR(CRTC_DATA, (DeviceExtension->ScanLines - 1) & 0x1F);
470     WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_CURSOREND);
471     data = READ_PORT_UCHAR(CRTC_DATA) & 0xE0;
472     WRITE_PORT_UCHAR(CRTC_DATA,
473                      data | ((DeviceExtension->ScanLines - 1) & 0x1F));
474 
475     _enable();
476 
477     /* Calculate number of text rows */
478     DeviceExtension->Rows = DeviceExtension->Rows / DeviceExtension->ScanLines;
479 
480     /* Set the cursor position, clipping it to the screen */
481     DeviceExtension->CursorX = (USHORT)(offset % DeviceExtension->Columns);
482     DeviceExtension->CursorY = (USHORT)(offset / DeviceExtension->Columns);
483     // DeviceExtension->CursorX = min(max(DeviceExtension->CursorX, 0), DeviceExtension->Columns - 1);
484     DeviceExtension->CursorY = min(max(DeviceExtension->CursorY, 0), DeviceExtension->Rows - 1);
485 
486     /* Set the font */
487     if (DeviceExtension->FontBitfield)
488         ScrSetFont(DeviceExtension->FontBitfield);
489 
490     DPRINT("%d Columns  %d Rows %d Scanlines\n",
491            DeviceExtension->Columns,
492            DeviceExtension->Rows,
493            DeviceExtension->ScanLines);
494 }
495 
496 static BOOLEAN
497 ScrResetScreen(
498     _In_ PDEVICE_EXTENSION DeviceExtension,
499     _In_ BOOLEAN FullReset,
500     _In_ BOOLEAN Enable)
501 {
502 #define FOREGROUND_LIGHTGRAY (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED)
503 
504     PHYSICAL_ADDRESS BaseAddress;
505 
506     /* Allow resets to the same state only for full resets */
507     if (!FullReset && (Enable == DeviceExtension->Enabled))
508         return FALSE; // STATUS_INVALID_PARAMETER; STATUS_INVALID_DEVICE_REQUEST;
509 
510     if (FullReset)
511     {
512         DeviceExtension->CursorSize    = 5; /* FIXME: value correct?? */
513         DeviceExtension->CursorVisible = TRUE;
514 
515         if (DeviceExtension->FontBitfield)
516         {
517             ExFreePoolWithTag(DeviceExtension->FontBitfield, TAG_BLUE);
518             DeviceExtension->FontBitfield = NULL;
519         }
520 
521         /* More initialization */
522         DeviceExtension->CharAttribute = BACKGROUND_BLUE | FOREGROUND_LIGHTGRAY;
523         DeviceExtension->Mode = ENABLE_PROCESSED_OUTPUT |
524                                 ENABLE_WRAP_AT_EOL_OUTPUT;
525     }
526 
527     if (Enable)
528     {
529         ScrAcquireOwnership(DeviceExtension);
530 
531         if (FullReset)
532         {
533             /*
534              * Fully reset the screen and all its settings.
535              */
536 
537             /* Unmap any previously mapped video memory */
538             if (DeviceExtension->VideoMemory)
539             {
540                 ASSERT(DeviceExtension->VideoMemorySize != 0);
541                 MmUnmapIoSpace(DeviceExtension->VideoMemory, DeviceExtension->VideoMemorySize);
542             }
543             DeviceExtension->VideoMemory = NULL;
544             DeviceExtension->VideoMemorySize = 0;
545 
546             /* Free any previously allocated backup screenbuffer */
547             if (DeviceExtension->ScreenBuffer)
548             {
549                 ASSERT(DeviceExtension->ScreenBufferSize != 0);
550                 ExFreePoolWithTag(DeviceExtension->ScreenBuffer, TAG_BLUE);
551             }
552             DeviceExtension->ScreenBuffer = NULL;
553             DeviceExtension->ScreenBufferSize = 0;
554 
555             /* Get a pointer to the video memory */
556             DeviceExtension->VideoMemorySize = DeviceExtension->Rows * DeviceExtension->Columns * 2;
557             if (DeviceExtension->VideoMemorySize == 0)
558                 return FALSE; // STATUS_INVALID_VIEW_SIZE; STATUS_MAPPED_FILE_SIZE_ZERO;
559 
560             /* Map the video memory */
561             BaseAddress.QuadPart = VIDMEM_BASE;
562             DeviceExtension->VideoMemory =
563                 (PUCHAR)MmMapIoSpace(BaseAddress, DeviceExtension->VideoMemorySize, MmNonCached);
564             if (!DeviceExtension->VideoMemory)
565             {
566                 DeviceExtension->VideoMemorySize = 0;
567                 return FALSE; // STATUS_NONE_MAPPED; STATUS_NOT_MAPPED_VIEW; STATUS_CONFLICTING_ADDRESSES;
568             }
569 
570             /* Initialize the backup screenbuffer in non-paged pool (must be accessible at high IRQL) */
571             DeviceExtension->ScreenBufferSize = DeviceExtension->VideoMemorySize;
572             DeviceExtension->ScreenBuffer =
573                 (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, DeviceExtension->ScreenBufferSize, TAG_BLUE);
574             if (!DeviceExtension->ScreenBuffer)
575             {
576                 DPRINT1("Could not allocate screenbuffer, ignore...\n");
577                 DeviceExtension->ScreenBufferSize = 0;
578             }
579 
580             /* (Re-)initialize INBV */
581             ScrInbvInitialize();
582         }
583         else
584         {
585             /*
586              * Restore the previously disabled screen.
587              */
588 
589             /* Restore the snapshot of the video memory from the backup screenbuffer */
590             if (DeviceExtension->ScreenBuffer)
591             {
592                 ASSERT(DeviceExtension->VideoMemory);
593                 ASSERT(DeviceExtension->ScreenBuffer);
594                 ASSERT(DeviceExtension->ScreenBufferSize != 0);
595                 ASSERT(DeviceExtension->VideoMemorySize == DeviceExtension->ScreenBufferSize);
596 
597                 RtlCopyMemory(DeviceExtension->VideoMemory,
598                               DeviceExtension->ScreenBuffer,
599                               DeviceExtension->VideoMemorySize);
600             }
601 
602             /* Restore the cursor state */
603             ScrSetCursor(DeviceExtension);
604             ScrSetCursorShape(DeviceExtension);
605         }
606         DeviceExtension->Enabled = TRUE;
607     }
608     else
609     {
610         DeviceExtension->Enabled = FALSE;
611         if (FullReset)
612         {
613             /*
614              * Fully disable the screen and reset all its settings.
615              */
616 
617             /* Clean INBV up */
618             ScrInbvCleanup();
619 
620             /* Unmap any previously mapped video memory */
621             if (DeviceExtension->VideoMemory)
622             {
623                 ASSERT(DeviceExtension->VideoMemorySize != 0);
624                 MmUnmapIoSpace(DeviceExtension->VideoMemory, DeviceExtension->VideoMemorySize);
625             }
626             DeviceExtension->VideoMemory = NULL;
627             DeviceExtension->VideoMemorySize = 0;
628 
629             /* Free any previously allocated backup screenbuffer */
630             if (DeviceExtension->ScreenBuffer)
631             {
632                 ASSERT(DeviceExtension->ScreenBufferSize != 0);
633                 ExFreePoolWithTag(DeviceExtension->ScreenBuffer, TAG_BLUE);
634             }
635             DeviceExtension->ScreenBuffer = NULL;
636             DeviceExtension->ScreenBufferSize = 0;
637 
638             /* Store dummy values */
639             DeviceExtension->Columns = 1;
640             DeviceExtension->Rows = 1;
641             DeviceExtension->ScanLines = 1;
642         }
643         else
644         {
645             /*
646              * Partially disable the screen such that it can be restored later.
647              */
648 
649             /* Take a snapshot of the video memory into the backup screenbuffer */
650             if (DeviceExtension->ScreenBuffer)
651             {
652                 ASSERT(DeviceExtension->VideoMemory);
653                 ASSERT(DeviceExtension->ScreenBuffer);
654                 ASSERT(DeviceExtension->ScreenBufferSize != 0);
655                 ASSERT(DeviceExtension->VideoMemorySize == DeviceExtension->ScreenBufferSize);
656 
657                 RtlCopyMemory(DeviceExtension->ScreenBuffer,
658                               DeviceExtension->VideoMemory,
659                               DeviceExtension->VideoMemorySize);
660             }
661         }
662     }
663 
664     return TRUE; // STATUS_SUCCESS;
665 }
666 
667 static DRIVER_DISPATCH ScrCreateClose;
668 static NTSTATUS
669 NTAPI
670 ScrCreateClose(
671     _In_ PDEVICE_OBJECT DeviceObject,
672     _In_ PIRP Irp)
673 {
674     PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(Irp);
675 
676     UNREFERENCED_PARAMETER(DeviceObject);
677 
678     if (stk->MajorFunction == IRP_MJ_CREATE)
679         Irp->IoStatus.Information = FILE_OPENED;
680     // else: IRP_MJ_CLOSE
681 
682     Irp->IoStatus.Status = STATUS_SUCCESS;
683     IoCompleteRequest(Irp, IO_NO_INCREMENT);
684     return STATUS_SUCCESS;
685 }
686 
687 static DRIVER_DISPATCH ScrWrite;
688 static NTSTATUS
689 NTAPI
690 ScrWrite(
691     _In_ PDEVICE_OBJECT DeviceObject,
692     _In_ PIRP Irp)
693 {
694     NTSTATUS Status;
695     PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(Irp);
696     PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
697     PCHAR pch = Irp->UserBuffer;
698     PUCHAR vidmem;
699     ULONG i;
700     ULONG j, offset;
701     USHORT cursorx, cursory;
702     USHORT rows, columns;
703     BOOLEAN processed = !!(DeviceExtension->Mode & ENABLE_PROCESSED_OUTPUT);
704 
705     if (!DeviceExtension->Enabled || !DeviceExtension->VideoMemory)
706     {
707         /* Display is not enabled, we're not allowed to touch it */
708         Status = STATUS_SUCCESS;
709 
710         Irp->IoStatus.Status = Status;
711         IoCompleteRequest(Irp, IO_NO_INCREMENT);
712 
713         return Status;
714     }
715 
716     vidmem  = DeviceExtension->VideoMemory;
717     rows    = DeviceExtension->Rows;
718     columns = DeviceExtension->Columns;
719     cursorx = DeviceExtension->CursorX;
720     cursory = DeviceExtension->CursorY;
721 
722     if (!processed)
723     {
724         /* Raw output mode */
725 
726         /* Calculate the offset from the cursor position */
727         offset = cursorx + cursory * columns;
728 
729         // FIXME: Does the buffer only contains chars? or chars + attributes?
730         // FIXME2: Fix buffer overflow.
731         RtlCopyMemory(&vidmem[offset * 2], pch, stk->Parameters.Write.Length);
732         offset += (stk->Parameters.Write.Length / 2);
733 
734         /* Set the cursor position, clipping it to the screen */
735         cursorx = (USHORT)(offset % columns);
736         cursory = (USHORT)(offset / columns);
737         // cursorx = min(max(cursorx, 0), columns - 1);
738         cursory = min(max(cursory, 0), rows - 1);
739     }
740     else
741     {
742         /* Cooked output mode */
743         for (i = 0; i < stk->Parameters.Write.Length; i++, pch++)
744         {
745             switch (*pch)
746             {
747             case '\b':
748             {
749                 if (cursorx > 0)
750                 {
751                     cursorx--;
752                 }
753                 else if (cursory > 0)
754                 {
755                     cursory--;
756                     cursorx = columns - 1;
757                 }
758                 offset = cursorx + cursory * columns;
759                 vidmem[offset * 2] = ' ';
760                 vidmem[offset * 2 + 1] = (char)DeviceExtension->CharAttribute;
761                 break;
762             }
763 
764             case '\n':
765                 cursory++;
766                 /* Fall back */
767             case '\r':
768                 cursorx = 0;
769                 break;
770 
771             case '\t':
772             {
773                 offset = TAB_WIDTH - (cursorx % TAB_WIDTH);
774                 while (offset--)
775                 {
776                     vidmem[(cursorx + cursory * columns) * 2] = ' ';
777                     cursorx++;
778                     if (cursorx >= columns)
779                     {
780                         cursorx = 0;
781                         cursory++;
782                         /* We jumped to the next line, stop there */
783                         break;
784                     }
785                 }
786                 break;
787             }
788 
789             default:
790             {
791                 offset = cursorx + cursory * columns;
792                 vidmem[offset * 2] = *pch;
793                 vidmem[offset * 2 + 1] = (char)DeviceExtension->CharAttribute;
794                 cursorx++;
795                 if (cursorx >= columns)
796                 {
797                     cursorx = 0;
798                     cursory++;
799                 }
800                 break;
801             }
802             }
803 
804             /* Scroll up the contents of the screen if we are at the end */
805             if (cursory >= rows)
806             {
807                 PUSHORT LinePtr;
808 
809                 RtlCopyMemory(vidmem,
810                               &vidmem[columns * 2],
811                               columns * (rows - 1) * 2);
812 
813                 LinePtr = (PUSHORT)&vidmem[columns * (rows - 1) * 2];
814 
815                 for (j = 0; j < columns; j++)
816                 {
817                     LinePtr[j] = DeviceExtension->CharAttribute << 8;
818                 }
819                 cursory = rows - 1;
820                 for (j = 0; j < columns; j++)
821                 {
822                     offset = j + cursory * columns;
823                     vidmem[offset * 2] = ' ';
824                     vidmem[offset * 2 + 1] = (char)DeviceExtension->CharAttribute;
825                 }
826             }
827         }
828     }
829 
830     /* Set the cursor position */
831     ASSERT((0 <= cursorx) && (cursorx < DeviceExtension->Columns));
832     ASSERT((0 <= cursory) && (cursory < DeviceExtension->Rows));
833     DeviceExtension->CursorX = cursorx;
834     DeviceExtension->CursorY = cursory;
835     ScrSetCursor(DeviceExtension);
836 
837     Status = STATUS_SUCCESS;
838 
839     Irp->IoStatus.Status = Status;
840     IoCompleteRequest(Irp, IO_VIDEO_INCREMENT);
841 
842     return Status;
843 }
844 
845 static DRIVER_DISPATCH ScrIoControl;
846 static NTSTATUS
847 NTAPI
848 ScrIoControl(
849     _In_ PDEVICE_OBJECT DeviceObject,
850     _In_ PIRP Irp)
851 {
852     NTSTATUS Status;
853     PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(Irp);
854     PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
855 
856     switch (stk->Parameters.DeviceIoControl.IoControlCode)
857     {
858         case IOCTL_CONSOLE_RESET_SCREEN:
859         {
860             BOOLEAN Enable;
861 
862             /* Validate input buffer */
863             if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG))
864             {
865                 Status = STATUS_INVALID_PARAMETER;
866                 break;
867             }
868             ASSERT(Irp->AssociatedIrp.SystemBuffer);
869 
870             Enable = !!*(PULONG)Irp->AssociatedIrp.SystemBuffer;
871 
872             /* Fully enable or disable the screen */
873             Status = (ScrResetScreen(DeviceExtension, TRUE, Enable)
874                         ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
875             Irp->IoStatus.Information = 0;
876             break;
877         }
878 
879         case IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO:
880         {
881             PCONSOLE_SCREEN_BUFFER_INFO pcsbi;
882             USHORT rows = DeviceExtension->Rows;
883             USHORT columns = DeviceExtension->Columns;
884 
885             /* Validate output buffer */
886             if (stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CONSOLE_SCREEN_BUFFER_INFO))
887             {
888                 Status = STATUS_INVALID_PARAMETER;
889                 break;
890             }
891             ASSERT(Irp->AssociatedIrp.SystemBuffer);
892 
893             pcsbi = (PCONSOLE_SCREEN_BUFFER_INFO)Irp->AssociatedIrp.SystemBuffer;
894             RtlZeroMemory(pcsbi, sizeof(CONSOLE_SCREEN_BUFFER_INFO));
895 
896             pcsbi->dwSize.X = columns;
897             pcsbi->dwSize.Y = rows;
898 
899             pcsbi->dwCursorPosition.X = DeviceExtension->CursorX;
900             pcsbi->dwCursorPosition.Y = DeviceExtension->CursorY;
901 
902             pcsbi->wAttributes = DeviceExtension->CharAttribute;
903 
904             pcsbi->srWindow.Left   = 0;
905             pcsbi->srWindow.Right  = columns - 1;
906             pcsbi->srWindow.Top    = 0;
907             pcsbi->srWindow.Bottom = rows - 1;
908 
909             pcsbi->dwMaximumWindowSize.X = columns;
910             pcsbi->dwMaximumWindowSize.Y = rows;
911 
912             Irp->IoStatus.Information = sizeof(CONSOLE_SCREEN_BUFFER_INFO);
913             Status = STATUS_SUCCESS;
914             break;
915         }
916 
917         case IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO:
918         {
919             PCONSOLE_SCREEN_BUFFER_INFO pcsbi;
920 
921             /* Validate input buffer */
922             if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(CONSOLE_SCREEN_BUFFER_INFO))
923             {
924                 Status = STATUS_INVALID_PARAMETER;
925                 break;
926             }
927             ASSERT(Irp->AssociatedIrp.SystemBuffer);
928 
929             pcsbi = (PCONSOLE_SCREEN_BUFFER_INFO)Irp->AssociatedIrp.SystemBuffer;
930 
931             if ( pcsbi->dwCursorPosition.X < 0 || pcsbi->dwCursorPosition.X >= DeviceExtension->Columns ||
932                  pcsbi->dwCursorPosition.Y < 0 || pcsbi->dwCursorPosition.Y >= DeviceExtension->Rows )
933             {
934                 Irp->IoStatus.Information = 0;
935                 Status = STATUS_INVALID_PARAMETER;
936                 break;
937             }
938 
939             DeviceExtension->CharAttribute = pcsbi->wAttributes;
940 
941             /* Set the cursor position */
942             ASSERT((0 <= pcsbi->dwCursorPosition.X) && (pcsbi->dwCursorPosition.X < DeviceExtension->Columns));
943             ASSERT((0 <= pcsbi->dwCursorPosition.Y) && (pcsbi->dwCursorPosition.Y < DeviceExtension->Rows));
944             DeviceExtension->CursorX = pcsbi->dwCursorPosition.X;
945             DeviceExtension->CursorY = pcsbi->dwCursorPosition.Y;
946             if (DeviceExtension->Enabled)
947                 ScrSetCursor(DeviceExtension);
948 
949             Irp->IoStatus.Information = 0;
950             Status = STATUS_SUCCESS;
951             break;
952         }
953 
954         case IOCTL_CONSOLE_GET_CURSOR_INFO:
955         {
956             PCONSOLE_CURSOR_INFO pcci;
957 
958             /* Validate output buffer */
959             if (stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CONSOLE_CURSOR_INFO))
960             {
961                 Status = STATUS_INVALID_PARAMETER;
962                 break;
963             }
964             ASSERT(Irp->AssociatedIrp.SystemBuffer);
965 
966             pcci = (PCONSOLE_CURSOR_INFO)Irp->AssociatedIrp.SystemBuffer;
967             RtlZeroMemory(pcci, sizeof(CONSOLE_CURSOR_INFO));
968 
969             pcci->dwSize = DeviceExtension->CursorSize;
970             pcci->bVisible = DeviceExtension->CursorVisible;
971 
972             Irp->IoStatus.Information = sizeof(CONSOLE_CURSOR_INFO);
973             Status = STATUS_SUCCESS;
974             break;
975         }
976 
977         case IOCTL_CONSOLE_SET_CURSOR_INFO:
978         {
979             PCONSOLE_CURSOR_INFO pcci;
980 
981             /* Validate input buffer */
982             if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(CONSOLE_CURSOR_INFO))
983             {
984                 Status = STATUS_INVALID_PARAMETER;
985                 break;
986             }
987             ASSERT(Irp->AssociatedIrp.SystemBuffer);
988 
989             pcci = (PCONSOLE_CURSOR_INFO)Irp->AssociatedIrp.SystemBuffer;
990 
991             DeviceExtension->CursorSize = pcci->dwSize;
992             DeviceExtension->CursorVisible = pcci->bVisible;
993             if (DeviceExtension->Enabled)
994                 ScrSetCursorShape(DeviceExtension);
995 
996             Irp->IoStatus.Information = 0;
997             Status = STATUS_SUCCESS;
998             break;
999         }
1000 
1001         case IOCTL_CONSOLE_GET_MODE:
1002         {
1003             PCONSOLE_MODE pcm;
1004 
1005             /* Validate output buffer */
1006             if (stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CONSOLE_MODE))
1007             {
1008                 Status = STATUS_INVALID_PARAMETER;
1009                 break;
1010             }
1011             ASSERT(Irp->AssociatedIrp.SystemBuffer);
1012 
1013             pcm = (PCONSOLE_MODE)Irp->AssociatedIrp.SystemBuffer;
1014             RtlZeroMemory(pcm, sizeof(CONSOLE_MODE));
1015 
1016             pcm->dwMode = DeviceExtension->Mode;
1017 
1018             Irp->IoStatus.Information = sizeof(CONSOLE_MODE);
1019             Status = STATUS_SUCCESS;
1020             break;
1021         }
1022 
1023         case IOCTL_CONSOLE_SET_MODE:
1024         {
1025             PCONSOLE_MODE pcm;
1026 
1027             /* Validate input buffer */
1028             if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(CONSOLE_MODE))
1029             {
1030                 Status = STATUS_INVALID_PARAMETER;
1031                 break;
1032             }
1033             ASSERT(Irp->AssociatedIrp.SystemBuffer);
1034 
1035             pcm = (PCONSOLE_MODE)Irp->AssociatedIrp.SystemBuffer;
1036             DeviceExtension->Mode = pcm->dwMode;
1037 
1038             Irp->IoStatus.Information = 0;
1039             Status = STATUS_SUCCESS;
1040             break;
1041         }
1042 
1043         case IOCTL_CONSOLE_FILL_OUTPUT_ATTRIBUTE:
1044         {
1045             POUTPUT_ATTRIBUTE Buf;
1046             PUCHAR vidmem;
1047             ULONG offset;
1048             ULONG dwCount;
1049             ULONG nMaxLength;
1050 
1051             /* Validate input and output buffers */
1052             if (stk->Parameters.DeviceIoControl.InputBufferLength  < sizeof(OUTPUT_ATTRIBUTE) ||
1053                 stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(OUTPUT_ATTRIBUTE))
1054             {
1055                 Status = STATUS_INVALID_PARAMETER;
1056                 break;
1057             }
1058             ASSERT(Irp->AssociatedIrp.SystemBuffer);
1059 
1060             Buf = (POUTPUT_ATTRIBUTE)Irp->AssociatedIrp.SystemBuffer;
1061             nMaxLength = Buf->nLength;
1062 
1063             Buf->dwTransfered = 0;
1064             Irp->IoStatus.Information = sizeof(OUTPUT_ATTRIBUTE);
1065 
1066             if ( Buf->dwCoord.X < 0 || Buf->dwCoord.X >= DeviceExtension->Columns ||
1067                  Buf->dwCoord.Y < 0 || Buf->dwCoord.Y >= DeviceExtension->Rows    ||
1068                  nMaxLength == 0 )
1069             {
1070                 Status = STATUS_SUCCESS;
1071                 break;
1072             }
1073 
1074             if (DeviceExtension->Enabled && DeviceExtension->VideoMemory)
1075             {
1076                 UCHAR attr = Buf->wAttribute;
1077 
1078                 vidmem = DeviceExtension->VideoMemory;
1079                 offset = (Buf->dwCoord.X + Buf->dwCoord.Y * DeviceExtension->Columns) * 2 + 1;
1080 
1081                 nMaxLength = min(nMaxLength,
1082                                  (DeviceExtension->Rows - Buf->dwCoord.Y)
1083                                     * DeviceExtension->Columns - Buf->dwCoord.X);
1084 
1085                 for (dwCount = 0; dwCount < nMaxLength; dwCount++)
1086                 {
1087                     vidmem[offset + (dwCount * 2)] = attr;
1088                 }
1089                 Buf->dwTransfered = dwCount;
1090             }
1091 
1092             Status = STATUS_SUCCESS;
1093             break;
1094         }
1095 
1096         case IOCTL_CONSOLE_READ_OUTPUT_ATTRIBUTE:
1097         {
1098             POUTPUT_ATTRIBUTE Buf;
1099             PUSHORT pAttr;
1100             PUCHAR vidmem;
1101             ULONG offset;
1102             ULONG dwCount;
1103             ULONG nMaxLength;
1104 
1105             /* Validate input buffer */
1106             if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(OUTPUT_ATTRIBUTE))
1107             {
1108                 Status = STATUS_INVALID_PARAMETER;
1109                 break;
1110             }
1111             ASSERT(Irp->AssociatedIrp.SystemBuffer);
1112 
1113             Buf = (POUTPUT_ATTRIBUTE)Irp->AssociatedIrp.SystemBuffer;
1114             Irp->IoStatus.Information = 0;
1115 
1116             /* Validate output buffer */
1117             if (stk->Parameters.DeviceIoControl.OutputBufferLength == 0)
1118             {
1119                 Status = STATUS_SUCCESS;
1120                 break;
1121             }
1122             ASSERT(Irp->MdlAddress);
1123             pAttr = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
1124             if (pAttr == NULL)
1125             {
1126                 Status = STATUS_INSUFFICIENT_RESOURCES;
1127                 break;
1128             }
1129 
1130             if ( Buf->dwCoord.X < 0 || Buf->dwCoord.X >= DeviceExtension->Columns ||
1131                  Buf->dwCoord.Y < 0 || Buf->dwCoord.Y >= DeviceExtension->Rows )
1132             {
1133                 Status = STATUS_SUCCESS;
1134                 break;
1135             }
1136 
1137             nMaxLength = stk->Parameters.DeviceIoControl.OutputBufferLength;
1138             nMaxLength /= sizeof(USHORT);
1139 
1140             if (DeviceExtension->Enabled && DeviceExtension->VideoMemory)
1141             {
1142                 vidmem = DeviceExtension->VideoMemory;
1143                 offset = (Buf->dwCoord.X + Buf->dwCoord.Y * DeviceExtension->Columns) * 2 + 1;
1144 
1145                 nMaxLength = min(nMaxLength,
1146                                  (DeviceExtension->Rows - Buf->dwCoord.Y)
1147                                     * DeviceExtension->Columns - Buf->dwCoord.X);
1148 
1149                 for (dwCount = 0; dwCount < nMaxLength; dwCount++, pAttr++)
1150                 {
1151                     *((PCHAR)pAttr) = vidmem[offset + (dwCount * 2)];
1152                 }
1153                 Irp->IoStatus.Information = dwCount * sizeof(USHORT);
1154             }
1155 
1156             Status = STATUS_SUCCESS;
1157             break;
1158         }
1159 
1160         case IOCTL_CONSOLE_WRITE_OUTPUT_ATTRIBUTE:
1161         {
1162             COORD dwCoord;
1163             PCOORD pCoord;
1164             PUSHORT pAttr;
1165             PUCHAR vidmem;
1166             ULONG offset;
1167             ULONG dwCount;
1168             ULONG nMaxLength;
1169 
1170             //
1171             // NOTE: For whatever reason no OUTPUT_ATTRIBUTE structure
1172             // is used for this IOCTL.
1173             //
1174 
1175             /* Validate output buffer */
1176             if (stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(COORD))
1177             {
1178                 Status = STATUS_INVALID_PARAMETER;
1179                 break;
1180             }
1181             ASSERT(Irp->MdlAddress);
1182             pCoord = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
1183             if (pCoord == NULL)
1184             {
1185                 Status = STATUS_INSUFFICIENT_RESOURCES;
1186                 break;
1187             }
1188             /* Capture the input info data */
1189             dwCoord = *pCoord;
1190 
1191             nMaxLength = stk->Parameters.DeviceIoControl.OutputBufferLength - sizeof(COORD);
1192             nMaxLength /= sizeof(USHORT);
1193 
1194             Irp->IoStatus.Information = 0;
1195 
1196             if ( dwCoord.X < 0 || dwCoord.X >= DeviceExtension->Columns ||
1197                  dwCoord.Y < 0 || dwCoord.Y >= DeviceExtension->Rows    ||
1198                  nMaxLength == 0 )
1199             {
1200                 Status = STATUS_SUCCESS;
1201                 break;
1202             }
1203 
1204             pAttr = (PUSHORT)(pCoord + 1);
1205 
1206             if (DeviceExtension->Enabled && DeviceExtension->VideoMemory)
1207             {
1208                 vidmem = DeviceExtension->VideoMemory;
1209                 offset = (dwCoord.X + dwCoord.Y * DeviceExtension->Columns) * 2 + 1;
1210 
1211                 nMaxLength = min(nMaxLength,
1212                                  (DeviceExtension->Rows - dwCoord.Y)
1213                                     * DeviceExtension->Columns - dwCoord.X);
1214 
1215                 for (dwCount = 0; dwCount < nMaxLength; dwCount++, pAttr++)
1216                 {
1217                     vidmem[offset + (dwCount * 2)] = *((PCHAR)pAttr);
1218                 }
1219                 Irp->IoStatus.Information = dwCount * sizeof(USHORT);
1220             }
1221 
1222             Status = STATUS_SUCCESS;
1223             break;
1224         }
1225 
1226         case IOCTL_CONSOLE_SET_TEXT_ATTRIBUTE:
1227         {
1228             /* Validate input buffer */
1229             if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(USHORT))
1230             {
1231                 Status = STATUS_INVALID_PARAMETER;
1232                 break;
1233             }
1234             ASSERT(Irp->AssociatedIrp.SystemBuffer);
1235 
1236             DeviceExtension->CharAttribute = *(PUSHORT)Irp->AssociatedIrp.SystemBuffer;
1237 
1238             Irp->IoStatus.Information = 0;
1239             Status = STATUS_SUCCESS;
1240             break;
1241         }
1242 
1243         case IOCTL_CONSOLE_FILL_OUTPUT_CHARACTER:
1244         {
1245             POUTPUT_CHARACTER Buf;
1246             PUCHAR vidmem;
1247             ULONG offset;
1248             ULONG dwCount;
1249             ULONG nMaxLength;
1250 
1251             /* Validate input and output buffers */
1252             if (stk->Parameters.DeviceIoControl.InputBufferLength  < sizeof(OUTPUT_CHARACTER) ||
1253                 stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(OUTPUT_CHARACTER))
1254             {
1255                 Status = STATUS_INVALID_PARAMETER;
1256                 break;
1257             }
1258             ASSERT(Irp->AssociatedIrp.SystemBuffer);
1259 
1260             Buf = (POUTPUT_CHARACTER)Irp->AssociatedIrp.SystemBuffer;
1261             nMaxLength = Buf->nLength;
1262 
1263             Buf->dwTransfered = 0;
1264             Irp->IoStatus.Information = sizeof(OUTPUT_CHARACTER);
1265 
1266             if ( Buf->dwCoord.X < 0 || Buf->dwCoord.X >= DeviceExtension->Columns ||
1267                  Buf->dwCoord.Y < 0 || Buf->dwCoord.Y >= DeviceExtension->Rows    ||
1268                  nMaxLength == 0 )
1269             {
1270                 Status = STATUS_SUCCESS;
1271                 break;
1272             }
1273 
1274             if (DeviceExtension->Enabled && DeviceExtension->VideoMemory)
1275             {
1276                 UCHAR ch = Buf->cCharacter;
1277 
1278                 vidmem = DeviceExtension->VideoMemory;
1279                 offset = (Buf->dwCoord.X + Buf->dwCoord.Y * DeviceExtension->Columns) * 2;
1280 
1281                 nMaxLength = min(nMaxLength,
1282                                  (DeviceExtension->Rows - Buf->dwCoord.Y)
1283                                     * DeviceExtension->Columns - Buf->dwCoord.X);
1284 
1285                 for (dwCount = 0; dwCount < nMaxLength; dwCount++)
1286                 {
1287                     vidmem[offset + (dwCount * 2)] = ch;
1288                 }
1289                 Buf->dwTransfered = dwCount;
1290             }
1291 
1292             Status = STATUS_SUCCESS;
1293             break;
1294         }
1295 
1296         case IOCTL_CONSOLE_READ_OUTPUT_CHARACTER:
1297         {
1298             POUTPUT_CHARACTER Buf;
1299             PCHAR pChar;
1300             PUCHAR vidmem;
1301             ULONG offset;
1302             ULONG dwCount;
1303             ULONG nMaxLength;
1304 
1305             /* Validate input buffer */
1306             if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(OUTPUT_CHARACTER))
1307             {
1308                 Status = STATUS_INVALID_PARAMETER;
1309                 break;
1310             }
1311             ASSERT(Irp->AssociatedIrp.SystemBuffer);
1312 
1313             Buf = (POUTPUT_CHARACTER)Irp->AssociatedIrp.SystemBuffer;
1314             Irp->IoStatus.Information = 0;
1315 
1316             /* Validate output buffer */
1317             if (stk->Parameters.DeviceIoControl.OutputBufferLength == 0)
1318             {
1319                 Status = STATUS_SUCCESS;
1320                 break;
1321             }
1322             ASSERT(Irp->MdlAddress);
1323             pChar = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
1324             if (pChar == NULL)
1325             {
1326                 Status = STATUS_INSUFFICIENT_RESOURCES;
1327                 break;
1328             }
1329 
1330             if ( Buf->dwCoord.X < 0 || Buf->dwCoord.X >= DeviceExtension->Columns ||
1331                  Buf->dwCoord.Y < 0 || Buf->dwCoord.Y >= DeviceExtension->Rows )
1332             {
1333                 Status = STATUS_SUCCESS;
1334                 break;
1335             }
1336 
1337             nMaxLength = stk->Parameters.DeviceIoControl.OutputBufferLength;
1338 
1339             if (DeviceExtension->Enabled && DeviceExtension->VideoMemory)
1340             {
1341                 vidmem = DeviceExtension->VideoMemory;
1342                 offset = (Buf->dwCoord.X + Buf->dwCoord.Y * DeviceExtension->Columns) * 2;
1343 
1344                 nMaxLength = min(nMaxLength,
1345                                  (DeviceExtension->Rows - Buf->dwCoord.Y)
1346                                     * DeviceExtension->Columns - Buf->dwCoord.X);
1347 
1348                 for (dwCount = 0; dwCount < nMaxLength; dwCount++, pChar++)
1349                 {
1350                     *pChar = vidmem[offset + (dwCount * 2)];
1351                 }
1352                 Irp->IoStatus.Information = dwCount * sizeof(CHAR);
1353             }
1354 
1355             Status = STATUS_SUCCESS;
1356             break;
1357         }
1358 
1359         case IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER:
1360         {
1361             COORD dwCoord;
1362             PCOORD pCoord;
1363             PCHAR pChar;
1364             PUCHAR vidmem;
1365             ULONG offset;
1366             ULONG dwCount;
1367             ULONG nMaxLength;
1368 
1369             //
1370             // NOTE: For whatever reason no OUTPUT_CHARACTER structure
1371             // is used for this IOCTL.
1372             //
1373 
1374             /* Validate output buffer */
1375             if (stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(COORD))
1376             {
1377                 Status = STATUS_INVALID_PARAMETER;
1378                 break;
1379             }
1380             ASSERT(Irp->MdlAddress);
1381             pCoord = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
1382             if (pCoord == NULL)
1383             {
1384                 Status = STATUS_INSUFFICIENT_RESOURCES;
1385                 break;
1386             }
1387             /* Capture the input info data */
1388             dwCoord = *pCoord;
1389 
1390             nMaxLength = stk->Parameters.DeviceIoControl.OutputBufferLength - sizeof(COORD);
1391             Irp->IoStatus.Information = 0;
1392 
1393             if ( dwCoord.X < 0 || dwCoord.X >= DeviceExtension->Columns ||
1394                  dwCoord.Y < 0 || dwCoord.Y >= DeviceExtension->Rows    ||
1395                  nMaxLength == 0 )
1396             {
1397                 Status = STATUS_SUCCESS;
1398                 break;
1399             }
1400 
1401             pChar = (PCHAR)(pCoord + 1);
1402 
1403             if (DeviceExtension->Enabled && DeviceExtension->VideoMemory)
1404             {
1405                 vidmem = DeviceExtension->VideoMemory;
1406                 offset = (dwCoord.X + dwCoord.Y * DeviceExtension->Columns) * 2;
1407 
1408                 nMaxLength = min(nMaxLength,
1409                                  (DeviceExtension->Rows - dwCoord.Y)
1410                                     * DeviceExtension->Columns - dwCoord.X);
1411 
1412                 for (dwCount = 0; dwCount < nMaxLength; dwCount++, pChar++)
1413                 {
1414                     vidmem[offset + (dwCount * 2)] = *pChar;
1415                 }
1416                 Irp->IoStatus.Information = dwCount * sizeof(CHAR);
1417             }
1418 
1419             Status = STATUS_SUCCESS;
1420             break;
1421         }
1422 
1423         case IOCTL_CONSOLE_DRAW:
1424         {
1425             CONSOLE_DRAW ConsoleDraw;
1426             PCONSOLE_DRAW pConsoleDraw;
1427             PUCHAR Src, Dest;
1428             UINT32 SrcDelta, DestDelta, i;
1429 
1430             /* Validate output buffer */
1431             if (stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CONSOLE_DRAW))
1432             {
1433                 Status = STATUS_INVALID_PARAMETER;
1434                 break;
1435             }
1436             ASSERT(Irp->MdlAddress);
1437             pConsoleDraw = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
1438             if (pConsoleDraw == NULL)
1439             {
1440                 Status = STATUS_INSUFFICIENT_RESOURCES;
1441                 break;
1442             }
1443             /* Capture the input info data */
1444             ConsoleDraw = *pConsoleDraw;
1445 
1446             /* Check whether we have the size for the header plus the data area */
1447             if ((stk->Parameters.DeviceIoControl.OutputBufferLength - sizeof(CONSOLE_DRAW)) / 2
1448                     < ((ULONG)ConsoleDraw.SizeX * (ULONG)ConsoleDraw.SizeY))
1449             {
1450                 Status = STATUS_INVALID_BUFFER_SIZE;
1451                 break;
1452             }
1453 
1454             Irp->IoStatus.Information = 0;
1455 
1456             /* Set the cursor position, clipping it to the screen */
1457             DeviceExtension->CursorX = min(max(ConsoleDraw.CursorX, 0), DeviceExtension->Columns - 1);
1458             DeviceExtension->CursorY = min(max(ConsoleDraw.CursorY, 0), DeviceExtension->Rows    - 1);
1459             if (DeviceExtension->Enabled)
1460                 ScrSetCursor(DeviceExtension);
1461 
1462             // TODO: For the moment if the ConsoleDraw rectangle has borders
1463             // out of the screen-buffer we just bail out. Would it be better
1464             // to actually clip the rectangle within its borders instead?
1465             if ( ConsoleDraw.X < 0 || ConsoleDraw.X >= DeviceExtension->Columns ||
1466                  ConsoleDraw.Y < 0 || ConsoleDraw.Y >= DeviceExtension->Rows )
1467             {
1468                 Status = STATUS_SUCCESS;
1469                 break;
1470             }
1471             if ( ConsoleDraw.SizeX > DeviceExtension->Columns - ConsoleDraw.X ||
1472                  ConsoleDraw.SizeY > DeviceExtension->Rows    - ConsoleDraw.Y )
1473             {
1474                 Status = STATUS_SUCCESS;
1475                 break;
1476             }
1477 
1478             if (DeviceExtension->Enabled && DeviceExtension->VideoMemory)
1479             {
1480                 Src = (PUCHAR)(pConsoleDraw + 1);
1481                 SrcDelta = ConsoleDraw.SizeX * 2;
1482                 Dest = DeviceExtension->VideoMemory +
1483                         (ConsoleDraw.X + ConsoleDraw.Y * DeviceExtension->Columns) * 2;
1484                 DestDelta = DeviceExtension->Columns * 2;
1485                 /* 2 == sizeof(CHAR) + sizeof(BYTE) */
1486 
1487                 /* Copy each line */
1488                 for (i = 0; i < ConsoleDraw.SizeY; i++)
1489                 {
1490                     RtlCopyMemory(Dest, Src, SrcDelta);
1491                     Src += SrcDelta;
1492                     Dest += DestDelta;
1493                 }
1494             }
1495 
1496             Status = STATUS_SUCCESS;
1497             break;
1498         }
1499 
1500         case IOCTL_CONSOLE_LOADFONT:
1501         {
1502             //
1503             // FIXME: For the moment we support only a fixed 256-char 8-bit font.
1504             //
1505 
1506             /* Validate input buffer */
1507             if (stk->Parameters.DeviceIoControl.InputBufferLength < 256 * 8)
1508             {
1509                 Status = STATUS_INVALID_PARAMETER;
1510                 break;
1511             }
1512             ASSERT(Irp->AssociatedIrp.SystemBuffer);
1513 
1514             if (DeviceExtension->FontBitfield)
1515                 ExFreePoolWithTag(DeviceExtension->FontBitfield, TAG_BLUE);
1516             DeviceExtension->FontBitfield = ExAllocatePoolWithTag(NonPagedPool, 256 * 8, TAG_BLUE);
1517             if (!DeviceExtension->FontBitfield)
1518             {
1519                 Status = STATUS_NO_MEMORY;
1520                 break;
1521             }
1522             RtlCopyMemory(DeviceExtension->FontBitfield, Irp->AssociatedIrp.SystemBuffer, 256 * 8);
1523 
1524             /* Set the font if needed */
1525             if (DeviceExtension->Enabled && DeviceExtension->VideoMemory)
1526                 ScrSetFont(DeviceExtension->FontBitfield);
1527 
1528             Irp->IoStatus.Information = 0;
1529             Status = STATUS_SUCCESS;
1530             break;
1531         }
1532 
1533         default:
1534             Status = STATUS_NOT_IMPLEMENTED;
1535     }
1536 
1537     Irp->IoStatus.Status = Status;
1538     IoCompleteRequest(Irp, IO_VIDEO_INCREMENT);
1539 
1540     return Status;
1541 }
1542 
1543 static DRIVER_DISPATCH ScrDispatch;
1544 static NTSTATUS
1545 NTAPI
1546 ScrDispatch(
1547     _In_ PDEVICE_OBJECT DeviceObject,
1548     _In_ PIRP Irp)
1549 {
1550     PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(Irp);
1551 
1552     UNREFERENCED_PARAMETER(DeviceObject);
1553 
1554     DPRINT1("ScrDispatch(0x%p): stk->MajorFunction = %lu UNIMPLEMENTED\n",
1555             DeviceObject, stk->MajorFunction);
1556 
1557     Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
1558     IoCompleteRequest(Irp, IO_NO_INCREMENT);
1559     return STATUS_NOT_IMPLEMENTED;
1560 }
1561 
1562 /*
1563  * Module entry point
1564  */
1565 NTSTATUS
1566 NTAPI
1567 DriverEntry(
1568     _In_ PDRIVER_OBJECT DriverObject,
1569     _In_ PUNICODE_STRING RegistryPath)
1570 {
1571     NTSTATUS Status;
1572     PDEVICE_OBJECT DeviceObject;
1573     UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\BlueScreen");
1574     UNICODE_STRING SymlinkName = RTL_CONSTANT_STRING(L"\\??\\BlueScreen");
1575 
1576     DPRINT("Screen Driver 0.0.6\n");
1577 
1578     DriverObject->MajorFunction[IRP_MJ_CREATE] = ScrCreateClose;
1579     DriverObject->MajorFunction[IRP_MJ_CLOSE]  = ScrCreateClose;
1580     DriverObject->MajorFunction[IRP_MJ_READ]   = ScrDispatch;
1581     DriverObject->MajorFunction[IRP_MJ_WRITE]  = ScrWrite;
1582     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ScrIoControl;
1583 
1584     Status = IoCreateDevice(DriverObject,
1585                             sizeof(DEVICE_EXTENSION),
1586                             &DeviceName,
1587                             FILE_DEVICE_SCREEN,
1588                             FILE_DEVICE_SECURE_OPEN,
1589                             TRUE,
1590                             &DeviceObject);
1591     if (!NT_SUCCESS(Status))
1592     {
1593         return Status;
1594     }
1595 
1596     Status = IoCreateSymbolicLink(&SymlinkName, &DeviceName);
1597     if (NT_SUCCESS(Status))
1598     {
1599         /* By default disable the screen (but don't touch INBV: ResetDisplayParametersDeviceExtension is still NULL) */
1600         ScrResetScreen(DeviceObject->DeviceExtension, TRUE, FALSE);
1601         /* Now set ResetDisplayParametersDeviceExtension to enable synchronizing with INBV */
1602         ResetDisplayParametersDeviceExtension = DeviceObject->DeviceExtension;
1603         DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
1604     }
1605     else
1606     {
1607         IoDeleteDevice(DeviceObject);
1608     }
1609     return Status;
1610 }
1611 
1612 /* EOF */
1613