2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/ex/hdlsterm.c
5 * PURPOSE: Headless Terminal Support
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
14 /* GLOBALS ********************************************************************/
16 PHEADLESS_GLOBALS HeadlessGlobals
;
18 /* FUNCTIONS ******************************************************************/
22 HdlspAcquireGlobalLock(VOID
)
26 /* Don't acquire the lock if we are bugchecking */
27 if (!HeadlessGlobals
->InBugCheck
)
29 KeAcquireSpinLock(&HeadlessGlobals
->SpinLock
, &OldIrql
);
41 HdlspReleaseGlobalLock(IN KIRQL OldIrql
)
43 /* Only release the lock if we aren't bugchecking */
46 KeReleaseSpinLock(&HeadlessGlobals
->SpinLock
, OldIrql
);
50 ASSERT(HeadlessGlobals
->InBugCheck
== TRUE
);
56 HdlspSendStringAtBaud(IN PUCHAR String
)
59 while (*String
!= ANSI_NULL
)
61 InbvPortPutByte(HeadlessGlobals
->TerminalPort
, *String
++);
67 HdlspPutData(IN PUCHAR Data
,
71 for (i
= 0; i
< DataSize
; i
++)
73 InbvPortPutByte(HeadlessGlobals
->TerminalPort
, Data
[i
]);
79 HdlspPutString(IN PUCHAR String
)
81 PUCHAR Dest
= HeadlessGlobals
->TmpBuffer
;
84 /* Scan each character */
85 while (*String
!= ANSI_NULL
)
87 /* Check for rotate, send existing buffer and restart from where we are */
88 if (Dest
>= &HeadlessGlobals
->TmpBuffer
[79])
90 HeadlessGlobals
->TmpBuffer
[79] = ANSI_NULL
;
91 HdlspSendStringAtBaud(HeadlessGlobals
->TmpBuffer
);
92 Dest
= HeadlessGlobals
->TmpBuffer
;
96 /* Get the current character and check for special graphical chars */
102 case 0xB0: case 0xB3: case 0xBA:
105 case 0xB1: case 0xDC: case 0xDD: case 0xDE: case 0xDF:
108 case 0xB2: case 0xDB:
111 case 0xA9: case 0xAA: case 0xBB: case 0xBC: case 0xBF:
112 case 0xC0: case 0xC8: case 0xC9: case 0xD9: case 0xDA:
124 /* Anything else must be Unicode */
127 /* Can't do Unicode yet */
132 /* Add the modified char to the temporary buffer */
136 /* Check the next char */
141 /* Finish and send */
143 HdlspSendStringAtBaud(HeadlessGlobals
->TmpBuffer
);
148 HdlspEnableTerminal(IN BOOLEAN Enable
)
150 /* Enable if requested, as long as this isn't a PCI serial port crashing */
152 !(HeadlessGlobals
->TerminalEnabled
) &&
153 !((HeadlessGlobals
->IsMMIODevice
) && (HeadlessGlobals
->InBugCheck
)))
155 /* Initialize the COM port with cportlib */
156 HeadlessGlobals
->TerminalEnabled
= InbvPortInitialize(HeadlessGlobals
->TerminalBaudRate
,
157 HeadlessGlobals
->TerminalPortNumber
,
158 HeadlessGlobals
->TerminalPortAddress
,
159 &HeadlessGlobals
->TerminalPort
,
160 HeadlessGlobals
->IsMMIODevice
);
161 if (!HeadlessGlobals
->TerminalEnabled
)
163 DPRINT1("Failed to initialize port through cportlib\n");
164 return STATUS_UNSUCCESSFUL
;
167 /* Cleanup the screen and reset the cursor */
168 HdlspSendStringAtBaud((PUCHAR
)"\x1B[2J");
169 HdlspSendStringAtBaud((PUCHAR
)"\x1B[H");
172 InbvPortEnableFifo(HeadlessGlobals
->TerminalPort
, TRUE
);
176 /* Specific case when headless is being disabled */
177 InbvPortTerminate(HeadlessGlobals
->TerminalPort
);
178 HeadlessGlobals
->TerminalPort
= 0;
179 HeadlessGlobals
->TerminalEnabled
= FALSE
;
183 return STATUS_SUCCESS
;
189 HeadlessInit(IN PLOADER_PARAMETER_BLOCK LoaderBlock
)
191 PHEADLESS_LOADER_BLOCK HeadlessBlock
;
193 /* Only initialize further if the loader found EMS enabled */
194 HeadlessBlock
= LoaderBlock
->Extension
->HeadlessLoaderBlock
;
195 if (!HeadlessBlock
) return;
197 /* Ignore invalid EMS settings */
198 if ((HeadlessBlock
->PortNumber
> 4) && (HeadlessBlock
->UsedBiosSettings
)) return;
200 /* Allocate the global headless data */
201 HeadlessGlobals
= ExAllocatePoolWithTag(NonPagedPool
,
202 sizeof(*HeadlessGlobals
),
204 if (!HeadlessGlobals
) return;
206 /* Zero and copy loader data */
207 RtlZeroMemory(HeadlessGlobals
, sizeof(*HeadlessGlobals
));
208 HeadlessGlobals
->TerminalPortNumber
= HeadlessBlock
->PortNumber
;
209 HeadlessGlobals
->TerminalPortAddress
= HeadlessBlock
->PortAddress
;
210 HeadlessGlobals
->TerminalBaudRate
= HeadlessBlock
->BaudRate
;
211 HeadlessGlobals
->TerminalParity
= HeadlessBlock
->Parity
;
212 HeadlessGlobals
->TerminalStopBits
= HeadlessBlock
->StopBits
;
213 HeadlessGlobals
->UsedBiosSettings
= HeadlessBlock
->UsedBiosSettings
;
214 HeadlessGlobals
->IsMMIODevice
= HeadlessBlock
->IsMMIODevice
;
215 HeadlessGlobals
->TerminalType
= HeadlessBlock
->TerminalType
;
216 HeadlessGlobals
->SystemGUID
= HeadlessBlock
->SystemGUID
;
217 DPRINT1("EMS on Port %lu (0x%p) at %lu bps\n",
218 HeadlessGlobals
->TerminalPortNumber
,
219 HeadlessGlobals
->TerminalPortAddress
,
220 HeadlessGlobals
->TerminalBaudRate
);
222 /* These two are opposites of each other */
223 if (HeadlessGlobals
->IsMMIODevice
) HeadlessGlobals
->IsNonLegacyDevice
= TRUE
;
225 /* Check for a PCI device, warn that this isn't supported */
226 if (HeadlessBlock
->PciDeviceId
!= PCI_INVALID_VENDORID
)
228 DPRINT1("PCI Serial Ports not supported\n");
231 /* Log entries are not yet supported */
232 DPRINT1("FIXME: No Headless logging support\n");
234 /* Allocate temporary buffer */
235 HeadlessGlobals
->TmpBuffer
= ExAllocatePoolWithTag(NonPagedPool
, 80, 'sldH');
236 if (!HeadlessGlobals
->TmpBuffer
) return;
238 /* Windows seems to apply some special hacks for 9600 bps */
239 if (HeadlessGlobals
->TerminalBaudRate
== 9600)
241 DPRINT1("Please use other baud rate than 9600bps for now\n");
244 /* Enable the terminal */
245 HdlspEnableTerminal(TRUE
);
250 HdlspDispatch(IN HEADLESS_CMD Command
,
251 IN PVOID InputBuffer
,
252 IN SIZE_T InputBufferSize
,
253 OUT PVOID OutputBuffer
,
254 OUT PSIZE_T OutputBufferSize
)
257 NTSTATUS Status
= STATUS_NOT_IMPLEMENTED
;
258 PHEADLESS_RSP_QUERY_INFO HeadlessInfo
;
259 PHEADLESS_CMD_PUT_STRING PutString
;
260 PHEADLESS_CMD_ENABLE_TERMINAL EnableTerminal
;
261 PHEADLESS_CMD_SET_COLOR SetColor
;
262 PHEADLESS_CMD_POSITION_CURSOR CursorPos
;
263 PHEADLESS_RSP_GET_BYTE GetByte
;
264 UCHAR DataBuffer
[80];
266 ASSERT(HeadlessGlobals
!= NULL
);
267 // ASSERT(HeadlessGlobals->PageLockHandle != NULL);
269 /* Ignore non-reentrant commands */
270 if ((Command
!= HeadlessCmdAddLogEntry
) &&
271 (Command
!= HeadlessCmdStartBugCheck
) &&
272 (Command
!= HeadlessCmdSendBlueScreenData
) &&
273 (Command
!= HeadlessCmdDoBugCheckProcessing
))
275 OldIrql
= HdlspAcquireGlobalLock();
277 if (HeadlessGlobals
->ProcessingCmd
)
279 HdlspReleaseGlobalLock(OldIrql
);
280 return STATUS_UNSUCCESSFUL
;
283 /* Don't allow these commands next time */
284 HeadlessGlobals
->ProcessingCmd
= TRUE
;
285 HdlspReleaseGlobalLock(OldIrql
);
288 /* Handle each command */
291 case HeadlessCmdEnableTerminal
:
293 /* Make sure the caller passed valid data */
294 if (!(InputBuffer
) ||
295 (InputBufferSize
!= sizeof(*EnableTerminal
)))
297 DPRINT1("Invalid buffer\n");
298 Status
= STATUS_INVALID_PARAMETER
;
302 /* Go and enable it */
303 EnableTerminal
= InputBuffer
;
304 Status
= HdlspEnableTerminal(EnableTerminal
->Enable
);
308 case HeadlessCmdCheckForReboot
:
311 case HeadlessCmdPutString
:
313 /* Validate the existence of an input buffer */
316 Status
= STATUS_INVALID_PARAMETER
;
320 /* Terminal should be on */
321 if (HeadlessGlobals
->TerminalEnabled
)
323 /* Print each byte in the string making sure VT100 chars are used */
324 PutString
= InputBuffer
;
325 HdlspPutString(PutString
->String
);
328 /* Return success either way */
329 Status
= STATUS_SUCCESS
;
333 case HeadlessCmdClearDisplay
:
334 case HeadlessCmdClearToEndOfDisplay
:
335 case HeadlessCmdClearToEndOfLine
:
336 case HeadlessCmdDisplayAttributesOff
:
337 case HeadlessCmdDisplayInverseVideo
:
338 case HeadlessCmdSetColor
:
339 case HeadlessCmdPositionCursor
:
341 /* By default return success */
342 Status
= STATUS_SUCCESS
;
344 /* Send the VT100 commands only if the terminal is enabled */
345 if (HeadlessGlobals
->TerminalEnabled
)
347 PUCHAR CommandStr
= NULL
;
349 if (Command
== HeadlessCmdClearDisplay
)
350 CommandStr
= (PUCHAR
)"\x1B[2J";
351 else if (Command
== HeadlessCmdClearToEndOfDisplay
)
352 CommandStr
= (PUCHAR
)"\x1B[0J";
353 else if (Command
== HeadlessCmdClearToEndOfLine
)
354 CommandStr
= (PUCHAR
)"\x1B[0K";
355 else if (Command
== HeadlessCmdDisplayAttributesOff
)
356 CommandStr
= (PUCHAR
)"\x1B[0m";
357 else if (Command
== HeadlessCmdDisplayInverseVideo
)
358 CommandStr
= (PUCHAR
)"\x1B[7m";
359 else if (Command
== HeadlessCmdSetColor
)
361 /* Make sure the caller passed valid data */
363 (InputBufferSize
!= sizeof(*SetColor
)))
365 DPRINT1("Invalid buffer\n");
366 Status
= STATUS_INVALID_PARAMETER
;
370 SetColor
= InputBuffer
;
371 Status
= RtlStringCbPrintfA((PCHAR
)DataBuffer
, sizeof(DataBuffer
),
374 SetColor
->TextColor
);
375 if (!NT_SUCCESS(Status
)) break;
377 CommandStr
= DataBuffer
;
379 else // if (Command == HeadlessCmdPositionCursor)
381 /* Make sure the caller passed valid data */
383 (InputBufferSize
!= sizeof(*CursorPos
)))
385 DPRINT1("Invalid buffer\n");
386 Status
= STATUS_INVALID_PARAMETER
;
390 CursorPos
= InputBuffer
;
391 /* Cursor position is 1-based */
392 Status
= RtlStringCbPrintfA((PCHAR
)DataBuffer
, sizeof(DataBuffer
),
394 CursorPos
->CursorRow
+ 1,
395 CursorPos
->CursorCol
+ 1);
396 if (!NT_SUCCESS(Status
)) break;
398 CommandStr
= DataBuffer
;
401 /* Send the command */
402 HdlspSendStringAtBaud(CommandStr
);
408 case HeadlessCmdTerminalPoll
:
411 case HeadlessCmdGetByte
:
413 /* Make sure the caller passed valid data */
414 if (!(OutputBuffer
) ||
415 !(OutputBufferSize
) ||
416 (*OutputBufferSize
< sizeof(*GetByte
)))
418 DPRINT1("Invalid buffer\n");
419 Status
= STATUS_INVALID_PARAMETER
;
423 /* Make sure the terminal is enabled */
424 GetByte
= OutputBuffer
;
425 if (HeadlessGlobals
->TerminalEnabled
)
427 /* Poll if something is on the wire */
428 if (InbvPortPollOnly(HeadlessGlobals
->TerminalPort
))
431 InbvPortGetByte(HeadlessGlobals
->TerminalPort
,
436 /* Nothing is there, return 0 */
442 /* Otherwise return nothing */
446 /* Return success either way */
447 Status
= STATUS_SUCCESS
;
451 case HeadlessCmdGetLine
:
453 case HeadlessCmdStartBugCheck
:
455 case HeadlessCmdDoBugCheckProcessing
:
458 case HeadlessCmdQueryInformation
:
460 /* Make sure the caller passed valid data */
461 if (!(OutputBuffer
) ||
462 !(OutputBufferSize
) ||
463 (*OutputBufferSize
< sizeof(*HeadlessInfo
)))
465 DPRINT1("Invalid buffer\n");
466 Status
= STATUS_INVALID_PARAMETER
;
470 /* If we got here, headless is enabled -- we know this much */
471 HeadlessInfo
= OutputBuffer
;
472 HeadlessInfo
->PortType
= HeadlessSerialPort
;
473 HeadlessInfo
->Serial
.TerminalAttached
= TRUE
;
474 HeadlessInfo
->Serial
.UsedBiosSettings
= HeadlessGlobals
->UsedBiosSettings
!= 0;
475 HeadlessInfo
->Serial
.TerminalBaudRate
= HeadlessGlobals
->TerminalBaudRate
;
476 HeadlessInfo
->Serial
.TerminalType
= HeadlessGlobals
->TerminalType
;
478 /* Now check on what port/baud it's enabled on */
479 if ((HeadlessGlobals
->TerminalPortNumber
>= 1) ||
480 (HeadlessGlobals
->UsedBiosSettings
))
482 /* Get the EMS information */
483 HeadlessInfo
->Serial
.TerminalPort
= HeadlessGlobals
->
485 HeadlessInfo
->Serial
.TerminalPortBaseAddress
= HeadlessGlobals
->
490 /* We don't know for sure */
491 HeadlessInfo
->Serial
.TerminalPort
= SerialPortUndefined
;
492 HeadlessInfo
->Serial
.TerminalPortBaseAddress
= 0;
496 Status
= STATUS_SUCCESS
;
500 case HeadlessCmdAddLogEntry
:
502 case HeadlessCmdDisplayLog
:
505 case HeadlessCmdSetBlueScreenData
:
507 /* Validate the existence of an input buffer */
510 Status
= STATUS_INVALID_PARAMETER
;
514 /* Lie so that we can get Hdl bringup a little bit further */
516 Status
= STATUS_SUCCESS
;
520 case HeadlessCmdSendBlueScreenData
:
522 case HeadlessCmdQueryGUID
:
525 case HeadlessCmdPutData
:
527 /* Validate the existence of an input buffer */
528 if (!(InputBuffer
) || !(InputBufferSize
))
530 Status
= STATUS_INVALID_PARAMETER
;
534 /* Terminal should be on */
535 if (HeadlessGlobals
->TerminalEnabled
)
537 /* Print each byte in the string making sure VT100 chars are used */
538 PutString
= InputBuffer
;
539 HdlspPutData(PutString
->String
, InputBufferSize
);
542 /* Return success either way */
543 Status
= STATUS_SUCCESS
;
551 /* Unset processing state */
552 if ((Command
!= HeadlessCmdAddLogEntry
) &&
553 (Command
!= HeadlessCmdStartBugCheck
) &&
554 (Command
!= HeadlessCmdSendBlueScreenData
) &&
555 (Command
!= HeadlessCmdDoBugCheckProcessing
))
557 ASSERT(HeadlessGlobals
->ProcessingCmd
== TRUE
);
558 HeadlessGlobals
->ProcessingCmd
= FALSE
;
570 HeadlessDispatch(IN HEADLESS_CMD Command
,
571 IN PVOID InputBuffer
,
572 IN SIZE_T InputBufferSize
,
573 OUT PVOID OutputBuffer
,
574 OUT PSIZE_T OutputBufferSize
)
576 /* Check for stubs that will expect something even with headless off */
577 if (!HeadlessGlobals
)
579 /* Don't allow the SAC to connect */
580 if (Command
== HeadlessCmdEnableTerminal
) return STATUS_UNSUCCESSFUL
;
582 /* Send bogus reply */
583 if ((Command
== HeadlessCmdQueryInformation
) ||
584 (Command
== HeadlessCmdGetByte
) ||
585 (Command
== HeadlessCmdGetLine
) ||
586 (Command
== HeadlessCmdCheckForReboot
) ||
587 (Command
== HeadlessCmdTerminalPoll
))
589 if (!(OutputBuffer
) || !(OutputBufferSize
))
591 return STATUS_INVALID_PARAMETER
;
594 RtlZeroMemory(OutputBuffer
, *OutputBufferSize
);
597 return STATUS_SUCCESS
;
600 /* Do the real work */
601 return HdlspDispatch(Command
,