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