]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Console/TerminalDxe/Terminal.c
MdeModulePkg/TerminalDxe: Separate state machine start/stop logic
[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 - 2017, Intel Corporation. All rights reserved.<BR>
6 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 *mTerminalType[] = {
33 &gEfiPcAnsiGuid,
34 &gEfiVT100Guid,
35 &gEfiVT100PlusGuid,
36 &gEfiVTUTF8Guid,
37 &gEfiTtyTermGuid
38 };
39
40
41 CHAR16 *mSerialConsoleNames[] = {
42 L"PC-ANSI Serial Console",
43 L"VT-100 Serial Console",
44 L"VT-100+ Serial Console",
45 L"VT-UTF8 Serial Console",
46 L"Tty Terminal Serial Console"
47 };
48
49 TERMINAL_DEV mTerminalDevTemplate = {
50 TERMINAL_DEV_SIGNATURE,
51 NULL,
52 0,
53 NULL,
54 NULL,
55 { // SimpleTextInput
56 TerminalConInReset,
57 TerminalConInReadKeyStroke,
58 NULL
59 },
60 { // SimpleTextOutput
61 TerminalConOutReset,
62 TerminalConOutOutputString,
63 TerminalConOutTestString,
64 TerminalConOutQueryMode,
65 TerminalConOutSetMode,
66 TerminalConOutSetAttribute,
67 TerminalConOutClearScreen,
68 TerminalConOutSetCursorPosition,
69 TerminalConOutEnableCursor,
70 NULL
71 },
72 { // SimpleTextOutputMode
73 1, // MaxMode
74 0, // Mode
75 EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK), // Attribute
76 0, // CursorColumn
77 0, // CursorRow
78 TRUE // CursorVisible
79 },
80 NULL, // TerminalConsoleModeData
81 0, // SerialInTimeOut
82
83 NULL, // RawFifo
84 NULL, // UnicodeFiFo
85 NULL, // EfiKeyFiFo
86 NULL, // EfiKeyFiFoForNotify
87
88 NULL, // ControllerNameTable
89 NULL, // TimerEvent
90 NULL, // TwoSecondTimeOut
91 INPUT_STATE_DEFAULT,
92 RESET_STATE_DEFAULT,
93 {
94 0,
95 0,
96 0
97 },
98 0,
99 FALSE,
100 { // SimpleTextInputEx
101 TerminalConInResetEx,
102 TerminalConInReadKeyStrokeEx,
103 NULL,
104 TerminalConInSetState,
105 TerminalConInRegisterKeyNotify,
106 TerminalConInUnregisterKeyNotify,
107 },
108 { // NotifyList
109 NULL,
110 NULL,
111 },
112 NULL // KeyNotifyProcessEvent
113 };
114
115 TERMINAL_CONSOLE_MODE_DATA mTerminalConsoleModeData[] = {
116 {80, 25},
117 {80, 50},
118 {100, 31},
119 //
120 // New modes can be added here.
121 //
122 };
123
124 /**
125 Convert the GUID representation of terminal type to enum type.
126
127 @param Guid The GUID representation of terminal type.
128
129 @return The terminal type in enum type.
130 **/
131 TERMINAL_TYPE
132 TerminalTypeFromGuid (
133 IN EFI_GUID *Guid
134 )
135 {
136 TERMINAL_TYPE Type;
137
138 for (Type = 0; Type < ARRAY_SIZE (mTerminalType); Type++) {
139 if (CompareGuid (Guid, mTerminalType[Type])) {
140 break;
141 }
142 }
143 return Type;
144 }
145
146 /**
147 Test to see if this driver supports Controller.
148
149 @param This Protocol instance pointer.
150 @param Controller Handle of device to test
151 @param RemainingDevicePath Optional parameter use to pick a specific child
152 device to start.
153
154 @retval EFI_SUCCESS This driver supports this device.
155 @retval EFI_ALREADY_STARTED This driver is already running on this device.
156 @retval other This driver does not support this device.
157
158 **/
159 EFI_STATUS
160 EFIAPI
161 TerminalDriverBindingSupported (
162 IN EFI_DRIVER_BINDING_PROTOCOL *This,
163 IN EFI_HANDLE Controller,
164 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
165 )
166 {
167 EFI_STATUS Status;
168 EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
169 EFI_SERIAL_IO_PROTOCOL *SerialIo;
170 VENDOR_DEVICE_PATH *Node;
171
172 //
173 // If remaining device path is not NULL, then make sure it is a
174 // device path that describes a terminal communications protocol.
175 //
176 if (RemainingDevicePath != NULL) {
177 //
178 // Check if RemainingDevicePath is the End of Device Path Node,
179 // if yes, go on checking other conditions
180 //
181 if (!IsDevicePathEnd (RemainingDevicePath)) {
182 //
183 // If RemainingDevicePath isn't the End of Device Path Node,
184 // check its validation
185 //
186 Node = (VENDOR_DEVICE_PATH *) RemainingDevicePath;
187
188 if (Node->Header.Type != MESSAGING_DEVICE_PATH ||
189 Node->Header.SubType != MSG_VENDOR_DP ||
190 DevicePathNodeLength(&Node->Header) != sizeof(VENDOR_DEVICE_PATH)) {
191
192 return EFI_UNSUPPORTED;
193
194 }
195 //
196 // only supports PC ANSI, VT100, VT100+, VT-UTF8, and TtyTerm terminal types
197 //
198 if (TerminalTypeFromGuid (&Node->Guid) == ARRAY_SIZE (mTerminalType)) {
199 return EFI_UNSUPPORTED;
200 }
201 }
202 }
203 //
204 // Open the IO Abstraction(s) needed to perform the supported test
205 // The Controller must support the Serial I/O Protocol.
206 // This driver is a bus driver with at most 1 child device, so it is
207 // ok for it to be already started.
208 //
209 Status = gBS->OpenProtocol (
210 Controller,
211 &gEfiSerialIoProtocolGuid,
212 (VOID **) &SerialIo,
213 This->DriverBindingHandle,
214 Controller,
215 EFI_OPEN_PROTOCOL_BY_DRIVER
216 );
217 if (Status == EFI_ALREADY_STARTED) {
218 return EFI_SUCCESS;
219 }
220
221 if (EFI_ERROR (Status)) {
222 return Status;
223 }
224
225 //
226 // Close the I/O Abstraction(s) used to perform the supported test
227 //
228 gBS->CloseProtocol (
229 Controller,
230 &gEfiSerialIoProtocolGuid,
231 This->DriverBindingHandle,
232 Controller
233 );
234
235 //
236 // Open the EFI Device Path protocol needed to perform the supported test
237 //
238 Status = gBS->OpenProtocol (
239 Controller,
240 &gEfiDevicePathProtocolGuid,
241 (VOID **) &ParentDevicePath,
242 This->DriverBindingHandle,
243 Controller,
244 EFI_OPEN_PROTOCOL_BY_DRIVER
245 );
246 if (Status == EFI_ALREADY_STARTED) {
247 return EFI_SUCCESS;
248 }
249
250 if (EFI_ERROR (Status)) {
251 return Status;
252 }
253
254 //
255 // Close protocol, don't use device path protocol in the Support() function
256 //
257 gBS->CloseProtocol (
258 Controller,
259 &gEfiDevicePathProtocolGuid,
260 This->DriverBindingHandle,
261 Controller
262 );
263
264 return Status;
265 }
266
267 /**
268 Build the terminal device path for the child device according to the
269 terminal type.
270
271 @param ParentDevicePath Parent device path.
272 @param RemainingDevicePath A specific child device.
273
274 @return The child device path built.
275
276 **/
277 EFI_DEVICE_PATH_PROTOCOL*
278 EFIAPI
279 BuildTerminalDevpath (
280 IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath,
281 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
282 )
283 {
284 EFI_DEVICE_PATH_PROTOCOL *TerminalDevicePath;
285 TERMINAL_TYPE TerminalType;
286 VENDOR_DEVICE_PATH *Node;
287 EFI_STATUS Status;
288
289 TerminalDevicePath = NULL;
290
291 //
292 // Use the RemainingDevicePath to determine the terminal type
293 //
294 Node = (VENDOR_DEVICE_PATH *) RemainingDevicePath;
295 if (Node == NULL) {
296 TerminalType = PcdGet8 (PcdDefaultTerminalType);
297
298 } else if (CompareGuid (&Node->Guid, &gEfiPcAnsiGuid)) {
299
300 TerminalType = TerminalTypePcAnsi;
301
302 } else if (CompareGuid (&Node->Guid, &gEfiVT100Guid)) {
303
304 TerminalType = TerminalTypeVt100;
305
306 } else if (CompareGuid (&Node->Guid, &gEfiVT100PlusGuid)) {
307
308 TerminalType = TerminalTypeVt100Plus;
309
310 } else if (CompareGuid (&Node->Guid, &gEfiVTUTF8Guid)) {
311
312 TerminalType = TerminalTypeVtUtf8;
313
314 } else if (CompareGuid (&Node->Guid, &gEfiTtyTermGuid)) {
315
316 TerminalType = TerminalTypeTtyTerm;
317
318 } else {
319 return NULL;
320 }
321
322 //
323 // Build the device path for the child device
324 //
325 Status = SetTerminalDevicePath (
326 TerminalType,
327 ParentDevicePath,
328 &TerminalDevicePath
329 );
330 if (EFI_ERROR (Status)) {
331 return NULL;
332 }
333 return TerminalDevicePath;
334 }
335
336 /**
337 Compare a device path data structure to that of all the nodes of a
338 second device path instance.
339
340 @param Multi A pointer to a multi-instance device path data structure.
341 @param Single A pointer to a single-instance device path data structure.
342
343 @retval TRUE If the Single is contained within Multi.
344 @retval FALSE The Single is not match within Multi.
345
346 **/
347 BOOLEAN
348 MatchDevicePaths (
349 IN EFI_DEVICE_PATH_PROTOCOL *Multi,
350 IN EFI_DEVICE_PATH_PROTOCOL *Single
351 )
352 {
353 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
354 EFI_DEVICE_PATH_PROTOCOL *DevicePathInst;
355 UINTN Size;
356
357 DevicePath = Multi;
358 DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size);
359 //
360 // Search for the match of 'Single' in 'Multi'
361 //
362 while (DevicePathInst != NULL) {
363 //
364 // If the single device path is found in multiple device paths,
365 // return success
366 //
367 if (CompareMem (Single, DevicePathInst, Size) == 0) {
368 FreePool (DevicePathInst);
369 return TRUE;
370 }
371
372 FreePool (DevicePathInst);
373 DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size);
374 }
375
376 return FALSE;
377 }
378
379 /**
380 Check whether the terminal device path is in the global variable.
381
382 @param VariableName Pointer to one global variable.
383 @param TerminalDevicePath Pointer to the terminal device's device path.
384
385 @retval TRUE The devcie is in the global variable.
386 @retval FALSE The devcie is not in the global variable.
387
388 **/
389 BOOLEAN
390 IsTerminalInConsoleVariable (
391 IN CHAR16 *VariableName,
392 IN EFI_DEVICE_PATH_PROTOCOL *TerminalDevicePath
393 )
394 {
395 EFI_DEVICE_PATH_PROTOCOL *Variable;
396 BOOLEAN ReturnFlag;
397
398 //
399 // Get global variable and its size according to the name given.
400 //
401 GetEfiGlobalVariable2 (VariableName, (VOID**)&Variable, NULL);
402 if (Variable == NULL) {
403 return FALSE;
404 }
405
406 //
407 // Check whether the terminal device path is one of the variable instances.
408 //
409 ReturnFlag = MatchDevicePaths (Variable, TerminalDevicePath);
410
411 FreePool (Variable);
412
413 return ReturnFlag;
414 }
415
416 /**
417 Free notify functions list.
418
419 @param ListHead The list head
420
421 @retval EFI_SUCCESS Free the notify list successfully.
422 @retval EFI_INVALID_PARAMETER ListHead is NULL.
423
424 **/
425 EFI_STATUS
426 TerminalFreeNotifyList (
427 IN OUT LIST_ENTRY *ListHead
428 )
429 {
430 TERMINAL_CONSOLE_IN_EX_NOTIFY *NotifyNode;
431
432 if (ListHead == NULL) {
433 return EFI_INVALID_PARAMETER;
434 }
435 while (!IsListEmpty (ListHead)) {
436 NotifyNode = CR (
437 ListHead->ForwardLink,
438 TERMINAL_CONSOLE_IN_EX_NOTIFY,
439 NotifyEntry,
440 TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE
441 );
442 RemoveEntryList (ListHead->ForwardLink);
443 FreePool (NotifyNode);
444 }
445
446 return EFI_SUCCESS;
447 }
448
449 /**
450 Initialize all the text modes which the terminal console supports.
451
452 It returns information for available text modes that the terminal can support.
453
454 @param[out] TextModeCount The total number of text modes that terminal console supports.
455
456 @return The buffer to the text modes column and row information.
457 Caller is responsible to free it when it's non-NULL.
458
459 **/
460 TERMINAL_CONSOLE_MODE_DATA *
461 InitializeTerminalConsoleTextMode (
462 OUT INT32 *TextModeCount
463 )
464 {
465 TERMINAL_CONSOLE_MODE_DATA *TextModeData;
466
467 ASSERT (TextModeCount != NULL);
468
469 //
470 // Here we make sure that the final mode exposed does not include the duplicated modes,
471 // and does not include the invalid modes which exceed the max column and row.
472 // Reserve 2 modes for 80x25, 80x50 of terminal console.
473 //
474 TextModeData = AllocateCopyPool (sizeof (mTerminalConsoleModeData), mTerminalConsoleModeData);
475 if (TextModeData == NULL) {
476 return NULL;
477 }
478 *TextModeCount = ARRAY_SIZE (mTerminalConsoleModeData);
479
480 DEBUG_CODE (
481 INT32 Index;
482 for (Index = 0; Index < *TextModeCount; Index++) {
483 DEBUG ((DEBUG_INFO, "Terminal - Mode %d, Column = %d, Row = %d\n",
484 Index, TextModeData[Index].Columns, TextModeData[Index].Rows));
485 }
486 );
487 return TextModeData;
488 }
489
490 /**
491 Stop the terminal state machine.
492
493 @param TerminalDevice The terminal device.
494 **/
495 VOID
496 StopTerminalStateMachine (
497 TERMINAL_DEV *TerminalDevice
498 )
499 {
500 EFI_TPL OriginalTpl;
501
502 OriginalTpl = gBS->RaiseTPL (TPL_NOTIFY);
503
504 gBS->CloseEvent (TerminalDevice->TimerEvent);
505 gBS->CloseEvent (TerminalDevice->TwoSecondTimeOut);
506
507 gBS->RestoreTPL (OriginalTpl);
508 }
509
510 /**
511 Start the terminal state machine.
512
513 @param TerminalDevice The terminal device.
514 **/
515 VOID
516 StartTerminalStateMachine (
517 TERMINAL_DEV *TerminalDevice
518 )
519 {
520 EFI_STATUS Status;
521 Status = gBS->CreateEvent (
522 EVT_TIMER | EVT_NOTIFY_SIGNAL,
523 TPL_NOTIFY,
524 TerminalConInTimerHandler,
525 TerminalDevice,
526 &TerminalDevice->TimerEvent
527 );
528 ASSERT_EFI_ERROR (Status);
529
530 Status = gBS->SetTimer (
531 TerminalDevice->TimerEvent,
532 TimerPeriodic,
533 KEYBOARD_TIMER_INTERVAL
534 );
535 ASSERT_EFI_ERROR (Status);
536
537 Status = gBS->CreateEvent (
538 EVT_TIMER,
539 TPL_CALLBACK,
540 NULL,
541 NULL,
542 &TerminalDevice->TwoSecondTimeOut
543 );
544 ASSERT_EFI_ERROR (Status);
545 }
546
547 /**
548 Initialize the controller name table.
549
550 @param TerminalType The terminal type.
551 @param ControllerNameTable The controller name table.
552
553 @retval EFI_SUCCESS The controller name table is initialized successfully.
554 @retval others Return status of AddUnicodeString2 ().
555 **/
556 EFI_STATUS
557 InitializeControllerNameTable (
558 TERMINAL_TYPE TerminalType,
559 EFI_UNICODE_STRING_TABLE **ControllerNameTable
560 )
561 {
562 EFI_STATUS Status;
563 EFI_UNICODE_STRING_TABLE *Table;
564
565 ASSERT (TerminalType < ARRAY_SIZE (mTerminalType));
566 Table = NULL;
567 Status = AddUnicodeString2 (
568 "eng",
569 gTerminalComponentName.SupportedLanguages,
570 &Table,
571 mSerialConsoleNames[TerminalType],
572 TRUE
573 );
574 if (!EFI_ERROR (Status)) {
575 Status = AddUnicodeString2 (
576 "en",
577 gTerminalComponentName2.SupportedLanguages,
578 &Table,
579 mSerialConsoleNames[TerminalType],
580 FALSE
581 );
582 if (EFI_ERROR (Status)) {
583 FreeUnicodeStringTable (Table);
584 }
585 }
586 if (!EFI_ERROR (Status)) {
587 *ControllerNameTable = Table;
588 }
589 return Status;
590 }
591
592 /**
593 Start this driver on Controller by opening a Serial IO protocol,
594 reading Device Path, and creating a child handle with a Simple Text In,
595 Simple Text In Ex and Simple Text Out protocol, and device path protocol.
596 And store Console Device Environment Variables.
597
598 @param This Protocol instance pointer.
599 @param Controller Handle of device to bind driver to
600 @param RemainingDevicePath Optional parameter use to pick a specific child
601 device to start.
602
603 @retval EFI_SUCCESS This driver is added to Controller.
604 @retval EFI_ALREADY_STARTED This driver is already running on Controller.
605 @retval other This driver does not support this device.
606
607 **/
608 EFI_STATUS
609 EFIAPI
610 TerminalDriverBindingStart (
611 IN EFI_DRIVER_BINDING_PROTOCOL *This,
612 IN EFI_HANDLE Controller,
613 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
614 )
615 {
616 EFI_STATUS Status;
617 EFI_SERIAL_IO_PROTOCOL *SerialIo;
618 EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
619 VENDOR_DEVICE_PATH *Node;
620 EFI_SERIAL_IO_MODE *Mode;
621 UINTN SerialInTimeOut;
622 TERMINAL_DEV *TerminalDevice;
623 TERMINAL_TYPE TerminalType;
624 EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer;
625 UINTN EntryCount;
626 UINTN Index;
627 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
628 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOutput;
629 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *SimpleTextInput;
630 BOOLEAN ConInSelected;
631 BOOLEAN ConOutSelected;
632 BOOLEAN NullRemaining;
633 BOOLEAN SimTxtInInstalled;
634 BOOLEAN SimTxtOutInstalled;
635 BOOLEAN FirstEnter;
636
637 TerminalDevice = NULL;
638 ConInSelected = FALSE;
639 ConOutSelected = FALSE;
640 NullRemaining = FALSE;
641 SimTxtInInstalled = FALSE;
642 SimTxtOutInstalled = FALSE;
643 FirstEnter = FALSE;
644 //
645 // Get the Device Path Protocol to build the device path of the child device
646 //
647 Status = gBS->OpenProtocol (
648 Controller,
649 &gEfiDevicePathProtocolGuid,
650 (VOID **) &ParentDevicePath,
651 This->DriverBindingHandle,
652 Controller,
653 EFI_OPEN_PROTOCOL_BY_DRIVER
654 );
655 if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
656 return Status;
657 }
658
659 //
660 // Open the Serial I/O Protocol BY_DRIVER. It might already be started.
661 //
662 Status = gBS->OpenProtocol (
663 Controller,
664 &gEfiSerialIoProtocolGuid,
665 (VOID **) &SerialIo,
666 This->DriverBindingHandle,
667 Controller,
668 EFI_OPEN_PROTOCOL_BY_DRIVER
669 );
670 if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
671 return Status;
672 }
673
674 if (Status != EFI_ALREADY_STARTED) {
675 //
676 // the serial I/O protocol never be opened before, it is the first
677 // time to start the serial Io controller
678 //
679 FirstEnter = TRUE;
680 }
681
682 //
683 // Serial I/O is not already open by this driver, then tag the handle
684 // with the Terminal Driver GUID and update the ConInDev, ConOutDev, and
685 // StdErrDev variables with the list of possible terminal types on this
686 // serial port.
687 //
688 Status = gBS->OpenProtocol (
689 Controller,
690 &gEfiCallerIdGuid,
691 NULL,
692 This->DriverBindingHandle,
693 Controller,
694 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
695 );
696 if (EFI_ERROR (Status)) {
697 Status = gBS->InstallMultipleProtocolInterfaces (
698 &Controller,
699 &gEfiCallerIdGuid,
700 DuplicateDevicePath (ParentDevicePath),
701 NULL
702 );
703 if (EFI_ERROR (Status)) {
704 goto Error;
705 }
706
707 if (!IsHotPlugDevice (ParentDevicePath)) {
708 //
709 // if the serial device is a hot plug device, do not update the
710 // ConInDev, ConOutDev, and StdErrDev variables.
711 //
712 TerminalUpdateConsoleDevVariable (L"ConInDev", ParentDevicePath);
713 TerminalUpdateConsoleDevVariable (L"ConOutDev", ParentDevicePath);
714 TerminalUpdateConsoleDevVariable (L"ErrOutDev", ParentDevicePath);
715 }
716 }
717
718 //
719 // Check the requirement for the SimpleTxtIn and SimpleTxtOut protocols
720 //
721 // Simple In/Out Protocol will not be installed onto the handle if the
722 // device path to the handle is not present in the ConIn/ConOut
723 // environment variable. But If RemainingDevicePath is NULL, then always
724 // produce both Simple In and Simple Text Output Protocols. This is required
725 // for the connect all sequences to make sure all possible consoles are
726 // produced no matter what the current values of ConIn, ConOut, or StdErr are.
727 //
728 if (RemainingDevicePath == NULL) {
729 NullRemaining = TRUE;
730 }
731
732 DevicePath = BuildTerminalDevpath (ParentDevicePath, RemainingDevicePath);
733 if (DevicePath != NULL) {
734 ConInSelected = IsTerminalInConsoleVariable (L"ConIn", DevicePath);
735 ConOutSelected = IsTerminalInConsoleVariable (L"ConOut", DevicePath);
736 FreePool (DevicePath);
737 } else {
738 goto Error;
739 }
740 //
741 // Not create the child terminal handle if both Simple In/In Ex and
742 // Simple text Out protocols are not required to be published
743 //
744 if ((!ConInSelected)&&(!ConOutSelected)&&(!NullRemaining)) {
745 goto Error;
746 }
747
748 //
749 // create the child terminal handle during first entry
750 //
751 if (FirstEnter) {
752 //
753 // First enther the start function
754 //
755 FirstEnter = FALSE;
756 //
757 // Make sure a child handle does not already exist. This driver can only
758 // produce one child per serial port.
759 //
760 Status = gBS->OpenProtocolInformation (
761 Controller,
762 &gEfiSerialIoProtocolGuid,
763 &OpenInfoBuffer,
764 &EntryCount
765 );
766 if (!EFI_ERROR (Status)) {
767 Status = EFI_SUCCESS;
768 for (Index = 0; Index < EntryCount; Index++) {
769 if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {
770 Status = EFI_ALREADY_STARTED;
771 }
772 }
773
774 FreePool (OpenInfoBuffer);
775 if (EFI_ERROR (Status)) {
776 goto Error;
777 }
778 }
779
780 //
781 // If RemainingDevicePath is NULL, use default terminal type
782 //
783 if (RemainingDevicePath == NULL) {
784 TerminalType = PcdGet8 (PcdDefaultTerminalType);
785 } else if (!IsDevicePathEnd (RemainingDevicePath)) {
786 //
787 // If RemainingDevicePath isn't the End of Device Path Node,
788 // Use the RemainingDevicePath to determine the terminal type
789 //
790 Node = (VENDOR_DEVICE_PATH *)RemainingDevicePath;
791 TerminalType = TerminalTypeFromGuid (&Node->Guid);
792 } else {
793 //
794 // If RemainingDevicePath is the End of Device Path Node,
795 // skip enumerate any device and return EFI_SUCESSS
796 //
797 return EFI_SUCCESS;
798 }
799
800 ASSERT (TerminalType < ARRAY_SIZE (mTerminalType));
801
802 //
803 // Initialize the Terminal Dev
804 //
805 TerminalDevice = AllocateCopyPool (sizeof (TERMINAL_DEV), &mTerminalDevTemplate);
806 if (TerminalDevice == NULL) {
807 Status = EFI_OUT_OF_RESOURCES;
808 goto Error;
809 }
810
811 TerminalDevice->TerminalType = TerminalType;
812 TerminalDevice->SerialIo = SerialIo;
813
814 InitializeListHead (&TerminalDevice->NotifyList);
815 Status = gBS->CreateEvent (
816 EVT_NOTIFY_WAIT,
817 TPL_NOTIFY,
818 TerminalConInWaitForKeyEx,
819 TerminalDevice,
820 &TerminalDevice->SimpleInputEx.WaitForKeyEx
821 );
822 if (EFI_ERROR (Status)) {
823 goto Error;
824 }
825
826 Status = gBS->CreateEvent (
827 EVT_NOTIFY_WAIT,
828 TPL_NOTIFY,
829 TerminalConInWaitForKey,
830 TerminalDevice,
831 &TerminalDevice->SimpleInput.WaitForKey
832 );
833 if (EFI_ERROR (Status)) {
834 goto Error;
835 }
836 //
837 // Allocates and initializes the FIFO buffer to be zero, used for accommodating
838 // the pre-read pending characters.
839 //
840 TerminalDevice->RawFiFo = AllocateZeroPool (sizeof (RAW_DATA_FIFO));
841 if (TerminalDevice->RawFiFo == NULL) {
842 goto Error;
843 }
844 TerminalDevice->UnicodeFiFo = AllocateZeroPool (sizeof (UNICODE_FIFO));
845 if (TerminalDevice->UnicodeFiFo == NULL) {
846 goto Error;
847 }
848 TerminalDevice->EfiKeyFiFo = AllocateZeroPool (sizeof (EFI_KEY_FIFO));
849 if (TerminalDevice->EfiKeyFiFo == NULL) {
850 goto Error;
851 }
852 TerminalDevice->EfiKeyFiFoForNotify = AllocateZeroPool (sizeof (EFI_KEY_FIFO));
853 if (TerminalDevice->EfiKeyFiFoForNotify == NULL) {
854 goto Error;
855 }
856
857 //
858 // Set the timeout value of serial buffer for
859 // keystroke response performance issue
860 //
861 Mode = TerminalDevice->SerialIo->Mode;
862
863 SerialInTimeOut = 0;
864 if (Mode->BaudRate != 0) {
865 SerialInTimeOut = (1 + Mode->DataBits + Mode->StopBits) * 2 * 1000000 / (UINTN) Mode->BaudRate;
866 }
867
868 Status = TerminalDevice->SerialIo->SetAttributes (
869 TerminalDevice->SerialIo,
870 Mode->BaudRate,
871 Mode->ReceiveFifoDepth,
872 (UINT32) SerialInTimeOut,
873 (EFI_PARITY_TYPE) (Mode->Parity),
874 (UINT8) Mode->DataBits,
875 (EFI_STOP_BITS_TYPE) (Mode->StopBits)
876 );
877 if (EFI_ERROR (Status)) {
878 //
879 // if set attributes operation fails, invalidate
880 // the value of SerialInTimeOut,thus make it
881 // inconsistent with the default timeout value
882 // of serial buffer. This will invoke the recalculation
883 // in the readkeystroke routine.
884 //
885 TerminalDevice->SerialInTimeOut = 0;
886 } else {
887 TerminalDevice->SerialInTimeOut = SerialInTimeOut;
888 }
889 //
890 // Set Simple Text Output Protocol from template.
891 //
892 SimpleTextOutput = CopyMem (
893 &TerminalDevice->SimpleTextOutput,
894 &mTerminalDevTemplate.SimpleTextOutput,
895 sizeof (mTerminalDevTemplate.SimpleTextOutput)
896 );
897 SimpleTextOutput->Mode = &TerminalDevice->SimpleTextOutputMode;
898
899 TerminalDevice->TerminalConsoleModeData = InitializeTerminalConsoleTextMode (
900 &SimpleTextOutput->Mode->MaxMode
901 );
902 if (TerminalDevice->TerminalConsoleModeData == NULL) {
903 goto ReportError;
904 }
905
906 //
907 // For terminal devices, cursor is always visible
908 //
909 TerminalDevice->SimpleTextOutputMode.CursorVisible = TRUE;
910 Status = TerminalConOutSetAttribute (
911 SimpleTextOutput,
912 EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)
913 );
914 if (EFI_ERROR (Status)) {
915 goto ReportError;
916 }
917
918 //
919 // Build the component name for the child device
920 //
921 Status = InitializeControllerNameTable (TerminalDevice->TerminalType, &TerminalDevice->ControllerNameTable);
922 if (EFI_ERROR (Status)) {
923 goto Error;
924 }
925
926 //
927 // Build the device path for the child device
928 //
929 Status = SetTerminalDevicePath (
930 TerminalDevice->TerminalType,
931 ParentDevicePath,
932 &TerminalDevice->DevicePath
933 );
934 if (EFI_ERROR (Status)) {
935 goto Error;
936 }
937
938 Status = TerminalConOutReset (SimpleTextOutput, FALSE);
939 if (EFI_ERROR (Status)) {
940 goto ReportError;
941 }
942
943 Status = TerminalConOutSetMode (SimpleTextOutput, 0);
944 if (EFI_ERROR (Status)) {
945 goto ReportError;
946 }
947
948 Status = TerminalConOutEnableCursor (SimpleTextOutput, TRUE);
949 if (EFI_ERROR (Status)) {
950 goto ReportError;
951 }
952
953 StartTerminalStateMachine (TerminalDevice);
954
955 Status = gBS->CreateEvent (
956 EVT_NOTIFY_SIGNAL,
957 TPL_CALLBACK,
958 KeyNotifyProcessHandler,
959 TerminalDevice,
960 &TerminalDevice->KeyNotifyProcessEvent
961 );
962 ASSERT_EFI_ERROR (Status);
963
964 Status = gBS->InstallProtocolInterface (
965 &TerminalDevice->Handle,
966 &gEfiDevicePathProtocolGuid,
967 EFI_NATIVE_INTERFACE,
968 TerminalDevice->DevicePath
969 );
970 if (EFI_ERROR (Status)) {
971 goto Error;
972 }
973
974 //
975 // Register the Parent-Child relationship via
976 // EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
977 //
978 Status = gBS->OpenProtocol (
979 Controller,
980 &gEfiSerialIoProtocolGuid,
981 (VOID **) &TerminalDevice->SerialIo,
982 This->DriverBindingHandle,
983 TerminalDevice->Handle,
984 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
985 );
986 if (EFI_ERROR (Status)) {
987 goto Error;
988 }
989 }
990
991 //
992 // Find the child handle, and get its TerminalDevice private data
993 //
994 Status = gBS->OpenProtocolInformation (
995 Controller,
996 &gEfiSerialIoProtocolGuid,
997 &OpenInfoBuffer,
998 &EntryCount
999 );
1000 if (!EFI_ERROR (Status)) {
1001 Status = EFI_NOT_FOUND;
1002 ASSERT (OpenInfoBuffer != NULL);
1003 for (Index = 0; Index < EntryCount; Index++) {
1004 if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {
1005 //
1006 // Find the child terminal handle.
1007 // Test whether the SimpleTxtIn and SimpleTxtOut have been published
1008 //
1009 Status = gBS->OpenProtocol (
1010 OpenInfoBuffer[Index].ControllerHandle,
1011 &gEfiSimpleTextInProtocolGuid,
1012 (VOID **) &SimpleTextInput,
1013 This->DriverBindingHandle,
1014 OpenInfoBuffer[Index].ControllerHandle,
1015 EFI_OPEN_PROTOCOL_GET_PROTOCOL
1016 );
1017 if (!EFI_ERROR (Status)) {
1018 SimTxtInInstalled = TRUE;
1019 TerminalDevice = TERMINAL_CON_IN_DEV_FROM_THIS (SimpleTextInput);
1020 }
1021
1022 Status = gBS->OpenProtocol (
1023 OpenInfoBuffer[Index].ControllerHandle,
1024 &gEfiSimpleTextOutProtocolGuid,
1025 (VOID **) &SimpleTextOutput,
1026 This->DriverBindingHandle,
1027 OpenInfoBuffer[Index].ControllerHandle,
1028 EFI_OPEN_PROTOCOL_GET_PROTOCOL
1029 );
1030 if (!EFI_ERROR (Status)) {
1031 SimTxtOutInstalled = TRUE;
1032 TerminalDevice = TERMINAL_CON_OUT_DEV_FROM_THIS (SimpleTextOutput);
1033 }
1034 Status = EFI_SUCCESS;
1035 break;
1036 }
1037 }
1038
1039 FreePool (OpenInfoBuffer);
1040 if (EFI_ERROR (Status)) {
1041 goto ReportError;
1042 }
1043 } else {
1044 goto ReportError;
1045 }
1046
1047 ASSERT (TerminalDevice != NULL);
1048 //
1049 // Only do the reset if the device path is in the Conout variable
1050 //
1051 if (ConInSelected && !SimTxtInInstalled) {
1052 Status = TerminalDevice->SimpleInput.Reset (
1053 &TerminalDevice->SimpleInput,
1054 FALSE
1055 );
1056 if (EFI_ERROR (Status)) {
1057 //
1058 // Need to report Error Code first
1059 //
1060 goto ReportError;
1061 }
1062 }
1063
1064 //
1065 // Only output the configure string to remote terminal if the device path
1066 // is in the Conout variable
1067 //
1068 if (ConOutSelected && !SimTxtOutInstalled) {
1069 Status = TerminalDevice->SimpleTextOutput.SetAttribute (
1070 &TerminalDevice->SimpleTextOutput,
1071 EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)
1072 );
1073 if (EFI_ERROR (Status)) {
1074 goto ReportError;
1075 }
1076
1077 Status = TerminalDevice->SimpleTextOutput.Reset (
1078 &TerminalDevice->SimpleTextOutput,
1079 FALSE
1080 );
1081 if (EFI_ERROR (Status)) {
1082 goto ReportError;
1083 }
1084
1085 Status = TerminalDevice->SimpleTextOutput.SetMode (
1086 &TerminalDevice->SimpleTextOutput,
1087 0
1088 );
1089 if (EFI_ERROR (Status)) {
1090 goto ReportError;
1091 }
1092
1093 Status = TerminalDevice->SimpleTextOutput.EnableCursor (
1094 &TerminalDevice->SimpleTextOutput,
1095 TRUE
1096 );
1097 if (EFI_ERROR (Status)) {
1098 goto ReportError;
1099 }
1100 }
1101
1102 //
1103 // Simple In/Out Protocol will not be installed onto the handle if the
1104 // device path to the handle is not present in the ConIn/ConOut
1105 // environment variable. But If RemainingDevicePath is NULL, then always
1106 // produce both Simple In and Simple Text Output Protocols. This is required
1107 // for the connect all sequences to make sure all possible consoles are
1108 // produced no matter what the current values of ConIn, ConOut, or StdErr are.
1109 //
1110 if (!SimTxtInInstalled && (ConInSelected || NullRemaining)) {
1111 Status = gBS->InstallMultipleProtocolInterfaces (
1112 &TerminalDevice->Handle,
1113 &gEfiSimpleTextInProtocolGuid,
1114 &TerminalDevice->SimpleInput,
1115 &gEfiSimpleTextInputExProtocolGuid,
1116 &TerminalDevice->SimpleInputEx,
1117 NULL
1118 );
1119 if (EFI_ERROR (Status)) {
1120 goto Error;
1121 }
1122 }
1123
1124 if (!SimTxtOutInstalled && (ConOutSelected || NullRemaining)) {
1125 Status = gBS->InstallProtocolInterface (
1126 &TerminalDevice->Handle,
1127 &gEfiSimpleTextOutProtocolGuid,
1128 EFI_NATIVE_INTERFACE,
1129 &TerminalDevice->SimpleTextOutput
1130 );
1131 if (EFI_ERROR (Status)) {
1132 goto Error;
1133 }
1134 }
1135
1136 return EFI_SUCCESS;
1137
1138 ReportError:
1139 //
1140 // Report error code before exiting
1141 //
1142 DevicePath = ParentDevicePath;
1143 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
1144 EFI_ERROR_CODE | EFI_ERROR_MINOR,
1145 (EFI_PERIPHERAL_REMOTE_CONSOLE | EFI_P_EC_CONTROLLER_ERROR),
1146 DevicePath
1147 );
1148
1149 Error:
1150 //
1151 // Use the Stop() function to free all resources allocated in Start()
1152 //
1153 if (TerminalDevice != NULL) {
1154
1155 if (TerminalDevice->Handle != NULL) {
1156 This->Stop (This, Controller, 1, &TerminalDevice->Handle);
1157 } else {
1158
1159 if (TerminalDevice->TwoSecondTimeOut != NULL) {
1160 gBS->CloseEvent (TerminalDevice->TwoSecondTimeOut);
1161 }
1162
1163 if (TerminalDevice->TimerEvent != NULL) {
1164 gBS->CloseEvent (TerminalDevice->TimerEvent);
1165 }
1166
1167 if (TerminalDevice->SimpleInput.WaitForKey != NULL) {
1168 gBS->CloseEvent (TerminalDevice->SimpleInput.WaitForKey);
1169 }
1170
1171 if (TerminalDevice->SimpleInputEx.WaitForKeyEx != NULL) {
1172 gBS->CloseEvent (TerminalDevice->SimpleInputEx.WaitForKeyEx);
1173 }
1174
1175 TerminalFreeNotifyList (&TerminalDevice->NotifyList);
1176
1177 if (TerminalDevice->RawFiFo != NULL) {
1178 FreePool (TerminalDevice->RawFiFo);
1179 }
1180 if (TerminalDevice->UnicodeFiFo != NULL) {
1181 FreePool (TerminalDevice->UnicodeFiFo);
1182 }
1183 if (TerminalDevice->EfiKeyFiFo != NULL) {
1184 FreePool (TerminalDevice->EfiKeyFiFo);
1185 }
1186 if (TerminalDevice->EfiKeyFiFoForNotify != NULL) {
1187 FreePool (TerminalDevice->EfiKeyFiFoForNotify);
1188 }
1189
1190 if (TerminalDevice->ControllerNameTable != NULL) {
1191 FreeUnicodeStringTable (TerminalDevice->ControllerNameTable);
1192 }
1193
1194 if (TerminalDevice->DevicePath != NULL) {
1195 FreePool (TerminalDevice->DevicePath);
1196 }
1197
1198 if (TerminalDevice->TerminalConsoleModeData != NULL) {
1199 FreePool (TerminalDevice->TerminalConsoleModeData);
1200 }
1201
1202 FreePool (TerminalDevice);
1203 }
1204 }
1205
1206 This->Stop (This, Controller, 0, NULL);
1207
1208 return Status;
1209 }
1210
1211 /**
1212 Stop this driver on Controller by closing Simple Text In, Simple Text
1213 In Ex, Simple Text Out protocol, and removing parent device path from
1214 Console Device Environment Variables.
1215
1216 @param This Protocol instance pointer.
1217 @param Controller Handle of device to stop driver on
1218 @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
1219 children is zero stop the entire bus driver.
1220 @param ChildHandleBuffer List of Child Handles to Stop.
1221
1222 @retval EFI_SUCCESS This driver is removed Controller.
1223 @retval other This driver could not be removed from this device.
1224
1225 **/
1226 EFI_STATUS
1227 EFIAPI
1228 TerminalDriverBindingStop (
1229 IN EFI_DRIVER_BINDING_PROTOCOL *This,
1230 IN EFI_HANDLE Controller,
1231 IN UINTN NumberOfChildren,
1232 IN EFI_HANDLE *ChildHandleBuffer
1233 )
1234 {
1235 EFI_STATUS Status;
1236 UINTN Index;
1237 BOOLEAN AllChildrenStopped;
1238 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOutput;
1239 TERMINAL_DEV *TerminalDevice;
1240 EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
1241 EFI_SERIAL_IO_PROTOCOL *SerialIo;
1242 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
1243
1244 Status = gBS->HandleProtocol (
1245 Controller,
1246 &gEfiDevicePathProtocolGuid,
1247 (VOID **) &DevicePath
1248 );
1249 if (EFI_ERROR (Status)) {
1250 return Status;
1251 }
1252
1253 //
1254 // Complete all outstanding transactions to Controller.
1255 // Don't allow any new transaction to Controller to be started.
1256 //
1257 if (NumberOfChildren == 0) {
1258 //
1259 // Close the bus driver
1260 //
1261 Status = gBS->OpenProtocol (
1262 Controller,
1263 &gEfiCallerIdGuid,
1264 (VOID **) &ParentDevicePath,
1265 This->DriverBindingHandle,
1266 Controller,
1267 EFI_OPEN_PROTOCOL_GET_PROTOCOL
1268 );
1269 if (!EFI_ERROR (Status)) {
1270 //
1271 // Remove Parent Device Path from
1272 // the Console Device Environment Variables
1273 //
1274 TerminalRemoveConsoleDevVariable (L"ConInDev", ParentDevicePath);
1275 TerminalRemoveConsoleDevVariable (L"ConOutDev", ParentDevicePath);
1276 TerminalRemoveConsoleDevVariable (L"ErrOutDev", ParentDevicePath);
1277
1278 //
1279 // Uninstall the Terminal Driver's GUID Tag from the Serial controller
1280 //
1281 Status = gBS->UninstallMultipleProtocolInterfaces (
1282 Controller,
1283 &gEfiCallerIdGuid,
1284 ParentDevicePath,
1285 NULL
1286 );
1287
1288 //
1289 // Free the ParentDevicePath that was duplicated in Start()
1290 //
1291 if (!EFI_ERROR (Status)) {
1292 FreePool (ParentDevicePath);
1293 }
1294 }
1295
1296 gBS->CloseProtocol (
1297 Controller,
1298 &gEfiSerialIoProtocolGuid,
1299 This->DriverBindingHandle,
1300 Controller
1301 );
1302
1303 gBS->CloseProtocol (
1304 Controller,
1305 &gEfiDevicePathProtocolGuid,
1306 This->DriverBindingHandle,
1307 Controller
1308 );
1309
1310 return EFI_SUCCESS;
1311 }
1312
1313 AllChildrenStopped = TRUE;
1314
1315 for (Index = 0; Index < NumberOfChildren; Index++) {
1316
1317 Status = gBS->OpenProtocol (
1318 ChildHandleBuffer[Index],
1319 &gEfiSimpleTextOutProtocolGuid,
1320 (VOID **) &SimpleTextOutput,
1321 This->DriverBindingHandle,
1322 ChildHandleBuffer[Index],
1323 EFI_OPEN_PROTOCOL_GET_PROTOCOL
1324 );
1325 if (!EFI_ERROR (Status)) {
1326
1327 TerminalDevice = TERMINAL_CON_OUT_DEV_FROM_THIS (SimpleTextOutput);
1328
1329 gBS->CloseProtocol (
1330 Controller,
1331 &gEfiSerialIoProtocolGuid,
1332 This->DriverBindingHandle,
1333 ChildHandleBuffer[Index]
1334 );
1335
1336 Status = gBS->UninstallMultipleProtocolInterfaces (
1337 ChildHandleBuffer[Index],
1338 &gEfiSimpleTextInProtocolGuid,
1339 &TerminalDevice->SimpleInput,
1340 &gEfiSimpleTextInputExProtocolGuid,
1341 &TerminalDevice->SimpleInputEx,
1342 &gEfiSimpleTextOutProtocolGuid,
1343 &TerminalDevice->SimpleTextOutput,
1344 &gEfiDevicePathProtocolGuid,
1345 TerminalDevice->DevicePath,
1346 NULL
1347 );
1348 if (EFI_ERROR (Status)) {
1349 gBS->OpenProtocol (
1350 Controller,
1351 &gEfiSerialIoProtocolGuid,
1352 (VOID **) &SerialIo,
1353 This->DriverBindingHandle,
1354 ChildHandleBuffer[Index],
1355 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
1356 );
1357 } else {
1358
1359 if (TerminalDevice->ControllerNameTable != NULL) {
1360 FreeUnicodeStringTable (TerminalDevice->ControllerNameTable);
1361 }
1362
1363 StopTerminalStateMachine (TerminalDevice);
1364 gBS->CloseEvent (TerminalDevice->SimpleInput.WaitForKey);
1365 gBS->CloseEvent (TerminalDevice->SimpleInputEx.WaitForKeyEx);
1366 gBS->CloseEvent (TerminalDevice->KeyNotifyProcessEvent);
1367 TerminalFreeNotifyList (&TerminalDevice->NotifyList);
1368 FreePool (TerminalDevice->DevicePath);
1369 if (TerminalDevice->TerminalConsoleModeData != NULL) {
1370 FreePool (TerminalDevice->TerminalConsoleModeData);
1371 }
1372 FreePool (TerminalDevice);
1373 }
1374 }
1375
1376 if (EFI_ERROR (Status)) {
1377 AllChildrenStopped = FALSE;
1378 }
1379 }
1380
1381 if (!AllChildrenStopped) {
1382 return EFI_DEVICE_ERROR;
1383 }
1384
1385 return EFI_SUCCESS;
1386 }
1387
1388 /**
1389 Update terminal device path in Console Device Environment Variables.
1390
1391 @param VariableName The Console Device Environment Variable.
1392 @param ParentDevicePath The terminal device path to be updated.
1393
1394 **/
1395 VOID
1396 TerminalUpdateConsoleDevVariable (
1397 IN CHAR16 *VariableName,
1398 IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath
1399 )
1400 {
1401 EFI_STATUS Status;
1402 UINTN NameSize;
1403 UINTN VariableSize;
1404 TERMINAL_TYPE TerminalType;
1405 EFI_DEVICE_PATH_PROTOCOL *Variable;
1406 EFI_DEVICE_PATH_PROTOCOL *NewVariable;
1407 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
1408 EDKII_SET_VARIABLE_STATUS *SetVariableStatus;
1409
1410 //
1411 // Get global variable and its size according to the name given.
1412 //
1413 GetEfiGlobalVariable2 (VariableName, (VOID**)&Variable, NULL);
1414 if (Variable == NULL) {
1415 return;
1416 }
1417
1418 //
1419 // Append terminal device path onto the variable.
1420 //
1421 for (TerminalType = 0; TerminalType < ARRAY_SIZE (mTerminalType); TerminalType++) {
1422 SetTerminalDevicePath (TerminalType, ParentDevicePath, &TempDevicePath);
1423 NewVariable = AppendDevicePathInstance (Variable, TempDevicePath);
1424 ASSERT (NewVariable != NULL);
1425 if (Variable != NULL) {
1426 FreePool (Variable);
1427 }
1428
1429 if (TempDevicePath != NULL) {
1430 FreePool (TempDevicePath);
1431 }
1432
1433 Variable = NewVariable;
1434 }
1435
1436 VariableSize = GetDevicePathSize (Variable);
1437
1438 Status = gRT->SetVariable (
1439 VariableName,
1440 &gEfiGlobalVariableGuid,
1441 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
1442 VariableSize,
1443 Variable
1444 );
1445
1446 if (EFI_ERROR (Status)) {
1447 NameSize = StrSize (VariableName);
1448 SetVariableStatus = AllocatePool (sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + VariableSize);
1449 if (SetVariableStatus != NULL) {
1450 CopyGuid (&SetVariableStatus->Guid, &gEfiGlobalVariableGuid);
1451 SetVariableStatus->NameSize = NameSize;
1452 SetVariableStatus->DataSize = VariableSize;
1453 SetVariableStatus->SetStatus = Status;
1454 SetVariableStatus->Attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
1455 CopyMem (SetVariableStatus + 1, VariableName, NameSize);
1456 CopyMem (((UINT8 *) (SetVariableStatus + 1)) + NameSize, Variable, VariableSize);
1457
1458 REPORT_STATUS_CODE_EX (
1459 EFI_ERROR_CODE,
1460 PcdGet32 (PcdErrorCodeSetVariable),
1461 0,
1462 NULL,
1463 &gEdkiiStatusCodeDataTypeVariableGuid,
1464 SetVariableStatus,
1465 sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + VariableSize
1466 );
1467
1468 FreePool (SetVariableStatus);
1469 }
1470 }
1471
1472 FreePool (Variable);
1473
1474 return ;
1475 }
1476
1477
1478 /**
1479 Remove terminal device path from Console Device Environment Variables.
1480
1481 @param VariableName Console Device Environment Variables.
1482 @param ParentDevicePath The terminal device path to be updated.
1483
1484 **/
1485 VOID
1486 TerminalRemoveConsoleDevVariable (
1487 IN CHAR16 *VariableName,
1488 IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath
1489 )
1490 {
1491 EFI_STATUS Status;
1492 BOOLEAN FoundOne;
1493 BOOLEAN Match;
1494 UINTN VariableSize;
1495 UINTN InstanceSize;
1496 TERMINAL_TYPE TerminalType;
1497 EFI_DEVICE_PATH_PROTOCOL *Instance;
1498 EFI_DEVICE_PATH_PROTOCOL *Variable;
1499 EFI_DEVICE_PATH_PROTOCOL *OriginalVariable;
1500 EFI_DEVICE_PATH_PROTOCOL *NewVariable;
1501 EFI_DEVICE_PATH_PROTOCOL *SavedNewVariable;
1502 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
1503
1504 Instance = NULL;
1505
1506 //
1507 // Get global variable and its size according to the name given.
1508 //
1509 GetEfiGlobalVariable2 (VariableName, (VOID**)&Variable, NULL);
1510 if (Variable == NULL) {
1511 return ;
1512 }
1513
1514 FoundOne = FALSE;
1515 OriginalVariable = Variable;
1516 NewVariable = NULL;
1517
1518 //
1519 // Get first device path instance from Variable
1520 //
1521 Instance = GetNextDevicePathInstance (&Variable, &InstanceSize);
1522 if (Instance == NULL) {
1523 FreePool (OriginalVariable);
1524 return ;
1525 }
1526 //
1527 // Loop through all the device path instances of Variable
1528 //
1529 do {
1530 //
1531 // Loop through all the terminal types that this driver supports
1532 //
1533 Match = FALSE;
1534 for (TerminalType = 0; TerminalType < ARRAY_SIZE (mTerminalType); TerminalType++) {
1535
1536 SetTerminalDevicePath (TerminalType, ParentDevicePath, &TempDevicePath);
1537
1538 //
1539 // Compare the generated device path to the current device path instance
1540 //
1541 if (TempDevicePath != NULL) {
1542 if (CompareMem (Instance, TempDevicePath, InstanceSize) == 0) {
1543 Match = TRUE;
1544 FoundOne = TRUE;
1545 }
1546
1547 FreePool (TempDevicePath);
1548 }
1549 }
1550 //
1551 // If a match was not found, then keep the current device path instance
1552 //
1553 if (!Match) {
1554 SavedNewVariable = NewVariable;
1555 NewVariable = AppendDevicePathInstance (NewVariable, Instance);
1556 if (SavedNewVariable != NULL) {
1557 FreePool (SavedNewVariable);
1558 }
1559 }
1560 //
1561 // Get next device path instance from Variable
1562 //
1563 FreePool (Instance);
1564 Instance = GetNextDevicePathInstance (&Variable, &InstanceSize);
1565 } while (Instance != NULL);
1566
1567 FreePool (OriginalVariable);
1568
1569 if (FoundOne) {
1570 VariableSize = GetDevicePathSize (NewVariable);
1571
1572 Status = gRT->SetVariable (
1573 VariableName,
1574 &gEfiGlobalVariableGuid,
1575 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
1576 VariableSize,
1577 NewVariable
1578 );
1579 //
1580 // Shrinking variable with existing variable driver implementation shouldn't fail.
1581 //
1582 ASSERT_EFI_ERROR (Status);
1583 }
1584
1585 if (NewVariable != NULL) {
1586 FreePool (NewVariable);
1587 }
1588
1589 return ;
1590 }
1591
1592 /**
1593 Build terminal device path according to terminal type.
1594
1595 @param TerminalType The terminal type is PC ANSI, VT100, VT100+ or VT-UTF8.
1596 @param ParentDevicePath Parent device path.
1597 @param TerminalDevicePath Returned terminal device path, if building successfully.
1598
1599 @retval EFI_UNSUPPORTED Terminal does not belong to the supported type.
1600 @retval EFI_OUT_OF_RESOURCES Generate terminal device path failed.
1601 @retval EFI_SUCCESS Build terminal device path successfully.
1602
1603 **/
1604 EFI_STATUS
1605 SetTerminalDevicePath (
1606 IN TERMINAL_TYPE TerminalType,
1607 IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath,
1608 OUT EFI_DEVICE_PATH_PROTOCOL **TerminalDevicePath
1609 )
1610 {
1611 VENDOR_DEVICE_PATH Node;
1612
1613 ASSERT (TerminalType < ARRAY_SIZE (mTerminalType));
1614 Node.Header.Type = MESSAGING_DEVICE_PATH;
1615 Node.Header.SubType = MSG_VENDOR_DP;
1616 SetDevicePathNodeLength (&Node, sizeof (VENDOR_DEVICE_PATH));
1617 CopyGuid (&Node.Guid, mTerminalType[TerminalType]);
1618
1619 //
1620 // Append the terminal node onto parent device path
1621 // to generate a complete terminal device path.
1622 //
1623 *TerminalDevicePath = AppendDevicePathNode (
1624 ParentDevicePath,
1625 (EFI_DEVICE_PATH_PROTOCOL *) &Node
1626 );
1627 if (*TerminalDevicePath == NULL) {
1628 return EFI_OUT_OF_RESOURCES;
1629 }
1630
1631 return EFI_SUCCESS;
1632 }
1633
1634 /**
1635 The user Entry Point for module Terminal. The user code starts with this function.
1636
1637 @param ImageHandle The firmware allocated handle for the EFI image.
1638 @param SystemTable A pointer to the EFI System Table.
1639
1640 @retval EFI_SUCCESS The entry point is executed successfully.
1641 @retval other Some error occurs when executing this entry point.
1642
1643 **/
1644 EFI_STATUS
1645 EFIAPI
1646 InitializeTerminal(
1647 IN EFI_HANDLE ImageHandle,
1648 IN EFI_SYSTEM_TABLE *SystemTable
1649 )
1650 {
1651 EFI_STATUS Status;
1652
1653 //
1654 // Install driver model protocol(s).
1655 //
1656 Status = EfiLibInstallDriverBindingComponentName2 (
1657 ImageHandle,
1658 SystemTable,
1659 &gTerminalDriverBinding,
1660 ImageHandle,
1661 &gTerminalComponentName,
1662 &gTerminalComponentName2
1663 );
1664 ASSERT_EFI_ERROR (Status);
1665
1666 return Status;
1667 }
1668
1669 /**
1670 Check if the device supports hot-plug through its device path.
1671
1672 This function could be updated to check more types of Hot Plug devices.
1673 Currently, it checks USB and PCCard device.
1674
1675 @param DevicePath Pointer to device's device path.
1676
1677 @retval TRUE The devcie is a hot-plug device
1678 @retval FALSE The devcie is not a hot-plug device.
1679
1680 **/
1681 BOOLEAN
1682 IsHotPlugDevice (
1683 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
1684 )
1685 {
1686 EFI_DEVICE_PATH_PROTOCOL *CheckDevicePath;
1687
1688 CheckDevicePath = DevicePath;
1689 while (!IsDevicePathEnd (CheckDevicePath)) {
1690 //
1691 // Check device whether is hot plug device or not throught Device Path
1692 //
1693 if ((DevicePathType (CheckDevicePath) == MESSAGING_DEVICE_PATH) &&
1694 (DevicePathSubType (CheckDevicePath) == MSG_USB_DP ||
1695 DevicePathSubType (CheckDevicePath) == MSG_USB_CLASS_DP ||
1696 DevicePathSubType (CheckDevicePath) == MSG_USB_WWID_DP)) {
1697 //
1698 // If Device is USB device
1699 //
1700 return TRUE;
1701 }
1702 if ((DevicePathType (CheckDevicePath) == HARDWARE_DEVICE_PATH) &&
1703 (DevicePathSubType (CheckDevicePath) == HW_PCCARD_DP)) {
1704 //
1705 // If Device is PCCard
1706 //
1707 return TRUE;
1708 }
1709
1710 CheckDevicePath = NextDevicePathNode (CheckDevicePath);
1711 }
1712
1713 return FALSE;
1714 }
1715