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