]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Console/TerminalDxe/Terminal.c
ac1e38838dcb868971309a3f6477c8f362470271
[mirror_edk2.git] / MdeModulePkg / Universal / Console / TerminalDxe / Terminal.c
1 /** @file
2 Produces Simple Text Input Protocol, Simple Text Input Extended Protocol and
3 Simple Text Output Protocol upon Serial IO Protocol.
4
5 Copyright (c) 2006 - 2008, Intel Corporation. <BR>
6 All rights reserved. This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16
17 #include "Terminal.h"
18
19 //
20 // Globals
21 //
22 EFI_DRIVER_BINDING_PROTOCOL gTerminalDriverBinding = {
23 TerminalDriverBindingSupported,
24 TerminalDriverBindingStart,
25 TerminalDriverBindingStop,
26 0xa,
27 NULL,
28 NULL
29 };
30
31
32 EFI_GUID *gTerminalType[] = {
33 &gEfiPcAnsiGuid,
34 &gEfiVT100Guid,
35 &gEfiVT100PlusGuid,
36 &gEfiVTUTF8Guid
37 };
38
39
40 TERMINAL_DEV mTerminalDevTemplate = {
41 TERMINAL_DEV_SIGNATURE,
42 NULL,
43 0,
44 NULL,
45 NULL,
46 { // SimpleTextInput
47 TerminalConInReset,
48 TerminalConInReadKeyStroke,
49 NULL
50 },
51 { // SimpleTextOutput
52 TerminalConOutReset,
53 TerminalConOutOutputString,
54 TerminalConOutTestString,
55 TerminalConOutQueryMode,
56 TerminalConOutSetMode,
57 TerminalConOutSetAttribute,
58 TerminalConOutClearScreen,
59 TerminalConOutSetCursorPosition,
60 TerminalConOutEnableCursor,
61 NULL
62 },
63 { // SimpleTextOutputMode
64 1, // MaxMode
65 0, // Mode
66 EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK), // Attribute
67 0, // CursorColumn
68 0, // CursorRow
69 TRUE // CursorVisible
70 },
71 0, // SerialInTimeOut
72 { // RawFiFo
73 0,
74 0,
75 { 0 }
76 },
77 { // UnicodeFiFo
78 0,
79 0,
80 { 0 }
81 },
82 { // EfiKeyFiFo
83 0,
84 0,
85 { {0} }
86 },
87 NULL, // ControllerNameTable
88 NULL, // TwoSecondTimeOut
89 INPUT_STATE_DEFAULT,
90 RESET_STATE_DEFAULT,
91 FALSE,
92 { // SimpleTextInputEx
93 TerminalConInResetEx,
94 TerminalConInReadKeyStrokeEx,
95 NULL,
96 TerminalConInSetState,
97 TerminalConInRegisterKeyNotify,
98 TerminalConInUnregisterKeyNotify,
99 },
100 { // NotifyList
101 NULL,
102 NULL,
103 }
104 };
105
106 /**
107 Test to see if this driver supports Controller.
108
109 @param This Protocol instance pointer.
110 @param Controller Handle of device to test
111 @param RemainingDevicePath Optional parameter use to pick a specific child
112 device to start.
113
114 @retval EFI_SUCCESS This driver supports this device.
115 @retval EFI_ALREADY_STARTED This driver is already running on this device.
116 @retval other This driver does not support this device.
117
118 **/
119 EFI_STATUS
120 EFIAPI
121 TerminalDriverBindingSupported (
122 IN EFI_DRIVER_BINDING_PROTOCOL *This,
123 IN EFI_HANDLE Controller,
124 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
125 )
126 {
127 EFI_STATUS Status;
128 EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
129 EFI_SERIAL_IO_PROTOCOL *SerialIo;
130 VENDOR_DEVICE_PATH *Node;
131
132 //
133 // If remaining device path is not NULL, then make sure it is a
134 // device path that describes a terminal communications protocol.
135 //
136 if (RemainingDevicePath != NULL) {
137
138 Node = (VENDOR_DEVICE_PATH *) RemainingDevicePath;
139
140 if (Node->Header.Type != MESSAGING_DEVICE_PATH ||
141 Node->Header.SubType != MSG_VENDOR_DP ||
142 DevicePathNodeLength(&Node->Header) != sizeof(VENDOR_DEVICE_PATH)) {
143
144 return EFI_UNSUPPORTED;
145
146 }
147 //
148 // only supports PC ANSI, VT100, VT100+ and VT-UTF8 terminal types
149 //
150 if (!CompareGuid (&Node->Guid, &gEfiPcAnsiGuid) &&
151 !CompareGuid (&Node->Guid, &gEfiVT100Guid) &&
152 !CompareGuid (&Node->Guid, &gEfiVT100PlusGuid) &&
153 !CompareGuid (&Node->Guid, &gEfiVTUTF8Guid)) {
154
155 return EFI_UNSUPPORTED;
156 }
157 }
158 //
159 // Open the IO Abstraction(s) needed to perform the supported test
160 //
161 Status = gBS->OpenProtocol (
162 Controller,
163 &gEfiDevicePathProtocolGuid,
164 (VOID **) &ParentDevicePath,
165 This->DriverBindingHandle,
166 Controller,
167 EFI_OPEN_PROTOCOL_BY_DRIVER
168 );
169 if (Status == EFI_ALREADY_STARTED) {
170 return EFI_SUCCESS;
171 }
172
173 if (EFI_ERROR (Status)) {
174 return Status;
175 }
176
177 gBS->CloseProtocol (
178 Controller,
179 &gEfiDevicePathProtocolGuid,
180 This->DriverBindingHandle,
181 Controller
182 );
183
184 //
185 // The Controller must support the Serial I/O Protocol.
186 // This driver is a bus driver with at most 1 child device, so it is
187 // ok for it to be already started.
188 //
189 Status = gBS->OpenProtocol (
190 Controller,
191 &gEfiSerialIoProtocolGuid,
192 (VOID **) &SerialIo,
193 This->DriverBindingHandle,
194 Controller,
195 EFI_OPEN_PROTOCOL_BY_DRIVER
196 );
197 if (Status == EFI_ALREADY_STARTED) {
198 return EFI_SUCCESS;
199 }
200
201 if (EFI_ERROR (Status)) {
202 return Status;
203 }
204 //
205 // Close the I/O Abstraction(s) used to perform the supported test
206 //
207 gBS->CloseProtocol (
208 Controller,
209 &gEfiSerialIoProtocolGuid,
210 This->DriverBindingHandle,
211 Controller
212 );
213
214 return Status;
215 }
216
217 /**
218 Start this driver on Controller by opening a Serial IO protocol,
219 reading Device Path, and creating a child handle with a Simple Text In,
220 Simple Text In Ex and Simple Text Out protocol, and device path protocol.
221 And store Console Device Environment Variables.
222
223 @param This Protocol instance pointer.
224 @param Controller Handle of device to bind driver to
225 @param RemainingDevicePath Optional parameter use to pick a specific child
226 device to start.
227
228 @retval EFI_SUCCESS This driver is added to Controller.
229 @retval EFI_ALREADY_STARTED This driver is already running on Controller.
230 @retval other This driver does not support this device.
231
232 **/
233 EFI_STATUS
234 EFIAPI
235 TerminalDriverBindingStart (
236 IN EFI_DRIVER_BINDING_PROTOCOL *This,
237 IN EFI_HANDLE Controller,
238 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
239 )
240 {
241 EFI_STATUS Status;
242 EFI_SERIAL_IO_PROTOCOL *SerialIo;
243 EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
244 VENDOR_DEVICE_PATH *Node;
245 VENDOR_DEVICE_PATH *DefaultNode;
246 EFI_SERIAL_IO_MODE *Mode;
247 UINTN SerialInTimeOut;
248 TERMINAL_DEV *TerminalDevice;
249 UINT8 TerminalType;
250 EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer;
251 UINTN EntryCount;
252 UINTN Index;
253 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
254 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL SimpleTextOutput;
255
256 TerminalDevice = NULL;
257 DefaultNode = NULL;
258 //
259 // Get the Device Path Protocol to build the device path of the child device
260 //
261 Status = gBS->OpenProtocol (
262 Controller,
263 &gEfiDevicePathProtocolGuid,
264 (VOID **) &ParentDevicePath,
265 This->DriverBindingHandle,
266 Controller,
267 EFI_OPEN_PROTOCOL_BY_DRIVER
268 );
269 if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
270 return Status;
271 }
272
273 //
274 // Open the Serial I/O Protocol BY_DRIVER. It might already be started.
275 //
276 Status = gBS->OpenProtocol (
277 Controller,
278 &gEfiSerialIoProtocolGuid,
279 (VOID **) &SerialIo,
280 This->DriverBindingHandle,
281 Controller,
282 EFI_OPEN_PROTOCOL_BY_DRIVER
283 );
284 if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
285 return Status;
286 }
287
288 if (Status != EFI_ALREADY_STARTED) {
289 //
290 // If Serial I/O is not already open by this driver, then tag the handle
291 // with the Terminal Driver GUID and update the ConInDev, ConOutDev, and
292 // StdErrDev variables with the list of possible terminal types on this
293 // serial port.
294 //
295 Status = gBS->OpenProtocol (
296 Controller,
297 &gEfiCallerIdGuid,
298 NULL,
299 This->DriverBindingHandle,
300 Controller,
301 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
302 );
303 if (EFI_ERROR (Status)) {
304 Status = gBS->InstallMultipleProtocolInterfaces (
305 &Controller,
306 &gEfiCallerIdGuid,
307 DuplicateDevicePath (ParentDevicePath),
308 NULL
309 );
310 if (EFI_ERROR (Status)) {
311 goto Error;
312 }
313 //
314 // if the serial device is a hot plug device, do not update the
315 // ConInDev, ConOutDev, and StdErrDev variables.
316 //
317 Status = gBS->OpenProtocol (
318 Controller,
319 &gEfiHotPlugDeviceGuid,
320 NULL,
321 This->DriverBindingHandle,
322 Controller,
323 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
324 );
325 if (EFI_ERROR (Status)) {
326 TerminalUpdateConsoleDevVariable (L"ConInDev", ParentDevicePath);
327 TerminalUpdateConsoleDevVariable (L"ConOutDev", ParentDevicePath);
328 TerminalUpdateConsoleDevVariable (L"ErrOutDev", ParentDevicePath);
329 }
330 }
331 }
332 //
333 // Make sure a child handle does not already exist. This driver can only
334 // produce one child per serial port.
335 //
336 Status = gBS->OpenProtocolInformation (
337 Controller,
338 &gEfiSerialIoProtocolGuid,
339 &OpenInfoBuffer,
340 &EntryCount
341 );
342 if (!EFI_ERROR (Status)) {
343 Status = EFI_SUCCESS;
344 for (Index = 0; Index < EntryCount; Index++) {
345 if (OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) {
346 Status = EFI_ALREADY_STARTED;
347 }
348 }
349
350 FreePool (OpenInfoBuffer);
351 if (EFI_ERROR (Status)) {
352 return Status;
353 }
354 }
355 //
356 // If RemainingDevicePath is NULL, then create default device path node
357 //
358 if (RemainingDevicePath == NULL) {
359 DefaultNode = AllocateZeroPool (sizeof (VENDOR_DEVICE_PATH));
360 if (DefaultNode == NULL) {
361 Status = EFI_OUT_OF_RESOURCES;
362 goto Error;
363 }
364
365 TerminalType = FixedPcdGet8 (PcdDefaultTerminalType);
366 //
367 // Must be between PCANSITYPE (0) and VTUTF8TYPE (3)
368 //
369 ASSERT (TerminalType <= VTUTF8TYPE);
370
371 CopyMem (&DefaultNode->Guid, gTerminalType[TerminalType], sizeof (EFI_GUID));
372 RemainingDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) DefaultNode;
373 } else {
374 //
375 // Use the RemainingDevicePath to determine the terminal type
376 //
377 Node = (VENDOR_DEVICE_PATH *)RemainingDevicePath;
378 if (CompareGuid (&Node->Guid, &gEfiPcAnsiGuid)) {
379 TerminalType = PCANSITYPE;
380 } else if (CompareGuid (&Node->Guid, &gEfiVT100Guid)) {
381 TerminalType = VT100TYPE;
382 } else if (CompareGuid (&Node->Guid, &gEfiVT100PlusGuid)) {
383 TerminalType = VT100PLUSTYPE;
384 } else if (CompareGuid (&Node->Guid, &gEfiVTUTF8Guid)) {
385 TerminalType = VTUTF8TYPE;
386 } else {
387 goto Error;
388 }
389 }
390
391 //
392 // Initialize the Terminal Dev
393 //
394 TerminalDevice = AllocateCopyPool (sizeof (TERMINAL_DEV), &mTerminalDevTemplate);
395 if (TerminalDevice == NULL) {
396 Status = EFI_OUT_OF_RESOURCES;
397 goto Error;
398 }
399
400 TerminalDevice->TerminalType = TerminalType;
401 TerminalDevice->SerialIo = SerialIo;
402
403 InitializeListHead (&TerminalDevice->NotifyList);
404 Status = gBS->CreateEvent (
405 EVT_NOTIFY_WAIT,
406 TPL_NOTIFY,
407 TerminalConInWaitForKeyEx,
408 &TerminalDevice->SimpleInputEx,
409 &TerminalDevice->SimpleInputEx.WaitForKeyEx
410 );
411 if (EFI_ERROR (Status)) {
412 goto Error;
413 }
414
415
416 Status = gBS->CreateEvent (
417 EVT_NOTIFY_WAIT,
418 TPL_NOTIFY,
419 TerminalConInWaitForKey,
420 &TerminalDevice->SimpleInput,
421 &TerminalDevice->SimpleInput.WaitForKey
422 );
423 if (EFI_ERROR (Status)) {
424 goto Error;
425 }
426 //
427 // initialize the FIFO buffer used for accommodating
428 // the pre-read pending characters
429 //
430 InitializeRawFiFo (TerminalDevice);
431 InitializeUnicodeFiFo (TerminalDevice);
432 InitializeEfiKeyFiFo (TerminalDevice);
433
434 //
435 // Set the timeout value of serial buffer for
436 // keystroke response performance issue
437 //
438 Mode = TerminalDevice->SerialIo->Mode;
439
440 SerialInTimeOut = 0;
441 if (Mode->BaudRate != 0) {
442 SerialInTimeOut = (1 + Mode->DataBits + Mode->StopBits) * 2 * 1000000 / (UINTN) Mode->BaudRate;
443 }
444
445 Status = TerminalDevice->SerialIo->SetAttributes (
446 TerminalDevice->SerialIo,
447 Mode->BaudRate,
448 Mode->ReceiveFifoDepth,
449 (UINT32) SerialInTimeOut,
450 (EFI_PARITY_TYPE) (Mode->Parity),
451 (UINT8) Mode->DataBits,
452 (EFI_STOP_BITS_TYPE) (Mode->StopBits)
453 );
454 if (EFI_ERROR (Status)) {
455 //
456 // if set attributes operation fails, invalidate
457 // the value of SerialInTimeOut,thus make it
458 // inconsistent with the default timeout value
459 // of serial buffer. This will invoke the recalculation
460 // in the readkeystroke routine.
461 //
462 TerminalDevice->SerialInTimeOut = 0;
463 } else {
464 TerminalDevice->SerialInTimeOut = SerialInTimeOut;
465 }
466 //
467 // Build the device path for the child device
468 //
469 Status = SetTerminalDevicePath (
470 TerminalDevice->TerminalType,
471 ParentDevicePath,
472 &TerminalDevice->DevicePath
473 );
474 if (EFI_ERROR (Status)) {
475 goto Error;
476 }
477
478 DevicePath = TerminalDevice->DevicePath;
479
480 Status = TerminalDevice->SimpleInput.Reset (
481 &TerminalDevice->SimpleInput,
482 FALSE
483 );
484 if (EFI_ERROR (Status)) {
485 //
486 // Need to report Error Code first
487 //
488 goto ReportError;
489 }
490 //
491 // Simple Text Output Protocol
492 //
493 SimpleTextOutput = TerminalDevice->SimpleTextOutput;
494
495 SimpleTextOutput.Reset = TerminalConOutReset;
496 SimpleTextOutput.OutputString = TerminalConOutOutputString;
497 SimpleTextOutput.TestString = TerminalConOutTestString;
498 SimpleTextOutput.QueryMode = TerminalConOutQueryMode;
499 SimpleTextOutput.SetMode = TerminalConOutSetMode;
500 SimpleTextOutput.SetAttribute = TerminalConOutSetAttribute;
501 SimpleTextOutput.ClearScreen = TerminalConOutClearScreen;
502 SimpleTextOutput.SetCursorPosition = TerminalConOutSetCursorPosition;
503 SimpleTextOutput.EnableCursor = TerminalConOutEnableCursor;
504 SimpleTextOutput.Mode = &TerminalDevice->SimpleTextOutputMode;
505
506 TerminalDevice->SimpleTextOutputMode.MaxMode = 3;
507 //
508 // For terminal devices, cursor is always visible
509 //
510 TerminalDevice->SimpleTextOutputMode.CursorVisible = TRUE;
511 Status = SimpleTextOutput.SetAttribute (
512 &TerminalDevice->SimpleTextOutput,
513 EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)
514 );
515 if (EFI_ERROR (Status)) {
516 goto ReportError;
517 }
518
519 Status = SimpleTextOutput.Reset (
520 &TerminalDevice->SimpleTextOutput,
521 FALSE
522 );
523 if (EFI_ERROR (Status)) {
524 goto ReportError;
525 }
526
527 Status = SimpleTextOutput.SetMode (
528 &TerminalDevice->SimpleTextOutput,
529 0
530 );
531 if (EFI_ERROR (Status)) {
532 goto ReportError;
533 }
534
535 Status = SimpleTextOutput.EnableCursor (
536 &TerminalDevice->SimpleTextOutput,
537 TRUE
538 );
539 if (EFI_ERROR (Status)) {
540 goto ReportError;
541 }
542
543 Status = gBS->CreateEvent (
544 EVT_TIMER,
545 TPL_CALLBACK,
546 NULL,
547 NULL,
548 &TerminalDevice->TwoSecondTimeOut
549 );
550
551 //
552 // Build the component name for the child device
553 //
554 TerminalDevice->ControllerNameTable = NULL;
555 switch (TerminalDevice->TerminalType) {
556 case PCANSITYPE:
557 AddUnicodeString2 (
558 "eng",
559 gTerminalComponentName.SupportedLanguages,
560 &TerminalDevice->ControllerNameTable,
561 (CHAR16 *)L"PC-ANSI Serial Console",
562 TRUE
563 );
564 AddUnicodeString2 (
565 "en",
566 gTerminalComponentName2.SupportedLanguages,
567 &TerminalDevice->ControllerNameTable,
568 (CHAR16 *)L"PC-ANSI Serial Console",
569 FALSE
570 );
571
572 break;
573
574 case VT100TYPE:
575 AddUnicodeString2 (
576 "eng",
577 gTerminalComponentName.SupportedLanguages,
578 &TerminalDevice->ControllerNameTable,
579 (CHAR16 *)L"VT-100 Serial Console",
580 TRUE
581 );
582 AddUnicodeString2 (
583 "en",
584 gTerminalComponentName2.SupportedLanguages,
585 &TerminalDevice->ControllerNameTable,
586 (CHAR16 *)L"VT-100 Serial Console",
587 FALSE
588 );
589
590 break;
591
592 case VT100PLUSTYPE:
593 AddUnicodeString2 (
594 "eng",
595 gTerminalComponentName.SupportedLanguages,
596 &TerminalDevice->ControllerNameTable,
597 (CHAR16 *)L"VT-100+ Serial Console",
598 TRUE
599 );
600 AddUnicodeString2 (
601 "en",
602 gTerminalComponentName2.SupportedLanguages,
603 &TerminalDevice->ControllerNameTable,
604 (CHAR16 *)L"VT-100+ Serial Console",
605 FALSE
606 );
607
608 break;
609
610 case VTUTF8TYPE:
611 AddUnicodeString2 (
612 "eng",
613 gTerminalComponentName.SupportedLanguages,
614 &TerminalDevice->ControllerNameTable,
615 (CHAR16 *)L"VT-UTF8 Serial Console",
616 TRUE
617 );
618 AddUnicodeString2 (
619 "en",
620 gTerminalComponentName2.SupportedLanguages,
621 &TerminalDevice->ControllerNameTable,
622 (CHAR16 *)L"VT-UTF8 Serial Console",
623 FALSE
624 );
625
626 break;
627 }
628 //
629 // Install protocol interfaces for the serial device.
630 //
631 Status = gBS->InstallMultipleProtocolInterfaces (
632 &TerminalDevice->Handle,
633 &gEfiDevicePathProtocolGuid,
634 TerminalDevice->DevicePath,
635 &gEfiSimpleTextInProtocolGuid,
636 &TerminalDevice->SimpleInput,
637 &gEfiSimpleTextInputExProtocolGuid,
638 &TerminalDevice->SimpleInputEx,
639 &gEfiSimpleTextOutProtocolGuid,
640 &TerminalDevice->SimpleTextOutput,
641 NULL
642 );
643 if (EFI_ERROR (Status)) {
644 goto Error;
645 }
646 //
647 // if the serial device is a hot plug device, attaches the HotPlugGuid
648 // onto the terminal device handle.
649 //
650 Status = gBS->OpenProtocol (
651 Controller,
652 &gEfiHotPlugDeviceGuid,
653 NULL,
654 This->DriverBindingHandle,
655 Controller,
656 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
657 );
658 if (!EFI_ERROR (Status)) {
659 Status = gBS->InstallMultipleProtocolInterfaces (
660 &TerminalDevice->Handle,
661 &gEfiHotPlugDeviceGuid,
662 NULL,
663 NULL
664 );
665 }
666 //
667 // Register the Parent-Child relationship via
668 // EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
669 //
670 Status = gBS->OpenProtocol (
671 Controller,
672 &gEfiSerialIoProtocolGuid,
673 (VOID **) &TerminalDevice->SerialIo,
674 This->DriverBindingHandle,
675 TerminalDevice->Handle,
676 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
677 );
678 if (EFI_ERROR (Status)) {
679 goto Error;
680 }
681
682 if (DefaultNode != NULL) {
683 FreePool (DefaultNode);
684 }
685
686 return EFI_SUCCESS;
687
688 ReportError:
689 //
690 // Report error code before exiting
691 //
692 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
693 EFI_ERROR_CODE | EFI_ERROR_MINOR,
694 PcdGet32 (PcdStatusCodeValueRemoteConsoleError),
695 DevicePath
696 );
697
698 Error:
699 //
700 // Use the Stop() function to free all resources allocated in Start()
701 //
702 if (TerminalDevice != NULL) {
703
704 if (TerminalDevice->Handle != NULL) {
705 This->Stop (This, Controller, 1, &TerminalDevice->Handle);
706 } else {
707
708 if (TerminalDevice->TwoSecondTimeOut != NULL) {
709 gBS->CloseEvent (TerminalDevice->TwoSecondTimeOut);
710 }
711
712 if (TerminalDevice->SimpleInput.WaitForKey != NULL) {
713 gBS->CloseEvent (TerminalDevice->SimpleInput.WaitForKey);
714 }
715
716 if (TerminalDevice->SimpleInputEx.WaitForKeyEx != NULL) {
717 gBS->CloseEvent (TerminalDevice->SimpleInputEx.WaitForKeyEx);
718 }
719
720 TerminalFreeNotifyList (&TerminalDevice->NotifyList);
721
722 if (TerminalDevice->ControllerNameTable != NULL) {
723 FreeUnicodeStringTable (TerminalDevice->ControllerNameTable);
724 }
725
726 if (TerminalDevice->DevicePath != NULL) {
727 FreePool (TerminalDevice->DevicePath);
728 }
729
730 FreePool (TerminalDevice);
731 }
732 }
733
734 if (DefaultNode != NULL) {
735 FreePool (DefaultNode);
736 }
737
738 This->Stop (This, Controller, 0, NULL);
739
740 return Status;
741 }
742
743 /**
744 Stop this driver on Controller by closing Simple Text In, Simple Text
745 In Ex, Simple Text Out protocol, and removing parent device path from
746 Console Device Environment Variables.
747
748 @param This Protocol instance pointer.
749 @param Controller Handle of device to stop driver on
750 @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
751 children is zero stop the entire bus driver.
752 @param ChildHandleBuffer List of Child Handles to Stop.
753
754 @retval EFI_SUCCESS This driver is removed Controller.
755 @retval other This driver could not be removed from this device.
756
757 **/
758 EFI_STATUS
759 EFIAPI
760 TerminalDriverBindingStop (
761 IN EFI_DRIVER_BINDING_PROTOCOL *This,
762 IN EFI_HANDLE Controller,
763 IN UINTN NumberOfChildren,
764 IN EFI_HANDLE *ChildHandleBuffer
765 )
766 {
767 EFI_STATUS Status;
768 UINTN Index;
769 BOOLEAN AllChildrenStopped;
770 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOutput;
771 TERMINAL_DEV *TerminalDevice;
772 EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
773 EFI_SERIAL_IO_PROTOCOL *SerialIo;
774 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
775
776 Status = gBS->HandleProtocol (
777 Controller,
778 &gEfiDevicePathProtocolGuid,
779 (VOID **) &DevicePath
780 );
781 if (EFI_ERROR (Status)) {
782 return Status;
783 }
784
785 //
786 // Complete all outstanding transactions to Controller.
787 // Don't allow any new transaction to Controller to be started.
788 //
789 if (NumberOfChildren == 0) {
790 //
791 // Close the bus driver
792 //
793 Status = gBS->OpenProtocol (
794 Controller,
795 &gEfiCallerIdGuid,
796 (VOID **) &ParentDevicePath,
797 This->DriverBindingHandle,
798 Controller,
799 EFI_OPEN_PROTOCOL_GET_PROTOCOL
800 );
801 if (!EFI_ERROR (Status)) {
802 //
803 // Remove Parent Device Path from
804 // the Console Device Environment Variables
805 //
806 TerminalRemoveConsoleDevVariable (L"ConInDev", ParentDevicePath);
807 TerminalRemoveConsoleDevVariable (L"ConOutDev", ParentDevicePath);
808 TerminalRemoveConsoleDevVariable (L"ErrOutDev", ParentDevicePath);
809
810 //
811 // Uninstall the Terminal Driver's GUID Tag from the Serial controller
812 //
813 Status = gBS->UninstallMultipleProtocolInterfaces (
814 Controller,
815 &gEfiCallerIdGuid,
816 ParentDevicePath,
817 NULL
818 );
819
820 //
821 // Free the ParentDevicePath that was duplicated in Start()
822 //
823 if (!EFI_ERROR (Status)) {
824 FreePool (ParentDevicePath);
825 }
826 }
827
828 gBS->CloseProtocol (
829 Controller,
830 &gEfiSerialIoProtocolGuid,
831 This->DriverBindingHandle,
832 Controller
833 );
834
835 gBS->CloseProtocol (
836 Controller,
837 &gEfiDevicePathProtocolGuid,
838 This->DriverBindingHandle,
839 Controller
840 );
841
842 return EFI_SUCCESS;
843 }
844
845 AllChildrenStopped = TRUE;
846
847 for (Index = 0; Index < NumberOfChildren; Index++) {
848
849 Status = gBS->OpenProtocol (
850 ChildHandleBuffer[Index],
851 &gEfiSimpleTextOutProtocolGuid,
852 (VOID **) &SimpleTextOutput,
853 This->DriverBindingHandle,
854 ChildHandleBuffer[Index],
855 EFI_OPEN_PROTOCOL_GET_PROTOCOL
856 );
857 if (!EFI_ERROR (Status)) {
858
859 TerminalDevice = TERMINAL_CON_OUT_DEV_FROM_THIS (SimpleTextOutput);
860
861 gBS->CloseProtocol (
862 Controller,
863 &gEfiSerialIoProtocolGuid,
864 This->DriverBindingHandle,
865 ChildHandleBuffer[Index]
866 );
867
868 Status = gBS->UninstallMultipleProtocolInterfaces (
869 ChildHandleBuffer[Index],
870 &gEfiSimpleTextInProtocolGuid,
871 &TerminalDevice->SimpleInput,
872 &gEfiSimpleTextInputExProtocolGuid,
873 &TerminalDevice->SimpleInputEx,
874 &gEfiSimpleTextOutProtocolGuid,
875 &TerminalDevice->SimpleTextOutput,
876 &gEfiDevicePathProtocolGuid,
877 TerminalDevice->DevicePath,
878 NULL
879 );
880 if (EFI_ERROR (Status)) {
881 gBS->OpenProtocol (
882 Controller,
883 &gEfiSerialIoProtocolGuid,
884 (VOID **) &SerialIo,
885 This->DriverBindingHandle,
886 ChildHandleBuffer[Index],
887 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
888 );
889 } else {
890
891 if (TerminalDevice->ControllerNameTable != NULL) {
892 FreeUnicodeStringTable (TerminalDevice->ControllerNameTable);
893 }
894
895 Status = gBS->OpenProtocol (
896 ChildHandleBuffer[Index],
897 &gEfiHotPlugDeviceGuid,
898 NULL,
899 This->DriverBindingHandle,
900 Controller,
901 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
902 );
903 if (!EFI_ERROR (Status)) {
904 Status = gBS->UninstallMultipleProtocolInterfaces (
905 ChildHandleBuffer[Index],
906 &gEfiHotPlugDeviceGuid,
907 NULL,
908 NULL
909 );
910 } else {
911 Status = EFI_SUCCESS;
912 }
913
914 gBS->CloseEvent (TerminalDevice->TwoSecondTimeOut);
915 gBS->CloseEvent (TerminalDevice->SimpleInput.WaitForKey);
916 gBS->CloseEvent (TerminalDevice->SimpleInputEx.WaitForKeyEx);
917 TerminalFreeNotifyList (&TerminalDevice->NotifyList);
918 FreePool (TerminalDevice->DevicePath);
919 FreePool (TerminalDevice);
920 }
921 }
922
923 if (EFI_ERROR (Status)) {
924 AllChildrenStopped = FALSE;
925 }
926 }
927
928 if (!AllChildrenStopped) {
929 return EFI_DEVICE_ERROR;
930 }
931
932 return EFI_SUCCESS;
933 }
934
935
936 /**
937 Free notify functions list.
938
939 @param ListHead The list head
940
941 @retval EFI_SUCCESS Free the notify list successfully.
942 @retval EFI_INVALID_PARAMETER ListHead is NULL.
943
944 **/
945 EFI_STATUS
946 TerminalFreeNotifyList (
947 IN OUT LIST_ENTRY *ListHead
948 )
949 {
950 TERMINAL_CONSOLE_IN_EX_NOTIFY *NotifyNode;
951
952 if (ListHead == NULL) {
953 return EFI_INVALID_PARAMETER;
954 }
955 while (!IsListEmpty (ListHead)) {
956 NotifyNode = CR (
957 ListHead->ForwardLink,
958 TERMINAL_CONSOLE_IN_EX_NOTIFY,
959 NotifyEntry,
960 TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE
961 );
962 RemoveEntryList (ListHead->ForwardLink);
963 FreePool (NotifyNode);
964 }
965
966 return EFI_SUCCESS;
967 }
968
969
970 /**
971 Update terminal device path in Console Device Environment Variables.
972
973 @param VariableName The Console Device Environment Variable.
974 @param ParentDevicePath The terminal device path to be updated.
975
976 @return None.
977
978 **/
979 VOID
980 TerminalUpdateConsoleDevVariable (
981 IN CHAR16 *VariableName,
982 IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath
983 )
984 {
985 EFI_STATUS Status;
986 UINTN VariableSize;
987 UINT8 TerminalType;
988 EFI_DEVICE_PATH_PROTOCOL *Variable;
989 EFI_DEVICE_PATH_PROTOCOL *NewVariable;
990 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
991
992 Variable = NULL;
993
994 //
995 // Get global variable and its size according to the name given.
996 //
997 Variable = TerminalGetVariableAndSize (
998 VariableName,
999 &gEfiGlobalVariableGuid,
1000 &VariableSize
1001 );
1002 //
1003 // Append terminal device path onto the variable.
1004 //
1005 for (TerminalType = PCANSITYPE; TerminalType <= VTUTF8TYPE; TerminalType++) {
1006 SetTerminalDevicePath (TerminalType, ParentDevicePath, &TempDevicePath);
1007 NewVariable = AppendDevicePathInstance (Variable, TempDevicePath);
1008 if (Variable != NULL) {
1009 FreePool (Variable);
1010 }
1011
1012 if (TempDevicePath != NULL) {
1013 FreePool (TempDevicePath);
1014 }
1015
1016 Variable = NewVariable;
1017 }
1018
1019 VariableSize = GetDevicePathSize (Variable);
1020
1021 Status = gRT->SetVariable (
1022 VariableName,
1023 &gEfiGlobalVariableGuid,
1024 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
1025 VariableSize,
1026 Variable
1027 );
1028 ASSERT_EFI_ERROR (Status);
1029 FreePool (Variable);
1030
1031 return ;
1032 }
1033
1034
1035 /**
1036 Remove terminal device path from Console Device Environment Variables.
1037
1038 @param VariableName Console Device Environment Variables.
1039 @param ParentDevicePath The terminal device path to be updated.
1040
1041 @return None.
1042
1043 **/
1044 VOID
1045 TerminalRemoveConsoleDevVariable (
1046 IN CHAR16 *VariableName,
1047 IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath
1048 )
1049 {
1050 EFI_STATUS Status;
1051 BOOLEAN FoundOne;
1052 BOOLEAN Match;
1053 UINTN VariableSize;
1054 UINTN InstanceSize;
1055 UINT8 TerminalType;
1056 EFI_DEVICE_PATH_PROTOCOL *Instance;
1057 EFI_DEVICE_PATH_PROTOCOL *Variable;
1058 EFI_DEVICE_PATH_PROTOCOL *OriginalVariable;
1059 EFI_DEVICE_PATH_PROTOCOL *NewVariable;
1060 EFI_DEVICE_PATH_PROTOCOL *SavedNewVariable;
1061 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
1062
1063 Variable = NULL;
1064 Instance = NULL;
1065
1066 //
1067 // Get global variable and its size according to the name given.
1068 //
1069 Variable = TerminalGetVariableAndSize (
1070 VariableName,
1071 &gEfiGlobalVariableGuid,
1072 &VariableSize
1073 );
1074 if (Variable == NULL) {
1075 return ;
1076 }
1077
1078 FoundOne = FALSE;
1079 OriginalVariable = Variable;
1080 NewVariable = NULL;
1081
1082 //
1083 // Get first device path instance from Variable
1084 //
1085 Instance = GetNextDevicePathInstance (&Variable, &InstanceSize);
1086 if (Instance == NULL) {
1087 FreePool (OriginalVariable);
1088 return ;
1089 }
1090 //
1091 // Loop through all the device path instances of Variable
1092 //
1093 do {
1094 //
1095 // Loop through all the terminal types that this driver supports
1096 //
1097 Match = FALSE;
1098 for (TerminalType = PCANSITYPE; TerminalType <= VTUTF8TYPE; TerminalType++) {
1099
1100 SetTerminalDevicePath (TerminalType, ParentDevicePath, &TempDevicePath);
1101
1102 //
1103 // Compare the generated device path to the current device path instance
1104 //
1105 if (TempDevicePath != NULL) {
1106 if (CompareMem (Instance, TempDevicePath, InstanceSize) == 0) {
1107 Match = TRUE;
1108 FoundOne = TRUE;
1109 }
1110
1111 FreePool (TempDevicePath);
1112 }
1113 }
1114 //
1115 // If a match was not found, then keep the current device path instance
1116 //
1117 if (!Match) {
1118 SavedNewVariable = NewVariable;
1119 NewVariable = AppendDevicePathInstance (NewVariable, Instance);
1120 if (SavedNewVariable != NULL) {
1121 FreePool (SavedNewVariable);
1122 }
1123 }
1124 //
1125 // Get next device path instance from Variable
1126 //
1127 FreePool (Instance);
1128 Instance = GetNextDevicePathInstance (&Variable, &InstanceSize);
1129 } while (Instance != NULL);
1130
1131 FreePool (OriginalVariable);
1132
1133 if (FoundOne) {
1134 VariableSize = GetDevicePathSize (NewVariable);
1135
1136 Status = gRT->SetVariable (
1137 VariableName,
1138 &gEfiGlobalVariableGuid,
1139 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
1140 VariableSize,
1141 NewVariable
1142 );
1143 ASSERT_EFI_ERROR (Status);
1144 }
1145
1146 if (NewVariable != NULL) {
1147 FreePool (NewVariable);
1148 }
1149
1150 return ;
1151 }
1152
1153
1154 /**
1155 Read the EFI variable (VendorGuid/Name) and return a dynamically allocated
1156 buffer, and the size of the buffer. On failure return NULL.
1157
1158 @param Name String part of EFI variable name
1159 @param VendorGuid GUID part of EFI variable name
1160 @param VariableSize Returns the size of the EFI variable that was read
1161
1162 @return Dynamically allocated memory that contains a copy of the EFI variable.
1163 Caller is responsible freeing the buffer. If variable was not read,
1164 NULL returned.
1165
1166 **/
1167 VOID *
1168 TerminalGetVariableAndSize (
1169 IN CHAR16 *Name,
1170 IN EFI_GUID *VendorGuid,
1171 OUT UINTN *VariableSize
1172 )
1173 {
1174 EFI_STATUS Status;
1175 UINTN BufferSize;
1176 VOID *Buffer;
1177
1178 Buffer = NULL;
1179
1180 //
1181 // Pass in a small size buffer to find the actual variable size.
1182 //
1183 BufferSize = 1;
1184 Buffer = AllocatePool (BufferSize);
1185 if (Buffer == NULL) {
1186 *VariableSize = 0;
1187 return NULL;
1188 }
1189
1190 Status = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer);
1191
1192 if (Status == EFI_SUCCESS) {
1193 *VariableSize = BufferSize;
1194 return Buffer;
1195
1196 } else if (Status == EFI_BUFFER_TOO_SMALL) {
1197 //
1198 // Allocate the buffer to return
1199 //
1200 FreePool (Buffer);
1201 Buffer = AllocatePool (BufferSize);
1202 if (Buffer == NULL) {
1203 *VariableSize = 0;
1204 return NULL;
1205 }
1206 //
1207 // Read variable into the allocated buffer.
1208 //
1209 Status = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer);
1210 if (EFI_ERROR (Status)) {
1211 BufferSize = 0;
1212 FreePool (Buffer);
1213 Buffer = NULL;
1214 }
1215 } else {
1216 //
1217 // Variable not found or other errors met.
1218 //
1219 BufferSize = 0;
1220 FreePool (Buffer);
1221 Buffer = NULL;
1222 }
1223
1224 *VariableSize = BufferSize;
1225 return Buffer;
1226 }
1227
1228 /**
1229 Build terminal device path according to terminal type.
1230
1231 @param TerminalType The terminal type is PC ANSI, VT100, VT100+ or VT-UTF8.
1232 @param ParentDevicePath Parent device path.
1233 @param TerminalDevicePath Returned terminal device path, if building successfully.
1234
1235 @retval EFI_UNSUPPORTED Terminal does not belong to the supported type.
1236 @retval EFI_OUT_OF_RESOURCES Generate terminal device path failed.
1237 @retval EFI_SUCCESS Build terminal device path successfully.
1238
1239 **/
1240 EFI_STATUS
1241 SetTerminalDevicePath (
1242 IN UINT8 TerminalType,
1243 IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath,
1244 OUT EFI_DEVICE_PATH_PROTOCOL **TerminalDevicePath
1245 )
1246 {
1247 VENDOR_DEVICE_PATH Node;
1248
1249 *TerminalDevicePath = NULL;
1250 Node.Header.Type = MESSAGING_DEVICE_PATH;
1251 Node.Header.SubType = MSG_VENDOR_DP;
1252
1253 //
1254 // Generate terminal device path node according to terminal type.
1255 //
1256 switch (TerminalType) {
1257
1258 case PCANSITYPE:
1259 CopyGuid (&Node.Guid, &gEfiPcAnsiGuid);
1260 break;
1261
1262 case VT100TYPE:
1263 CopyGuid (&Node.Guid, &gEfiVT100Guid);
1264 break;
1265
1266 case VT100PLUSTYPE:
1267 CopyGuid (&Node.Guid, &gEfiVT100PlusGuid);
1268 break;
1269
1270 case VTUTF8TYPE:
1271 CopyGuid (&Node.Guid, &gEfiVTUTF8Guid);
1272 break;
1273
1274 default:
1275 return EFI_UNSUPPORTED;
1276 }
1277
1278 //
1279 // Get VENDOR_DEVCIE_PATH size and put into Node.Header
1280 //
1281 SetDevicePathNodeLength (
1282 &Node.Header,
1283 sizeof (VENDOR_DEVICE_PATH)
1284 );
1285
1286 //
1287 // Append the terminal node onto parent device path
1288 // to generate a complete terminal device path.
1289 //
1290 *TerminalDevicePath = AppendDevicePathNode (
1291 ParentDevicePath,
1292 (EFI_DEVICE_PATH_PROTOCOL *) &Node
1293 );
1294 if (*TerminalDevicePath == NULL) {
1295 return EFI_OUT_OF_RESOURCES;
1296 }
1297
1298 return EFI_SUCCESS;
1299 }
1300
1301 /**
1302 Initialize the Raw Data FIFO.
1303
1304 @param TerminalDevice The terminal device.
1305
1306 @return None.
1307
1308 **/
1309 VOID
1310 InitializeRawFiFo (
1311 IN TERMINAL_DEV *TerminalDevice
1312 )
1313 {
1314 //
1315 // Make the raw FIFO empty.
1316 //
1317 TerminalDevice->RawFiFo.Head = TerminalDevice->RawFiFo.Tail;
1318 }
1319
1320 /**
1321 Initialize the Unicode FIFO.
1322
1323 @param TerminalDevice The terminal device.
1324
1325 @return None.
1326
1327 **/
1328 VOID
1329 InitializeUnicodeFiFo (
1330 IN TERMINAL_DEV *TerminalDevice
1331 )
1332 {
1333 //
1334 // Make the unicode FIFO empty
1335 //
1336 TerminalDevice->UnicodeFiFo.Head = TerminalDevice->UnicodeFiFo.Tail;
1337 }
1338
1339 /**
1340 Initialize the EFI Key FIFO.
1341
1342 @param TerminalDevice The terminal device.
1343
1344 @return None.
1345
1346 **/
1347 VOID
1348 InitializeEfiKeyFiFo (
1349 IN TERMINAL_DEV *TerminalDevice
1350 )
1351 {
1352 //
1353 // Make the efi key FIFO empty
1354 //
1355 TerminalDevice->EfiKeyFiFo.Head = TerminalDevice->EfiKeyFiFo.Tail;
1356 }
1357
1358
1359 /**
1360 The user Entry Point for module Terminal. The user code starts with this function.
1361
1362 @param ImageHandle The firmware allocated handle for the EFI image.
1363 @param SystemTable A pointer to the EFI System Table.
1364
1365 @retval EFI_SUCCESS The entry point is executed successfully.
1366 @retval other Some error occurs when executing this entry point.
1367
1368 **/
1369 EFI_STATUS
1370 EFIAPI
1371 InitializeTerminal(
1372 IN EFI_HANDLE ImageHandle,
1373 IN EFI_SYSTEM_TABLE *SystemTable
1374 )
1375 {
1376 EFI_STATUS Status;
1377
1378 //
1379 // Install driver model protocol(s).
1380 //
1381 Status = EfiLibInstallDriverBindingComponentName2 (
1382 ImageHandle,
1383 SystemTable,
1384 &gTerminalDriverBinding,
1385 ImageHandle,
1386 &gTerminalComponentName,
1387 &gTerminalComponentName2
1388 );
1389 ASSERT_EFI_ERROR (Status);
1390
1391 return Status;
1392 }