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 HdlspReleaselobalLock(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 HdlspEnableTerminal(IN BOOLEAN Enable
)
69 /* Enable if requested, as long as this isn't a PCI serial port crashing */
71 !(HeadlessGlobals
->TerminalEnabled
) &&
72 !((HeadlessGlobals
->IsMMIODevice
) && (HeadlessGlobals
->InBugCheck
)))
74 /* Initialize the COM port with cportlib */
75 HeadlessGlobals
->TerminalEnabled
= InbvPortInitialize(HeadlessGlobals
->
85 if (!HeadlessGlobals
->TerminalEnabled
)
87 DPRINT1("Failed to initialize port through cportlib\n");
88 return STATUS_UNSUCCESSFUL
;
91 /* Cleanup the screen and reset the cursor */
92 HdlspSendStringAtBaud((PUCHAR
)"\x1B[2J");
93 HdlspSendStringAtBaud((PUCHAR
)"\x1B[H");
96 InbvPortEnableFifo(HeadlessGlobals
->TerminalPort
, TRUE
);
100 /* Specific case when headless is being disabled */
101 InbvPortTerminate(HeadlessGlobals
->TerminalPort
);
102 HeadlessGlobals
->TerminalPort
= 0;
103 HeadlessGlobals
->TerminalEnabled
= FALSE
;
107 return STATUS_SUCCESS
;
113 HeadlessInit(IN PLOADER_PARAMETER_BLOCK LoaderBlock
)
115 PHEADLESS_LOADER_BLOCK HeadlessBlock
;
117 /* Only initialize further if the loader found EMS enabled */
118 HeadlessBlock
= LoaderBlock
->Extension
->HeadlessLoaderBlock
;
119 if (!HeadlessBlock
) return;
121 /* Ignore invalid EMS settings */
122 if ((HeadlessBlock
->PortNumber
> 4) && (HeadlessBlock
->UsedBiosSettings
)) return;
124 /* Allocate the global headless data */
125 HeadlessGlobals
= ExAllocatePoolWithTag(NonPagedPool
,
126 sizeof(*HeadlessGlobals
),
128 if (!HeadlessGlobals
) return;
130 /* Zero and copy loader data */
131 RtlZeroMemory(HeadlessGlobals
, sizeof(*HeadlessGlobals
));
132 HeadlessGlobals
->TerminalPortNumber
= HeadlessBlock
->PortNumber
;
133 HeadlessGlobals
->TerminalPortAddress
= HeadlessBlock
->PortAddress
;
134 HeadlessGlobals
->TerminalBaudRate
= HeadlessBlock
->BaudRate
;
135 HeadlessGlobals
->TerminalParity
= HeadlessBlock
->Parity
;
136 HeadlessGlobals
->TerminalStopBits
= HeadlessBlock
->StopBits
;
137 HeadlessGlobals
->UsedBiosSettings
= HeadlessBlock
->UsedBiosSettings
;
138 HeadlessGlobals
->IsMMIODevice
= HeadlessBlock
->IsMMIODevice
;
139 HeadlessGlobals
->TerminalType
= HeadlessBlock
->TerminalType
;
140 HeadlessGlobals
->SystemGUID
= HeadlessBlock
->SystemGUID
;
141 DPRINT1("EMS on Port %lu (0x%p) at %lu bps\n",
142 HeadlessGlobals
->TerminalPortNumber
,
143 HeadlessGlobals
->TerminalPortAddress
,
144 HeadlessGlobals
->TerminalBaudRate
);
146 /* These two are opposites of each other */
147 if (HeadlessGlobals
->IsMMIODevice
) HeadlessGlobals
->IsNonLegacyDevice
= TRUE
;
149 /* Check for a PCI device, warn that this isn't supported */
150 if (HeadlessBlock
->PciDeviceId
!= PCI_INVALID_VENDORID
)
152 DPRINT1("PCI Serial Ports not supported\n");
155 /* Log entries are not yet supported */
156 DPRINT1("FIXME: No Headless logging support\n");
158 /* Allocate temporary buffer */
159 HeadlessGlobals
->TmpBuffer
= ExAllocatePoolWithTag(NonPagedPool
, 80, 'sldH');
160 if (!HeadlessGlobals
->TmpBuffer
) return;
162 /* Windows seems to apply some special hacks for 9600 bps */
163 if (HeadlessGlobals
->TerminalBaudRate
== 9600)
165 DPRINT1("Please use other baud rate than 9600bps for now\n");
168 /* Enable the terminal */
169 HdlspEnableTerminal(TRUE
);
174 HdlspPutData(IN PUCHAR Data
,
178 for (i
= 0; i
< DataSize
; i
++)
180 InbvPortPutByte(HeadlessGlobals
->TerminalPort
, Data
[i
]++);
186 HdlspPutString(IN PUCHAR String
)
188 PUCHAR Dest
= HeadlessGlobals
->TmpBuffer
;
191 /* Scan each character */
192 while (*String
!= ANSI_NULL
)
194 /* Check for rotate, send existing buffer and restart from where we are */
195 if (Dest
>= &HeadlessGlobals
->TmpBuffer
[79])
197 HeadlessGlobals
->TmpBuffer
[79] = ANSI_NULL
;
198 HdlspSendStringAtBaud(HeadlessGlobals
->TmpBuffer
);
199 Dest
= HeadlessGlobals
->TmpBuffer
;
203 /* Get the current character and check for special graphical chars */
209 case 0xB0: case 0xB3: case 0xBA:
212 case 0xB1: case 0xDC: case 0xDD: case 0xDE: case 0xDF:
215 case 0xB2: case 0xDB:
218 case 0xA9: case 0xAA: case 0xBB: case 0xBC: case 0xBF:
219 case 0xC0: case 0xC8: case 0xC9: case 0xD9: case 0xDA:
231 /* Anything else must be Unicode */
234 /* Can't do Unicode yet */
239 /* Add the modified char to the temporary buffer */
243 /* Check the next char */
248 /* Finish and send */
250 HdlspSendStringAtBaud(HeadlessGlobals
->TmpBuffer
);
255 HdlspDispatch(IN HEADLESS_CMD Command
,
256 IN PVOID InputBuffer
,
257 IN SIZE_T InputBufferSize
,
258 OUT PVOID OutputBuffer
,
259 OUT PSIZE_T OutputBufferSize
)
262 PHEADLESS_RSP_QUERY_INFO HeadlessInfo
;
263 PHEADLESS_CMD_PUT_STRING PutString
;
264 PHEADLESS_CMD_ENABLE_TERMINAL EnableTerminal
;
265 PHEADLESS_RSP_GET_BYTE GetByte
;
266 NTSTATUS Status
= STATUS_NOT_IMPLEMENTED
;
267 ASSERT(HeadlessGlobals
!= NULL
);
268 // ASSERT(HeadlessGlobals->PageLockHandle != NULL);
270 /* Ignore non-reentrant commands */
271 if ((Command
!= HeadlessCmdAddLogEntry
) &&
272 (Command
!= HeadlessCmdStartBugCheck
) &&
273 (Command
!= HeadlessCmdSendBlueScreenData
) &&
274 (Command
!= HeadlessCmdDoBugCheckProcessing
))
276 OldIrql
= HdlspAcquireGlobalLock();
278 if (HeadlessGlobals
->ProcessingCmd
)
280 HdlspReleaselobalLock(OldIrql
);
281 return STATUS_UNSUCCESSFUL
;
284 /* Don't allow these commands next time */
285 HeadlessGlobals
->ProcessingCmd
= TRUE
;
286 HdlspReleaselobalLock(OldIrql
);
289 /* Handle each command */
292 case HeadlessCmdEnableTerminal
:
294 /* Make sure the caller passed valid data */
295 if (!(InputBuffer
) ||
296 (InputBufferSize
!= sizeof(*EnableTerminal
)))
298 DPRINT1("Invalid buffer\n");
299 Status
= STATUS_INVALID_PARAMETER
;
303 /* Go and enable it */
304 EnableTerminal
= InputBuffer
;
305 Status
= HdlspEnableTerminal(EnableTerminal
->Enable
);
309 case HeadlessCmdCheckForReboot
:
312 case HeadlessCmdPutString
:
314 /* Validate the existence of an input buffer */
317 Status
= STATUS_INVALID_PARAMETER
;
321 /* Terminal should be on */
322 if (HeadlessGlobals
->TerminalEnabled
)
324 /* Print each byte in the string making sure VT100 chars are used */
325 PutString
= InputBuffer
;
326 HdlspPutString(PutString
->String
);
329 /* Return success either way */
330 Status
= STATUS_SUCCESS
;
334 case HeadlessCmdClearDisplay
:
336 /* Send the VT100 clear screen command if the terminal is enabled */
337 if (HeadlessGlobals
->TerminalEnabled
)
339 HdlspSendStringAtBaud((PUCHAR
)"\033[2J");
342 /* Return success either way */
343 Status
= STATUS_SUCCESS
;
347 case HeadlessCmdClearToEndOfDisplay
:
349 case HeadlessCmdClearToEndOfLine
:
351 case HeadlessCmdDisplayAttributesOff
:
353 case HeadlessCmdDisplayInverseVideo
:
355 case HeadlessCmdSetColor
:
357 case HeadlessCmdPositionCursor
:
359 case HeadlessCmdTerminalPoll
:
362 case HeadlessCmdGetByte
:
364 /* Make sure the caller passed valid data */
365 if (!(OutputBuffer
) ||
366 !(OutputBufferSize
) ||
367 (*OutputBufferSize
< sizeof(*GetByte
)))
369 DPRINT1("Invalid buffer\n");
370 Status
= STATUS_INVALID_PARAMETER
;
374 /* Make sure the terminal is enabled */
375 GetByte
= OutputBuffer
;
376 if (HeadlessGlobals
->TerminalEnabled
)
378 /* Poll if something is on the wire */
379 if (InbvPortPollOnly(HeadlessGlobals
->TerminalPort
))
382 InbvPortGetByte(HeadlessGlobals
->TerminalPort
,
387 /* Nothing is there, return 0 */
393 /* Otherwise return nothing */
397 /* Return success either way */
398 Status
= STATUS_SUCCESS
;
402 case HeadlessCmdGetLine
:
404 case HeadlessCmdStartBugCheck
:
406 case HeadlessCmdDoBugCheckProcessing
:
409 case HeadlessCmdQueryInformation
:
411 /* Make sure the caller passed valid data */
412 if (!(OutputBuffer
) ||
413 !(OutputBufferSize
) ||
414 (*OutputBufferSize
< sizeof(*HeadlessInfo
)))
416 DPRINT1("Invalid buffer\n");
417 Status
= STATUS_INVALID_PARAMETER
;
421 /* If we got here, headless is enabled -- we know this much */
422 HeadlessInfo
= OutputBuffer
;
423 HeadlessInfo
->PortType
= HeadlessSerialPort
;
424 HeadlessInfo
->Serial
.TerminalAttached
= TRUE
;
425 HeadlessInfo
->Serial
.UsedBiosSettings
= HeadlessGlobals
->UsedBiosSettings
!= 0;
426 HeadlessInfo
->Serial
.TerminalBaudRate
= HeadlessGlobals
->TerminalBaudRate
;
427 HeadlessInfo
->Serial
.TerminalType
= HeadlessGlobals
->TerminalType
;
429 /* Now check on what port/baud it's enabled on */
430 if ((HeadlessGlobals
->TerminalPortNumber
>= 1) ||
431 (HeadlessGlobals
->UsedBiosSettings
))
433 /* Get the EMS information */
434 HeadlessInfo
->Serial
.TerminalPort
= HeadlessGlobals
->
436 HeadlessInfo
->Serial
.TerminalPortBaseAddress
= HeadlessGlobals
->
441 /* We don't know for sure */
442 HeadlessInfo
->Serial
.TerminalPort
= SerialPortUndefined
;
443 HeadlessInfo
->Serial
.TerminalPortBaseAddress
= 0;
447 Status
= STATUS_SUCCESS
;
451 case HeadlessCmdAddLogEntry
:
453 case HeadlessCmdDisplayLog
:
456 case HeadlessCmdSetBlueScreenData
:
458 /* Validate the existence of an input buffer */
461 Status
= STATUS_INVALID_PARAMETER
;
465 /* Lie so that we can get Hdl bringup a little bit further */
467 Status
= STATUS_SUCCESS
;
471 case HeadlessCmdSendBlueScreenData
:
473 case HeadlessCmdQueryGUID
:
476 case HeadlessCmdPutData
:
478 /* Validate the existence of an input buffer */
479 if (!(InputBuffer
) || !(InputBufferSize
))
481 Status
= STATUS_INVALID_PARAMETER
;
485 /* Terminal should be on */
486 if (HeadlessGlobals
->TerminalEnabled
)
488 /* Print each byte in the string making sure VT100 chars are used */
489 PutString
= InputBuffer
;
490 HdlspPutData(PutString
->String
, InputBufferSize
);
493 /* Return success either way */
494 Status
= STATUS_SUCCESS
;
502 /* Unset processing state */
503 if ((Command
!= HeadlessCmdAddLogEntry
) &&
504 (Command
!= HeadlessCmdStartBugCheck
) &&
505 (Command
!= HeadlessCmdSendBlueScreenData
) &&
506 (Command
!= HeadlessCmdDoBugCheckProcessing
))
508 ASSERT(HeadlessGlobals
->ProcessingCmd
== TRUE
);
509 HeadlessGlobals
->ProcessingCmd
= FALSE
;
521 HeadlessDispatch(IN HEADLESS_CMD Command
,
522 IN PVOID InputBuffer
,
523 IN SIZE_T InputBufferSize
,
524 OUT PVOID OutputBuffer
,
525 OUT PSIZE_T OutputBufferSize
)
527 /* Check for stubs that will expect something even with headless off */
528 if (!HeadlessGlobals
)
530 /* Don't allow the SAC to connect */
531 if (Command
== HeadlessCmdEnableTerminal
) return STATUS_UNSUCCESSFUL
;
533 /* Send bogus reply */
534 if ((Command
== HeadlessCmdQueryInformation
) ||
535 (Command
== HeadlessCmdGetByte
) ||
536 (Command
== HeadlessCmdGetLine
) ||
537 (Command
== HeadlessCmdCheckForReboot
) ||
538 (Command
== HeadlessCmdTerminalPoll
))
540 if (!(OutputBuffer
) || !(OutputBufferSize
))
542 return STATUS_INVALID_PARAMETER
;
545 RtlZeroMemory(OutputBuffer
, *OutputBufferSize
);
548 return STATUS_SUCCESS
;
551 /* Do the real work */
552 return HdlspDispatch(Command
,