xref: /reactos/drivers/setup/blue/blue.c (revision 37b2c145)
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     ULONG   CodePage;   /* Specifies the font associated to this code page */
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     /* Upload a default font for the current codepage */
487     ScrLoadFontTable(DeviceExtension->CodePage);
488 
489     DPRINT("%d Columns  %d Rows %d Scanlines\n",
490            DeviceExtension->Columns,
491            DeviceExtension->Rows,
492            DeviceExtension->ScanLines);
493 }
494 
495 static BOOLEAN
496 ScrResetScreen(
497     _In_ PDEVICE_EXTENSION DeviceExtension,
498     _In_ BOOLEAN FullReset,
499     _In_ BOOLEAN Enable)
500 {
501 #define FOREGROUND_LIGHTGRAY (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED)
502 
503     PHYSICAL_ADDRESS BaseAddress;
504 
505     /* Allow resets to the same state only for full resets */
506     if (!FullReset && (Enable == DeviceExtension->Enabled))
507         return FALSE; // STATUS_INVALID_PARAMETER; STATUS_INVALID_DEVICE_REQUEST;
508 
509     if (FullReset)
510     {
511         DeviceExtension->CursorSize    = 5; /* FIXME: value correct?? */
512         DeviceExtension->CursorVisible = TRUE;
513 
514         /* More initialization */
515         DeviceExtension->CharAttribute = BACKGROUND_BLUE | FOREGROUND_LIGHTGRAY;
516         DeviceExtension->Mode = ENABLE_PROCESSED_OUTPUT |
517                                 ENABLE_WRAP_AT_EOL_OUTPUT;
518         DeviceExtension->CodePage = 437; /* Use default codepage */
519     }
520 
521     if (Enable)
522     {
523         ScrAcquireOwnership(DeviceExtension);
524 
525         if (FullReset)
526         {
527             /*
528              * Fully reset the screen and all its settings.
529              */
530 
531             /* Unmap any previously mapped video memory */
532             if (DeviceExtension->VideoMemory)
533             {
534                 ASSERT(DeviceExtension->VideoMemorySize != 0);
535                 MmUnmapIoSpace(DeviceExtension->VideoMemory, DeviceExtension->VideoMemorySize);
536             }
537             DeviceExtension->VideoMemory = NULL;
538             DeviceExtension->VideoMemorySize = 0;
539 
540             /* Free any previously allocated backup screenbuffer */
541             if (DeviceExtension->ScreenBuffer)
542             {
543                 ASSERT(DeviceExtension->ScreenBufferSize != 0);
544                 ExFreePoolWithTag(DeviceExtension->ScreenBuffer, TAG_BLUE);
545             }
546             DeviceExtension->ScreenBuffer = NULL;
547             DeviceExtension->ScreenBufferSize = 0;
548 
549             /* Get a pointer to the video memory */
550             DeviceExtension->VideoMemorySize = DeviceExtension->Rows * DeviceExtension->Columns * 2;
551             if (DeviceExtension->VideoMemorySize == 0)
552                 return FALSE; // STATUS_INVALID_VIEW_SIZE; STATUS_MAPPED_FILE_SIZE_ZERO;
553 
554             /* Map the video memory */
555             BaseAddress.QuadPart = VIDMEM_BASE;
556             DeviceExtension->VideoMemory =
557                 (PUCHAR)MmMapIoSpace(BaseAddress, DeviceExtension->VideoMemorySize, MmNonCached);
558             if (!DeviceExtension->VideoMemory)
559             {
560                 DeviceExtension->VideoMemorySize = 0;
561                 return FALSE; // STATUS_NONE_MAPPED; STATUS_NOT_MAPPED_VIEW; STATUS_CONFLICTING_ADDRESSES;
562             }
563 
564             /* Initialize the backup screenbuffer in non-paged pool (must be accessible at high IRQL) */
565             DeviceExtension->ScreenBufferSize = DeviceExtension->VideoMemorySize;
566             DeviceExtension->ScreenBuffer =
567                 (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, DeviceExtension->ScreenBufferSize, TAG_BLUE);
568             if (!DeviceExtension->ScreenBuffer)
569             {
570                 DPRINT1("Could not allocate screenbuffer, ignore...\n");
571                 DeviceExtension->ScreenBufferSize = 0;
572             }
573 
574             /* (Re-)initialize INBV */
575             ScrInbvInitialize();
576         }
577         else
578         {
579             /*
580              * Restore the previously disabled screen.
581              */
582 
583             /* Restore the snapshot of the video memory from the backup screenbuffer */
584             if (DeviceExtension->ScreenBuffer)
585             {
586                 ASSERT(DeviceExtension->VideoMemory);
587                 ASSERT(DeviceExtension->ScreenBuffer);
588                 ASSERT(DeviceExtension->ScreenBufferSize != 0);
589                 ASSERT(DeviceExtension->VideoMemorySize == DeviceExtension->ScreenBufferSize);
590 
591                 RtlCopyMemory(DeviceExtension->VideoMemory,
592                               DeviceExtension->ScreenBuffer,
593                               DeviceExtension->VideoMemorySize);
594             }
595 
596             /* Restore the cursor state */
597             ScrSetCursor(DeviceExtension);
598             ScrSetCursorShape(DeviceExtension);
599         }
600         DeviceExtension->Enabled = TRUE;
601     }
602     else
603     {
604         DeviceExtension->Enabled = FALSE;
605         if (FullReset)
606         {
607             /*
608              * Fully disable the screen and reset all its settings.
609              */
610 
611             /* Clean INBV up */
612             ScrInbvCleanup();
613 
614             /* Unmap any previously mapped video memory */
615             if (DeviceExtension->VideoMemory)
616             {
617                 ASSERT(DeviceExtension->VideoMemorySize != 0);
618                 MmUnmapIoSpace(DeviceExtension->VideoMemory, DeviceExtension->VideoMemorySize);
619             }
620             DeviceExtension->VideoMemory = NULL;
621             DeviceExtension->VideoMemorySize = 0;
622 
623             /* Free any previously allocated backup screenbuffer */
624             if (DeviceExtension->ScreenBuffer)
625             {
626                 ASSERT(DeviceExtension->ScreenBufferSize != 0);
627                 ExFreePoolWithTag(DeviceExtension->ScreenBuffer, TAG_BLUE);
628             }
629             DeviceExtension->ScreenBuffer = NULL;
630             DeviceExtension->ScreenBufferSize = 0;
631 
632             /* Store dummy values */
633             DeviceExtension->Columns = 1;
634             DeviceExtension->Rows = 1;
635             DeviceExtension->ScanLines = 1;
636         }
637         else
638         {
639             /*
640              * Partially disable the screen such that it can be restored later.
641              */
642 
643             /* Take a snapshot of the video memory into the backup screenbuffer */
644             if (DeviceExtension->ScreenBuffer)
645             {
646                 ASSERT(DeviceExtension->VideoMemory);
647                 ASSERT(DeviceExtension->ScreenBuffer);
648                 ASSERT(DeviceExtension->ScreenBufferSize != 0);
649                 ASSERT(DeviceExtension->VideoMemorySize == DeviceExtension->ScreenBufferSize);
650 
651                 RtlCopyMemory(DeviceExtension->ScreenBuffer,
652                               DeviceExtension->VideoMemory,
653                               DeviceExtension->VideoMemorySize);
654             }
655         }
656     }
657 
658     return TRUE; // STATUS_SUCCESS;
659 }
660 
661 static DRIVER_DISPATCH ScrCreateClose;
662 static NTSTATUS
663 NTAPI
664 ScrCreateClose(
665     _In_ PDEVICE_OBJECT DeviceObject,
666     _In_ PIRP Irp)
667 {
668     PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(Irp);
669 
670     UNREFERENCED_PARAMETER(DeviceObject);
671 
672     if (stk->MajorFunction == IRP_MJ_CREATE)
673         Irp->IoStatus.Information = FILE_OPENED;
674     // else: IRP_MJ_CLOSE
675 
676     Irp->IoStatus.Status = STATUS_SUCCESS;
677     IoCompleteRequest(Irp, IO_NO_INCREMENT);
678     return STATUS_SUCCESS;
679 }
680 
681 static DRIVER_DISPATCH ScrWrite;
682 static NTSTATUS
683 NTAPI
684 ScrWrite(
685     _In_ PDEVICE_OBJECT DeviceObject,
686     _In_ PIRP Irp)
687 {
688     NTSTATUS Status;
689     PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(Irp);
690     PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
691     PCHAR pch = Irp->UserBuffer;
692     PUCHAR vidmem;
693     ULONG i;
694     ULONG j, offset;
695     USHORT cursorx, cursory;
696     USHORT rows, columns;
697     BOOLEAN processed = !!(DeviceExtension->Mode & ENABLE_PROCESSED_OUTPUT);
698 
699     if (!DeviceExtension->Enabled || !DeviceExtension->VideoMemory)
700     {
701         /* Display is not enabled, we're not allowed to touch it */
702         Status = STATUS_SUCCESS;
703 
704         Irp->IoStatus.Status = Status;
705         IoCompleteRequest(Irp, IO_NO_INCREMENT);
706 
707         return Status;
708     }
709 
710     vidmem  = DeviceExtension->VideoMemory;
711     rows    = DeviceExtension->Rows;
712     columns = DeviceExtension->Columns;
713     cursorx = DeviceExtension->CursorX;
714     cursory = DeviceExtension->CursorY;
715 
716     if (!processed)
717     {
718         /* Raw output mode */
719 
720         /* Calculate the offset from the cursor position */
721         offset = cursorx + cursory * columns;
722 
723         // FIXME: Does the buffer only contains chars? or chars + attributes?
724         // FIXME2: Fix buffer overflow.
725         RtlCopyMemory(&vidmem[offset * 2], pch, stk->Parameters.Write.Length);
726         offset += (stk->Parameters.Write.Length / 2);
727 
728         /* Set the cursor position, clipping it to the screen */
729         cursorx = (USHORT)(offset % columns);
730         cursory = (USHORT)(offset / columns);
731         // cursorx = min(max(cursorx, 0), columns - 1);
732         cursory = min(max(cursory, 0), rows - 1);
733     }
734     else
735     {
736         /* Cooked output mode */
737         for (i = 0; i < stk->Parameters.Write.Length; i++, pch++)
738         {
739             switch (*pch)
740             {
741             case '\b':
742             {
743                 if (cursorx > 0)
744                 {
745                     cursorx--;
746                 }
747                 else if (cursory > 0)
748                 {
749                     cursory--;
750                     cursorx = columns - 1;
751                 }
752                 offset = cursorx + cursory * columns;
753                 vidmem[offset * 2] = ' ';
754                 vidmem[offset * 2 + 1] = (char)DeviceExtension->CharAttribute;
755                 break;
756             }
757 
758             case '\n':
759                 cursory++;
760                 /* Fall back */
761             case '\r':
762                 cursorx = 0;
763                 break;
764 
765             case '\t':
766             {
767                 offset = TAB_WIDTH - (cursorx % TAB_WIDTH);
768                 while (offset--)
769                 {
770                     vidmem[(cursorx + cursory * columns) * 2] = ' ';
771                     cursorx++;
772                     if (cursorx >= columns)
773                     {
774                         cursorx = 0;
775                         cursory++;
776                         /* We jumped to the next line, stop there */
777                         break;
778                     }
779                 }
780                 break;
781             }
782 
783             default:
784             {
785                 offset = cursorx + cursory * columns;
786                 vidmem[offset * 2] = *pch;
787                 vidmem[offset * 2 + 1] = (char)DeviceExtension->CharAttribute;
788                 cursorx++;
789                 if (cursorx >= columns)
790                 {
791                     cursorx = 0;
792                     cursory++;
793                 }
794                 break;
795             }
796             }
797 
798             /* Scroll up the contents of the screen if we are at the end */
799             if (cursory >= rows)
800             {
801                 PUSHORT LinePtr;
802 
803                 RtlCopyMemory(vidmem,
804                               &vidmem[columns * 2],
805                               columns * (rows - 1) * 2);
806 
807                 LinePtr = (PUSHORT)&vidmem[columns * (rows - 1) * 2];
808 
809                 for (j = 0; j < columns; j++)
810                 {
811                     LinePtr[j] = DeviceExtension->CharAttribute << 8;
812                 }
813                 cursory = rows - 1;
814                 for (j = 0; j < columns; j++)
815                 {
816                     offset = j + cursory * columns;
817                     vidmem[offset * 2] = ' ';
818                     vidmem[offset * 2 + 1] = (char)DeviceExtension->CharAttribute;
819                 }
820             }
821         }
822     }
823 
824     /* Set the cursor position */
825     ASSERT((0 <= cursorx) && (cursorx < DeviceExtension->Columns));
826     ASSERT((0 <= cursory) && (cursory < DeviceExtension->Rows));
827     DeviceExtension->CursorX = cursorx;
828     DeviceExtension->CursorY = cursory;
829     ScrSetCursor(DeviceExtension);
830 
831     Status = STATUS_SUCCESS;
832 
833     Irp->IoStatus.Status = Status;
834     IoCompleteRequest(Irp, IO_VIDEO_INCREMENT);
835 
836     return Status;
837 }
838 
839 static DRIVER_DISPATCH ScrIoControl;
840 static NTSTATUS
841 NTAPI
842 ScrIoControl(
843     _In_ PDEVICE_OBJECT DeviceObject,
844     _In_ PIRP Irp)
845 {
846     NTSTATUS Status;
847     PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(Irp);
848     PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
849 
850     switch (stk->Parameters.DeviceIoControl.IoControlCode)
851     {
852         case IOCTL_CONSOLE_RESET_SCREEN:
853         {
854             BOOLEAN Enable;
855 
856             /* Validate input buffer */
857             if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG))
858             {
859                 Status = STATUS_INVALID_PARAMETER;
860                 break;
861             }
862             ASSERT(Irp->AssociatedIrp.SystemBuffer);
863 
864             Enable = !!*(PULONG)Irp->AssociatedIrp.SystemBuffer;
865 
866             /* Fully enable or disable the screen */
867             Status = (ScrResetScreen(DeviceExtension, TRUE, Enable)
868                         ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
869             Irp->IoStatus.Information = 0;
870             break;
871         }
872 
873         case IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO:
874         {
875             PCONSOLE_SCREEN_BUFFER_INFO pcsbi;
876             USHORT rows = DeviceExtension->Rows;
877             USHORT columns = DeviceExtension->Columns;
878 
879             /* Validate output buffer */
880             if (stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CONSOLE_SCREEN_BUFFER_INFO))
881             {
882                 Status = STATUS_INVALID_PARAMETER;
883                 break;
884             }
885             ASSERT(Irp->AssociatedIrp.SystemBuffer);
886 
887             pcsbi = (PCONSOLE_SCREEN_BUFFER_INFO)Irp->AssociatedIrp.SystemBuffer;
888             RtlZeroMemory(pcsbi, sizeof(CONSOLE_SCREEN_BUFFER_INFO));
889 
890             pcsbi->dwSize.X = columns;
891             pcsbi->dwSize.Y = rows;
892 
893             pcsbi->dwCursorPosition.X = DeviceExtension->CursorX;
894             pcsbi->dwCursorPosition.Y = DeviceExtension->CursorY;
895 
896             pcsbi->wAttributes = DeviceExtension->CharAttribute;
897 
898             pcsbi->srWindow.Left   = 0;
899             pcsbi->srWindow.Right  = columns - 1;
900             pcsbi->srWindow.Top    = 0;
901             pcsbi->srWindow.Bottom = rows - 1;
902 
903             pcsbi->dwMaximumWindowSize.X = columns;
904             pcsbi->dwMaximumWindowSize.Y = rows;
905 
906             Irp->IoStatus.Information = sizeof(CONSOLE_SCREEN_BUFFER_INFO);
907             Status = STATUS_SUCCESS;
908             break;
909         }
910 
911         case IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO:
912         {
913             PCONSOLE_SCREEN_BUFFER_INFO pcsbi;
914 
915             /* Validate input buffer */
916             if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(CONSOLE_SCREEN_BUFFER_INFO))
917             {
918                 Status = STATUS_INVALID_PARAMETER;
919                 break;
920             }
921             ASSERT(Irp->AssociatedIrp.SystemBuffer);
922 
923             pcsbi = (PCONSOLE_SCREEN_BUFFER_INFO)Irp->AssociatedIrp.SystemBuffer;
924 
925             if ( pcsbi->dwCursorPosition.X < 0 || pcsbi->dwCursorPosition.X >= DeviceExtension->Columns ||
926                  pcsbi->dwCursorPosition.Y < 0 || pcsbi->dwCursorPosition.Y >= DeviceExtension->Rows )
927             {
928                 Irp->IoStatus.Information = 0;
929                 Status = STATUS_INVALID_PARAMETER;
930                 break;
931             }
932 
933             DeviceExtension->CharAttribute = pcsbi->wAttributes;
934 
935             /* Set the cursor position */
936             ASSERT((0 <= pcsbi->dwCursorPosition.X) && (pcsbi->dwCursorPosition.X < DeviceExtension->Columns));
937             ASSERT((0 <= pcsbi->dwCursorPosition.Y) && (pcsbi->dwCursorPosition.Y < DeviceExtension->Rows));
938             DeviceExtension->CursorX = pcsbi->dwCursorPosition.X;
939             DeviceExtension->CursorY = pcsbi->dwCursorPosition.Y;
940             if (DeviceExtension->Enabled)
941                 ScrSetCursor(DeviceExtension);
942 
943             Irp->IoStatus.Information = 0;
944             Status = STATUS_SUCCESS;
945             break;
946         }
947 
948         case IOCTL_CONSOLE_GET_CURSOR_INFO:
949         {
950             PCONSOLE_CURSOR_INFO pcci;
951 
952             /* Validate output buffer */
953             if (stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CONSOLE_CURSOR_INFO))
954             {
955                 Status = STATUS_INVALID_PARAMETER;
956                 break;
957             }
958             ASSERT(Irp->AssociatedIrp.SystemBuffer);
959 
960             pcci = (PCONSOLE_CURSOR_INFO)Irp->AssociatedIrp.SystemBuffer;
961             RtlZeroMemory(pcci, sizeof(CONSOLE_CURSOR_INFO));
962 
963             pcci->dwSize = DeviceExtension->CursorSize;
964             pcci->bVisible = DeviceExtension->CursorVisible;
965 
966             Irp->IoStatus.Information = sizeof(CONSOLE_CURSOR_INFO);
967             Status = STATUS_SUCCESS;
968             break;
969         }
970 
971         case IOCTL_CONSOLE_SET_CURSOR_INFO:
972         {
973             PCONSOLE_CURSOR_INFO pcci;
974 
975             /* Validate input buffer */
976             if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(CONSOLE_CURSOR_INFO))
977             {
978                 Status = STATUS_INVALID_PARAMETER;
979                 break;
980             }
981             ASSERT(Irp->AssociatedIrp.SystemBuffer);
982 
983             pcci = (PCONSOLE_CURSOR_INFO)Irp->AssociatedIrp.SystemBuffer;
984 
985             DeviceExtension->CursorSize = pcci->dwSize;
986             DeviceExtension->CursorVisible = pcci->bVisible;
987             if (DeviceExtension->Enabled)
988                 ScrSetCursorShape(DeviceExtension);
989 
990             Irp->IoStatus.Information = 0;
991             Status = STATUS_SUCCESS;
992             break;
993         }
994 
995         case IOCTL_CONSOLE_GET_MODE:
996         {
997             PCONSOLE_MODE pcm;
998 
999             /* Validate output buffer */
1000             if (stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CONSOLE_MODE))
1001             {
1002                 Status = STATUS_INVALID_PARAMETER;
1003                 break;
1004             }
1005             ASSERT(Irp->AssociatedIrp.SystemBuffer);
1006 
1007             pcm = (PCONSOLE_MODE)Irp->AssociatedIrp.SystemBuffer;
1008             RtlZeroMemory(pcm, sizeof(CONSOLE_MODE));
1009 
1010             pcm->dwMode = DeviceExtension->Mode;
1011 
1012             Irp->IoStatus.Information = sizeof(CONSOLE_MODE);
1013             Status = STATUS_SUCCESS;
1014             break;
1015         }
1016 
1017         case IOCTL_CONSOLE_SET_MODE:
1018         {
1019             PCONSOLE_MODE pcm;
1020 
1021             /* Validate input buffer */
1022             if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(CONSOLE_MODE))
1023             {
1024                 Status = STATUS_INVALID_PARAMETER;
1025                 break;
1026             }
1027             ASSERT(Irp->AssociatedIrp.SystemBuffer);
1028 
1029             pcm = (PCONSOLE_MODE)Irp->AssociatedIrp.SystemBuffer;
1030             DeviceExtension->Mode = pcm->dwMode;
1031 
1032             Irp->IoStatus.Information = 0;
1033             Status = STATUS_SUCCESS;
1034             break;
1035         }
1036 
1037         case IOCTL_CONSOLE_FILL_OUTPUT_ATTRIBUTE:
1038         {
1039             POUTPUT_ATTRIBUTE Buf;
1040             PUCHAR vidmem;
1041             ULONG offset;
1042             ULONG dwCount;
1043             ULONG nMaxLength;
1044 
1045             /* Validate input and output buffers */
1046             if (stk->Parameters.DeviceIoControl.InputBufferLength  < sizeof(OUTPUT_ATTRIBUTE) ||
1047                 stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(OUTPUT_ATTRIBUTE))
1048             {
1049                 Status = STATUS_INVALID_PARAMETER;
1050                 break;
1051             }
1052             ASSERT(Irp->AssociatedIrp.SystemBuffer);
1053 
1054             Buf = (POUTPUT_ATTRIBUTE)Irp->AssociatedIrp.SystemBuffer;
1055             nMaxLength = Buf->nLength;
1056 
1057             Buf->dwTransfered = 0;
1058             Irp->IoStatus.Information = sizeof(OUTPUT_ATTRIBUTE);
1059 
1060             if ( Buf->dwCoord.X < 0 || Buf->dwCoord.X >= DeviceExtension->Columns ||
1061                  Buf->dwCoord.Y < 0 || Buf->dwCoord.Y >= DeviceExtension->Rows    ||
1062                  nMaxLength == 0 )
1063             {
1064                 Status = STATUS_SUCCESS;
1065                 break;
1066             }
1067 
1068             if (DeviceExtension->Enabled && DeviceExtension->VideoMemory)
1069             {
1070                 vidmem = DeviceExtension->VideoMemory;
1071                 offset = (Buf->dwCoord.X + Buf->dwCoord.Y * DeviceExtension->Columns) * 2 + 1;
1072 
1073                 nMaxLength = min(nMaxLength,
1074                                  (DeviceExtension->Rows - Buf->dwCoord.Y)
1075                                     * DeviceExtension->Columns - Buf->dwCoord.X);
1076 
1077                 for (dwCount = 0; dwCount < nMaxLength; dwCount++)
1078                 {
1079                     vidmem[offset + (dwCount * 2)] = (char)Buf->wAttribute;
1080                 }
1081                 Buf->dwTransfered = dwCount;
1082             }
1083 
1084             Status = STATUS_SUCCESS;
1085             break;
1086         }
1087 
1088         case IOCTL_CONSOLE_READ_OUTPUT_ATTRIBUTE:
1089         {
1090             POUTPUT_ATTRIBUTE Buf;
1091             PUSHORT pAttr;
1092             PUCHAR vidmem;
1093             ULONG offset;
1094             ULONG dwCount;
1095             ULONG nMaxLength;
1096 
1097             /* Validate input buffer */
1098             if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(OUTPUT_ATTRIBUTE))
1099             {
1100                 Status = STATUS_INVALID_PARAMETER;
1101                 break;
1102             }
1103             ASSERT(Irp->AssociatedIrp.SystemBuffer);
1104 
1105             Buf = (POUTPUT_ATTRIBUTE)Irp->AssociatedIrp.SystemBuffer;
1106             Irp->IoStatus.Information = 0;
1107 
1108             /* Validate output buffer */
1109             if (stk->Parameters.DeviceIoControl.OutputBufferLength == 0)
1110             {
1111                 Status = STATUS_SUCCESS;
1112                 break;
1113             }
1114             ASSERT(Irp->MdlAddress);
1115             pAttr = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
1116             if (pAttr == NULL)
1117             {
1118                 Status = STATUS_INSUFFICIENT_RESOURCES;
1119                 break;
1120             }
1121 
1122             if ( Buf->dwCoord.X < 0 || Buf->dwCoord.X >= DeviceExtension->Columns ||
1123                  Buf->dwCoord.Y < 0 || Buf->dwCoord.Y >= DeviceExtension->Rows )
1124             {
1125                 Status = STATUS_SUCCESS;
1126                 break;
1127             }
1128 
1129             nMaxLength = stk->Parameters.DeviceIoControl.OutputBufferLength;
1130             nMaxLength /= sizeof(USHORT);
1131 
1132             if (DeviceExtension->Enabled && DeviceExtension->VideoMemory)
1133             {
1134                 vidmem = DeviceExtension->VideoMemory;
1135                 offset = (Buf->dwCoord.X + Buf->dwCoord.Y * DeviceExtension->Columns) * 2 + 1;
1136 
1137                 nMaxLength = min(nMaxLength,
1138                                  (DeviceExtension->Rows - Buf->dwCoord.Y)
1139                                     * DeviceExtension->Columns - Buf->dwCoord.X);
1140 
1141                 for (dwCount = 0; dwCount < nMaxLength; dwCount++, pAttr++)
1142                 {
1143                     *((PCHAR)pAttr) = vidmem[offset + (dwCount * 2)];
1144                 }
1145                 Irp->IoStatus.Information = dwCount * sizeof(USHORT);
1146             }
1147 
1148             Status = STATUS_SUCCESS;
1149             break;
1150         }
1151 
1152         case IOCTL_CONSOLE_WRITE_OUTPUT_ATTRIBUTE:
1153         {
1154             COORD dwCoord;
1155             PCOORD pCoord;
1156             PUSHORT pAttr;
1157             PUCHAR vidmem;
1158             ULONG offset;
1159             ULONG dwCount;
1160             ULONG nMaxLength;
1161 
1162             //
1163             // NOTE: For whatever reason no OUTPUT_ATTRIBUTE structure
1164             // is used for this IOCTL.
1165             //
1166 
1167             /* Validate output buffer */
1168             if (stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(COORD))
1169             {
1170                 Status = STATUS_INVALID_PARAMETER;
1171                 break;
1172             }
1173             ASSERT(Irp->MdlAddress);
1174             pCoord = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
1175             if (pCoord == NULL)
1176             {
1177                 Status = STATUS_INSUFFICIENT_RESOURCES;
1178                 break;
1179             }
1180             /* Capture the input info data */
1181             dwCoord = *pCoord;
1182 
1183             nMaxLength = stk->Parameters.DeviceIoControl.OutputBufferLength - sizeof(COORD);
1184             nMaxLength /= sizeof(USHORT);
1185 
1186             Irp->IoStatus.Information = 0;
1187 
1188             if ( dwCoord.X < 0 || dwCoord.X >= DeviceExtension->Columns ||
1189                  dwCoord.Y < 0 || dwCoord.Y >= DeviceExtension->Rows    ||
1190                  nMaxLength == 0 )
1191             {
1192                 Status = STATUS_SUCCESS;
1193                 break;
1194             }
1195 
1196             pAttr = (PUSHORT)(pCoord + 1);
1197 
1198             if (DeviceExtension->Enabled && DeviceExtension->VideoMemory)
1199             {
1200                 vidmem = DeviceExtension->VideoMemory;
1201                 offset = (dwCoord.X + dwCoord.Y * DeviceExtension->Columns) * 2 + 1;
1202 
1203                 nMaxLength = min(nMaxLength,
1204                                  (DeviceExtension->Rows - dwCoord.Y)
1205                                     * DeviceExtension->Columns - dwCoord.X);
1206 
1207                 for (dwCount = 0; dwCount < nMaxLength; dwCount++, pAttr++)
1208                 {
1209                     vidmem[offset + (dwCount * 2)] = *((PCHAR)pAttr);
1210                 }
1211                 Irp->IoStatus.Information = dwCount * sizeof(USHORT);
1212             }
1213 
1214             Status = STATUS_SUCCESS;
1215             break;
1216         }
1217 
1218         case IOCTL_CONSOLE_SET_TEXT_ATTRIBUTE:
1219         {
1220             /* Validate input buffer */
1221             if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(USHORT))
1222             {
1223                 Status = STATUS_INVALID_PARAMETER;
1224                 break;
1225             }
1226             ASSERT(Irp->AssociatedIrp.SystemBuffer);
1227 
1228             DeviceExtension->CharAttribute = *(PUSHORT)Irp->AssociatedIrp.SystemBuffer;
1229 
1230             Irp->IoStatus.Information = 0;
1231             Status = STATUS_SUCCESS;
1232             break;
1233         }
1234 
1235         case IOCTL_CONSOLE_FILL_OUTPUT_CHARACTER:
1236         {
1237             POUTPUT_CHARACTER Buf;
1238             PUCHAR vidmem;
1239             ULONG offset;
1240             ULONG dwCount;
1241             ULONG nMaxLength;
1242 
1243             /* Validate input and output buffers */
1244             if (stk->Parameters.DeviceIoControl.InputBufferLength  < sizeof(OUTPUT_CHARACTER) ||
1245                 stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(OUTPUT_CHARACTER))
1246             {
1247                 Status = STATUS_INVALID_PARAMETER;
1248                 break;
1249             }
1250             ASSERT(Irp->AssociatedIrp.SystemBuffer);
1251 
1252             Buf = (POUTPUT_CHARACTER)Irp->AssociatedIrp.SystemBuffer;
1253             nMaxLength = Buf->nLength;
1254 
1255             Buf->dwTransfered = 0;
1256             Irp->IoStatus.Information = sizeof(OUTPUT_CHARACTER);
1257 
1258             if ( Buf->dwCoord.X < 0 || Buf->dwCoord.X >= DeviceExtension->Columns ||
1259                  Buf->dwCoord.Y < 0 || Buf->dwCoord.Y >= DeviceExtension->Rows    ||
1260                  nMaxLength == 0 )
1261             {
1262                 Status = STATUS_SUCCESS;
1263                 break;
1264             }
1265 
1266             if (DeviceExtension->Enabled && DeviceExtension->VideoMemory)
1267             {
1268                 vidmem = DeviceExtension->VideoMemory;
1269                 offset = (Buf->dwCoord.X + Buf->dwCoord.Y * DeviceExtension->Columns) * 2;
1270 
1271                 nMaxLength = min(nMaxLength,
1272                                  (DeviceExtension->Rows - Buf->dwCoord.Y)
1273                                     * DeviceExtension->Columns - Buf->dwCoord.X);
1274 
1275                 for (dwCount = 0; dwCount < nMaxLength; dwCount++)
1276                 {
1277                     vidmem[offset + (dwCount * 2)] = (char)Buf->cCharacter;
1278                 }
1279                 Buf->dwTransfered = dwCount;
1280             }
1281 
1282             Status = STATUS_SUCCESS;
1283             break;
1284         }
1285 
1286         case IOCTL_CONSOLE_READ_OUTPUT_CHARACTER:
1287         {
1288             POUTPUT_CHARACTER Buf;
1289             PCHAR pChar;
1290             PUCHAR vidmem;
1291             ULONG offset;
1292             ULONG dwCount;
1293             ULONG nMaxLength;
1294 
1295             /* Validate input buffer */
1296             if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(OUTPUT_CHARACTER))
1297             {
1298                 Status = STATUS_INVALID_PARAMETER;
1299                 break;
1300             }
1301             ASSERT(Irp->AssociatedIrp.SystemBuffer);
1302 
1303             Buf = (POUTPUT_CHARACTER)Irp->AssociatedIrp.SystemBuffer;
1304             Irp->IoStatus.Information = 0;
1305 
1306             /* Validate output buffer */
1307             if (stk->Parameters.DeviceIoControl.OutputBufferLength == 0)
1308             {
1309                 Status = STATUS_SUCCESS;
1310                 break;
1311             }
1312             ASSERT(Irp->MdlAddress);
1313             pChar = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
1314             if (pChar == NULL)
1315             {
1316                 Status = STATUS_INSUFFICIENT_RESOURCES;
1317                 break;
1318             }
1319 
1320             if ( Buf->dwCoord.X < 0 || Buf->dwCoord.X >= DeviceExtension->Columns ||
1321                  Buf->dwCoord.Y < 0 || Buf->dwCoord.Y >= DeviceExtension->Rows )
1322             {
1323                 Status = STATUS_SUCCESS;
1324                 break;
1325             }
1326 
1327             nMaxLength = stk->Parameters.DeviceIoControl.OutputBufferLength;
1328 
1329             if (DeviceExtension->Enabled && DeviceExtension->VideoMemory)
1330             {
1331                 vidmem = DeviceExtension->VideoMemory;
1332                 offset = (Buf->dwCoord.X + Buf->dwCoord.Y * DeviceExtension->Columns) * 2;
1333 
1334                 nMaxLength = min(nMaxLength,
1335                                  (DeviceExtension->Rows - Buf->dwCoord.Y)
1336                                     * DeviceExtension->Columns - Buf->dwCoord.X);
1337 
1338                 for (dwCount = 0; dwCount < nMaxLength; dwCount++, pChar++)
1339                 {
1340                     *pChar = vidmem[offset + (dwCount * 2)];
1341                 }
1342                 Irp->IoStatus.Information = dwCount * sizeof(CHAR);
1343             }
1344 
1345             Status = STATUS_SUCCESS;
1346             break;
1347         }
1348 
1349         case IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER:
1350         {
1351             COORD dwCoord;
1352             PCOORD pCoord;
1353             PCHAR pChar;
1354             PUCHAR vidmem;
1355             ULONG offset;
1356             ULONG dwCount;
1357             ULONG nMaxLength;
1358 
1359             //
1360             // NOTE: For whatever reason no OUTPUT_CHARACTER structure
1361             // is used for this IOCTL.
1362             //
1363 
1364             /* Validate output buffer */
1365             if (stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(COORD))
1366             {
1367                 Status = STATUS_INVALID_PARAMETER;
1368                 break;
1369             }
1370             ASSERT(Irp->MdlAddress);
1371             pCoord = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
1372             if (pCoord == NULL)
1373             {
1374                 Status = STATUS_INSUFFICIENT_RESOURCES;
1375                 break;
1376             }
1377             /* Capture the input info data */
1378             dwCoord = *pCoord;
1379 
1380             nMaxLength = stk->Parameters.DeviceIoControl.OutputBufferLength - sizeof(COORD);
1381             Irp->IoStatus.Information = 0;
1382 
1383             if ( dwCoord.X < 0 || dwCoord.X >= DeviceExtension->Columns ||
1384                  dwCoord.Y < 0 || dwCoord.Y >= DeviceExtension->Rows    ||
1385                  nMaxLength == 0 )
1386             {
1387                 Status = STATUS_SUCCESS;
1388                 break;
1389             }
1390 
1391             pChar = (PCHAR)(pCoord + 1);
1392 
1393             if (DeviceExtension->Enabled && DeviceExtension->VideoMemory)
1394             {
1395                 vidmem = DeviceExtension->VideoMemory;
1396                 offset = (dwCoord.X + dwCoord.Y * DeviceExtension->Columns) * 2;
1397 
1398                 nMaxLength = min(nMaxLength,
1399                                  (DeviceExtension->Rows - dwCoord.Y)
1400                                     * DeviceExtension->Columns - dwCoord.X);
1401 
1402                 for (dwCount = 0; dwCount < nMaxLength; dwCount++, pChar++)
1403                 {
1404                     vidmem[offset + (dwCount * 2)] = *pChar;
1405                 }
1406                 Irp->IoStatus.Information = dwCount * sizeof(CHAR);
1407             }
1408 
1409             Status = STATUS_SUCCESS;
1410             break;
1411         }
1412 
1413         case IOCTL_CONSOLE_DRAW:
1414         {
1415             CONSOLE_DRAW ConsoleDraw;
1416             PCONSOLE_DRAW pConsoleDraw;
1417             PUCHAR Src, Dest;
1418             UINT32 SrcDelta, DestDelta, i;
1419 
1420             /* Validate output buffer */
1421             if (stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CONSOLE_DRAW))
1422             {
1423                 Status = STATUS_INVALID_PARAMETER;
1424                 break;
1425             }
1426             ASSERT(Irp->MdlAddress);
1427             pConsoleDraw = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
1428             if (pConsoleDraw == NULL)
1429             {
1430                 Status = STATUS_INSUFFICIENT_RESOURCES;
1431                 break;
1432             }
1433             /* Capture the input info data */
1434             ConsoleDraw = *pConsoleDraw;
1435 
1436             /* Check whether we have the size for the header plus the data area */
1437             if ((stk->Parameters.DeviceIoControl.OutputBufferLength - sizeof(CONSOLE_DRAW)) / 2
1438                     < ((ULONG)ConsoleDraw.SizeX * (ULONG)ConsoleDraw.SizeY))
1439             {
1440                 Status = STATUS_INVALID_BUFFER_SIZE;
1441                 break;
1442             }
1443 
1444             // TODO: For the moment if the ConsoleDraw rectangle has borders
1445             // out of the screen-buffer we just bail out. Would it be better
1446             // to actually clip the rectangle within its borders instead?
1447             if ( ConsoleDraw.X < 0 || ConsoleDraw.X >= DeviceExtension->Columns ||
1448                  ConsoleDraw.Y < 0 || ConsoleDraw.Y >= DeviceExtension->Rows )
1449             {
1450                 Status = STATUS_SUCCESS;
1451                 break;
1452             }
1453             if ( ConsoleDraw.SizeX >= DeviceExtension->Columns - ConsoleDraw.X ||
1454                  ConsoleDraw.SizeY >= DeviceExtension->Rows    - ConsoleDraw.Y )
1455             {
1456                 Status = STATUS_SUCCESS;
1457                 break;
1458             }
1459 
1460             if (DeviceExtension->Enabled && DeviceExtension->VideoMemory)
1461             {
1462                 Src = (PUCHAR)(pConsoleDraw + 1);
1463                 SrcDelta = ConsoleDraw.SizeX * 2;
1464                 Dest = DeviceExtension->VideoMemory +
1465                         (ConsoleDraw.X + ConsoleDraw.Y * DeviceExtension->Columns) * 2;
1466                 DestDelta = DeviceExtension->Columns * 2;
1467                 /* 2 == sizeof(CHAR) + sizeof(BYTE) */
1468 
1469                 /* Copy each line */
1470                 for (i = 0; i < ConsoleDraw.SizeY; i++)
1471                 {
1472                     RtlCopyMemory(Dest, Src, SrcDelta);
1473                     Src += SrcDelta;
1474                     Dest += DestDelta;
1475                 }
1476             }
1477 
1478             /* Set the cursor position, clipping it to the screen */
1479             DeviceExtension->CursorX = min(max(ConsoleDraw.CursorX, 0), DeviceExtension->Columns - 1);
1480             DeviceExtension->CursorY = min(max(ConsoleDraw.CursorY, 0), DeviceExtension->Rows    - 1);
1481             if (DeviceExtension->Enabled)
1482                 ScrSetCursor(DeviceExtension);
1483 
1484             Irp->IoStatus.Information = 0;
1485             Status = STATUS_SUCCESS;
1486             break;
1487         }
1488 
1489         case IOCTL_CONSOLE_LOADFONT:
1490         {
1491             /* Validate input buffer */
1492             if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG))
1493             {
1494                 Status = STATUS_INVALID_PARAMETER;
1495                 break;
1496             }
1497             ASSERT(Irp->AssociatedIrp.SystemBuffer);
1498 
1499             DeviceExtension->CodePage = *(PULONG)Irp->AssociatedIrp.SystemBuffer;
1500 
1501             /* Upload a font for the codepage if needed */
1502             if (DeviceExtension->Enabled && DeviceExtension->VideoMemory)
1503                 ScrLoadFontTable(DeviceExtension->CodePage);
1504 
1505             Irp->IoStatus.Information = 0;
1506             Status = STATUS_SUCCESS;
1507             break;
1508         }
1509 
1510         default:
1511             Status = STATUS_NOT_IMPLEMENTED;
1512     }
1513 
1514     Irp->IoStatus.Status = Status;
1515     IoCompleteRequest(Irp, IO_VIDEO_INCREMENT);
1516 
1517     return Status;
1518 }
1519 
1520 static DRIVER_DISPATCH ScrDispatch;
1521 static NTSTATUS
1522 NTAPI
1523 ScrDispatch(
1524     _In_ PDEVICE_OBJECT DeviceObject,
1525     _In_ PIRP Irp)
1526 {
1527     PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(Irp);
1528 
1529     UNREFERENCED_PARAMETER(DeviceObject);
1530 
1531     DPRINT1("ScrDispatch(0x%p): stk->MajorFunction = %lu UNIMPLEMENTED\n",
1532             DeviceObject, stk->MajorFunction);
1533 
1534     Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
1535     IoCompleteRequest(Irp, IO_NO_INCREMENT);
1536     return STATUS_NOT_IMPLEMENTED;
1537 }
1538 
1539 /*
1540  * Module entry point
1541  */
1542 NTSTATUS
1543 NTAPI
1544 DriverEntry(
1545     _In_ PDRIVER_OBJECT DriverObject,
1546     _In_ PUNICODE_STRING RegistryPath)
1547 {
1548     NTSTATUS Status;
1549     PDEVICE_OBJECT DeviceObject;
1550     UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\BlueScreen");
1551     UNICODE_STRING SymlinkName = RTL_CONSTANT_STRING(L"\\??\\BlueScreen");
1552 
1553     DPRINT("Screen Driver 0.0.6\n");
1554 
1555     DriverObject->MajorFunction[IRP_MJ_CREATE] = ScrCreateClose;
1556     DriverObject->MajorFunction[IRP_MJ_CLOSE]  = ScrCreateClose;
1557     DriverObject->MajorFunction[IRP_MJ_READ]   = ScrDispatch;
1558     DriverObject->MajorFunction[IRP_MJ_WRITE]  = ScrWrite;
1559     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ScrIoControl;
1560 
1561     Status = IoCreateDevice(DriverObject,
1562                             sizeof(DEVICE_EXTENSION),
1563                             &DeviceName,
1564                             FILE_DEVICE_SCREEN,
1565                             FILE_DEVICE_SECURE_OPEN,
1566                             TRUE,
1567                             &DeviceObject);
1568     if (!NT_SUCCESS(Status))
1569     {
1570         return Status;
1571     }
1572 
1573     Status = IoCreateSymbolicLink(&SymlinkName, &DeviceName);
1574     if (NT_SUCCESS(Status))
1575     {
1576         /* By default disable the screen (but don't touch INBV: ResetDisplayParametersDeviceExtension is still NULL) */
1577         ScrResetScreen(DeviceObject->DeviceExtension, TRUE, FALSE);
1578         /* Now set ResetDisplayParametersDeviceExtension to enable synchronizing with INBV */
1579         ResetDisplayParametersDeviceExtension = DeviceObject->DeviceExtension;
1580         DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
1581     }
1582     else
1583     {
1584         IoDeleteDevice(DeviceObject);
1585     }
1586     return Status;
1587 }
1588 
1589 /* EOF */
1590