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