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