[NTOS:HEADLESS]
[reactos.git] / reactos / ntoskrnl / ex / hdlsterm.c
1 /*
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
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <ntoskrnl.h>
12 #include <debug.h>
13
14 /* GLOBALS ********************************************************************/
15
16 PHEADLESS_GLOBALS HeadlessGlobals;
17
18 /* FUNCTIONS ******************************************************************/
19
20 FORCEINLINE
21 KIRQL
22 HdlspAcquireGlobalLock(VOID)
23 {
24 KIRQL OldIrql;
25
26 /* Don't acquire the lock if we are bugchecking */
27 if (!HeadlessGlobals->InBugCheck)
28 {
29 KeAcquireSpinLock(&HeadlessGlobals->SpinLock, &OldIrql);
30 }
31 else
32 {
33 OldIrql = 0xFF;
34 }
35
36 return OldIrql;
37 }
38
39 FORCEINLINE
40 VOID
41 HdlspReleaseGlobalLock(IN KIRQL OldIrql)
42 {
43 /* Only release the lock if we aren't bugchecking */
44 if (OldIrql != 0xFF)
45 {
46 KeReleaseSpinLock(&HeadlessGlobals->SpinLock, OldIrql);
47 }
48 else
49 {
50 ASSERT(HeadlessGlobals->InBugCheck == TRUE);
51 }
52 }
53
54 VOID
55 NTAPI
56 HdlspSendStringAtBaud(IN PUCHAR String)
57 {
58 /* Send every byte */
59 while (*String != ANSI_NULL)
60 {
61 InbvPortPutByte(HeadlessGlobals->TerminalPort, *String++);
62 }
63 }
64
65 VOID
66 NTAPI
67 HdlspPutData(IN PUCHAR Data,
68 IN ULONG DataSize)
69 {
70 ULONG i;
71 for (i = 0; i < DataSize; i++)
72 {
73 InbvPortPutByte(HeadlessGlobals->TerminalPort, Data[i]);
74 }
75 }
76
77 VOID
78 NTAPI
79 HdlspPutString(IN PUCHAR String)
80 {
81 PUCHAR Dest = HeadlessGlobals->TmpBuffer;
82 UCHAR Char = 0;
83
84 /* Scan each character */
85 while (*String != ANSI_NULL)
86 {
87 /* Check for rotate, send existing buffer and restart from where we are */
88 if (Dest >= &HeadlessGlobals->TmpBuffer[79])
89 {
90 HeadlessGlobals->TmpBuffer[79] = ANSI_NULL;
91 HdlspSendStringAtBaud(HeadlessGlobals->TmpBuffer);
92 Dest = HeadlessGlobals->TmpBuffer;
93 }
94 else
95 {
96 /* Get the current character and check for special graphical chars */
97 Char = *String;
98 if (Char & 0x80)
99 {
100 switch (Char)
101 {
102 case 0xB0: case 0xB3: case 0xBA:
103 Char = '|';
104 break;
105 case 0xB1: case 0xDC: case 0xDD: case 0xDE: case 0xDF:
106 Char = '%';
107 break;
108 case 0xB2: case 0xDB:
109 Char = '#';
110 break;
111 case 0xA9: case 0xAA: case 0xBB: case 0xBC: case 0xBF:
112 case 0xC0: case 0xC8: case 0xC9: case 0xD9: case 0xDA:
113 Char = '+';
114 break;
115 case 0xC4:
116 Char = '-';
117 break;
118 case 0xCD:
119 Char = '=';
120 break;
121 }
122 }
123
124 /* Anything else must be Unicode */
125 if (Char & 0x80)
126 {
127 /* Can't do Unicode yet */
128 UNIMPLEMENTED;
129 }
130 else
131 {
132 /* Add the modified char to the temporary buffer */
133 *Dest++ = Char;
134 }
135
136 /* Check the next char */
137 String++;
138 }
139 }
140
141 /* Finish and send */
142 *Dest = ANSI_NULL;
143 HdlspSendStringAtBaud(HeadlessGlobals->TmpBuffer);
144 }
145
146 NTSTATUS
147 NTAPI
148 HdlspEnableTerminal(IN BOOLEAN Enable)
149 {
150 /* Enable if requested, as long as this isn't a PCI serial port crashing */
151 if ((Enable) &&
152 !(HeadlessGlobals->TerminalEnabled) &&
153 !((HeadlessGlobals->IsMMIODevice) && (HeadlessGlobals->InBugCheck)))
154 {
155 /* Initialize the COM port with cportlib */
156 HeadlessGlobals->TerminalEnabled = InbvPortInitialize(HeadlessGlobals->
157 TerminalBaudRate,
158 HeadlessGlobals->
159 TerminalPortNumber,
160 HeadlessGlobals->
161 TerminalPortAddress,
162 &HeadlessGlobals->
163 TerminalPort,
164 HeadlessGlobals->
165 IsMMIODevice);
166 if (!HeadlessGlobals->TerminalEnabled)
167 {
168 DPRINT1("Failed to initialize port through cportlib\n");
169 return STATUS_UNSUCCESSFUL;
170 }
171
172 /* Cleanup the screen and reset the cursor */
173 HdlspSendStringAtBaud((PUCHAR)"\x1B[2J");
174 HdlspSendStringAtBaud((PUCHAR)"\x1B[H");
175
176 /* Enable FIFO */
177 InbvPortEnableFifo(HeadlessGlobals->TerminalPort, TRUE);
178 }
179 else if (!Enable)
180 {
181 /* Specific case when headless is being disabled */
182 InbvPortTerminate(HeadlessGlobals->TerminalPort);
183 HeadlessGlobals->TerminalPort = 0;
184 HeadlessGlobals->TerminalEnabled = FALSE;
185 }
186
187 /* All done */
188 return STATUS_SUCCESS;
189 }
190
191 VOID
192 NTAPI
193 INIT_FUNCTION
194 HeadlessInit(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
195 {
196 PHEADLESS_LOADER_BLOCK HeadlessBlock;
197
198 /* Only initialize further if the loader found EMS enabled */
199 HeadlessBlock = LoaderBlock->Extension->HeadlessLoaderBlock;
200 if (!HeadlessBlock) return;
201
202 /* Ignore invalid EMS settings */
203 if ((HeadlessBlock->PortNumber > 4) && (HeadlessBlock->UsedBiosSettings)) return;
204
205 /* Allocate the global headless data */
206 HeadlessGlobals = ExAllocatePoolWithTag(NonPagedPool,
207 sizeof(*HeadlessGlobals),
208 'sldH');
209 if (!HeadlessGlobals) return;
210
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);
226
227 /* These two are opposites of each other */
228 if (HeadlessGlobals->IsMMIODevice) HeadlessGlobals->IsNonLegacyDevice = TRUE;
229
230 /* Check for a PCI device, warn that this isn't supported */
231 if (HeadlessBlock->PciDeviceId != PCI_INVALID_VENDORID)
232 {
233 DPRINT1("PCI Serial Ports not supported\n");
234 }
235
236 /* Log entries are not yet supported */
237 DPRINT1("FIXME: No Headless logging support\n");
238
239 /* Allocate temporary buffer */
240 HeadlessGlobals->TmpBuffer = ExAllocatePoolWithTag(NonPagedPool, 80, 'sldH');
241 if (!HeadlessGlobals->TmpBuffer) return;
242
243 /* Windows seems to apply some special hacks for 9600 bps */
244 if (HeadlessGlobals->TerminalBaudRate == 9600)
245 {
246 DPRINT1("Please use other baud rate than 9600bps for now\n");
247 }
248
249 /* Enable the terminal */
250 HdlspEnableTerminal(TRUE);
251 }
252
253 NTSTATUS
254 NTAPI
255 HdlspDispatch(IN HEADLESS_CMD Command,
256 IN PVOID InputBuffer,
257 IN SIZE_T InputBufferSize,
258 OUT PVOID OutputBuffer,
259 OUT PSIZE_T OutputBufferSize)
260 {
261 KIRQL OldIrql;
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);
269
270 /* Ignore non-reentrant commands */
271 if ((Command != HeadlessCmdAddLogEntry) &&
272 (Command != HeadlessCmdStartBugCheck) &&
273 (Command != HeadlessCmdSendBlueScreenData) &&
274 (Command != HeadlessCmdDoBugCheckProcessing))
275 {
276 OldIrql = HdlspAcquireGlobalLock();
277
278 if (HeadlessGlobals->ProcessingCmd)
279 {
280 HdlspReleaseGlobalLock(OldIrql);
281 return STATUS_UNSUCCESSFUL;
282 }
283
284 /* Don't allow these commands next time */
285 HeadlessGlobals->ProcessingCmd = TRUE;
286 HdlspReleaseGlobalLock(OldIrql);
287 }
288
289 /* Handle each command */
290 switch (Command)
291 {
292 case HeadlessCmdEnableTerminal:
293 {
294 /* Make sure the caller passed valid data */
295 if (!(InputBuffer) ||
296 (InputBufferSize != sizeof(*EnableTerminal)))
297 {
298 DPRINT1("Invalid buffer\n");
299 Status = STATUS_INVALID_PARAMETER;
300 break;
301 }
302
303 /* Go and enable it */
304 EnableTerminal = InputBuffer;
305 Status = HdlspEnableTerminal(EnableTerminal->Enable);
306 break;
307 }
308
309 case HeadlessCmdCheckForReboot:
310 break;
311
312 case HeadlessCmdPutString:
313 {
314 /* Validate the existence of an input buffer */
315 if (!InputBuffer)
316 {
317 Status = STATUS_INVALID_PARAMETER;
318 break;
319 }
320
321 /* Terminal should be on */
322 if (HeadlessGlobals->TerminalEnabled)
323 {
324 /* Print each byte in the string making sure VT100 chars are used */
325 PutString = InputBuffer;
326 HdlspPutString(PutString->String);
327 }
328
329 /* Return success either way */
330 Status = STATUS_SUCCESS;
331 break;
332 }
333
334 case HeadlessCmdClearDisplay:
335 {
336 /* Send the VT100 clear screen command if the terminal is enabled */
337 if (HeadlessGlobals->TerminalEnabled)
338 {
339 HdlspSendStringAtBaud((PUCHAR)"\033[2J");
340 }
341
342 /* Return success either way */
343 Status = STATUS_SUCCESS;
344 break;
345 }
346
347 case HeadlessCmdClearToEndOfDisplay:
348 break;
349 case HeadlessCmdClearToEndOfLine:
350 break;
351 case HeadlessCmdDisplayAttributesOff:
352 break;
353 case HeadlessCmdDisplayInverseVideo:
354 break;
355 case HeadlessCmdSetColor:
356 break;
357 case HeadlessCmdPositionCursor:
358 break;
359 case HeadlessCmdTerminalPoll:
360 break;
361
362 case HeadlessCmdGetByte:
363 {
364 /* Make sure the caller passed valid data */
365 if (!(OutputBuffer) ||
366 !(OutputBufferSize) ||
367 (*OutputBufferSize < sizeof(*GetByte)))
368 {
369 DPRINT1("Invalid buffer\n");
370 Status = STATUS_INVALID_PARAMETER;
371 break;
372 }
373
374 /* Make sure the terminal is enabled */
375 GetByte = OutputBuffer;
376 if (HeadlessGlobals->TerminalEnabled)
377 {
378 /* Poll if something is on the wire */
379 if (InbvPortPollOnly(HeadlessGlobals->TerminalPort))
380 {
381 /* If so, read it */
382 InbvPortGetByte(HeadlessGlobals->TerminalPort,
383 &GetByte->Value);
384 }
385 else
386 {
387 /* Nothing is there, return 0 */
388 GetByte->Value = 0;
389 }
390 }
391 else
392 {
393 /* Otherwise return nothing */
394 GetByte->Value = 0;
395 }
396
397 /* Return success either way */
398 Status = STATUS_SUCCESS;
399 break;
400 }
401
402 case HeadlessCmdGetLine:
403 break;
404 case HeadlessCmdStartBugCheck:
405 break;
406 case HeadlessCmdDoBugCheckProcessing:
407 break;
408
409 case HeadlessCmdQueryInformation:
410 {
411 /* Make sure the caller passed valid data */
412 if (!(OutputBuffer) ||
413 !(OutputBufferSize) ||
414 (*OutputBufferSize < sizeof(*HeadlessInfo)))
415 {
416 DPRINT1("Invalid buffer\n");
417 Status = STATUS_INVALID_PARAMETER;
418 break;
419 }
420
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;
428
429 /* Now check on what port/baud it's enabled on */
430 if ((HeadlessGlobals->TerminalPortNumber >= 1) ||
431 (HeadlessGlobals->UsedBiosSettings))
432 {
433 /* Get the EMS information */
434 HeadlessInfo->Serial.TerminalPort = HeadlessGlobals->
435 TerminalPortNumber;
436 HeadlessInfo->Serial.TerminalPortBaseAddress = HeadlessGlobals->
437 TerminalPortAddress;
438 }
439 else
440 {
441 /* We don't know for sure */
442 HeadlessInfo->Serial.TerminalPort = SerialPortUndefined;
443 HeadlessInfo->Serial.TerminalPortBaseAddress = 0;
444 }
445
446 /* All done */
447 Status = STATUS_SUCCESS;
448 break;
449 }
450
451 case HeadlessCmdAddLogEntry:
452 break;
453 case HeadlessCmdDisplayLog:
454 break;
455
456 case HeadlessCmdSetBlueScreenData:
457 {
458 /* Validate the existence of an input buffer */
459 if (!InputBuffer)
460 {
461 Status = STATUS_INVALID_PARAMETER;
462 break;
463 }
464
465 /* Lie so that we can get Hdl bringup a little bit further */
466 UNIMPLEMENTED;
467 Status = STATUS_SUCCESS;
468 break;
469 }
470
471 case HeadlessCmdSendBlueScreenData:
472 break;
473 case HeadlessCmdQueryGUID:
474 break;
475
476 case HeadlessCmdPutData:
477 {
478 /* Validate the existence of an input buffer */
479 if (!(InputBuffer) || !(InputBufferSize))
480 {
481 Status = STATUS_INVALID_PARAMETER;
482 break;
483 }
484
485 /* Terminal should be on */
486 if (HeadlessGlobals->TerminalEnabled)
487 {
488 /* Print each byte in the string making sure VT100 chars are used */
489 PutString = InputBuffer;
490 HdlspPutData(PutString->String, InputBufferSize);
491 }
492
493 /* Return success either way */
494 Status = STATUS_SUCCESS;
495 break;
496 }
497
498 default:
499 break;
500 }
501
502 /* Unset processing state */
503 if ((Command != HeadlessCmdAddLogEntry) &&
504 (Command != HeadlessCmdStartBugCheck) &&
505 (Command != HeadlessCmdSendBlueScreenData) &&
506 (Command != HeadlessCmdDoBugCheckProcessing))
507 {
508 ASSERT(HeadlessGlobals->ProcessingCmd == TRUE);
509 HeadlessGlobals->ProcessingCmd = FALSE;
510 }
511
512 /* All done */
513 return Status;
514 }
515
516 /*
517 * @implemented
518 */
519 NTSTATUS
520 NTAPI
521 HeadlessDispatch(IN HEADLESS_CMD Command,
522 IN PVOID InputBuffer,
523 IN SIZE_T InputBufferSize,
524 OUT PVOID OutputBuffer,
525 OUT PSIZE_T OutputBufferSize)
526 {
527 /* Check for stubs that will expect something even with headless off */
528 if (!HeadlessGlobals)
529 {
530 /* Don't allow the SAC to connect */
531 if (Command == HeadlessCmdEnableTerminal) return STATUS_UNSUCCESSFUL;
532
533 /* Send bogus reply */
534 if ((Command == HeadlessCmdQueryInformation) ||
535 (Command == HeadlessCmdGetByte) ||
536 (Command == HeadlessCmdGetLine) ||
537 (Command == HeadlessCmdCheckForReboot) ||
538 (Command == HeadlessCmdTerminalPoll))
539 {
540 if (!(OutputBuffer) || !(OutputBufferSize))
541 {
542 return STATUS_INVALID_PARAMETER;
543 }
544
545 RtlZeroMemory(OutputBuffer, *OutputBufferSize);
546 }
547
548 return STATUS_SUCCESS;
549 }
550
551 /* Do the real work */
552 return HdlspDispatch(Command,
553 InputBuffer,
554 InputBufferSize,
555 OutputBuffer,
556 OutputBufferSize);
557 }
558
559 /* EOF */