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