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