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