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
ScrResetDisplayParametersEx(_In_ ULONG Columns,_In_ ULONG Rows,_In_ BOOLEAN CalledByInbv)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
ScrResetDisplayParameters(_In_ ULONG Columns,_In_ ULONG Rows)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
InbvMonitorThread(_In_ PVOID Context)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
ScrInbvInitialize(VOID)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
ScrInbvCleanup(VOID)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
ScrSetRegisters(const VGA_REGISTERS * Registers)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
ScrSetCursor(_In_ PDEVICE_EXTENSION DeviceExtension)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
ScrSetCursorShape(_In_ PDEVICE_EXTENSION DeviceExtension)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
ScrAcquireOwnership(_In_ PDEVICE_EXTENSION DeviceExtension)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
ScrResetScreen(_In_ PDEVICE_EXTENSION DeviceExtension,_In_ BOOLEAN FullReset,_In_ BOOLEAN Enable)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
ScrCreateClose(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp)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
ScrWrite(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp)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
ScrIoControl(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp)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
ScrDispatch(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp)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
DriverEntry(_In_ PDRIVER_OBJECT DriverObject,_In_ PUNICODE_STRING RegistryPath)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