e47764c852bfb262e1ed51c9e34a45e915a7e0e0
[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 HdlspReleaselobalLock(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 NTSTATUS
66 NTAPI
67 HdlspEnableTerminal(IN BOOLEAN Enable)
68 {
69 /* Enable if requested, as long as this isn't a PCI serial port crashing */
70 if ((Enable) &&
71 !(HeadlessGlobals->TerminalEnabled) &&
72 !((HeadlessGlobals->IsMMIODevice) && (HeadlessGlobals->InBugCheck)))
73 {
74 /* Initialize the COM port with cportlib */
75 HeadlessGlobals->TerminalEnabled = InbvPortInitialize(HeadlessGlobals->
76 TerminalBaudRate,
77 HeadlessGlobals->
78 TerminalPortNumber,
79 HeadlessGlobals->
80 TerminalPortAddress,
81 &HeadlessGlobals->
82 TerminalPort,
83 HeadlessGlobals->
84 IsMMIODevice);
85 if (!HeadlessGlobals->TerminalEnabled)
86 {
87 DPRINT1("Failed to initialize port through cportlib\n");
88 return STATUS_UNSUCCESSFUL;
89 }
90
91 /* Cleanup the screen and reset the cursor */
92 HdlspSendStringAtBaud((PUCHAR)"\x1B[2J");
93 HdlspSendStringAtBaud((PUCHAR)"\x1B[H");
94
95 /* Enable FIFO */
96 InbvPortEnableFifo(HeadlessGlobals->TerminalPort, TRUE);
97 }
98 else if (!Enable)
99 {
100 /* Specific case when headless is being disabled */
101 InbvPortTerminate(HeadlessGlobals->TerminalPort);
102 HeadlessGlobals->TerminalPort = 0;
103 HeadlessGlobals->TerminalEnabled = FALSE;
104 }
105
106 /* All done */
107 return STATUS_SUCCESS;
108 }
109
110 VOID
111 NTAPI
112 INIT_FUNCTION
113 HeadlessInit(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
114 {
115 PHEADLESS_LOADER_BLOCK HeadlessBlock;
116
117 /* Only initialize further if the loader found EMS enabled */
118 HeadlessBlock = LoaderBlock->Extension->HeadlessLoaderBlock;
119 if (!HeadlessBlock) return;
120
121 /* Ignore invalid EMS settings */
122 if ((HeadlessBlock->PortNumber > 4) && (HeadlessBlock->UsedBiosSettings)) return;
123
124 /* Allocate the global headless data */
125 HeadlessGlobals = ExAllocatePoolWithTag(NonPagedPool,
126 sizeof(*HeadlessGlobals),
127 'sldH');
128 if (!HeadlessGlobals) return;
129
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);
145
146 /* These two are opposites of each other */
147 if (HeadlessGlobals->IsMMIODevice) HeadlessGlobals->IsNonLegacyDevice = TRUE;
148
149 /* Check for a PCI device, warn that this isn't supported */
150 if (HeadlessBlock->PciDeviceId != PCI_INVALID_VENDORID)
151 {
152 DPRINT1("PCI Serial Ports not supported\n");
153 }
154
155 /* Log entries are not yet supported */
156 DPRINT1("FIXME: No Headless logging support\n");
157
158 /* Allocate temporary buffer */
159 HeadlessGlobals->TmpBuffer = ExAllocatePoolWithTag(NonPagedPool, 80, 'sldH');
160 if (!HeadlessGlobals->TmpBuffer) return;
161
162 /* Windows seems to apply some special hacks for 9600 bps */
163 if (HeadlessGlobals->TerminalBaudRate == 9600)
164 {
165 DPRINT1("Please use other baud rate than 9600bps for now\n");
166 }
167
168 /* Enable the terminal */
169 HdlspEnableTerminal(TRUE);
170 }
171
172 VOID
173 NTAPI
174 HdlspPutData(IN PUCHAR Data,
175 IN ULONG DataSize)
176 {
177 ULONG i;
178 for (i = 0; i < DataSize; i++)
179 {
180 InbvPortPutByte(HeadlessGlobals->TerminalPort, Data[i]++);
181 }
182 }
183
184 VOID
185 NTAPI
186 HdlspPutString(IN PUCHAR String)
187 {
188 PUCHAR Dest = HeadlessGlobals->TmpBuffer;
189 UCHAR Char = 0;
190
191 /* Scan each character */
192 while (*String != ANSI_NULL)
193 {
194 /* Check for rotate, send existing buffer and restart from where we are */
195 if (Dest >= &HeadlessGlobals->TmpBuffer[79])
196 {
197 HeadlessGlobals->TmpBuffer[79] = ANSI_NULL;
198 HdlspSendStringAtBaud(HeadlessGlobals->TmpBuffer);
199 Dest = HeadlessGlobals->TmpBuffer;
200 }
201 else
202 {
203 /* Get the current character and check for special graphical chars */
204 Char = *String;
205 if (Char & 0x80)
206 {
207 switch (Char)
208 {
209 case 0xB0: case 0xB3: case 0xBA:
210 Char = '|';
211 break;
212 case 0xB1: case 0xDC: case 0xDD: case 0xDE: case 0xDF:
213 Char = '%';
214 break;
215 case 0xB2: case 0xDB:
216 Char = '#';
217 break;
218 case 0xA9: case 0xAA: case 0xBB: case 0xBC: case 0xBF:
219 case 0xC0: case 0xC8: case 0xC9: case 0xD9: case 0xDA:
220 Char = '+';
221 break;
222 case 0xC4:
223 Char = '-';
224 break;
225 case 0xCD:
226 Char = '=';
227 break;
228 }
229 }
230
231 /* Anything else must be Unicode */
232 if (Char & 0x80)
233 {
234 /* Can't do Unicode yet */
235 UNIMPLEMENTED;
236 }
237 else
238 {
239 /* Add the modified char to the temporary buffer */
240 *Dest++ = Char;
241 }
242
243 /* Check the next char */
244 String++;
245 }
246 }
247
248 /* Finish and send */
249 *Dest = ANSI_NULL;
250 HdlspSendStringAtBaud(HeadlessGlobals->TmpBuffer);
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 HdlspReleaselobalLock(OldIrql);
281 return STATUS_UNSUCCESSFUL;
282 }
283
284 /* Don't allow these commands next time */
285 HeadlessGlobals->ProcessingCmd = TRUE;
286 HdlspReleaselobalLock(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 case HeadlessCmdCheckForReboot:
309 break;
310
311 case HeadlessCmdPutString:
312
313 /* Validate the existence of an input buffer */
314 if (!InputBuffer)
315 {
316 Status = STATUS_INVALID_PARAMETER;
317 break;
318 }
319
320 /* Terminal should be on */
321 if (HeadlessGlobals->TerminalEnabled)
322 {
323 /* Print each byte in the string making sure VT100 chars are used */
324 PutString = InputBuffer;
325 HdlspPutString(PutString->String);
326 }
327
328 /* Return success either way */
329 Status = STATUS_SUCCESS;
330 break;
331
332 case HeadlessCmdClearDisplay:
333
334 /* Send the VT100 clear screen command if the terminal is enabled */
335 if (HeadlessGlobals->TerminalEnabled)
336 {
337 HdlspSendStringAtBaud((PUCHAR)"\033[2J");
338 }
339
340 /* Return success either way */
341 Status = STATUS_SUCCESS;
342 break;
343
344 case HeadlessCmdClearToEndOfDisplay:
345 break;
346 case HeadlessCmdClearToEndOfLine:
347 break;
348 case HeadlessCmdDisplayAttributesOff:
349 break;
350 case HeadlessCmdDisplayInverseVideo:
351 break;
352 case HeadlessCmdSetColor:
353 break;
354 case HeadlessCmdPositionCursor:
355 break;
356 case HeadlessCmdTerminalPoll:
357 break;
358
359 case HeadlessCmdGetByte:
360
361 /* Make sure the caller passed valid data */
362 if (!(OutputBuffer) ||
363 !(OutputBufferSize) ||
364 (*OutputBufferSize < sizeof(*GetByte)))
365 {
366 DPRINT1("Invalid buffer\n");
367 Status = STATUS_INVALID_PARAMETER;
368 break;
369 }
370
371 /* Make sure the terminal is enabled */
372 GetByte = OutputBuffer;
373 if (HeadlessGlobals->TerminalEnabled)
374 {
375 /* Poll if something is on the wire */
376 if (InbvPortPollOnly(HeadlessGlobals->TerminalPort))
377 {
378 /* If so, read it */
379 InbvPortGetByte(HeadlessGlobals->TerminalPort,
380 &GetByte->Value);
381 }
382 else
383 {
384 /* Nothing is there, return 0 */
385 GetByte->Value = 0;
386 }
387 }
388 else
389 {
390 /* Otherwise return nothing */
391 GetByte->Value = 0;
392 }
393
394 /* Return success either way */
395 Status = STATUS_SUCCESS;
396 break;
397
398 case HeadlessCmdGetLine:
399 break;
400 case HeadlessCmdStartBugCheck:
401 break;
402 case HeadlessCmdDoBugCheckProcessing:
403 break;
404 case HeadlessCmdQueryInformation:
405
406 /* Make sure the caller passed valid data */
407 if (!(OutputBuffer) ||
408 !(OutputBufferSize) ||
409 (*OutputBufferSize < sizeof(*HeadlessInfo)))
410 {
411 DPRINT1("Invalid buffer\n");
412 Status = STATUS_INVALID_PARAMETER;
413 break;
414 }
415
416 /* If we got here, headless is enabled -- we know this much */
417 HeadlessInfo = OutputBuffer;
418 HeadlessInfo->PortType = HeadlessSerialPort;
419 HeadlessInfo->Serial.TerminalAttached = TRUE;
420 HeadlessInfo->Serial.UsedBiosSettings = HeadlessGlobals->UsedBiosSettings != 0;
421 HeadlessInfo->Serial.TerminalBaudRate = HeadlessGlobals->TerminalBaudRate;
422 HeadlessInfo->Serial.TerminalType = HeadlessGlobals->TerminalType;
423
424 /* Now check on what port/baud it's enabled on */
425 if ((HeadlessGlobals->TerminalPortNumber >= 1) ||
426 (HeadlessGlobals->UsedBiosSettings))
427 {
428 /* Get the EMS information */
429 HeadlessInfo->Serial.TerminalPort = HeadlessGlobals->
430 TerminalPortNumber;
431 HeadlessInfo->Serial.TerminalPortBaseAddress = HeadlessGlobals->
432 TerminalPortAddress;
433 }
434 else
435 {
436 /* We don't know for sure */
437 HeadlessInfo->Serial.TerminalPort = SerialPortUndefined;
438 HeadlessInfo->Serial.TerminalPortBaseAddress = 0;
439 }
440
441 /* All done */
442 Status = STATUS_SUCCESS;
443 break;
444 case HeadlessCmdAddLogEntry:
445 break;
446 case HeadlessCmdDisplayLog:
447 break;
448 case HeadlessCmdSetBlueScreenData:
449
450 /* Validate the existence of an input buffer */
451 if (!InputBuffer)
452 {
453 Status = STATUS_INVALID_PARAMETER;
454 break;
455 }
456
457 /* Lie so that we can get Hdl bringup a little bit further */
458 UNIMPLEMENTED;
459 Status = STATUS_SUCCESS;
460 break;
461 case HeadlessCmdSendBlueScreenData:
462 break;
463 case HeadlessCmdQueryGUID:
464 break;
465 case HeadlessCmdPutData:
466
467 /* Validate the existence of an input buffer */
468 if (!(InputBuffer) || !(InputBufferSize))
469 {
470 Status = STATUS_INVALID_PARAMETER;
471 break;
472 }
473
474 /* Terminal should be on */
475 if (HeadlessGlobals->TerminalEnabled)
476 {
477 /* Print each byte in the string making sure VT100 chars are used */
478 PutString = InputBuffer;
479 HdlspPutData(PutString->String, InputBufferSize);
480 }
481
482 /* Return success either way */
483 Status = STATUS_SUCCESS;
484 break;
485
486 default:
487 break;
488 }
489
490 /* Unset processing state */
491 if ((Command != HeadlessCmdAddLogEntry) &&
492 (Command != HeadlessCmdStartBugCheck) &&
493 (Command != HeadlessCmdSendBlueScreenData) &&
494 (Command != HeadlessCmdDoBugCheckProcessing))
495 {
496 ASSERT(HeadlessGlobals->ProcessingCmd == TRUE);
497 HeadlessGlobals->ProcessingCmd = FALSE;
498 }
499
500 /* All done */
501 return Status;
502 }
503
504 /*
505 * @implemented
506 */
507 NTSTATUS
508 NTAPI
509 HeadlessDispatch(IN HEADLESS_CMD Command,
510 IN PVOID InputBuffer,
511 IN SIZE_T InputBufferSize,
512 OUT PVOID OutputBuffer,
513 OUT PSIZE_T OutputBufferSize)
514 {
515 /* Check for stubs that will expect something even with headless off */
516 if (!HeadlessGlobals)
517 {
518 /* Don't allow the SAC to connect */
519 if (Command == HeadlessCmdEnableTerminal) return STATUS_UNSUCCESSFUL;
520
521 /* Send bogus reply */
522 if ((Command == HeadlessCmdQueryInformation) ||
523 (Command == HeadlessCmdGetByte) ||
524 (Command == HeadlessCmdGetLine) ||
525 (Command == HeadlessCmdCheckForReboot) ||
526 (Command == HeadlessCmdTerminalPoll))
527 {
528 if (!(OutputBuffer) || !(OutputBufferSize))
529 {
530 return STATUS_INVALID_PARAMETER;
531 }
532
533 RtlZeroMemory(OutputBuffer, *OutputBufferSize);
534 }
535 return STATUS_SUCCESS;
536 }
537
538 /* Do the real work */
539 return HdlspDispatch(Command,
540 InputBuffer,
541 InputBufferSize,
542 OutputBuffer,
543 OutputBufferSize);
544 }
545
546 /* EOF */