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