]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Console/TerminalDxe/Terminal.c
8a38aa8026fcc443dd1ed8232ab7be6170ebf6b1
[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.Reset = TerminalConOutReset;
493 TerminalDevice->SimpleTextOutput.OutputString = TerminalConOutOutputString;
494 TerminalDevice->SimpleTextOutput.TestString = TerminalConOutTestString;
495 TerminalDevice->SimpleTextOutput.QueryMode = TerminalConOutQueryMode;
496 TerminalDevice->SimpleTextOutput.SetMode = TerminalConOutSetMode;
497 TerminalDevice->SimpleTextOutput.SetAttribute = TerminalConOutSetAttribute;
498 TerminalDevice->SimpleTextOutput.ClearScreen = TerminalConOutClearScreen;
499 TerminalDevice->SimpleTextOutput.SetCursorPosition = TerminalConOutSetCursorPosition;
500 TerminalDevice->SimpleTextOutput.EnableCursor = TerminalConOutEnableCursor;
501 TerminalDevice->SimpleTextOutput.Mode = &TerminalDevice->SimpleTextOutputMode;
502
503 TerminalDevice->SimpleTextOutputMode.MaxMode = 2;
504 //
505 // For terminal devices, cursor is always visible
506 //
507 TerminalDevice->SimpleTextOutputMode.CursorVisible = TRUE;
508 Status = TerminalDevice->SimpleTextOutput.SetAttribute (
509 &TerminalDevice->SimpleTextOutput,
510 EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)
511 );
512 if (EFI_ERROR (Status)) {
513 goto ReportError;
514 }
515
516 Status = TerminalDevice->SimpleTextOutput.Reset (
517 &TerminalDevice->SimpleTextOutput,
518 FALSE
519 );
520 if (EFI_ERROR (Status)) {
521 goto ReportError;
522 }
523
524 Status = TerminalDevice->SimpleTextOutput.SetMode (
525 &TerminalDevice->SimpleTextOutput,
526 0
527 );
528 if (EFI_ERROR (Status)) {
529 goto ReportError;
530 }
531
532 Status = TerminalDevice->SimpleTextOutput.EnableCursor (
533 &TerminalDevice->SimpleTextOutput,
534 TRUE
535 );
536 if (EFI_ERROR (Status)) {
537 goto ReportError;
538 }
539
540 Status = gBS->CreateEvent (
541 EVT_TIMER,
542 TPL_CALLBACK,
543 NULL,
544 NULL,
545 &TerminalDevice->TwoSecondTimeOut
546 );
547
548 //
549 // Build the component name for the child device
550 //
551 TerminalDevice->ControllerNameTable = NULL;
552 switch (TerminalDevice->TerminalType) {
553 case PcAnsiType:
554 AddUnicodeString2 (
555 "eng",
556 gTerminalComponentName.SupportedLanguages,
557 &TerminalDevice->ControllerNameTable,
558 (CHAR16 *)L"PC-ANSI Serial Console",
559 TRUE
560 );
561 AddUnicodeString2 (
562 "en",
563 gTerminalComponentName2.SupportedLanguages,
564 &TerminalDevice->ControllerNameTable,
565 (CHAR16 *)L"PC-ANSI Serial Console",
566 FALSE
567 );
568
569 break;
570
571 case VT100Type:
572 AddUnicodeString2 (
573 "eng",
574 gTerminalComponentName.SupportedLanguages,
575 &TerminalDevice->ControllerNameTable,
576 (CHAR16 *)L"VT-100 Serial Console",
577 TRUE
578 );
579 AddUnicodeString2 (
580 "en",
581 gTerminalComponentName2.SupportedLanguages,
582 &TerminalDevice->ControllerNameTable,
583 (CHAR16 *)L"VT-100 Serial Console",
584 FALSE
585 );
586
587 break;
588
589 case VT100PlusType:
590 AddUnicodeString2 (
591 "eng",
592 gTerminalComponentName.SupportedLanguages,
593 &TerminalDevice->ControllerNameTable,
594 (CHAR16 *)L"VT-100+ Serial Console",
595 TRUE
596 );
597 AddUnicodeString2 (
598 "en",
599 gTerminalComponentName2.SupportedLanguages,
600 &TerminalDevice->ControllerNameTable,
601 (CHAR16 *)L"VT-100+ Serial Console",
602 FALSE
603 );
604
605 break;
606
607 case VTUTF8Type:
608 AddUnicodeString2 (
609 "eng",
610 gTerminalComponentName.SupportedLanguages,
611 &TerminalDevice->ControllerNameTable,
612 (CHAR16 *)L"VT-UTF8 Serial Console",
613 TRUE
614 );
615 AddUnicodeString2 (
616 "en",
617 gTerminalComponentName2.SupportedLanguages,
618 &TerminalDevice->ControllerNameTable,
619 (CHAR16 *)L"VT-UTF8 Serial Console",
620 FALSE
621 );
622
623 break;
624 }
625 //
626 // Install protocol interfaces for the serial device.
627 //
628 Status = gBS->InstallMultipleProtocolInterfaces (
629 &TerminalDevice->Handle,
630 &gEfiDevicePathProtocolGuid,
631 TerminalDevice->DevicePath,
632 &gEfiSimpleTextInProtocolGuid,
633 &TerminalDevice->SimpleInput,
634 &gEfiSimpleTextInputExProtocolGuid,
635 &TerminalDevice->SimpleInputEx,
636 &gEfiSimpleTextOutProtocolGuid,
637 &TerminalDevice->SimpleTextOutput,
638 NULL
639 );
640 if (EFI_ERROR (Status)) {
641 goto Error;
642 }
643 //
644 // if the serial device is a hot plug device, attaches the HotPlugGuid
645 // onto the terminal device handle.
646 //
647 Status = gBS->OpenProtocol (
648 Controller,
649 &gEfiHotPlugDeviceGuid,
650 NULL,
651 This->DriverBindingHandle,
652 Controller,
653 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
654 );
655 if (!EFI_ERROR (Status)) {
656 Status = gBS->InstallMultipleProtocolInterfaces (
657 &TerminalDevice->Handle,
658 &gEfiHotPlugDeviceGuid,
659 NULL,
660 NULL
661 );
662 }
663 //
664 // Register the Parent-Child relationship via
665 // EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
666 //
667 Status = gBS->OpenProtocol (
668 Controller,
669 &gEfiSerialIoProtocolGuid,
670 (VOID **) &TerminalDevice->SerialIo,
671 This->DriverBindingHandle,
672 TerminalDevice->Handle,
673 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
674 );
675 if (EFI_ERROR (Status)) {
676 goto Error;
677 }
678
679 if (DefaultNode != NULL) {
680 FreePool (DefaultNode);
681 }
682
683 return EFI_SUCCESS;
684
685 ReportError:
686 //
687 // Report error code before exiting
688 //
689 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
690 EFI_ERROR_CODE | EFI_ERROR_MINOR,
691 PcdGet32 (PcdStatusCodeValueRemoteConsoleError),
692 DevicePath
693 );
694
695 Error:
696 //
697 // Use the Stop() function to free all resources allocated in Start()
698 //
699 if (TerminalDevice != NULL) {
700
701 if (TerminalDevice->Handle != NULL) {
702 This->Stop (This, Controller, 1, &TerminalDevice->Handle);
703 } else {
704
705 if (TerminalDevice->TwoSecondTimeOut != NULL) {
706 gBS->CloseEvent (TerminalDevice->TwoSecondTimeOut);
707 }
708
709 if (TerminalDevice->SimpleInput.WaitForKey != NULL) {
710 gBS->CloseEvent (TerminalDevice->SimpleInput.WaitForKey);
711 }
712
713 if (TerminalDevice->SimpleInputEx.WaitForKeyEx != NULL) {
714 gBS->CloseEvent (TerminalDevice->SimpleInputEx.WaitForKeyEx);
715 }
716
717 TerminalFreeNotifyList (&TerminalDevice->NotifyList);
718
719 if (TerminalDevice->ControllerNameTable != NULL) {
720 FreeUnicodeStringTable (TerminalDevice->ControllerNameTable);
721 }
722
723 if (TerminalDevice->DevicePath != NULL) {
724 FreePool (TerminalDevice->DevicePath);
725 }
726
727 FreePool (TerminalDevice);
728 }
729 }
730
731 if (DefaultNode != NULL) {
732 FreePool (DefaultNode);
733 }
734
735 This->Stop (This, Controller, 0, NULL);
736
737 return Status;
738 }
739
740 EFI_STATUS
741 EFIAPI
742 TerminalDriverBindingStop (
743 IN EFI_DRIVER_BINDING_PROTOCOL *This,
744 IN EFI_HANDLE Controller,
745 IN UINTN NumberOfChildren,
746 IN EFI_HANDLE *ChildHandleBuffer
747 )
748 /*++
749
750 Routine Description:
751
752 Stop a device controller.
753
754 Arguments:
755
756 This - A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
757 Controller - A handle to the device being stopped.
758 NumberOfChildren - The number of child device handles in ChildHandleBuffer.
759 ChildHandleBuffer - An array of child handles to be freed.
760
761 Returns:
762
763 EFI_SUCCESS - Operation successful.
764 EFI_DEVICE_ERROR - Devices error.
765
766 --*/
767 {
768 EFI_STATUS Status;
769 UINTN Index;
770 BOOLEAN AllChildrenStopped;
771 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOutput;
772 TERMINAL_DEV *TerminalDevice;
773 EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
774 EFI_SERIAL_IO_PROTOCOL *SerialIo;
775 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
776
777 Status = gBS->HandleProtocol (
778 Controller,
779 &gEfiDevicePathProtocolGuid,
780 (VOID **) &DevicePath
781 );
782 if (EFI_ERROR (Status)) {
783 return Status;
784 }
785
786 //
787 // Complete all outstanding transactions to Controller.
788 // Don't allow any new transaction to Controller to be started.
789 //
790 if (NumberOfChildren == 0) {
791 //
792 // Close the bus driver
793 //
794 Status = gBS->OpenProtocol (
795 Controller,
796 &gEfiCallerIdGuid,
797 (VOID **) &ParentDevicePath,
798 This->DriverBindingHandle,
799 Controller,
800 EFI_OPEN_PROTOCOL_GET_PROTOCOL
801 );
802 if (!EFI_ERROR (Status)) {
803 //
804 // Remove Parent Device Path from
805 // the Console Device Environment Variables
806 //
807 TerminalRemoveConsoleDevVariable (L"ConInDev", ParentDevicePath);
808 TerminalRemoveConsoleDevVariable (L"ConOutDev", ParentDevicePath);
809 TerminalRemoveConsoleDevVariable (L"ErrOutDev", ParentDevicePath);
810
811 //
812 // Uninstall the Terminal Driver's GUID Tag from the Serial controller
813 //
814 Status = gBS->UninstallMultipleProtocolInterfaces (
815 Controller,
816 &gEfiCallerIdGuid,
817 ParentDevicePath,
818 NULL
819 );
820
821 //
822 // Free the ParentDevicePath that was duplicated in Start()
823 //
824 if (!EFI_ERROR (Status)) {
825 FreePool (ParentDevicePath);
826 }
827 }
828
829 gBS->CloseProtocol (
830 Controller,
831 &gEfiSerialIoProtocolGuid,
832 This->DriverBindingHandle,
833 Controller
834 );
835
836 gBS->CloseProtocol (
837 Controller,
838 &gEfiDevicePathProtocolGuid,
839 This->DriverBindingHandle,
840 Controller
841 );
842
843 return EFI_SUCCESS;
844 }
845
846 AllChildrenStopped = TRUE;
847
848 for (Index = 0; Index < NumberOfChildren; Index++) {
849
850 Status = gBS->OpenProtocol (
851 ChildHandleBuffer[Index],
852 &gEfiSimpleTextOutProtocolGuid,
853 (VOID **) &SimpleTextOutput,
854 This->DriverBindingHandle,
855 ChildHandleBuffer[Index],
856 EFI_OPEN_PROTOCOL_GET_PROTOCOL
857 );
858 if (!EFI_ERROR (Status)) {
859
860 TerminalDevice = TERMINAL_CON_OUT_DEV_FROM_THIS (SimpleTextOutput);
861
862 gBS->CloseProtocol (
863 Controller,
864 &gEfiSerialIoProtocolGuid,
865 This->DriverBindingHandle,
866 ChildHandleBuffer[Index]
867 );
868
869 Status = gBS->UninstallMultipleProtocolInterfaces (
870 ChildHandleBuffer[Index],
871 &gEfiSimpleTextInProtocolGuid,
872 &TerminalDevice->SimpleInput,
873 &gEfiSimpleTextInputExProtocolGuid,
874 &TerminalDevice->SimpleInputEx,
875 &gEfiSimpleTextOutProtocolGuid,
876 &TerminalDevice->SimpleTextOutput,
877 &gEfiDevicePathProtocolGuid,
878 TerminalDevice->DevicePath,
879 NULL
880 );
881 if (EFI_ERROR (Status)) {
882 gBS->OpenProtocol (
883 Controller,
884 &gEfiSerialIoProtocolGuid,
885 (VOID **) &SerialIo,
886 This->DriverBindingHandle,
887 ChildHandleBuffer[Index],
888 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
889 );
890 } else {
891
892 if (TerminalDevice->ControllerNameTable != NULL) {
893 FreeUnicodeStringTable (TerminalDevice->ControllerNameTable);
894 }
895
896 Status = gBS->OpenProtocol (
897 ChildHandleBuffer[Index],
898 &gEfiHotPlugDeviceGuid,
899 NULL,
900 This->DriverBindingHandle,
901 Controller,
902 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
903 );
904 if (!EFI_ERROR (Status)) {
905 Status = gBS->UninstallMultipleProtocolInterfaces (
906 ChildHandleBuffer[Index],
907 &gEfiHotPlugDeviceGuid,
908 NULL,
909 NULL
910 );
911 } else {
912 Status = EFI_SUCCESS;
913 }
914
915 gBS->CloseEvent (TerminalDevice->TwoSecondTimeOut);
916 gBS->CloseEvent (TerminalDevice->SimpleInput.WaitForKey);
917 gBS->CloseEvent (TerminalDevice->SimpleInputEx.WaitForKeyEx);
918 TerminalFreeNotifyList (&TerminalDevice->NotifyList);
919 FreePool (TerminalDevice->DevicePath);
920 FreePool (TerminalDevice);
921 }
922 }
923
924 if (EFI_ERROR (Status)) {
925 AllChildrenStopped = FALSE;
926 }
927 }
928
929 if (!AllChildrenStopped) {
930 return EFI_DEVICE_ERROR;
931 }
932
933 return EFI_SUCCESS;
934 }
935
936 STATIC
937 EFI_STATUS
938 TerminalFreeNotifyList (
939 IN OUT LIST_ENTRY *ListHead
940 )
941 /*++
942
943 Routine Description:
944
945 Arguments:
946
947 ListHead - The list head
948
949 Returns:
950
951 EFI_SUCCESS - Free the notify list successfully
952 EFI_INVALID_PARAMETER - ListHead is invalid.
953
954 --*/
955 {
956 TERMINAL_CONSOLE_IN_EX_NOTIFY *NotifyNode;
957
958 if (ListHead == NULL) {
959 return EFI_INVALID_PARAMETER;
960 }
961 while (!IsListEmpty (ListHead)) {
962 NotifyNode = CR (
963 ListHead->ForwardLink,
964 TERMINAL_CONSOLE_IN_EX_NOTIFY,
965 NotifyEntry,
966 TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE
967 );
968 RemoveEntryList (ListHead->ForwardLink);
969 gBS->FreePool (NotifyNode);
970 }
971
972 return EFI_SUCCESS;
973 }
974
975
976
977 VOID
978 TerminalUpdateConsoleDevVariable (
979 IN CHAR16 *VariableName,
980 IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath
981 )
982 {
983 EFI_STATUS Status;
984 UINTN VariableSize;
985 UINT8 TerminalType;
986 EFI_DEVICE_PATH_PROTOCOL *Variable;
987 EFI_DEVICE_PATH_PROTOCOL *NewVariable;
988 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
989
990 Variable = NULL;
991
992 //
993 // Get global variable and its size according to the name given.
994 //
995 Variable = TerminalGetVariableAndSize (
996 VariableName,
997 &gEfiGlobalVariableGuid,
998 &VariableSize
999 );
1000 //
1001 // Append terminal device path onto the variable.
1002 //
1003 for (TerminalType = PcAnsiType; TerminalType <= VTUTF8Type; TerminalType++) {
1004 SetTerminalDevicePath (TerminalType, ParentDevicePath, &TempDevicePath);
1005 NewVariable = AppendDevicePathInstance (Variable, TempDevicePath);
1006 if (Variable != NULL) {
1007 FreePool (Variable);
1008 }
1009
1010 if (TempDevicePath != NULL) {
1011 FreePool (TempDevicePath);
1012 }
1013
1014 Variable = NewVariable;
1015 }
1016
1017 VariableSize = GetDevicePathSize (Variable);
1018
1019 Status = gRT->SetVariable (
1020 VariableName,
1021 &gEfiGlobalVariableGuid,
1022 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
1023 VariableSize,
1024 Variable
1025 );
1026 ASSERT_EFI_ERROR (Status);
1027 FreePool (Variable);
1028
1029 return ;
1030 }
1031
1032 VOID
1033 TerminalRemoveConsoleDevVariable (
1034 IN CHAR16 *VariableName,
1035 IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath
1036 )
1037 /*++
1038
1039 Routine Description:
1040
1041 Remove console device variable.
1042
1043 Arguments:
1044
1045 VariableName - A pointer to the variable name.
1046 ParentDevicePath - A pointer to the parent device path.
1047
1048 Returns:
1049
1050 --*/
1051 {
1052 EFI_STATUS Status;
1053 BOOLEAN FoundOne;
1054 BOOLEAN Match;
1055 UINTN VariableSize;
1056 UINTN InstanceSize;
1057 UINT8 TerminalType;
1058 EFI_DEVICE_PATH_PROTOCOL *Instance;
1059 EFI_DEVICE_PATH_PROTOCOL *Variable;
1060 EFI_DEVICE_PATH_PROTOCOL *OriginalVariable;
1061 EFI_DEVICE_PATH_PROTOCOL *NewVariable;
1062 EFI_DEVICE_PATH_PROTOCOL *SavedNewVariable;
1063 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
1064
1065 Variable = NULL;
1066 Instance = NULL;
1067
1068 //
1069 // Get global variable and its size according to the name given.
1070 //
1071 Variable = TerminalGetVariableAndSize (
1072 VariableName,
1073 &gEfiGlobalVariableGuid,
1074 &VariableSize
1075 );
1076 if (Variable == NULL) {
1077 return ;
1078 }
1079
1080 FoundOne = FALSE;
1081 OriginalVariable = Variable;
1082 NewVariable = NULL;
1083
1084 //
1085 // Get first device path instance from Variable
1086 //
1087 Instance = GetNextDevicePathInstance (&Variable, &InstanceSize);
1088 if (Instance == NULL) {
1089 FreePool (OriginalVariable);
1090 return ;
1091 }
1092 //
1093 // Loop through all the device path instances of Variable
1094 //
1095 do {
1096 //
1097 // Loop through all the terminal types that this driver supports
1098 //
1099 Match = FALSE;
1100 for (TerminalType = PcAnsiType; TerminalType <= VTUTF8Type; TerminalType++) {
1101
1102 SetTerminalDevicePath (TerminalType, ParentDevicePath, &TempDevicePath);
1103
1104 //
1105 // Compare the genterated device path to the current device path instance
1106 //
1107 if (TempDevicePath != NULL) {
1108 if (CompareMem (Instance, TempDevicePath, InstanceSize) == 0) {
1109 Match = TRUE;
1110 FoundOne = TRUE;
1111 }
1112
1113 FreePool (TempDevicePath);
1114 }
1115 }
1116 //
1117 // If a match was not found, then keep the current device path instance
1118 //
1119 if (!Match) {
1120 SavedNewVariable = NewVariable;
1121 NewVariable = AppendDevicePathInstance (NewVariable, Instance);
1122 if (SavedNewVariable != NULL) {
1123 FreePool (SavedNewVariable);
1124 }
1125 }
1126 //
1127 // Get next device path instance from Variable
1128 //
1129 FreePool (Instance);
1130 Instance = GetNextDevicePathInstance (&Variable, &InstanceSize);
1131 } while (Instance != NULL);
1132
1133 FreePool (OriginalVariable);
1134
1135 if (FoundOne) {
1136 VariableSize = GetDevicePathSize (NewVariable);
1137
1138 Status = gRT->SetVariable (
1139 VariableName,
1140 &gEfiGlobalVariableGuid,
1141 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
1142 VariableSize,
1143 NewVariable
1144 );
1145 ASSERT_EFI_ERROR (Status);
1146 }
1147
1148 if (NewVariable != NULL) {
1149 FreePool (NewVariable);
1150 }
1151
1152 return ;
1153 }
1154
1155 VOID *
1156 TerminalGetVariableAndSize (
1157 IN CHAR16 *Name,
1158 IN EFI_GUID *VendorGuid,
1159 OUT UINTN *VariableSize
1160 )
1161 /*++
1162
1163 Routine Description:
1164 Read the EFI variable (VendorGuid/Name) and return a dynamically allocated
1165 buffer, and the size of the buffer. On failure return NULL.
1166
1167 Arguments:
1168 Name - String part of EFI variable name
1169
1170 VendorGuid - GUID part of EFI variable name
1171
1172 VariableSize - Returns the size of the EFI variable that was read
1173
1174 Returns:
1175 Dynamically allocated memory that contains a copy of the EFI variable.
1176 Caller is repsoncible freeing the buffer.
1177
1178 NULL - Variable was not read
1179
1180 --*/
1181 {
1182 EFI_STATUS Status;
1183 UINTN BufferSize;
1184 VOID *Buffer;
1185
1186 Buffer = NULL;
1187
1188 //
1189 // Pass in a small size buffer to find the actual variable size.
1190 //
1191 BufferSize = 1;
1192 Buffer = AllocatePool (BufferSize);
1193 if (Buffer == NULL) {
1194 *VariableSize = 0;
1195 return NULL;
1196 }
1197
1198 Status = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer);
1199
1200 if (Status == EFI_SUCCESS) {
1201 *VariableSize = BufferSize;
1202 return Buffer;
1203
1204 } else if (Status == EFI_BUFFER_TOO_SMALL) {
1205 //
1206 // Allocate the buffer to return
1207 //
1208 FreePool (Buffer);
1209 Buffer = AllocatePool (BufferSize);
1210 if (Buffer == NULL) {
1211 *VariableSize = 0;
1212 return NULL;
1213 }
1214 //
1215 // Read variable into the allocated buffer.
1216 //
1217 Status = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer);
1218 if (EFI_ERROR (Status)) {
1219 BufferSize = 0;
1220 FreePool (Buffer);
1221 Buffer = NULL;
1222 }
1223 } else {
1224 //
1225 // Variable not found or other errors met.
1226 //
1227 BufferSize = 0;
1228 FreePool (Buffer);
1229 Buffer = NULL;
1230 }
1231
1232 *VariableSize = BufferSize;
1233 return Buffer;
1234 }
1235
1236 EFI_STATUS
1237 SetTerminalDevicePath (
1238 IN UINT8 TerminalType,
1239 IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath,
1240 OUT EFI_DEVICE_PATH_PROTOCOL **TerminalDevicePath
1241 )
1242 {
1243 VENDOR_DEVICE_PATH Node;
1244
1245 *TerminalDevicePath = NULL;
1246 Node.Header.Type = MESSAGING_DEVICE_PATH;
1247 Node.Header.SubType = MSG_VENDOR_DP;
1248
1249 //
1250 // generate terminal device path node according to terminal type.
1251 //
1252 switch (TerminalType) {
1253
1254 case PcAnsiType:
1255 CopyMem (
1256 &Node.Guid,
1257 &gEfiPcAnsiGuid,
1258 sizeof (EFI_GUID)
1259 );
1260 break;
1261
1262 case VT100Type:
1263 CopyMem (
1264 &Node.Guid,
1265 &gEfiVT100Guid,
1266 sizeof (EFI_GUID)
1267 );
1268 break;
1269
1270 case VT100PlusType:
1271 CopyMem (
1272 &Node.Guid,
1273 &gEfiVT100PlusGuid,
1274 sizeof (EFI_GUID)
1275 );
1276 break;
1277
1278 case VTUTF8Type:
1279 CopyMem (
1280 &Node.Guid,
1281 &gEfiVTUTF8Guid,
1282 sizeof (EFI_GUID)
1283 );
1284 break;
1285
1286 default:
1287 return EFI_UNSUPPORTED;
1288 break;
1289 }
1290
1291 SetDevicePathNodeLength (
1292 &Node.Header,
1293 sizeof (VENDOR_DEVICE_PATH)
1294 );
1295 //
1296 // append the terminal node onto parent device path
1297 // to generate a complete terminal device path.
1298 //
1299 *TerminalDevicePath = AppendDevicePathNode (
1300 ParentDevicePath,
1301 (EFI_DEVICE_PATH_PROTOCOL *) &Node
1302 );
1303 if (*TerminalDevicePath == NULL) {
1304 return EFI_OUT_OF_RESOURCES;
1305 }
1306
1307 return EFI_SUCCESS;
1308 }
1309
1310 VOID
1311 InitializeRawFiFo (
1312 IN TERMINAL_DEV *TerminalDevice
1313 )
1314 {
1315 //
1316 // Make the raw fifo empty.
1317 //
1318 TerminalDevice->RawFiFo.Head = TerminalDevice->RawFiFo.Tail;
1319 }
1320
1321 VOID
1322 InitializeUnicodeFiFo (
1323 IN TERMINAL_DEV *TerminalDevice
1324 )
1325 {
1326 //
1327 // Make the unicode fifo empty
1328 //
1329 TerminalDevice->UnicodeFiFo.Head = TerminalDevice->UnicodeFiFo.Tail;
1330 }
1331
1332 VOID
1333 InitializeEfiKeyFiFo (
1334 IN TERMINAL_DEV *TerminalDevice
1335 )
1336 {
1337 //
1338 // Make the efi key fifo empty
1339 //
1340 TerminalDevice->EfiKeyFiFo.Head = TerminalDevice->EfiKeyFiFo.Tail;
1341 }
1342
1343
1344 /**
1345 The user Entry Point for module Terminal. The user code starts with this function.
1346
1347 @param[in] ImageHandle The firmware allocated handle for the EFI image.
1348 @param[in] SystemTable A pointer to the EFI System Table.
1349
1350 @retval EFI_SUCCESS The entry point is executed successfully.
1351 @retval other Some error occurs when executing this entry point.
1352
1353 **/
1354 EFI_STATUS
1355 EFIAPI
1356 InitializeTerminal(
1357 IN EFI_HANDLE ImageHandle,
1358 IN EFI_SYSTEM_TABLE *SystemTable
1359 )
1360 {
1361 EFI_STATUS Status;
1362
1363 //
1364 // Install driver model protocol(s).
1365 //
1366 Status = EfiLibInstallDriverBindingComponentName2 (
1367 ImageHandle,
1368 SystemTable,
1369 &gTerminalDriverBinding,
1370 ImageHandle,
1371 &gTerminalComponentName,
1372 &gTerminalComponentName2
1373 );
1374 ASSERT_EFI_ERROR (Status);
1375
1376
1377 return Status;
1378 }