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 %d (0x%lx) at %d 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 NTSTATUS Status
= STATUS_NOT_IMPLEMENTED
;
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 HdlspReleaselobalLock(OldIrql
);
280 return STATUS_UNSUCCESSFUL
;
283 /* Don't allow these commands next time */
284 HeadlessGlobals
->ProcessingCmd
= TRUE
;
285 HdlspReleaselobalLock(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
);
307 case HeadlessCmdCheckForReboot
:
310 case HeadlessCmdPutString
:
312 /* Validate the existence of an input buffer */
315 Status
= STATUS_INVALID_PARAMETER
;
319 /* Terminal should be on */
320 if (HeadlessGlobals
->TerminalEnabled
)
322 /* Print each byte in the string making sure VT100 chars are used */
323 PutString
= InputBuffer
;
324 HdlspPutString(PutString
->String
);
327 /* Return success either way */
328 Status
= STATUS_SUCCESS
;
331 case HeadlessCmdClearDisplay
:
333 case HeadlessCmdClearToEndOfDisplay
:
335 case HeadlessCmdClearToEndOfLine
:
337 case HeadlessCmdDisplayAttributesOff
:
339 case HeadlessCmdDisplayInverseVideo
:
341 case HeadlessCmdSetColor
:
343 case HeadlessCmdPositionCursor
:
345 case HeadlessCmdTerminalPoll
:
347 case HeadlessCmdGetByte
:
349 case HeadlessCmdGetLine
:
351 case HeadlessCmdStartBugCheck
:
353 case HeadlessCmdDoBugCheckProcessing
:
355 case HeadlessCmdQueryInformation
:
357 /* Make sure the caller passed valid data */
358 if (!(OutputBuffer
) ||
359 !(OutputBufferSize
) ||
360 (*OutputBufferSize
< sizeof(*HeadlessInfo
)))
362 DPRINT1("Invalid buffer\n");
363 Status
= STATUS_INVALID_PARAMETER
;
367 /* If we got here, headless is enabled -- we know this much */
368 HeadlessInfo
= OutputBuffer
;
369 HeadlessInfo
->PortType
= HeadlessSerialPort
;
370 HeadlessInfo
->Serial
.TerminalAttached
= TRUE
;
371 HeadlessInfo
->Serial
.UsedBiosSettings
= HeadlessGlobals
->UsedBiosSettings
;
372 HeadlessInfo
->Serial
.TerminalBaudRate
= HeadlessGlobals
->TerminalBaudRate
;
373 HeadlessInfo
->Serial
.TerminalType
= HeadlessGlobals
->TerminalType
;
375 /* Now check on what port/baud it's enabled on */
376 if ((HeadlessGlobals
->TerminalPortNumber
>= 1) ||
377 (HeadlessGlobals
->UsedBiosSettings
))
379 /* Get the EMS information */
380 HeadlessInfo
->Serial
.TerminalPort
= HeadlessGlobals
->
382 HeadlessInfo
->Serial
.TerminalPortBaseAddress
= HeadlessGlobals
->
387 /* We don't know for sure */
388 HeadlessInfo
->Serial
.TerminalPort
= SerialPortUndefined
;
389 HeadlessInfo
->Serial
.TerminalPortBaseAddress
= 0;
393 Status
= STATUS_SUCCESS
;
395 case HeadlessCmdAddLogEntry
:
397 case HeadlessCmdDisplayLog
:
399 case HeadlessCmdSetBlueScreenData
:
401 /* Validate the existence of an input buffer */
404 Status
= STATUS_INVALID_PARAMETER
;
408 /* Lie so that we can get Hdl bringup a little bit further */
410 Status
= STATUS_SUCCESS
;
412 case HeadlessCmdSendBlueScreenData
:
414 case HeadlessCmdQueryGUID
:
416 case HeadlessCmdPutData
:
418 /* Validate the existence of an input buffer */
419 if (!(InputBuffer
) || !(InputBufferSize
))
421 Status
= STATUS_INVALID_PARAMETER
;
425 /* Terminal should be on */
426 if (HeadlessGlobals
->TerminalEnabled
)
428 /* Print each byte in the string making sure VT100 chars are used */
429 PutString
= InputBuffer
;
430 HdlspPutData(PutString
->String
, InputBufferSize
);
433 /* Return success either way */
434 Status
= STATUS_SUCCESS
;
441 /* Unset processing state */
442 if ((Command
!= HeadlessCmdAddLogEntry
) &&
443 (Command
!= HeadlessCmdStartBugCheck
) &&
444 (Command
!= HeadlessCmdSendBlueScreenData
) &&
445 (Command
!= HeadlessCmdDoBugCheckProcessing
))
447 ASSERT(HeadlessGlobals
->ProcessingCmd
== TRUE
);
448 HeadlessGlobals
->ProcessingCmd
= FALSE
;
460 HeadlessDispatch(IN HEADLESS_CMD Command
,
461 IN PVOID InputBuffer
,
462 IN SIZE_T InputBufferSize
,
463 OUT PVOID OutputBuffer
,
464 OUT PSIZE_T OutputBufferSize
)
466 /* Check for stubs that will expect something even with headless off */
467 if (!HeadlessGlobals
)
469 /* Don't allow the SAC to connect */
470 if (Command
== HeadlessCmdEnableTerminal
) return STATUS_UNSUCCESSFUL
;
472 /* Send bogus reply */
473 if ((Command
== HeadlessCmdQueryInformation
) ||
474 (Command
== HeadlessCmdGetByte
) ||
475 (Command
== HeadlessCmdGetLine
) ||
476 (Command
== HeadlessCmdCheckForReboot
) ||
477 (Command
== HeadlessCmdTerminalPoll
))
479 if (!(OutputBuffer
) || !(OutputBufferSize
))
481 return STATUS_INVALID_PARAMETER
;
484 RtlZeroMemory(OutputBuffer
, *OutputBufferSize
);
486 return STATUS_SUCCESS
;
489 /* Do the real work */
490 return HdlspDispatch(Command
,