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
->
166 if (!HeadlessGlobals
->TerminalEnabled
)
168 DPRINT1("Failed to initialize port through cportlib\n");
169 return STATUS_UNSUCCESSFUL
;
172 /* Cleanup the screen and reset the cursor */
173 HdlspSendStringAtBaud((PUCHAR
)"\x1B[2J");
174 HdlspSendStringAtBaud((PUCHAR
)"\x1B[H");
177 InbvPortEnableFifo(HeadlessGlobals
->TerminalPort
, TRUE
);
181 /* Specific case when headless is being disabled */
182 InbvPortTerminate(HeadlessGlobals
->TerminalPort
);
183 HeadlessGlobals
->TerminalPort
= 0;
184 HeadlessGlobals
->TerminalEnabled
= FALSE
;
188 return STATUS_SUCCESS
;
194 HeadlessInit(IN PLOADER_PARAMETER_BLOCK LoaderBlock
)
196 PHEADLESS_LOADER_BLOCK HeadlessBlock
;
198 /* Only initialize further if the loader found EMS enabled */
199 HeadlessBlock
= LoaderBlock
->Extension
->HeadlessLoaderBlock
;
200 if (!HeadlessBlock
) return;
202 /* Ignore invalid EMS settings */
203 if ((HeadlessBlock
->PortNumber
> 4) && (HeadlessBlock
->UsedBiosSettings
)) return;
205 /* Allocate the global headless data */
206 HeadlessGlobals
= ExAllocatePoolWithTag(NonPagedPool
,
207 sizeof(*HeadlessGlobals
),
209 if (!HeadlessGlobals
) return;
211 /* Zero and copy loader data */
212 RtlZeroMemory(HeadlessGlobals
, sizeof(*HeadlessGlobals
));
213 HeadlessGlobals
->TerminalPortNumber
= HeadlessBlock
->PortNumber
;
214 HeadlessGlobals
->TerminalPortAddress
= HeadlessBlock
->PortAddress
;
215 HeadlessGlobals
->TerminalBaudRate
= HeadlessBlock
->BaudRate
;
216 HeadlessGlobals
->TerminalParity
= HeadlessBlock
->Parity
;
217 HeadlessGlobals
->TerminalStopBits
= HeadlessBlock
->StopBits
;
218 HeadlessGlobals
->UsedBiosSettings
= HeadlessBlock
->UsedBiosSettings
;
219 HeadlessGlobals
->IsMMIODevice
= HeadlessBlock
->IsMMIODevice
;
220 HeadlessGlobals
->TerminalType
= HeadlessBlock
->TerminalType
;
221 HeadlessGlobals
->SystemGUID
= HeadlessBlock
->SystemGUID
;
222 DPRINT1("EMS on Port %lu (0x%p) at %lu bps\n",
223 HeadlessGlobals
->TerminalPortNumber
,
224 HeadlessGlobals
->TerminalPortAddress
,
225 HeadlessGlobals
->TerminalBaudRate
);
227 /* These two are opposites of each other */
228 if (HeadlessGlobals
->IsMMIODevice
) HeadlessGlobals
->IsNonLegacyDevice
= TRUE
;
230 /* Check for a PCI device, warn that this isn't supported */
231 if (HeadlessBlock
->PciDeviceId
!= PCI_INVALID_VENDORID
)
233 DPRINT1("PCI Serial Ports not supported\n");
236 /* Log entries are not yet supported */
237 DPRINT1("FIXME: No Headless logging support\n");
239 /* Allocate temporary buffer */
240 HeadlessGlobals
->TmpBuffer
= ExAllocatePoolWithTag(NonPagedPool
, 80, 'sldH');
241 if (!HeadlessGlobals
->TmpBuffer
) return;
243 /* Windows seems to apply some special hacks for 9600 bps */
244 if (HeadlessGlobals
->TerminalBaudRate
== 9600)
246 DPRINT1("Please use other baud rate than 9600bps for now\n");
249 /* Enable the terminal */
250 HdlspEnableTerminal(TRUE
);
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 HdlspReleaseGlobalLock(OldIrql
);
281 return STATUS_UNSUCCESSFUL
;
284 /* Don't allow these commands next time */
285 HeadlessGlobals
->ProcessingCmd
= TRUE
;
286 HdlspReleaseGlobalLock(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
,