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