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