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