From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: mx.groups.io; dkim=missing; spf=pass (domain: intel.com, ip: 192.55.52.115, mailfrom: michael.d.kinney@intel.com) Received: from mga14.intel.com (mga14.intel.com [192.55.52.115]) by groups.io with SMTP; Thu, 09 May 2019 20:35:11 -0700 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga002.jf.intel.com ([10.7.209.21]) by fmsmga103.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 09 May 2019 20:35:02 -0700 X-ExtLoop1: 1 Received: from unknown (HELO mdkinney-MOBL2.amr.corp.intel.com) ([10.241.98.74]) by orsmga002.jf.intel.com with ESMTP; 09 May 2019 20:34:57 -0700 From: "Michael D Kinney" To: devel@edk2.groups.io Cc: Ray Ni , Leif Lindholm , Ard Biesheuvel Subject: [edk2-platforms: Patch 7/8] Drivers/OptionRomPkg: Import OptionRomPkg from edk2 Date: Thu, 9 May 2019 20:34:34 -0700 Message-Id: <20190510033435.24112-8-michael.d.kinney@intel.com> X-Mailer: git-send-email 2.21.0.windows.1 In-Reply-To: <20190510033435.24112-1-michael.d.kinney@intel.com> References: <20190510033435.24112-1-michael.d.kinney@intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit https://bugzilla.tianocore.org/show_bug.cgi?id=1793 Import OptionRomPkg from edk2/master. Cc: Ray Ni Cc: Leif Lindholm Cc: Ard Biesheuvel Signed-off-by: Michael D Kinney --- .../Application/BltLibSample/BltLibSample.c | 279 ++ .../Application/BltLibSample/BltLibSample.inf | 30 + .../AtapiPassThruDxe/AtapiPassThru.c | 3410 ++++++++++++++++ .../AtapiPassThruDxe/AtapiPassThru.h | 1618 ++++++++ .../AtapiPassThruDxe/AtapiPassThruDxe.inf | 70 + .../AtapiPassThruDxe/ComponentName.c | 169 + .../DriverSupportedEfiVersion.c | 14 + .../FtdiUsbSerialDxe/CompatibleDevices.txt | 5 + .../Bus/Usb/FtdiUsbSerialDxe/ComponentName.c | 218 + .../FtdiUsbSerialDxe/FtdiUsbSerialDriver.c | 2580 ++++++++++++ .../FtdiUsbSerialDxe/FtdiUsbSerialDriver.h | 589 +++ .../Usb/FtdiUsbSerialDxe/FtdiUsbSerialDxe.inf | 55 + .../Bus/Usb/FtdiUsbSerialDxe/ReadMe.txt | 32 + .../Bus/Usb/UsbNetworking/Ax88772/Ax88772.c | 1318 ++++++ .../Bus/Usb/UsbNetworking/Ax88772/Ax88772.h | 969 +++++ .../Bus/Usb/UsbNetworking/Ax88772/Ax88772.inf | 61 + .../Usb/UsbNetworking/Ax88772/ComponentName.c | 178 + .../Usb/UsbNetworking/Ax88772/DriverBinding.c | 507 +++ .../Usb/UsbNetworking/Ax88772/SimpleNetwork.c | 1503 +++++++ .../Bus/Usb/UsbNetworking/Ax88772b/Ax88772.c | 875 ++++ .../Bus/Usb/UsbNetworking/Ax88772b/Ax88772.h | 1026 +++++ .../Usb/UsbNetworking/Ax88772b/Ax88772b.inf | 61 + .../UsbNetworking/Ax88772b/ComponentName.c | 175 + .../UsbNetworking/Ax88772b/DriverBinding.c | 696 ++++ .../UsbNetworking/Ax88772b/SimpleNetwork.c | 1657 ++++++++ .../CirrusLogic5430Dxe/CirrusLogic5430.c | 917 +++++ .../CirrusLogic5430Dxe/CirrusLogic5430.h | 432 ++ .../CirrusLogic5430Dxe/CirrusLogic5430Dxe.inf | 84 + .../CirrusLogic5430GraphicsOutput.c | 556 +++ .../CirrusLogic5430Dxe/CirrusLogic5430I2c.c | 427 ++ .../CirrusLogic5430Dxe/CirrusLogic5430I2c.h | 62 + .../CirrusLogic5430UgaDraw.c | 412 ++ .../CirrusLogic5430Dxe/ComponentName.c | 203 + .../DriverSupportedEfiVersion.c | 14 + .../OptionRomPkg/CirrusLogic5430Dxe/Edid.c | 525 +++ Drivers/OptionRomPkg/Include/Library/BltLib.h | 253 ++ .../FrameBufferBltLib/FrameBufferBltLib.c | 744 ++++ .../FrameBufferBltLib/FrameBufferBltLib.inf | 29 + .../Library/GopBltLib/GopBltLib.c | 449 +++ .../Library/GopBltLib/GopBltLib.inf | 31 + Drivers/OptionRomPkg/OptionRomPkg.dec | 41 + Drivers/OptionRomPkg/OptionRomPkg.dsc | 113 + Drivers/OptionRomPkg/ReadMe.txt | 17 + .../UndiRuntimeDxe/ComponentName.c | 359 ++ Drivers/OptionRomPkg/UndiRuntimeDxe/Decode.c | 1516 +++++++ Drivers/OptionRomPkg/UndiRuntimeDxe/E100b.c | 3541 +++++++++++++++++ Drivers/OptionRomPkg/UndiRuntimeDxe/E100b.h | 665 ++++ Drivers/OptionRomPkg/UndiRuntimeDxe/Init.c | 1051 +++++ Drivers/OptionRomPkg/UndiRuntimeDxe/Undi32.h | 439 ++ .../OptionRomPkg/UndiRuntimeDxe/UndiAipImpl.c | 145 + .../UndiRuntimeDxe/UndiRuntimeDxe.inf | 72 + 51 files changed, 31192 insertions(+) create mode 100644 Drivers/OptionRomPkg/Application/BltLibSample/BltLibSample.c create mode 100644 Drivers/OptionRomPkg/Application/BltLibSample/BltLibSample.inf create mode 100644 Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThru.c create mode 100644 Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThru.h create mode 100644 Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThruDxe.inf create mode 100644 Drivers/OptionRomPkg/AtapiPassThruDxe/ComponentName.c create mode 100644 Drivers/OptionRomPkg/AtapiPassThruDxe/DriverSupportedEfiVersion.c create mode 100644 Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/CompatibleDevices.txt create mode 100644 Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ComponentName.c create mode 100644 Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.c create mode 100644 Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.h create mode 100644 Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDxe.inf create mode 100644 Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ReadMe.txt create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.c create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.h create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.inf create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/ComponentName.c create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/DriverBinding.c create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/SimpleNetwork.c create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772.c create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772.h create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772b.inf create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/ComponentName.c create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/DriverBinding.c create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/SimpleNetwork.c create mode 100644 Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430.c create mode 100644 Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430.h create mode 100644 Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430Dxe.inf create mode 100644 Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430GraphicsOutput.c create mode 100644 Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430I2c.c create mode 100644 Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430I2c.h create mode 100644 Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430UgaDraw.c create mode 100644 Drivers/OptionRomPkg/CirrusLogic5430Dxe/ComponentName.c create mode 100644 Drivers/OptionRomPkg/CirrusLogic5430Dxe/DriverSupportedEfiVersion.c create mode 100644 Drivers/OptionRomPkg/CirrusLogic5430Dxe/Edid.c create mode 100644 Drivers/OptionRomPkg/Include/Library/BltLib.h create mode 100644 Drivers/OptionRomPkg/Library/FrameBufferBltLib/FrameBufferBltLib.c create mode 100644 Drivers/OptionRomPkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf create mode 100644 Drivers/OptionRomPkg/Library/GopBltLib/GopBltLib.c create mode 100644 Drivers/OptionRomPkg/Library/GopBltLib/GopBltLib.inf create mode 100644 Drivers/OptionRomPkg/OptionRomPkg.dec create mode 100644 Drivers/OptionRomPkg/OptionRomPkg.dsc create mode 100644 Drivers/OptionRomPkg/ReadMe.txt create mode 100644 Drivers/OptionRomPkg/UndiRuntimeDxe/ComponentName.c create mode 100644 Drivers/OptionRomPkg/UndiRuntimeDxe/Decode.c create mode 100644 Drivers/OptionRomPkg/UndiRuntimeDxe/E100b.c create mode 100644 Drivers/OptionRomPkg/UndiRuntimeDxe/E100b.h create mode 100644 Drivers/OptionRomPkg/UndiRuntimeDxe/Init.c create mode 100644 Drivers/OptionRomPkg/UndiRuntimeDxe/Undi32.h create mode 100644 Drivers/OptionRomPkg/UndiRuntimeDxe/UndiAipImpl.c create mode 100644 Drivers/OptionRomPkg/UndiRuntimeDxe/UndiRuntimeDxe.inf diff --git a/Drivers/OptionRomPkg/Application/BltLibSample/BltLibSample.c b/Drivers/OptionRomPkg/Application/BltLibSample/BltLibSample.c new file mode 100644 index 0000000000..6f901383b6 --- /dev/null +++ b/Drivers/OptionRomPkg/Application/BltLibSample/BltLibSample.c @@ -0,0 +1,279 @@ +/** @file + Example program using BltLib + + Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include + + +UINT64 +ReadTimestamp ( + VOID + ) +{ +#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64) + return AsmReadTsc (); +#else +#error ReadTimestamp not supported for this architecture! +#endif +} + +UINT32 +Rand32 ( + VOID + ) +{ + UINTN Found; + INTN Bits; + UINT64 Tsc1; + UINT64 Tsc2; + UINT64 TscBits; + UINT32 R32; + + R32 = 0; + Found = 0; + Tsc1 = ReadTimestamp (); + Tsc2 = ReadTimestamp (); + do { + Tsc2 = ReadTimestamp (); + TscBits = Tsc2 ^ Tsc1; + Bits = HighBitSet64 (TscBits); + if (Bits > 0) { + Bits = Bits - 1; + } + R32 = (UINT32)((R32 << Bits) | + RShiftU64 (LShiftU64 (TscBits, (UINTN) (64 - Bits)), (UINTN) (64 - Bits))); + Found = Found + Bits; + } while (Found < 32); + + return R32; +} + + +VOID +TestFills ( + VOID + ) +{ + EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color; + UINTN Loop; + UINTN X; + UINTN Y; + UINTN W; + UINTN H; + UINTN Width; + UINTN Height; + + BltLibGetSizes (&Width, &Height); + for (Loop = 0; Loop < 10000; Loop++) { + W = Width - (Rand32 () % Width); + H = Height - (Rand32 () % Height); + if (W != Width) { + X = Rand32 () % (Width - W); + } else { + X = 0; + } + if (H != Height) { + Y = Rand32 () % (Height - H); + } else { + Y = 0; + } + *(UINT32*) (&Color) = Rand32 () & 0xffffff; + BltLibVideoFill (&Color, X, Y, W, H); + } +} + + +VOID +TestColor1 ( + VOID + ) +{ + EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color; + UINTN X; + UINTN Y; + UINTN Width; + UINTN Height; + + BltLibGetSizes (&Width, &Height); + *(UINT32*) (&Color) = 0; + + for (Y = 0; Y < Height; Y++) { + for (X = 0; X < Width; X++) { + Color.Red = (UINT8) ((X * 0x100) / Width); + Color.Green = (UINT8) ((Y * 0x100) / Height); + Color.Blue = (UINT8) ((Y * 0x100) / Height); + BltLibVideoFill (&Color, X, Y, 1, 1); + } + } +} + + +UINT32 +Uint32SqRt ( + IN UINT32 Uint32 + ) +{ + UINT32 Mask; + UINT32 SqRt; + UINT32 SqRtMask; + UINT32 Squared; + + if (Uint32 == 0) { + return 0; + } + + for (SqRt = 0, Mask = (UINT32) (1 << (HighBitSet32 (Uint32) / 2)); + Mask != 0; + Mask = Mask >> 1 + ) { + SqRtMask = SqRt | Mask; + //DEBUG ((EFI_D_INFO, "Uint32=0x%x SqRtMask=0x%x\n", Uint32, SqRtMask)); + Squared = (UINT32) (SqRtMask * SqRtMask); + if (Squared > Uint32) { + continue; + } else if (Squared < Uint32) { + SqRt = SqRtMask; + } else { + return SqRtMask; + } + } + + return SqRt; +} + + +UINT32 +Uint32Dist ( + IN UINTN X, + IN UINTN Y + ) +{ + return Uint32SqRt ((UINT32) ((X * X) + (Y * Y))); +} + +UINT8 +GetTriColor ( + IN UINTN ColorDist, + IN UINTN TriWidth + ) +{ + return (UINT8) (((TriWidth - ColorDist) * 0x100) / TriWidth); + //return (((TriWidth * TriWidth - ColorDist * ColorDist) * 0x100) / (TriWidth * TriWidth)); +} + +VOID +TestColor ( + VOID + ) +{ + EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color; + UINTN X, Y; + UINTN X1, X2, X3; + UINTN Y1, Y2; + UINTN LineWidth, TriWidth, ScreenWidth; + UINTN TriHeight, ScreenHeight; + UINT32 ColorDist; + + BltLibGetSizes (&ScreenWidth, &ScreenHeight); + *(UINT32*) (&Color) = 0; + BltLibVideoFill (&Color, 0, 0, ScreenWidth, ScreenHeight); + + TriWidth = (UINTN) DivU64x32 ( + MultU64x32 (11547005, (UINT32) ScreenHeight), + 10000000 + ); + TriHeight = (UINTN) DivU64x32 ( + MultU64x32 (8660254, (UINT32) ScreenWidth), + 10000000 + ); + if (TriWidth > ScreenWidth) { + DEBUG ((EFI_D_INFO, "TriWidth at %d was too big\n", TriWidth)); + TriWidth = ScreenWidth; + } else if (TriHeight > ScreenHeight) { + DEBUG ((EFI_D_INFO, "TriHeight at %d was too big\n", TriHeight)); + TriHeight = ScreenHeight; + } + + DEBUG ((EFI_D_INFO, "Triangle Width: %d; Height: %d\n", TriWidth, TriHeight)); + + X1 = (ScreenWidth - TriWidth) / 2; + X3 = X1 + TriWidth - 1; + X2 = (X1 + X3) / 2; + Y2 = (ScreenHeight - TriHeight) / 2; + Y1 = Y2 + TriHeight - 1; + + for (Y = Y2; Y <= Y1; Y++) { + LineWidth = + (UINTN) DivU64x32 ( + MultU64x32 (11547005, (UINT32) (Y - Y2)), + 20000000 + ); + for (X = X2 - LineWidth; X < (X2 + LineWidth); X++) { + ColorDist = Uint32Dist(X - X1, Y1 - Y); + Color.Red = GetTriColor (ColorDist, TriWidth); + + ColorDist = Uint32Dist((X < X2) ? X2 - X : X - X2, Y - Y2); + Color.Green = GetTriColor (ColorDist, TriWidth); + + ColorDist = Uint32Dist(X3 - X, Y1 - Y); + Color.Blue = GetTriColor (ColorDist, TriWidth); + + BltLibVideoFill (&Color, X, Y, 1, 1); + } + } +} + + +/** + The user Entry Point for Application. The user code starts with this function + as the real entry point for the application. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +UefiMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop; + + Status = gBS->HandleProtocol ( + gST->ConsoleOutHandle, + &gEfiGraphicsOutputProtocolGuid, + (VOID **) &Gop + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = BltLibConfigure ( + (VOID*)(UINTN) Gop->Mode->FrameBufferBase, + Gop->Mode->Info + ); + if (EFI_ERROR (Status)) { + return Status; + } + + TestFills (); + + TestColor (); + + return EFI_SUCCESS; +} diff --git a/Drivers/OptionRomPkg/Application/BltLibSample/BltLibSample.inf b/Drivers/OptionRomPkg/Application/BltLibSample/BltLibSample.inf new file mode 100644 index 0000000000..b544f960ab --- /dev/null +++ b/Drivers/OptionRomPkg/Application/BltLibSample/BltLibSample.inf @@ -0,0 +1,30 @@ +## @file +# Test the BltLib interface +# +# Copyright (c) 2008 - 2011, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BltLibSample + FILE_GUID = f7763316-8c04-41d8-a87d-45b73c13c43c + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = UefiMain + +[Sources] + BltLibSample.c + +[Packages] + MdePkg/MdePkg.dec + OptionRomPkg/OptionRomPkg.dec + +[LibraryClasses] + BltLib + UefiApplicationEntryPoint + UefiLib + diff --git a/Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThru.c b/Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThru.c new file mode 100644 index 0000000000..20de2bc392 --- /dev/null +++ b/Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThru.c @@ -0,0 +1,3410 @@ +/** @file + Copyright (c) 2006, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "AtapiPassThru.h" + + +SCSI_COMMAND_SET gEndTable = { 0xff, (DATA_DIRECTION) 0xff }; + +/// +/// This table contains all the supported ATAPI commands. +/// +SCSI_COMMAND_SET gSupportedATAPICommands[] = { + { OP_INQUIRY, DataIn }, + { OP_LOAD_UNLOAD_CD, NoData }, + { OP_MECHANISM_STATUS, DataIn }, + { OP_MODE_SELECT_10, DataOut }, + { OP_MODE_SENSE_10, DataIn }, + { OP_PAUSE_RESUME, NoData }, + { OP_PLAY_AUDIO_10, DataIn }, + { OP_PLAY_AUDIO_MSF, DataIn }, + { OP_PLAY_CD, DataIn }, + { OP_PLAY_CD_MSF, DataIn }, + { OP_PREVENT_ALLOW_MEDIUM_REMOVAL,NoData }, + { OP_READ_10, DataIn }, + { OP_READ_12, DataIn }, + { OP_READ_CAPACITY, DataIn }, + { OP_READ_CD, DataIn }, + { OP_READ_CD_MSF, DataIn }, + { OP_READ_HEADER, DataIn }, + { OP_READ_SUB_CHANNEL, DataIn }, + { OP_READ_TOC, DataIn }, + { OP_REQUEST_SENSE, DataIn }, + { OP_SCAN, NoData }, + { OP_SEEK_10, NoData }, + { OP_SET_CD_SPEED, DataOut }, + { OP_STOPPLAY_SCAN, NoData }, + { OP_START_STOP_UNIT, NoData }, + { OP_TEST_UNIT_READY, NoData }, + { OP_FORMAT_UNIT, DataOut }, + { OP_READ_FORMAT_CAPACITIES, DataIn }, + { OP_VERIFY, DataOut }, + { OP_WRITE_10, DataOut }, + { OP_WRITE_12, DataOut }, + { OP_WRITE_AND_VERIFY, DataOut }, + { 0xff, (DATA_DIRECTION) 0xff } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_SCSI_PASS_THRU_MODE gScsiPassThruMode = { + L"ATAPI Controller", + L"ATAPI Channel", + 4, + EFI_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL, + 0 +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_SCSI_PASS_THRU_PROTOCOL gScsiPassThruProtocolTemplate = { + &gScsiPassThruMode, + AtapiScsiPassThruFunction, + AtapiScsiPassThruGetNextDevice, + AtapiScsiPassThruBuildDevicePath, + AtapiScsiPassThruGetTargetLun, + AtapiScsiPassThruResetChannel, + AtapiScsiPassThruResetTarget +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_EXT_SCSI_PASS_THRU_MODE gExtScsiPassThruMode = { + 4, + EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL, + 0 +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_EXT_SCSI_PASS_THRU_PROTOCOL gExtScsiPassThruProtocolTemplate = { + &gExtScsiPassThruMode, + AtapiExtScsiPassThruFunction, + AtapiExtScsiPassThruGetNextTargetLun, + AtapiExtScsiPassThruBuildDevicePath, + AtapiExtScsiPassThruGetTargetLun, + AtapiExtScsiPassThruResetChannel, + AtapiExtScsiPassThruResetTarget, + AtapiExtScsiPassThruGetNextTarget +}; + +EFI_DRIVER_BINDING_PROTOCOL gAtapiScsiPassThruDriverBinding = { + AtapiScsiPassThruDriverBindingSupported, + AtapiScsiPassThruDriverBindingStart, + AtapiScsiPassThruDriverBindingStop, + 0x10, + NULL, + NULL +}; + +EFI_STATUS +EFIAPI +AtapiScsiPassThruDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +/*++ + +Routine Description: + Test to see if this driver supports ControllerHandle. Any ControllerHandle + that has gEfiPciIoProtocolGuid installed and is IDE Controller it will be supported. + +Arguments: + + This - Protocol instance pointer. + Controller - Handle of device to test + RemainingDevicePath - Not used + +Returns: + EFI_STATUS + +--*/ +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE00 Pci; + + + // + // Open the IO Abstraction(s) needed to perform the supported test + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Use the PCI I/O Protocol to see if Controller is a IDE Controller that + // can be managed by this driver. Read the PCI Configuration Header + // for this device. + // + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (Pci) / sizeof (UINT32), + &Pci + ); + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return EFI_UNSUPPORTED; + } + + if (Pci.Hdr.ClassCode[2] != PCI_CLASS_MASS_STORAGE || Pci.Hdr.ClassCode[1] != PCI_CLASS_MASS_STORAGE_IDE) { + + Status = EFI_UNSUPPORTED; + } + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + +EFI_STATUS +EFIAPI +AtapiScsiPassThruDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +/*++ + +Routine Description: + Create handles for IDE channels specified by RemainingDevicePath. + Install SCSI Pass Thru Protocol onto each created handle. + +Arguments: + + This - Protocol instance pointer. + Controller - Handle of device to test + RemainingDevicePath - Not used + +Returns: + EFI_STATUS + +--*/ +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT64 Supports; + UINT64 OriginalPciAttributes; + BOOLEAN PciAttributesSaved; + + PciIo = NULL; + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + PciAttributesSaved = FALSE; + // + // Save original PCI attributes + // + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationGet, + 0, + &OriginalPciAttributes + ); + + if (EFI_ERROR (Status)) { + goto Done; + } + PciAttributesSaved = TRUE; + + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSupported, + 0, + &Supports + ); + if (!EFI_ERROR (Status)) { + Supports &= (EFI_PCI_DEVICE_ENABLE | + EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO | + EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO); + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationEnable, + Supports, + NULL + ); + } + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Create SCSI Pass Thru instance for the IDE channel. + // + Status = RegisterAtapiScsiPassThru (This, Controller, PciIo, OriginalPciAttributes); + +Done: + if (EFI_ERROR (Status)) { + if (PciAttributesSaved == TRUE) { + // + // Restore original PCI attributes + // + PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSet, + OriginalPciAttributes, + NULL + ); + } + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + + return Status; +} + +EFI_STATUS +EFIAPI +AtapiScsiPassThruDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +/*++ + +Routine Description: + + Stop this driver on ControllerHandle. Support stopping any child handles + created by this driver. + +Arguments: + + This - Protocol instance pointer. + Controller - Handle of device to stop driver on + NumberOfChildren - Number of Children in the ChildHandleBuffer + ChildHandleBuffer - List of handles for the children we need to stop. + +Returns: + + EFI_STATUS + +--*/ +{ + EFI_STATUS Status; + EFI_SCSI_PASS_THRU_PROTOCOL *ScsiPassThru; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiPassThru; + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; + + if (FeaturePcdGet (PcdSupportScsiPassThru)) { + Status = gBS->OpenProtocol ( + Controller, + &gEfiScsiPassThruProtocolGuid, + (VOID **) &ScsiPassThru, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (ScsiPassThru); + if (FeaturePcdGet (PcdSupportExtScsiPassThru)) { + Status = gBS->UninstallMultipleProtocolInterfaces ( + Controller, + &gEfiScsiPassThruProtocolGuid, + &AtapiScsiPrivate->ScsiPassThru, + &gEfiExtScsiPassThruProtocolGuid, + &AtapiScsiPrivate->ExtScsiPassThru, + NULL + ); + } else { + Status = gBS->UninstallMultipleProtocolInterfaces ( + Controller, + &gEfiScsiPassThruProtocolGuid, + &AtapiScsiPrivate->ScsiPassThru, + NULL + ); + } + } else { + Status = gBS->OpenProtocol ( + Controller, + &gEfiExtScsiPassThruProtocolGuid, + (VOID **) &ExtScsiPassThru, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + AtapiScsiPrivate = ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS (ExtScsiPassThru); + Status = gBS->UninstallMultipleProtocolInterfaces ( + Controller, + &gEfiExtScsiPassThruProtocolGuid, + &AtapiScsiPrivate->ExtScsiPassThru, + NULL + ); + } + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Restore original PCI attributes + // + AtapiScsiPrivate->PciIo->Attributes ( + AtapiScsiPrivate->PciIo, + EfiPciIoAttributeOperationSet, + AtapiScsiPrivate->OriginalPciAttributes, + NULL + ); + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + gBS->FreePool (AtapiScsiPrivate); + + return EFI_SUCCESS; +} + +EFI_STATUS +RegisterAtapiScsiPassThru ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT64 OriginalPciAttributes + ) +/*++ + +Routine Description: + Attaches SCSI Pass Thru Protocol for specified IDE channel. + +Arguments: + This - Protocol instance pointer. + Controller - Parent device handle to the IDE channel. + PciIo - PCI I/O protocol attached on the "Controller". + +Returns: + Always return EFI_SUCCESS unless installing SCSI Pass Thru Protocol failed. + +--*/ +{ + EFI_STATUS Status; + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; + IDE_REGISTERS_BASE_ADDR IdeRegsBaseAddr[ATAPI_MAX_CHANNEL]; + + AtapiScsiPrivate = AllocateZeroPool (sizeof (ATAPI_SCSI_PASS_THRU_DEV)); + if (AtapiScsiPrivate == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + AtapiScsiPrivate->Signature = ATAPI_SCSI_PASS_THRU_DEV_SIGNATURE; + AtapiScsiPrivate->Handle = Controller; + + // + // will reset the IoPort inside each API function. + // + AtapiScsiPrivate->IoPort = NULL; + AtapiScsiPrivate->PciIo = PciIo; + AtapiScsiPrivate->OriginalPciAttributes = OriginalPciAttributes; + + // + // Obtain IDE IO port registers' base addresses + // + Status = GetIdeRegistersBaseAddr (PciIo, IdeRegsBaseAddr); + if (EFI_ERROR (Status)) { + return Status; + } + + InitAtapiIoPortRegisters(AtapiScsiPrivate, IdeRegsBaseAddr); + + // + // Initialize the LatestTargetId to MAX_TARGET_ID. + // + AtapiScsiPrivate->LatestTargetId = MAX_TARGET_ID; + AtapiScsiPrivate->LatestLun = 0; + + Status = InstallScsiPassThruProtocols (&Controller, AtapiScsiPrivate); + + return Status; +} + +EFI_STATUS +EFIAPI +AtapiScsiPassThruFunction ( + IN EFI_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT32 Target, + IN UINT64 Lun, + IN OUT EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ) +/*++ + +Routine Description: + + Implements EFI_SCSI_PASS_THRU_PROTOCOL.PassThru() function. + +Arguments: + + This: The EFI_SCSI_PASS_THRU_PROTOCOL instance. + Target: The Target ID of the ATAPI device to send the SCSI + Request Packet. To ATAPI devices attached on an IDE + Channel, Target ID 0 indicates Master device;Target + ID 1 indicates Slave device. + Lun: The LUN of the ATAPI device to send the SCSI Request + Packet. To the ATAPI device, Lun is always 0. + Packet: The SCSI Request Packet to send to the ATAPI device + specified by Target and Lun. + Event: If non-blocking I/O is not supported then Event is ignored, + and blocking I/O is performed. + If Event is NULL, then blocking I/O is performed. + If Event is not NULL and non blocking I/O is supported, + then non-blocking I/O is performed, and Event will be signaled + when the SCSI Request Packet completes. + +Returns: + + EFI_STATUS + +--*/ +{ + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; + EFI_STATUS Status; + + AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This); + + // + // Target is not allowed beyond MAX_TARGET_ID + // + if ((Target > MAX_TARGET_ID) || (Lun != 0)) { + return EFI_INVALID_PARAMETER; + } + + // + // check the data fields in Packet parameter. + // + Status = CheckSCSIRequestPacket (Packet); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // If Request Packet targets at the IDE channel itself, + // do nothing. + // + if (Target == This->Mode->AdapterId) { + Packet->TransferLength = 0; + return EFI_SUCCESS; + } + + // + // According to Target ID, reset the Atapi I/O Register mapping + // (Target Id in [0,1] area, using AtapiIoPortRegisters[0], + // Target Id in [2,3] area, using AtapiIoPortRegisters[1] + // + if ((Target / 2) == 0) { + Target = Target % 2; + AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[0]; + } else { + Target = Target % 2; + AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[1]; + } + + // + // the ATAPI SCSI interface does not support non-blocking I/O + // ignore the Event parameter + // + // Performs blocking I/O. + // + Status = SubmitBlockingIoCommand (AtapiScsiPrivate, Target, Packet); + return Status; +} + +EFI_STATUS +EFIAPI +AtapiScsiPassThruGetNextDevice ( + IN EFI_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT32 *Target, + IN OUT UINT64 *Lun + ) +/*++ + +Routine Description: + + Used to retrieve the list of legal Target IDs for SCSI devices + on a SCSI channel. + +Arguments: + + This - Protocol instance pointer. + Target - On input, a pointer to the Target ID of a SCSI + device present on the SCSI channel. On output, + a pointer to the Target ID of the next SCSI device + present on a SCSI channel. An input value of + 0xFFFFFFFF retrieves the Target ID of the first + SCSI device present on a SCSI channel. + Lun - On input, a pointer to the LUN of a SCSI device + present on the SCSI channel. On output, a pointer + to the LUN of the next SCSI device present on + a SCSI channel. +Returns: + + EFI_SUCCESS - The Target ID and Lun of the next SCSI device + on the SCSI channel was returned in Target and Lun. + EFI_NOT_FOUND - There are no more SCSI devices on this SCSI channel. + EFI_INVALID_PARAMETER - Target is not 0xFFFFFFFF,and Target and Lun were not + returned on a previous call to GetNextDevice(). +--*/ +{ + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; + + // + // Retrieve Device Private Data Structure. + // + AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This); + + // + // Check whether Target is valid. + // + if (Target == NULL || Lun == NULL) { + return EFI_INVALID_PARAMETER; + } + + if ((*Target != 0xFFFFFFFF) && + ((*Target != AtapiScsiPrivate->LatestTargetId) || + (*Lun != AtapiScsiPrivate->LatestLun))) { + return EFI_INVALID_PARAMETER; + } + + if (*Target == MAX_TARGET_ID) { + return EFI_NOT_FOUND; + } + + if (*Target == 0xFFFFFFFF) { + *Target = 0; + } else { + *Target = AtapiScsiPrivate->LatestTargetId + 1; + } + + *Lun = 0; + + // + // Update the LatestTargetId. + // + AtapiScsiPrivate->LatestTargetId = *Target; + AtapiScsiPrivate->LatestLun = *Lun; + + return EFI_SUCCESS; + +} + +EFI_STATUS +EFIAPI +AtapiScsiPassThruBuildDevicePath ( + IN EFI_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT32 Target, + IN UINT64 Lun, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ) +/*++ + +Routine Description: + + Used to allocate and build a device path node for a SCSI device + on a SCSI channel. Would not build device path for a SCSI Host Controller. + +Arguments: + + This - Protocol instance pointer. + Target - The Target ID of the SCSI device for which + a device path node is to be allocated and built. + Lun - The LUN of the SCSI device for which a device + path node is to be allocated and built. + DevicePath - A pointer to a single device path node that + describes the SCSI device specified by + Target and Lun. This function is responsible + for allocating the buffer DevicePath with the boot + service AllocatePool(). It is the caller's + responsibility to free DevicePath when the caller + is finished with DevicePath. + Returns: + EFI_SUCCESS - The device path node that describes the SCSI device + specified by Target and Lun was allocated and + returned in DevicePath. + EFI_NOT_FOUND - The SCSI devices specified by Target and Lun does + not exist on the SCSI channel. + EFI_INVALID_PARAMETER - DevicePath is NULL. + EFI_OUT_OF_RESOURCES - There are not enough resources to allocate + DevicePath. +--*/ +{ + EFI_DEV_PATH *Node; + + + // + // Validate parameters passed in. + // + + if (DevicePath == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // can not build device path for the SCSI Host Controller. + // + if ((Target > (MAX_TARGET_ID - 1)) || (Lun != 0)) { + return EFI_NOT_FOUND; + } + + Node = AllocateZeroPool (sizeof (EFI_DEV_PATH)); + if (Node == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Node->DevPath.Type = MESSAGING_DEVICE_PATH; + Node->DevPath.SubType = MSG_ATAPI_DP; + SetDevicePathNodeLength (&Node->DevPath, sizeof (ATAPI_DEVICE_PATH)); + + Node->Atapi.PrimarySecondary = (UINT8) (Target / 2); + Node->Atapi.SlaveMaster = (UINT8) (Target % 2); + Node->Atapi.Lun = (UINT16) Lun; + + *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) Node; + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +AtapiScsiPassThruGetTargetLun ( + IN EFI_SCSI_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT32 *Target, + OUT UINT64 *Lun + ) +/*++ + +Routine Description: + + Used to translate a device path node to a Target ID and LUN. + +Arguments: + + This - Protocol instance pointer. + DevicePath - A pointer to the device path node that + describes a SCSI device on the SCSI channel. + Target - A pointer to the Target ID of a SCSI device + on the SCSI channel. + Lun - A pointer to the LUN of a SCSI device on + the SCSI channel. +Returns: + + EFI_SUCCESS - DevicePath was successfully translated to a + Target ID and LUN, and they were returned + in Target and Lun. + EFI_INVALID_PARAMETER - DevicePath/Target/Lun is NULL. + EFI_UNSUPPORTED - This driver does not support the device path + node type in DevicePath. + EFI_NOT_FOUND - A valid translation from DevicePath to a + Target ID and LUN does not exist. +--*/ +{ + EFI_DEV_PATH *Node; + + // + // Validate parameters passed in. + // + if (DevicePath == NULL || Target == NULL || Lun == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether the DevicePath belongs to SCSI_DEVICE_PATH + // + if ((DevicePath->Type != MESSAGING_DEVICE_PATH) || + (DevicePath->SubType != MSG_ATAPI_DP) || + (DevicePathNodeLength(DevicePath) != sizeof(ATAPI_DEVICE_PATH))) { + return EFI_UNSUPPORTED; + } + + Node = (EFI_DEV_PATH *) DevicePath; + + *Target = Node->Atapi.PrimarySecondary * 2 + Node->Atapi.SlaveMaster; + *Lun = Node->Atapi.Lun; + + if (*Target > (MAX_TARGET_ID - 1) || *Lun != 0) { + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +AtapiScsiPassThruResetChannel ( + IN EFI_SCSI_PASS_THRU_PROTOCOL *This + ) +/*++ + +Routine Description: + + Resets a SCSI channel.This operation resets all the + SCSI devices connected to the SCSI channel. + +Arguments: + + This - Protocol instance pointer. + +Returns: + + EFI_SUCCESS - The SCSI channel was reset. + EFI_UNSUPPORTED - The SCSI channel does not support + a channel reset operation. + EFI_DEVICE_ERROR - A device error occurred while + attempting to reset the SCSI channel. + EFI_TIMEOUT - A timeout occurred while attempting + to reset the SCSI channel. +--*/ +{ + UINT8 DeviceControlValue; + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; + UINT8 Index; + BOOLEAN ResetFlag; + + AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This); + ResetFlag = FALSE; + + // + // Reset both Primary channel and Secondary channel. + // so, the IoPort pointer must point to the right I/O Register group + // + for (Index = 0; Index < 2; Index++) { + // + // Reset + // + AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[Index]; + + DeviceControlValue = 0; + // + // set SRST bit to initiate soft reset + // + DeviceControlValue |= SRST; + // + // disable Interrupt + // + DeviceControlValue |= BIT1; + WritePortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Alt.DeviceControl, + DeviceControlValue + ); + + // + // Wait 10us + // + gBS->Stall (10); + + // + // Clear SRST bit + // 0xfb:1111,1011 + // + DeviceControlValue &= 0xfb; + + WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Alt.DeviceControl, DeviceControlValue); + + // + // slave device needs at most 31s to clear BSY + // + if (StatusWaitForBSYClear (AtapiScsiPrivate, 31000000) != EFI_TIMEOUT) { + ResetFlag = TRUE; + } + } + + if (ResetFlag) { + return EFI_SUCCESS; + } + + return EFI_TIMEOUT; +} + +EFI_STATUS +EFIAPI +AtapiScsiPassThruResetTarget ( + IN EFI_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT32 Target, + IN UINT64 Lun + ) +/*++ + +Routine Description: + + Resets a SCSI device that is connected to a SCSI channel. + +Arguments: + + This - Protocol instance pointer. + Target - The Target ID of the SCSI device to reset. + Lun - The LUN of the SCSI device to reset. + +Returns: + + EFI_SUCCESS - The SCSI device specified by Target and + Lun was reset. + EFI_UNSUPPORTED - The SCSI channel does not support a target + reset operation. + EFI_INVALID_PARAMETER - Target or Lun are invalid. + EFI_DEVICE_ERROR - A device error occurred while attempting + to reset the SCSI device specified by Target + and Lun. + EFI_TIMEOUT - A timeout occurred while attempting to reset + the SCSI device specified by Target and Lun. +--*/ +{ + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; + UINT8 Command; + UINT8 DeviceSelect; + + AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This); + + if ((Target > MAX_TARGET_ID) || (Lun != 0)) { + return EFI_INVALID_PARAMETER; + } + // + // Directly return EFI_SUCCESS if want to reset the host controller + // + if (Target == This->Mode->AdapterId) { + return EFI_SUCCESS; + } + + // + // According to Target ID, reset the Atapi I/O Register mapping + // (Target Id in [0,1] area, using AtapiIoPortRegisters[0], + // Target Id in [2,3] area, using AtapiIoPortRegisters[1] + // + if ((Target / 2) == 0) { + AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[0]; + } else { + AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[1]; + } + + // + // for ATAPI device, no need to wait DRDY ready after device selecting. + // + // bit7 and bit5 are both set to 1 for backward compatibility + // + DeviceSelect = (UINT8) (((BIT7 | BIT5) | (Target << 4))); + WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Head, DeviceSelect); + + Command = ATAPI_SOFT_RESET_CMD; + WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Reg.Command, Command); + + // + // BSY clear is the only status return to the host by the device + // when reset is complete. + // slave device needs at most 31s to clear BSY + // + if (EFI_ERROR (StatusWaitForBSYClear (AtapiScsiPrivate, 31000000))) { + return EFI_TIMEOUT; + } + + // + // stall 5 seconds to make the device status stable + // + gBS->Stall (5000000); + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +AtapiExtScsiPassThruFunction ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ) +/*++ + +Routine Description: + + Implements EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() function. + +Arguments: + + This: The EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + Target: The Target ID of the ATAPI device to send the SCSI + Request Packet. To ATAPI devices attached on an IDE + Channel, Target ID 0 indicates Master device;Target + ID 1 indicates Slave device. + Lun: The LUN of the ATAPI device to send the SCSI Request + Packet. To the ATAPI device, Lun is always 0. + Packet: The SCSI Request Packet to send to the ATAPI device + specified by Target and Lun. + Event: If non-blocking I/O is not supported then Event is ignored, + and blocking I/O is performed. + If Event is NULL, then blocking I/O is performed. + If Event is not NULL and non blocking I/O is supported, + then non-blocking I/O is performed, and Event will be signaled + when the SCSI Request Packet completes. + +Returns: + + EFI_STATUS + +--*/ +{ + EFI_STATUS Status; + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; + UINT8 TargetId; + + AtapiScsiPrivate = ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS (This); + + // + // For ATAPI device, UINT8 is enough to represent the SCSI ID on channel. + // + TargetId = Target[0]; + + // + // Target is not allowed beyond MAX_TARGET_ID + // + if ((TargetId > MAX_TARGET_ID) || (Lun != 0)) { + return EFI_INVALID_PARAMETER; + } + + // + // check the data fields in Packet parameter. + // + Status = CheckExtSCSIRequestPacket (Packet); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // If Request Packet targets at the IDE channel itself, + // do nothing. + // + if (TargetId == (UINT8)This->Mode->AdapterId) { + Packet->InTransferLength = Packet->OutTransferLength = 0; + return EFI_SUCCESS; + } + + // + // According to Target ID, reset the Atapi I/O Register mapping + // (Target Id in [0,1] area, using AtapiIoPortRegisters[0], + // Target Id in [2,3] area, using AtapiIoPortRegisters[1] + // + if ((TargetId / 2) == 0) { + TargetId = (UINT8) (TargetId % 2); + AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[0]; + } else { + TargetId = (UINT8) (TargetId % 2); + AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[1]; + } + + // + // the ATAPI SCSI interface does not support non-blocking I/O + // ignore the Event parameter + // + // Performs blocking I/O. + // + Status = SubmitExtBlockingIoCommand (AtapiScsiPrivate, TargetId, Packet); + return Status; +} + +EFI_STATUS +EFIAPI +AtapiExtScsiPassThruGetNextTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target, + IN OUT UINT64 *Lun + ) +/*++ + +Routine Description: + + Used to retrieve the list of legal Target IDs for SCSI devices + on a SCSI channel. + +Arguments: + + This - Protocol instance pointer. + Target - On input, a pointer to the Target ID of a SCSI + device present on the SCSI channel. On output, + a pointer to the Target ID of the next SCSI device + present on a SCSI channel. An input value of + 0xFFFFFFFF retrieves the Target ID of the first + SCSI device present on a SCSI channel. + Lun - On input, a pointer to the LUN of a SCSI device + present on the SCSI channel. On output, a pointer + to the LUN of the next SCSI device present on + a SCSI channel. +Returns: + + EFI_SUCCESS - The Target ID and Lun of the next SCSI device + on the SCSI channel was returned in Target and Lun. + EFI_NOT_FOUND - There are no more SCSI devices on this SCSI channel. + EFI_INVALID_PARAMETER - Target is not 0xFFFFFFFF,and Target and Lun were not + returned on a previous call to GetNextDevice(). +--*/ +{ + UINT8 ByteIndex; + UINT8 TargetId; + UINT8 ScsiId[TARGET_MAX_BYTES]; + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; + + // + // Retrieve Device Private Data Structure. + // + AtapiScsiPrivate = ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS (This); + + // + // Check whether Target is valid. + // + if (*Target == NULL || Lun == NULL) { + return EFI_INVALID_PARAMETER; + } + + SetMem (ScsiId, TARGET_MAX_BYTES, 0xFF); + + TargetId = (*Target)[0]; + + // + // For ATAPI device, we use UINT8 to represent the SCSI ID on channel. + // + if (CompareMem(*Target, ScsiId, TARGET_MAX_BYTES) != 0) { + for (ByteIndex = 1; ByteIndex < TARGET_MAX_BYTES; ByteIndex++) { + if ((*Target)[ByteIndex] != 0) { + return EFI_INVALID_PARAMETER; + } + } + } + + if ((CompareMem(*Target, ScsiId, TARGET_MAX_BYTES) != 0) && + ((TargetId != AtapiScsiPrivate->LatestTargetId) || + (*Lun != AtapiScsiPrivate->LatestLun))) { + return EFI_INVALID_PARAMETER; + } + + if (TargetId == MAX_TARGET_ID) { + return EFI_NOT_FOUND; + } + + if (CompareMem(*Target, ScsiId, TARGET_MAX_BYTES) == 0) { + SetMem (*Target, TARGET_MAX_BYTES,0); + } else { + (*Target)[0] = (UINT8) (AtapiScsiPrivate->LatestTargetId + 1); + } + + *Lun = 0; + + // + // Update the LatestTargetId. + // + AtapiScsiPrivate->LatestTargetId = (*Target)[0]; + AtapiScsiPrivate->LatestLun = *Lun; + + return EFI_SUCCESS; + +} + +EFI_STATUS +EFIAPI +AtapiExtScsiPassThruBuildDevicePath ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ) +/*++ + +Routine Description: + + Used to allocate and build a device path node for a SCSI device + on a SCSI channel. Would not build device path for a SCSI Host Controller. + +Arguments: + + This - Protocol instance pointer. + Target - The Target ID of the SCSI device for which + a device path node is to be allocated and built. + Lun - The LUN of the SCSI device for which a device + path node is to be allocated and built. + DevicePath - A pointer to a single device path node that + describes the SCSI device specified by + Target and Lun. This function is responsible + for allocating the buffer DevicePath with the boot + service AllocatePool(). It is the caller's + responsibility to free DevicePath when the caller + is finished with DevicePath. + Returns: + EFI_SUCCESS - The device path node that describes the SCSI device + specified by Target and Lun was allocated and + returned in DevicePath. + EFI_NOT_FOUND - The SCSI devices specified by Target and Lun does + not exist on the SCSI channel. + EFI_INVALID_PARAMETER - DevicePath is NULL. + EFI_OUT_OF_RESOURCES - There are not enough resources to allocate + DevicePath. +--*/ +{ + EFI_DEV_PATH *Node; + UINT8 TargetId; + + TargetId = Target[0]; + + // + // Validate parameters passed in. + // + + if (DevicePath == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // can not build device path for the SCSI Host Controller. + // + if ((TargetId > (MAX_TARGET_ID - 1)) || (Lun != 0)) { + return EFI_NOT_FOUND; + } + + Node = AllocateZeroPool (sizeof (EFI_DEV_PATH)); + if (Node == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Node->DevPath.Type = MESSAGING_DEVICE_PATH; + Node->DevPath.SubType = MSG_ATAPI_DP; + SetDevicePathNodeLength (&Node->DevPath, sizeof (ATAPI_DEVICE_PATH)); + + Node->Atapi.PrimarySecondary = (UINT8) (TargetId / 2); + Node->Atapi.SlaveMaster = (UINT8) (TargetId % 2); + Node->Atapi.Lun = (UINT16) Lun; + + *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) Node; + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +AtapiExtScsiPassThruGetTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT8 **Target, + OUT UINT64 *Lun + ) +/*++ + +Routine Description: + + Used to translate a device path node to a Target ID and LUN. + +Arguments: + + This - Protocol instance pointer. + DevicePath - A pointer to the device path node that + describes a SCSI device on the SCSI channel. + Target - A pointer to the Target ID of a SCSI device + on the SCSI channel. + Lun - A pointer to the LUN of a SCSI device on + the SCSI channel. +Returns: + + EFI_SUCCESS - DevicePath was successfully translated to a + Target ID and LUN, and they were returned + in Target and Lun. + EFI_INVALID_PARAMETER - DevicePath/Target/Lun is NULL. + EFI_UNSUPPORTED - This driver does not support the device path + node type in DevicePath. + EFI_NOT_FOUND - A valid translation from DevicePath to a + Target ID and LUN does not exist. +--*/ +{ + EFI_DEV_PATH *Node; + + // + // Validate parameters passed in. + // + if (DevicePath == NULL || Target == NULL || Lun == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether the DevicePath belongs to SCSI_DEVICE_PATH + // + if ((DevicePath->Type != MESSAGING_DEVICE_PATH) || + (DevicePath->SubType != MSG_ATAPI_DP) || + (DevicePathNodeLength(DevicePath) != sizeof(ATAPI_DEVICE_PATH))) { + return EFI_UNSUPPORTED; + } + + ZeroMem (*Target, TARGET_MAX_BYTES); + + Node = (EFI_DEV_PATH *) DevicePath; + + (*Target)[0] = (UINT8) (Node->Atapi.PrimarySecondary * 2 + Node->Atapi.SlaveMaster); + *Lun = Node->Atapi.Lun; + + if ((*Target)[0] > (MAX_TARGET_ID - 1) || *Lun != 0) { + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +AtapiExtScsiPassThruResetChannel ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This + ) +/*++ + +Routine Description: + + Resets a SCSI channel.This operation resets all the + SCSI devices connected to the SCSI channel. + +Arguments: + + This - Protocol instance pointer. + +Returns: + + EFI_SUCCESS - The SCSI channel was reset. + EFI_UNSUPPORTED - The SCSI channel does not support + a channel reset operation. + EFI_DEVICE_ERROR - A device error occurred while + attempting to reset the SCSI channel. + EFI_TIMEOUT - A timeout occurred while attempting + to reset the SCSI channel. +--*/ +{ + UINT8 DeviceControlValue; + UINT8 Index; + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; + BOOLEAN ResetFlag; + + AtapiScsiPrivate = ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS (This); + ResetFlag = FALSE; + // + // Reset both Primary channel and Secondary channel. + // so, the IoPort pointer must point to the right I/O Register group + // And if there is a channel reset successfully, return EFI_SUCCESS. + // + for (Index = 0; Index < 2; Index++) { + // + // Reset + // + AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[Index]; + + DeviceControlValue = 0; + // + // set SRST bit to initiate soft reset + // + DeviceControlValue |= SRST; + // + // disable Interrupt + // + DeviceControlValue |= BIT1; + WritePortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Alt.DeviceControl, + DeviceControlValue + ); + + // + // Wait 10us + // + gBS->Stall (10); + + // + // Clear SRST bit + // 0xfb:1111,1011 + // + DeviceControlValue &= 0xfb; + + WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Alt.DeviceControl, DeviceControlValue); + + // + // slave device needs at most 31s to clear BSY + // + if (StatusWaitForBSYClear (AtapiScsiPrivate, 31000000) != EFI_TIMEOUT) { + ResetFlag = TRUE; + } + } + + if (ResetFlag) { + return EFI_SUCCESS; + } + + return EFI_TIMEOUT; +} + +EFI_STATUS +EFIAPI +AtapiExtScsiPassThruResetTarget ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun + ) +/*++ + +Routine Description: + + Resets a SCSI device that is connected to a SCSI channel. + +Arguments: + + This - Protocol instance pointer. + Target - The Target ID of the SCSI device to reset. + Lun - The LUN of the SCSI device to reset. + +Returns: + + EFI_SUCCESS - The SCSI device specified by Target and + Lun was reset. + EFI_UNSUPPORTED - The SCSI channel does not support a target + reset operation. + EFI_INVALID_PARAMETER - Target or Lun are invalid. + EFI_DEVICE_ERROR - A device error occurred while attempting + to reset the SCSI device specified by Target + and Lun. + EFI_TIMEOUT - A timeout occurred while attempting to reset + the SCSI device specified by Target and Lun. +--*/ +{ + UINT8 Command; + UINT8 DeviceSelect; + UINT8 TargetId; + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; + + AtapiScsiPrivate = ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS (This); + TargetId = Target[0]; + + if ((TargetId > MAX_TARGET_ID) || (Lun != 0)) { + return EFI_INVALID_PARAMETER; + } + // + // Directly return EFI_SUCCESS if want to reset the host controller + // + if (TargetId == This->Mode->AdapterId) { + return EFI_SUCCESS; + } + + // + // According to Target ID, reset the Atapi I/O Register mapping + // (Target Id in [0,1] area, using AtapiIoPortRegisters[0], + // Target Id in [2,3] area, using AtapiIoPortRegisters[1] + // + if ((TargetId / 2) == 0) { + AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[0]; + } else { + AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[1]; + } + + // + // for ATAPI device, no need to wait DRDY ready after device selecting. + // + // bit7 and bit5 are both set to 1 for backward compatibility + // + DeviceSelect = (UINT8) ((BIT7 | BIT5) | (TargetId << 4)); + WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Head, DeviceSelect); + + Command = ATAPI_SOFT_RESET_CMD; + WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Reg.Command, Command); + + // + // BSY clear is the only status return to the host by the device + // when reset is complete. + // slave device needs at most 31s to clear BSY + // + if (EFI_ERROR (StatusWaitForBSYClear (AtapiScsiPrivate, 31000000))) { + return EFI_TIMEOUT; + } + + // + // stall 5 seconds to make the device status stable + // + gBS->Stall (5000000); + + return EFI_SUCCESS; +} + + +EFI_STATUS +EFIAPI +AtapiExtScsiPassThruGetNextTarget ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target + ) +/*++ + +Routine Description: + Used to retrieve the list of legal Target IDs for SCSI devices + on a SCSI channel. + +Arguments: + This - Protocol instance pointer. + Target - On input, a pointer to the Target ID of a SCSI + device present on the SCSI channel. On output, + a pointer to the Target ID of the next SCSI device + present on a SCSI channel. An input value of + 0xFFFFFFFF retrieves the Target ID of the first + SCSI device present on a SCSI channel. + Lun - On input, a pointer to the LUN of a SCSI device + present on the SCSI channel. On output, a pointer + to the LUN of the next SCSI device present on + a SCSI channel. + +Returns: + EFI_SUCCESS - The Target ID and Lun of the next SCSI device + on the SCSI channel was returned in Target and Lun. + EFI_NOT_FOUND - There are no more SCSI devices on this SCSI channel. + EFI_INVALID_PARAMETER - Target is not 0xFFFFFFFF,and Target and Lun were not + returned on a previous call to GetNextDevice(). +--*/ +{ + UINT8 TargetId; + UINT8 ScsiId[TARGET_MAX_BYTES]; + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; + UINT8 ByteIndex; + + // + // Retrieve Device Private Data Structure. + // + AtapiScsiPrivate = ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS (This); + + // + // Check whether Target is valid. + // + if (*Target == NULL ) { + return EFI_INVALID_PARAMETER; + } + + TargetId = (*Target)[0]; + SetMem (ScsiId, TARGET_MAX_BYTES, 0xFF); + + // + // For ATAPI device, we use UINT8 to represent the SCSI ID on channel. + // + if (CompareMem(*Target, ScsiId, TARGET_MAX_BYTES) != 0) { + for (ByteIndex = 1; ByteIndex < TARGET_MAX_BYTES; ByteIndex++) { + if ((*Target)[ByteIndex] != 0) { + return EFI_INVALID_PARAMETER; + } + } + } + + if ((CompareMem(*Target, ScsiId, TARGET_MAX_BYTES) != 0) &&(TargetId != AtapiScsiPrivate->LatestTargetId)) { + return EFI_INVALID_PARAMETER; + } + + if (TargetId == MAX_TARGET_ID) { + return EFI_NOT_FOUND; + } + + if ((CompareMem(*Target, ScsiId, TARGET_MAX_BYTES) == 0)) { + SetMem (*Target, TARGET_MAX_BYTES, 0); + } else { + (*Target)[0] = (UINT8) (AtapiScsiPrivate->LatestTargetId + 1); + } + + // + // Update the LatestTargetId. + // + AtapiScsiPrivate->LatestTargetId = (*Target)[0]; + AtapiScsiPrivate->LatestLun = 0; + + return EFI_SUCCESS; +} + +EFI_STATUS +GetIdeRegistersBaseAddr ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + OUT IDE_REGISTERS_BASE_ADDR *IdeRegsBaseAddr + ) +/*++ + +Routine Description: + Get IDE IO port registers' base addresses by mode. In 'Compatibility' mode, + use fixed addresses. In Native-PCI mode, get base addresses from BARs in + the PCI IDE controller's Configuration Space. + +Arguments: + PciIo - Pointer to the EFI_PCI_IO_PROTOCOL instance + IdeRegsBaseAddr - Pointer to IDE_REGISTERS_BASE_ADDR to + receive IDE IO port registers' base addresses + +Returns: + + EFI_STATUS + +--*/ +{ + EFI_STATUS Status; + PCI_TYPE00 PciData; + + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + 0, + sizeof (PciData), + &PciData + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + if ((PciData.Hdr.ClassCode[0] & IDE_PRIMARY_OPERATING_MODE) == 0) { + IdeRegsBaseAddr[IdePrimary].CommandBlockBaseAddr = 0x1f0; + IdeRegsBaseAddr[IdePrimary].ControlBlockBaseAddr = 0x3f6; + } else { + // + // The BARs should be of IO type + // + if ((PciData.Device.Bar[0] & BIT0) == 0 || + (PciData.Device.Bar[1] & BIT0) == 0) { + return EFI_UNSUPPORTED; + } + + IdeRegsBaseAddr[IdePrimary].CommandBlockBaseAddr = + (UINT16) (PciData.Device.Bar[0] & 0x0000fff8); + IdeRegsBaseAddr[IdePrimary].ControlBlockBaseAddr = + (UINT16) ((PciData.Device.Bar[1] & 0x0000fffc) + 2); + } + + if ((PciData.Hdr.ClassCode[0] & IDE_SECONDARY_OPERATING_MODE) == 0) { + IdeRegsBaseAddr[IdeSecondary].CommandBlockBaseAddr = 0x170; + IdeRegsBaseAddr[IdeSecondary].ControlBlockBaseAddr = 0x376; + } else { + // + // The BARs should be of IO type + // + if ((PciData.Device.Bar[2] & BIT0) == 0 || + (PciData.Device.Bar[3] & BIT0) == 0) { + return EFI_UNSUPPORTED; + } + + IdeRegsBaseAddr[IdeSecondary].CommandBlockBaseAddr = + (UINT16) (PciData.Device.Bar[2] & 0x0000fff8); + IdeRegsBaseAddr[IdeSecondary].ControlBlockBaseAddr = + (UINT16) ((PciData.Device.Bar[3] & 0x0000fffc) + 2); + } + + return EFI_SUCCESS; +} + +VOID +InitAtapiIoPortRegisters ( + IN ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + IN IDE_REGISTERS_BASE_ADDR *IdeRegsBaseAddr + ) +/*++ + +Routine Description: + + Initialize each Channel's Base Address of CommandBlock and ControlBlock. + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + IdeRegsBaseAddr - The pointer of IDE_REGISTERS_BASE_ADDR + +Returns: + + None + +--*/ +{ + + UINT8 IdeChannel; + UINT16 CommandBlockBaseAddr; + UINT16 ControlBlockBaseAddr; + IDE_BASE_REGISTERS *RegisterPointer; + + + for (IdeChannel = 0; IdeChannel < ATAPI_MAX_CHANNEL; IdeChannel++) { + + RegisterPointer = &AtapiScsiPrivate->AtapiIoPortRegisters[IdeChannel]; + + // + // Initialize IDE IO port addresses, including Command Block registers + // and Control Block registers + // + CommandBlockBaseAddr = IdeRegsBaseAddr[IdeChannel].CommandBlockBaseAddr; + ControlBlockBaseAddr = IdeRegsBaseAddr[IdeChannel].ControlBlockBaseAddr; + + RegisterPointer->Data = CommandBlockBaseAddr; + (*(UINT16 *) &RegisterPointer->Reg1) = (UINT16) (CommandBlockBaseAddr + 0x01); + RegisterPointer->SectorCount = (UINT16) (CommandBlockBaseAddr + 0x02); + RegisterPointer->SectorNumber = (UINT16) (CommandBlockBaseAddr + 0x03); + RegisterPointer->CylinderLsb = (UINT16) (CommandBlockBaseAddr + 0x04); + RegisterPointer->CylinderMsb = (UINT16) (CommandBlockBaseAddr + 0x05); + RegisterPointer->Head = (UINT16) (CommandBlockBaseAddr + 0x06); + (*(UINT16 *) &RegisterPointer->Reg) = (UINT16) (CommandBlockBaseAddr + 0x07); + + (*(UINT16 *) &RegisterPointer->Alt) = ControlBlockBaseAddr; + RegisterPointer->DriveAddress = (UINT16) (ControlBlockBaseAddr + 0x01); + } + +} + + +EFI_STATUS +CheckSCSIRequestPacket ( + EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +/*++ + +Routine Description: + + Checks the parameters in the SCSI Request Packet to make sure + they are valid for a SCSI Pass Thru request. + +Arguments: + + Packet - The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET + +Returns: + + EFI_STATUS + +--*/ +{ + if (Packet == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (!ValidCdbLength (Packet->CdbLength)) { + return EFI_INVALID_PARAMETER; + } + + if (Packet->Cdb == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Checks whether the request command is supported. + // + if (!IsCommandValid (Packet)) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +BOOLEAN +IsCommandValid ( + EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +/*++ + +Routine Description: + + Checks the requested SCSI command: + Is it supported by this driver? + Is the Data transfer direction reasonable? + +Arguments: + + Packet - The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET + +Returns: + + EFI_STATUS + +--*/ +{ + UINT8 Index; + UINT8 *OpCode; + UINT8 ArrayLen; + + OpCode = (UINT8 *) (Packet->Cdb); + ArrayLen = (UINT8) (ARRAY_SIZE (gSupportedATAPICommands)); + + for (Index = 0; (Index < ArrayLen) && (CompareMem (&gSupportedATAPICommands[Index], &gEndTable, sizeof (SCSI_COMMAND_SET)) != 0); Index++) { + + if (*OpCode == gSupportedATAPICommands[Index].OpCode) { + // + // Check whether the requested Command is supported by this driver + // + if (Packet->DataDirection == DataIn) { + // + // Check whether the requested data direction conforms to + // what it should be. + // + if (gSupportedATAPICommands[Index].Direction == DataOut) { + return FALSE; + } + } + + if (Packet->DataDirection == DataOut) { + // + // Check whether the requested data direction conforms to + // what it should be. + // + if (gSupportedATAPICommands[Index].Direction == DataIn) { + return FALSE; + } + } + + return TRUE; + } + } + + return FALSE; +} + +EFI_STATUS +SubmitBlockingIoCommand ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT32 Target, + EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +/*++ + +Routine Description: + + Performs blocking I/O request. + +Arguments: + + AtapiScsiPrivate: Private data structure for the specified channel. + Target: The Target ID of the ATAPI device to send the SCSI + Request Packet. To ATAPI devices attached on an IDE + Channel, Target ID 0 indicates Master device;Target + ID 1 indicates Slave device. + Packet: The SCSI Request Packet to send to the ATAPI device + specified by Target. + + Returns: EFI_STATUS + +--*/ +{ + UINT8 PacketCommand[12]; + UINT64 TimeoutInMicroSeconds; + EFI_STATUS PacketCommandStatus; + + // + // Fill ATAPI Command Packet according to CDB + // + ZeroMem (&PacketCommand, 12); + CopyMem (&PacketCommand, Packet->Cdb, Packet->CdbLength); + + // + // Timeout is 100ns unit, convert it to 1000ns (1us) unit. + // + TimeoutInMicroSeconds = DivU64x32 (Packet->Timeout, (UINT32) 10); + + // + // Submit ATAPI Command Packet + // + PacketCommandStatus = AtapiPacketCommand ( + AtapiScsiPrivate, + Target, + PacketCommand, + Packet->DataBuffer, + &(Packet->TransferLength), + (DATA_DIRECTION) Packet->DataDirection, + TimeoutInMicroSeconds + ); + if (!EFI_ERROR (PacketCommandStatus) || (Packet->SenseData == NULL)) { + Packet->SenseDataLength = 0; + return PacketCommandStatus; + } + + // + // Return SenseData if PacketCommandStatus matches + // the following return codes. + // + if ((PacketCommandStatus == EFI_BAD_BUFFER_SIZE) || + (PacketCommandStatus == EFI_DEVICE_ERROR) || + (PacketCommandStatus == EFI_TIMEOUT)) { + + // + // avoid submit request sense command continuously. + // + if (PacketCommand[0] == OP_REQUEST_SENSE) { + Packet->SenseDataLength = 0; + return PacketCommandStatus; + } + + RequestSenseCommand ( + AtapiScsiPrivate, + Target, + Packet->Timeout, + Packet->SenseData, + &Packet->SenseDataLength + ); + } + + return PacketCommandStatus; +} + +EFI_STATUS +RequestSenseCommand ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT32 Target, + UINT64 Timeout, + VOID *SenseData, + UINT8 *SenseDataLength + ) +/*++ + +Routine Description: + + Submit request sense command + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + Target - The target ID + Timeout - The time to complete the command + SenseData - The buffer to fill in sense data + SenseDataLength - The length of buffer + +Returns: + + EFI_STATUS + +--*/ +{ + EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET Packet; + UINT8 Cdb[12]; + EFI_STATUS Status; + + ZeroMem (&Packet, sizeof (EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET)); + ZeroMem (Cdb, 12); + + Cdb[0] = OP_REQUEST_SENSE; + Cdb[4] = (UINT8) (*SenseDataLength); + + Packet.Timeout = Timeout; + Packet.DataBuffer = SenseData; + Packet.SenseData = NULL; + Packet.Cdb = Cdb; + Packet.TransferLength = *SenseDataLength; + Packet.CdbLength = 12; + Packet.DataDirection = DataIn; + + Status = SubmitBlockingIoCommand (AtapiScsiPrivate, Target, &Packet); + *SenseDataLength = (UINT8) (Packet.TransferLength); + return Status; +} + +EFI_STATUS +CheckExtSCSIRequestPacket ( + EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +/*++ + +Routine Description: + + Checks the parameters in the SCSI Request Packet to make sure + they are valid for a SCSI Pass Thru request. + +Arguments: + + Packet - The pointer of EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET + +Returns: + + EFI_STATUS + +--*/ +{ + if (Packet == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (!ValidCdbLength (Packet->CdbLength)) { + return EFI_INVALID_PARAMETER; + } + + if (Packet->Cdb == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Checks whether the request command is supported. + // + if (!IsExtCommandValid (Packet)) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + + +BOOLEAN +IsExtCommandValid ( + EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +/*++ + +Routine Description: + + Checks the requested SCSI command: + Is it supported by this driver? + Is the Data transfer direction reasonable? + +Arguments: + + Packet - The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET + +Returns: + + EFI_STATUS + +--*/ +{ + UINT8 Index; + UINT8 *OpCode; + UINT8 ArrayLen; + + OpCode = (UINT8 *) (Packet->Cdb); + ArrayLen = (UINT8) (ARRAY_SIZE (gSupportedATAPICommands)); + + for (Index = 0; (Index < ArrayLen) && (CompareMem (&gSupportedATAPICommands[Index], &gEndTable, sizeof (SCSI_COMMAND_SET)) != 0); Index++) { + + if (*OpCode == gSupportedATAPICommands[Index].OpCode) { + // + // Check whether the requested Command is supported by this driver + // + if (Packet->DataDirection == DataIn) { + // + // Check whether the requested data direction conforms to + // what it should be. + // + if (gSupportedATAPICommands[Index].Direction == DataOut) { + return FALSE; + } + } + + if (Packet->DataDirection == DataOut) { + // + // Check whether the requested data direction conforms to + // what it should be. + // + if (gSupportedATAPICommands[Index].Direction == DataIn) { + return FALSE; + } + } + + return TRUE; + } + } + + return FALSE; +} + +EFI_STATUS +SubmitExtBlockingIoCommand ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT8 Target, + EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +/*++ + +Routine Description: + + Performs blocking I/O request. + +Arguments: + + AtapiScsiPrivate: Private data structure for the specified channel. + Target: The Target ID of the ATAPI device to send the SCSI + Request Packet. To ATAPI devices attached on an IDE + Channel, Target ID 0 indicates Master device;Target + ID 1 indicates Slave device. + Packet: The SCSI Request Packet to send to the ATAPI device + specified by Target. + + Returns: EFI_STATUS + +--*/ +{ + UINT8 PacketCommand[12]; + UINT64 TimeoutInMicroSeconds; + EFI_STATUS PacketCommandStatus; + + // + // Fill ATAPI Command Packet according to CDB + // + ZeroMem (&PacketCommand, 12); + CopyMem (&PacketCommand, Packet->Cdb, Packet->CdbLength); + + // + // Timeout is 100ns unit, convert it to 1000ns (1us) unit. + // + TimeoutInMicroSeconds = DivU64x32 (Packet->Timeout, (UINT32) 10); + + // + // Submit ATAPI Command Packet + // + if (Packet->DataDirection == DataIn) { + PacketCommandStatus = AtapiPacketCommand ( + AtapiScsiPrivate, + Target, + PacketCommand, + Packet->InDataBuffer, + &(Packet->InTransferLength), + DataIn, + TimeoutInMicroSeconds + ); + } else { + + PacketCommandStatus = AtapiPacketCommand ( + AtapiScsiPrivate, + Target, + PacketCommand, + Packet->OutDataBuffer, + &(Packet->OutTransferLength), + DataOut, + TimeoutInMicroSeconds + ); + } + + if (!EFI_ERROR (PacketCommandStatus) || (Packet->SenseData == NULL)) { + Packet->SenseDataLength = 0; + return PacketCommandStatus; + } + + // + // Return SenseData if PacketCommandStatus matches + // the following return codes. + // + if ((PacketCommandStatus == EFI_BAD_BUFFER_SIZE) || + (PacketCommandStatus == EFI_DEVICE_ERROR) || + (PacketCommandStatus == EFI_TIMEOUT)) { + + // + // avoid submit request sense command continuously. + // + if (PacketCommand[0] == OP_REQUEST_SENSE) { + Packet->SenseDataLength = 0; + return PacketCommandStatus; + } + + RequestSenseCommand ( + AtapiScsiPrivate, + Target, + Packet->Timeout, + Packet->SenseData, + &Packet->SenseDataLength + ); + } + + return PacketCommandStatus; +} + + +EFI_STATUS +AtapiPacketCommand ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT32 Target, + UINT8 *PacketCommand, + VOID *Buffer, + UINT32 *ByteCount, + DATA_DIRECTION Direction, + UINT64 TimeoutInMicroSeconds + ) +/*++ + +Routine Description: + + Submits ATAPI command packet to the specified ATAPI device. + +Arguments: + + AtapiScsiPrivate: Private data structure for the specified channel. + Target: The Target ID of the ATAPI device to send the SCSI + Request Packet. To ATAPI devices attached on an IDE + Channel, Target ID 0 indicates Master device;Target + ID 1 indicates Slave device. + PacketCommand: Points to the ATAPI command packet. + Buffer: Points to the transferred data. + ByteCount: When input,indicates the buffer size; when output, + indicates the actually transferred data size. + Direction: Indicates the data transfer direction. + TimeoutInMicroSeconds: + The timeout, in micro second units, to use for the + execution of this ATAPI command. + A TimeoutInMicroSeconds value of 0 means that + this function will wait indefinitely for the ATAPI + command to execute. + If TimeoutInMicroSeconds is greater than zero, then + this function will return EFI_TIMEOUT if the time + required to execute the ATAPI command is greater + than TimeoutInMicroSeconds. + +Returns: + + EFI_STATUS + +--*/ +{ + + UINT16 *CommandIndex; + UINT8 Count; + EFI_STATUS Status; + + // + // Set all the command parameters by fill related registers. + // Before write to all the following registers, BSY must be 0. + // + Status = StatusWaitForBSYClear (AtapiScsiPrivate, TimeoutInMicroSeconds); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + + // + // Select device via Device/Head Register. + // "Target = 0" indicates device 0; "Target = 1" indicates device 1 + // + WritePortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Head, + (UINT8) ((Target << 4) | DEFAULT_CMD) // DEFAULT_CMD: 0xa0 (1010,0000) + ); + + // + // Set all the command parameters by fill related registers. + // Before write to all the following registers, BSY DRQ must be 0. + // + Status = StatusDRQClear(AtapiScsiPrivate, TimeoutInMicroSeconds); + + if (EFI_ERROR (Status)) { + if (Status == EFI_ABORTED) { + Status = EFI_DEVICE_ERROR; + } + *ByteCount = 0; + return Status; + } + + // + // No OVL; No DMA (by setting feature register) + // + WritePortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Reg1.Feature, + 0x00 + ); + + // + // set the transfersize to MAX_ATAPI_BYTE_COUNT to let the device + // determine how much data should be transfered. + // + WritePortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->CylinderLsb, + (UINT8) (MAX_ATAPI_BYTE_COUNT & 0x00ff) + ); + WritePortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->CylinderMsb, + (UINT8) (MAX_ATAPI_BYTE_COUNT >> 8) + ); + + // + // DEFAULT_CTL:0x0a (0000,1010) + // Disable interrupt + // + WritePortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Alt.DeviceControl, + DEFAULT_CTL + ); + + // + // Send Packet command to inform device + // that the following data bytes are command packet. + // + WritePortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Reg.Command, + PACKET_CMD + ); + + // + // Before data transfer, BSY should be 0 and DRQ should be 1. + // if they are not in specified time frame, + // retrieve Sense Key from Error Register before return. + // + Status = StatusDRQReady (AtapiScsiPrivate, TimeoutInMicroSeconds); + if (EFI_ERROR (Status)) { + if (Status == EFI_ABORTED) { + Status = EFI_DEVICE_ERROR; + } + + *ByteCount = 0; + return Status; + } + + // + // Send out command packet + // + CommandIndex = (UINT16 *) PacketCommand; + for (Count = 0; Count < 6; Count++, CommandIndex++) { + WritePortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data, *CommandIndex); + } + + // + // call AtapiPassThruPioReadWriteData() function to get + // requested transfer data form device. + // + return AtapiPassThruPioReadWriteData ( + AtapiScsiPrivate, + Buffer, + ByteCount, + Direction, + TimeoutInMicroSeconds + ); +} + +EFI_STATUS +AtapiPassThruPioReadWriteData ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT16 *Buffer, + UINT32 *ByteCount, + DATA_DIRECTION Direction, + UINT64 TimeoutInMicroSeconds + ) +/*++ + +Routine Description: + + Performs data transfer between ATAPI device and host after the + ATAPI command packet is sent. + +Arguments: + + AtapiScsiPrivate: Private data structure for the specified channel. + Buffer: Points to the transferred data. + ByteCount: When input,indicates the buffer size; when output, + indicates the actually transferred data size. + Direction: Indicates the data transfer direction. + TimeoutInMicroSeconds: + The timeout, in micro second units, to use for the + execution of this ATAPI command. + A TimeoutInMicroSeconds value of 0 means that + this function will wait indefinitely for the ATAPI + command to execute. + If TimeoutInMicroSeconds is greater than zero, then + this function will return EFI_TIMEOUT if the time + required to execute the ATAPI command is greater + than TimeoutInMicroSeconds. + Returns: + + EFI_STATUS + +--*/ +{ + UINT32 Index; + UINT32 RequiredWordCount; + UINT32 ActualWordCount; + UINT32 WordCount; + EFI_STATUS Status; + UINT16 *ptrBuffer; + + Status = EFI_SUCCESS; + + // + // Non Data transfer request is also supported. + // + if (*ByteCount == 0 || Buffer == NULL) { + *ByteCount = 0; + if (EFI_ERROR (StatusWaitForBSYClear (AtapiScsiPrivate, TimeoutInMicroSeconds))) { + return EFI_DEVICE_ERROR; + } + } + + ptrBuffer = Buffer; + RequiredWordCount = *ByteCount / 2; + + // + // ActuralWordCount means the word count of data really transfered. + // + ActualWordCount = 0; + + while (ActualWordCount < RequiredWordCount) { + // + // before each data transfer stream, the host should poll DRQ bit ready, + // which indicates device's ready for data transfer . + // + Status = StatusDRQReady (AtapiScsiPrivate, TimeoutInMicroSeconds); + if (EFI_ERROR (Status)) { + *ByteCount = ActualWordCount * 2; + + AtapiPassThruCheckErrorStatus (AtapiScsiPrivate); + + if (ActualWordCount == 0) { + return EFI_DEVICE_ERROR; + } + // + // ActualWordCount > 0 + // + if (ActualWordCount < RequiredWordCount) { + return EFI_BAD_BUFFER_SIZE; + } + } + // + // get current data transfer size from Cylinder Registers. + // + WordCount = ReadPortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->CylinderMsb) << 8; + WordCount = WordCount | ReadPortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->CylinderLsb); + WordCount = WordCount & 0xffff; + WordCount /= 2; + + // + // perform a series data In/Out. + // + for (Index = 0; (Index < WordCount) && (ActualWordCount < RequiredWordCount); Index++, ActualWordCount++) { + + if (Direction == DataIn) { + + *ptrBuffer = ReadPortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data); + } else { + + WritePortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data, *ptrBuffer); + } + + ptrBuffer++; + + } + } + // + // After data transfer is completed, normally, DRQ bit should clear. + // + StatusDRQClear (AtapiScsiPrivate, TimeoutInMicroSeconds); + + // + // read status register to check whether error happens. + // + Status = AtapiPassThruCheckErrorStatus (AtapiScsiPrivate); + + *ByteCount = ActualWordCount * 2; + + return Status; +} + + +UINT8 +ReadPortB ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT16 Port + ) +/*++ + +Routine Description: + + Read one byte from a specified I/O port. + +Arguments: + + PciIo - The pointer of EFI_PCI_IO_PROTOCOL + Port - IO port + +Returns: + + A byte read out + +--*/ +{ + UINT8 Data; + + Data = 0; + PciIo->Io.Read ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) Port, + 1, + &Data + ); + return Data; +} + + +UINT16 +ReadPortW ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT16 Port + ) +/*++ + +Routine Description: + + Read one word from a specified I/O port. + +Arguments: + + PciIo - The pointer of EFI_PCI_IO_PROTOCOL + Port - IO port + +Returns: + + A word read out +--*/ +{ + UINT16 Data; + + Data = 0; + PciIo->Io.Read ( + PciIo, + EfiPciIoWidthUint16, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) Port, + 1, + &Data + ); + return Data; +} + + +VOID +WritePortB ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT16 Port, + IN UINT8 Data + ) +/*++ + +Routine Description: + + Write one byte to a specified I/O port. + +Arguments: + + PciIo - The pointer of EFI_PCI_IO_PROTOCOL + Port - IO port + Data - The data to write + +Returns: + + NONE + +--*/ +{ + PciIo->Io.Write ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) Port, + 1, + &Data + ); +} + + +VOID +WritePortW ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT16 Port, + IN UINT16 Data + ) +/*++ + +Routine Description: + + Write one word to a specified I/O port. + +Arguments: + + PciIo - The pointer of EFI_PCI_IO_PROTOCOL + Port - IO port + Data - The data to write + +Returns: + + NONE + +--*/ +{ + PciIo->Io.Write ( + PciIo, + EfiPciIoWidthUint16, + EFI_PCI_IO_PASS_THROUGH_BAR, + (UINT64) Port, + 1, + &Data + ); +} + +EFI_STATUS +StatusDRQClear ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT64 TimeoutInMicroSeconds + ) +/*++ + +Routine Description: + + Check whether DRQ is clear in the Status Register. (BSY must also be cleared) + If TimeoutInMicroSeconds is zero, this routine should wait infinitely for + DRQ clear. Otherwise, it will return EFI_TIMEOUT when specified time is + elapsed. + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + TimeoutInMicroSeconds - The time to wait for + +Returns: + + EFI_STATUS + +--*/ +{ + UINT64 Delay; + UINT8 StatusRegister; + UINT8 ErrRegister; + + if (TimeoutInMicroSeconds == 0) { + Delay = 2; + } else { + Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; + } + + do { + + StatusRegister = ReadPortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Reg.Status + ); + + // + // wait for BSY == 0 and DRQ == 0 + // + if ((StatusRegister & (DRQ | BSY)) == 0) { + break; + } + // + // check whether the command is aborted by the device + // + if ((StatusRegister & (BSY | ERR)) == ERR) { + + ErrRegister = ReadPortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Reg1.Error + ); + if ((ErrRegister & ABRT_ERR) == ABRT_ERR) { + + return EFI_ABORTED; + } + } + // + // Stall for 30 us + // + gBS->Stall (30); + + // + // Loop infinitely if not meeting expected condition + // + if (TimeoutInMicroSeconds == 0) { + Delay = 2; + } + + Delay--; + } while (Delay); + + if (Delay == 0) { + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +AltStatusDRQClear ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT64 TimeoutInMicroSeconds + ) +/*++ + +Routine Description: + + Check whether DRQ is clear in the Alternate Status Register. + (BSY must also be cleared).If TimeoutInMicroSeconds is zero, this routine should + wait infinitely for DRQ clear. Otherwise, it will return EFI_TIMEOUT when specified time is + elapsed. + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + TimeoutInMicroSeconds - The time to wait for + +Returns: + + EFI_STATUS + +--*/ +{ + UINT64 Delay; + UINT8 AltStatusRegister; + UINT8 ErrRegister; + + if (TimeoutInMicroSeconds == 0) { + Delay = 2; + } else { + Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; + } + + do { + + AltStatusRegister = ReadPortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Alt.AltStatus + ); + + // + // wait for BSY == 0 and DRQ == 0 + // + if ((AltStatusRegister & (DRQ | BSY)) == 0) { + break; + } + + if ((AltStatusRegister & (BSY | ERR)) == ERR) { + + ErrRegister = ReadPortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Reg1.Error + ); + if ((ErrRegister & ABRT_ERR) == ABRT_ERR) { + + return EFI_ABORTED; + } + } + // + // Stall for 30 us + // + gBS->Stall (30); + + // + // Loop infinitely if not meeting expected condition + // + if (TimeoutInMicroSeconds == 0) { + Delay = 2; + } + + Delay--; + } while (Delay); + + if (Delay == 0) { + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +StatusDRQReady ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT64 TimeoutInMicroSeconds + ) +/*++ + +Routine Description: + + Check whether DRQ is ready in the Status Register. (BSY must also be cleared) + If TimeoutInMicroSeconds is zero, this routine should wait infinitely for + DRQ ready. Otherwise, it will return EFI_TIMEOUT when specified time is + elapsed. + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + TimeoutInMicroSeconds - The time to wait for + +Returns: + + EFI_STATUS + +--*/ +{ + UINT64 Delay; + UINT8 StatusRegister; + UINT8 ErrRegister; + + if (TimeoutInMicroSeconds == 0) { + Delay = 2; + } else { + Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; + } + + do { + // + // read Status Register will clear interrupt + // + StatusRegister = ReadPortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Reg.Status + ); + + // + // BSY==0,DRQ==1 + // + if ((StatusRegister & (BSY | DRQ)) == DRQ) { + break; + } + + if ((StatusRegister & (BSY | ERR)) == ERR) { + + ErrRegister = ReadPortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Reg1.Error + ); + if ((ErrRegister & ABRT_ERR) == ABRT_ERR) { + return EFI_ABORTED; + } + } + + // + // Stall for 30 us + // + gBS->Stall (30); + + // + // Loop infinitely if not meeting expected condition + // + if (TimeoutInMicroSeconds == 0) { + Delay = 2; + } + + Delay--; + } while (Delay); + + if (Delay == 0) { + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +AltStatusDRQReady ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT64 TimeoutInMicroSeconds + ) +/*++ + +Routine Description: + + Check whether DRQ is ready in the Alternate Status Register. + (BSY must also be cleared) + If TimeoutInMicroSeconds is zero, this routine should wait infinitely for + DRQ ready. Otherwise, it will return EFI_TIMEOUT when specified time is + elapsed. + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + TimeoutInMicroSeconds - The time to wait for + +Returns: + + EFI_STATUS + +--*/ +{ + UINT64 Delay; + UINT8 AltStatusRegister; + UINT8 ErrRegister; + + if (TimeoutInMicroSeconds == 0) { + Delay = 2; + } else { + Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; + } + + do { + // + // read Status Register will clear interrupt + // + AltStatusRegister = ReadPortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Alt.AltStatus + ); + // + // BSY==0,DRQ==1 + // + if ((AltStatusRegister & (BSY | DRQ)) == DRQ) { + break; + } + + if ((AltStatusRegister & (BSY | ERR)) == ERR) { + + ErrRegister = ReadPortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Reg1.Error + ); + if ((ErrRegister & ABRT_ERR) == ABRT_ERR) { + return EFI_ABORTED; + } + } + + // + // Stall for 30 us + // + gBS->Stall (30); + + // + // Loop infinitely if not meeting expected condition + // + if (TimeoutInMicroSeconds == 0) { + Delay = 2; + } + + Delay--; + } while (Delay); + + if (Delay == 0) { + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +StatusWaitForBSYClear ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT64 TimeoutInMicroSeconds + ) +/*++ + +Routine Description: + + Check whether BSY is clear in the Status Register. + If TimeoutInMicroSeconds is zero, this routine should wait infinitely for + BSY clear. Otherwise, it will return EFI_TIMEOUT when specified time is + elapsed. + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + TimeoutInMicroSeconds - The time to wait for + +Returns: + + EFI_STATUS + +--*/ +{ + UINT64 Delay; + UINT8 StatusRegister; + + if (TimeoutInMicroSeconds == 0) { + Delay = 2; + } else { + Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; + } + + do { + + StatusRegister = ReadPortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Reg.Status + ); + if ((StatusRegister & BSY) == 0x00) { + break; + } + + // + // Stall for 30 us + // + gBS->Stall (30); + + // + // Loop infinitely if not meeting expected condition + // + if (TimeoutInMicroSeconds == 0) { + Delay = 2; + } + + Delay--; + } while (Delay); + + if (Delay == 0) { + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +AltStatusWaitForBSYClear ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT64 TimeoutInMicroSeconds + ) +/*++ + +Routine Description: + + Check whether BSY is clear in the Alternate Status Register. + If TimeoutInMicroSeconds is zero, this routine should wait infinitely for + BSY clear. Otherwise, it will return EFI_TIMEOUT when specified time is + elapsed. + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + TimeoutInMicroSeconds - The time to wait for + +Returns: + + EFI_STATUS + +--*/ +{ + UINT64 Delay; + UINT8 AltStatusRegister; + + if (TimeoutInMicroSeconds == 0) { + Delay = 2; + } else { + Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; + } + + do { + + AltStatusRegister = ReadPortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Alt.AltStatus + ); + if ((AltStatusRegister & BSY) == 0x00) { + break; + } + + // + // Stall for 30 us + // + gBS->Stall (30); + // + // Loop infinitely if not meeting expected condition + // + if (TimeoutInMicroSeconds == 0) { + Delay = 2; + } + + Delay--; + } while (Delay); + + if (Delay == 0) { + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +StatusDRDYReady ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT64 TimeoutInMicroSeconds + ) +/*++ + +Routine Description: + + Check whether DRDY is ready in the Status Register. + (BSY must also be cleared) + If TimeoutInMicroSeconds is zero, this routine should wait infinitely for + DRDY ready. Otherwise, it will return EFI_TIMEOUT when specified time is + elapsed. + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + TimeoutInMicroSeconds - The time to wait for + +Returns: + + EFI_STATUS + +--*/ +{ + UINT64 Delay; + UINT8 StatusRegister; + UINT8 ErrRegister; + + if (TimeoutInMicroSeconds == 0) { + Delay = 2; + } else { + Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; + } + + do { + StatusRegister = ReadPortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Reg.Status + ); + // + // BSY == 0 , DRDY == 1 + // + if ((StatusRegister & (DRDY | BSY)) == DRDY) { + break; + } + + if ((StatusRegister & (BSY | ERR)) == ERR) { + + ErrRegister = ReadPortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Reg1.Error + ); + if ((ErrRegister & ABRT_ERR) == ABRT_ERR) { + return EFI_ABORTED; + } + } + + // + // Stall for 30 us + // + gBS->Stall (30); + // + // Loop infinitely if not meeting expected condition + // + if (TimeoutInMicroSeconds == 0) { + Delay = 2; + } + + Delay--; + } while (Delay); + + if (Delay == 0) { + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +AltStatusDRDYReady ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT64 TimeoutInMicroSeconds + ) +/*++ + +Routine Description: + + Check whether DRDY is ready in the Alternate Status Register. + (BSY must also be cleared) + If TimeoutInMicroSeconds is zero, this routine should wait infinitely for + DRDY ready. Otherwise, it will return EFI_TIMEOUT when specified time is + elapsed. + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + TimeoutInMicroSeconds - The time to wait for + +Returns: + + EFI_STATUS + +--*/ +{ + UINT64 Delay; + UINT8 AltStatusRegister; + UINT8 ErrRegister; + + if (TimeoutInMicroSeconds == 0) { + Delay = 2; + } else { + Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; + } + + do { + AltStatusRegister = ReadPortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Alt.AltStatus + ); + // + // BSY == 0 , DRDY == 1 + // + if ((AltStatusRegister & (DRDY | BSY)) == DRDY) { + break; + } + + if ((AltStatusRegister & (BSY | ERR)) == ERR) { + + ErrRegister = ReadPortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Reg1.Error + ); + if ((ErrRegister & ABRT_ERR) == ABRT_ERR) { + return EFI_ABORTED; + } + } + + // + // Stall for 30 us + // + gBS->Stall (30); + // + // Loop infinitely if not meeting expected condition + // + if (TimeoutInMicroSeconds == 0) { + Delay = 2; + } + + Delay--; + } while (Delay); + + if (Delay == 0) { + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +AtapiPassThruCheckErrorStatus ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate + ) +/*++ + +Routine Description: + + Check Error Register for Error Information. + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + +Returns: + + EFI_STATUS + +--*/ +{ + UINT8 StatusRegister; + UINT8 ErrorRegister; + + StatusRegister = ReadPortB ( + AtapiScsiPrivate->PciIo, + AtapiScsiPrivate->IoPort->Reg.Status + ); + + DEBUG_CODE_BEGIN (); + + if (StatusRegister & DWF) { + DEBUG ( + (EFI_D_BLKIO, + "AtapiPassThruCheckErrorStatus()-- %02x : Error : Write Fault\n", + StatusRegister) + ); + } + + if (StatusRegister & CORR) { + DEBUG ( + (EFI_D_BLKIO, + "AtapiPassThruCheckErrorStatus()-- %02x : Error : Corrected Data\n", + StatusRegister) + ); + } + + if (StatusRegister & ERR) { + ErrorRegister = ReadPortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Reg1.Error); + + + if (ErrorRegister & BBK_ERR) { + DEBUG ( + (EFI_D_BLKIO, + "AtapiPassThruCheckErrorStatus()-- %02x : Error : Bad Block Detected\n", + ErrorRegister) + ); + } + + if (ErrorRegister & UNC_ERR) { + DEBUG ( + (EFI_D_BLKIO, + "AtapiPassThruCheckErrorStatus()-- %02x : Error : Uncorrectable Data\n", + ErrorRegister) + ); + } + + if (ErrorRegister & MC_ERR) { + DEBUG ( + (EFI_D_BLKIO, + "AtapiPassThruCheckErrorStatus()-- %02x : Error : Media Change\n", + ErrorRegister) + ); + } + + if (ErrorRegister & ABRT_ERR) { + DEBUG ( + (EFI_D_BLKIO, + "AtapiPassThruCheckErrorStatus()-- %02x : Error : Abort\n", + ErrorRegister) + ); + } + + if (ErrorRegister & TK0NF_ERR) { + DEBUG ( + (EFI_D_BLKIO, + "AtapiPassThruCheckErrorStatus()-- %02x : Error : Track 0 Not Found\n", + ErrorRegister) + ); + } + + if (ErrorRegister & AMNF_ERR) { + DEBUG ( + (EFI_D_BLKIO, + "AtapiPassThruCheckErrorStatus()-- %02x : Error : Address Mark Not Found\n", + ErrorRegister) + ); + } + } + + DEBUG_CODE_END (); + + if ((StatusRegister & (ERR | DWF | CORR)) == 0) { + return EFI_SUCCESS; + } + + + return EFI_DEVICE_ERROR; +} + + +/** + Installs Scsi Pass Thru and/or Ext Scsi Pass Thru + protocols based on feature flags. + + @param Controller The controller handle to + install these protocols on. + @param AtapiScsiPrivate A pointer to the protocol private + data structure. + + @retval EFI_SUCCESS The installation succeeds. + @retval other The installation fails. + +**/ +EFI_STATUS +InstallScsiPassThruProtocols ( + IN EFI_HANDLE *ControllerHandle, + IN ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate + ) +{ + EFI_STATUS Status; + EFI_SCSI_PASS_THRU_PROTOCOL *ScsiPassThru; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiPassThru; + + ScsiPassThru = &AtapiScsiPrivate->ScsiPassThru; + ExtScsiPassThru = &AtapiScsiPrivate->ExtScsiPassThru; + + if (FeaturePcdGet (PcdSupportScsiPassThru)) { + ScsiPassThru = CopyMem (ScsiPassThru, &gScsiPassThruProtocolTemplate, sizeof (*ScsiPassThru)); + if (FeaturePcdGet (PcdSupportExtScsiPassThru)) { + ExtScsiPassThru = CopyMem (ExtScsiPassThru, &gExtScsiPassThruProtocolTemplate, sizeof (*ExtScsiPassThru)); + Status = gBS->InstallMultipleProtocolInterfaces ( + ControllerHandle, + &gEfiScsiPassThruProtocolGuid, + ScsiPassThru, + &gEfiExtScsiPassThruProtocolGuid, + ExtScsiPassThru, + NULL + ); + } else { + Status = gBS->InstallMultipleProtocolInterfaces ( + ControllerHandle, + &gEfiScsiPassThruProtocolGuid, + ScsiPassThru, + NULL + ); + } + } else { + if (FeaturePcdGet (PcdSupportExtScsiPassThru)) { + ExtScsiPassThru = CopyMem (ExtScsiPassThru, &gExtScsiPassThruProtocolTemplate, sizeof (*ExtScsiPassThru)); + Status = gBS->InstallMultipleProtocolInterfaces ( + ControllerHandle, + &gEfiExtScsiPassThruProtocolGuid, + ExtScsiPassThru, + NULL + ); + } else { + // + // This driver must support either ScsiPassThru or + // ExtScsiPassThru protocols + // + ASSERT (FALSE); + Status = EFI_UNSUPPORTED; + } + } + + return Status; +} + +/** + The user Entry Point for module AtapiPassThru. The user code starts with this function. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializeAtapiPassThru( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gAtapiScsiPassThruDriverBinding, + ImageHandle, + &gAtapiScsiPassThruComponentName, + &gAtapiScsiPassThruComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + // + // Install EFI Driver Supported EFI Version Protocol required for + // EFI drivers that are on PCI and other plug in cards. + // + gAtapiScsiPassThruDriverSupportedEfiVersion.FirmwareVersion = PcdGet32 (PcdDriverSupportedEfiVersion); + Status = gBS->InstallMultipleProtocolInterfaces ( + &ImageHandle, + &gEfiDriverSupportedEfiVersionProtocolGuid, + &gAtapiScsiPassThruDriverSupportedEfiVersion, + NULL + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThru.h b/Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThru.h new file mode 100644 index 0000000000..9fca7b6ac2 --- /dev/null +++ b/Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThru.h @@ -0,0 +1,1618 @@ +/** @file + Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + + Module Name: AtapiPassThru.h + +**/ + +#ifndef _APT_H +#define _APT_H + + + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MAX_TARGET_ID 4 + +// +// IDE Registers +// +typedef union { + UINT16 Command; /* when write */ + UINT16 Status; /* when read */ +} IDE_CMD_OR_STATUS; + +typedef union { + UINT16 Error; /* when read */ + UINT16 Feature; /* when write */ +} IDE_ERROR_OR_FEATURE; + +typedef union { + UINT16 AltStatus; /* when read */ + UINT16 DeviceControl; /* when write */ +} IDE_AltStatus_OR_DeviceControl; + + +typedef enum { + IdePrimary = 0, + IdeSecondary = 1, + IdeMaxChannel = 2 +} EFI_IDE_CHANNEL; + +/// + + +// +// Bit definitions in Programming Interface byte of the Class Code field +// in PCI IDE controller's Configuration Space +// +#define IDE_PRIMARY_OPERATING_MODE BIT0 +#define IDE_PRIMARY_PROGRAMMABLE_INDICATOR BIT1 +#define IDE_SECONDARY_OPERATING_MODE BIT2 +#define IDE_SECONDARY_PROGRAMMABLE_INDICATOR BIT3 + + +#define ATAPI_MAX_CHANNEL 2 + +/// +/// IDE registers set +/// +typedef struct { + UINT16 Data; + IDE_ERROR_OR_FEATURE Reg1; + UINT16 SectorCount; + UINT16 SectorNumber; + UINT16 CylinderLsb; + UINT16 CylinderMsb; + UINT16 Head; + IDE_CMD_OR_STATUS Reg; + IDE_AltStatus_OR_DeviceControl Alt; + UINT16 DriveAddress; +} IDE_BASE_REGISTERS; + +#define ATAPI_SCSI_PASS_THRU_DEV_SIGNATURE SIGNATURE_32 ('a', 's', 'p', 't') + +typedef struct { + UINTN Signature; + EFI_HANDLE Handle; + EFI_SCSI_PASS_THRU_PROTOCOL ScsiPassThru; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL ExtScsiPassThru; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT64 OriginalPciAttributes; + // + // Local Data goes here + // + IDE_BASE_REGISTERS *IoPort; + IDE_BASE_REGISTERS AtapiIoPortRegisters[2]; + UINT32 LatestTargetId; + UINT64 LatestLun; +} ATAPI_SCSI_PASS_THRU_DEV; + +// +// IDE registers' base addresses +// +typedef struct { + UINT16 CommandBlockBaseAddr; + UINT16 ControlBlockBaseAddr; +} IDE_REGISTERS_BASE_ADDR; + +#define ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS(a) \ + CR (a, \ + ATAPI_SCSI_PASS_THRU_DEV, \ + ScsiPassThru, \ + ATAPI_SCSI_PASS_THRU_DEV_SIGNATURE \ + ) + +#define ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS(a) \ + CR (a, \ + ATAPI_SCSI_PASS_THRU_DEV, \ + ExtScsiPassThru, \ + ATAPI_SCSI_PASS_THRU_DEV_SIGNATURE \ + ) + +// +// Global Variables +// +extern EFI_DRIVER_BINDING_PROTOCOL gAtapiScsiPassThruDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gAtapiScsiPassThruComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gAtapiScsiPassThruComponentName2; +extern EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL gAtapiScsiPassThruDriverSupportedEfiVersion; + +// +// ATAPI Command op code +// +#define OP_INQUIRY 0x12 +#define OP_LOAD_UNLOAD_CD 0xa6 +#define OP_MECHANISM_STATUS 0xbd +#define OP_MODE_SELECT_10 0x55 +#define OP_MODE_SENSE_10 0x5a +#define OP_PAUSE_RESUME 0x4b +#define OP_PLAY_AUDIO_10 0x45 +#define OP_PLAY_AUDIO_MSF 0x47 +#define OP_PLAY_CD 0xbc +#define OP_PLAY_CD_MSF 0xb4 +#define OP_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e +#define OP_READ_10 0x28 +#define OP_READ_12 0xa8 +#define OP_READ_CAPACITY 0x25 +#define OP_READ_CD 0xbe +#define OP_READ_CD_MSF 0xb9 +#define OP_READ_HEADER 0x44 +#define OP_READ_SUB_CHANNEL 0x42 +#define OP_READ_TOC 0x43 +#define OP_REQUEST_SENSE 0x03 +#define OP_SCAN 0xba +#define OP_SEEK_10 0x2b +#define OP_SET_CD_SPEED 0xbb +#define OP_STOPPLAY_SCAN 0x4e +#define OP_START_STOP_UNIT 0x1b +#define OP_TEST_UNIT_READY 0x00 + +#define OP_FORMAT_UNIT 0x04 +#define OP_READ_FORMAT_CAPACITIES 0x23 +#define OP_VERIFY 0x2f +#define OP_WRITE_10 0x2a +#define OP_WRITE_12 0xaa +#define OP_WRITE_AND_VERIFY 0x2e + +// +// ATA Command +// +#define ATAPI_SOFT_RESET_CMD 0x08 + +typedef enum { + DataIn = 0, + DataOut = 1, + DataBi = 2, + NoData = 3, + End = 0xff +} DATA_DIRECTION; + +typedef struct { + UINT8 OpCode; + DATA_DIRECTION Direction; +} SCSI_COMMAND_SET; + +#define MAX_CHANNEL 2 + +#define ValidCdbLength(Len) ((Len) == 6 || (Len) == 10 || (Len) == 12) ? 1 : 0 + +// +// IDE registers bit definitions +// +// ATA Err Reg bitmap +// +#define BBK_ERR BIT7 ///< Bad block detected +#define UNC_ERR BIT6 ///< Uncorrectable Data +#define MC_ERR BIT5 ///< Media Change +#define IDNF_ERR BIT4 ///< ID Not Found +#define MCR_ERR BIT3 ///< Media Change Requested +#define ABRT_ERR BIT2 ///< Aborted Command +#define TK0NF_ERR BIT1 ///< Track 0 Not Found +#define AMNF_ERR BIT0 ///< Address Mark Not Found + +// +// ATAPI Err Reg bitmap +// +#define SENSE_KEY_ERR (BIT7 | BIT6 | BIT5 | BIT4) +#define EOM_ERR BIT1 ///< End of Media Detected +#define ILI_ERR BIT0 ///< Illegal Length Indication + +// +// Device/Head Reg +// +#define LBA_MODE BIT6 +#define DEV BIT4 +#define HS3 BIT3 +#define HS2 BIT2 +#define HS1 BIT1 +#define HS0 BIT0 +#define CHS_MODE (0) +#define DRV0 (0) +#define DRV1 (1) +#define MST_DRV DRV0 +#define SLV_DRV DRV1 + +// +// Status Reg +// +#define BSY BIT7 ///< Controller Busy +#define DRDY BIT6 ///< Drive Ready +#define DWF BIT5 ///< Drive Write Fault +#define DSC BIT4 ///< Disk Seek Complete +#define DRQ BIT3 ///< Data Request +#define CORR BIT2 ///< Corrected Data +#define IDX BIT1 ///< Index +#define ERR BIT0 ///< Error +#define CHECK BIT0 ///< Check bit for ATAPI Status Reg + +// +// Device Control Reg +// +#define SRST BIT2 ///< Software Reset +#define IEN_L BIT1 ///< Interrupt Enable + +// +// ATAPI Feature Register +// +#define OVERLAP BIT1 +#define DMA BIT0 + +// +// ATAPI Interrupt Reason Reson Reg (ATA Sector Count Register) +// +#define RELEASE BIT2 +#define IO BIT1 +#define CoD BIT0 + +#define PACKET_CMD 0xA0 + +#define DEFAULT_CMD (0xa0) +// +// default content of device control register, disable INT +// +#define DEFAULT_CTL (0x0a) +#define MAX_ATAPI_BYTE_COUNT (0xfffe) + +// +// function prototype +// + +EFI_STATUS +EFIAPI +AtapiScsiPassThruDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +EFI_STATUS +EFIAPI +AtapiScsiPassThruDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +EFI_STATUS +EFIAPI +AtapiScsiPassThruDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +AtapiScsiPassThruComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +AtapiScsiPassThruComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +EFI_STATUS +EFIAPI +AtapiScsiPassThruDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) + /*++ + +Routine Description: + + Entry point for EFI drivers. + +Arguments: + + ImageHandle - EFI_HANDLE + SystemTable - EFI_SYSTEM_TABLE + +Returns: + + EFI_SUCCESS + Others + +--*/ +; + +EFI_STATUS +RegisterAtapiScsiPassThru ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT64 OriginalPciAttributes + ) +/*++ + +Routine Description: + Attaches SCSI Pass Thru Protocol for specified IDE channel. + +Arguments: + This - Protocol instance pointer. + Controller - Parent device handle to the IDE channel. + PciIo - PCI I/O protocol attached on the "Controller". + +Returns: + Always return EFI_SUCCESS unless installing SCSI Pass Thru Protocol failed. + +--*/ +; + +EFI_STATUS +EFIAPI +AtapiScsiPassThruFunction ( + IN EFI_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT32 Target, + IN UINT64 Lun, + IN OUT EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ) +/*++ + +Routine Description: + + Implements EFI_SCSI_PASS_THRU_PROTOCOL.PassThru() function. + +Arguments: + + This: The EFI_SCSI_PASS_THRU_PROTOCOL instance. + Target: The Target ID of the ATAPI device to send the SCSI + Request Packet. To ATAPI devices attached on an IDE + Channel, Target ID 0 indicates Master device;Target + ID 1 indicates Slave device. + Lun: The LUN of the ATAPI device to send the SCSI Request + Packet. To the ATAPI device, Lun is always 0. + Packet: The SCSI Request Packet to send to the ATAPI device + specified by Target and Lun. + Event: If non-blocking I/O is not supported then Event is ignored, + and blocking I/O is performed. + If Event is NULL, then blocking I/O is performed. + If Event is not NULL and non blocking I/O is supported, + then non-blocking I/O is performed, and Event will be signaled + when the SCSI Request Packet completes. + +Returns: + + EFI_STATUS + +--*/ +; + +EFI_STATUS +EFIAPI +AtapiScsiPassThruGetNextDevice ( + IN EFI_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT32 *Target, + IN OUT UINT64 *Lun + ) +/*++ + +Routine Description: + + Used to retrieve the list of legal Target IDs for SCSI devices + on a SCSI channel. + +Arguments: + + This - Protocol instance pointer. + Target - On input, a pointer to the Target ID of a SCSI + device present on the SCSI channel. On output, + a pointer to the Target ID of the next SCSI device + present on a SCSI channel. An input value of + 0xFFFFFFFF retrieves the Target ID of the first + SCSI device present on a SCSI channel. + Lun - On input, a pointer to the LUN of a SCSI device + present on the SCSI channel. On output, a pointer + to the LUN of the next SCSI device present on + a SCSI channel. +Returns: + + EFI_SUCCESS - The Target ID and Lun of the next SCSI device + on the SCSI channel was returned in Target and Lun. + EFI_NOT_FOUND - There are no more SCSI devices on this SCSI channel. + EFI_INVALID_PARAMETER - Target is not 0xFFFFFFFF,and Target and Lun were not + returned on a previous call to GetNextDevice(). + +--*/ +; + +EFI_STATUS +EFIAPI +AtapiScsiPassThruBuildDevicePath ( + IN EFI_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT32 Target, + IN UINT64 Lun, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ) +/*++ + +Routine Description: + + Used to allocate and build a device path node for a SCSI device + on a SCSI channel. Would not build device path for a SCSI Host Controller. + +Arguments: + + This - Protocol instance pointer. + Target - The Target ID of the SCSI device for which + a device path node is to be allocated and built. + Lun - The LUN of the SCSI device for which a device + path node is to be allocated and built. + DevicePath - A pointer to a single device path node that + describes the SCSI device specified by + Target and Lun. This function is responsible + for allocating the buffer DevicePath with the boot + service AllocatePool(). It is the caller's + responsibility to free DevicePath when the caller + is finished with DevicePath. + Returns: + EFI_SUCCESS - The device path node that describes the SCSI device + specified by Target and Lun was allocated and + returned in DevicePath. + EFI_NOT_FOUND - The SCSI devices specified by Target and Lun does + not exist on the SCSI channel. + EFI_INVALID_PARAMETER - DevicePath is NULL. + EFI_OUT_OF_RESOURCES - There are not enough resources to allocate + DevicePath. + +--*/ +; + +EFI_STATUS +EFIAPI +AtapiScsiPassThruGetTargetLun ( + IN EFI_SCSI_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT32 *Target, + OUT UINT64 *Lun + ) +/*++ + +Routine Description: + + Used to translate a device path node to a Target ID and LUN. + +Arguments: + + This - Protocol instance pointer. + DevicePath - A pointer to the device path node that + describes a SCSI device on the SCSI channel. + Target - A pointer to the Target ID of a SCSI device + on the SCSI channel. + Lun - A pointer to the LUN of a SCSI device on + the SCSI channel. +Returns: + + EFI_SUCCESS - DevicePath was successfully translated to a + Target ID and LUN, and they were returned + in Target and Lun. + EFI_INVALID_PARAMETER - DevicePath/Target/Lun is NULL. + EFI_UNSUPPORTED - This driver does not support the device path + node type in DevicePath. + EFI_NOT_FOUND - A valid translation from DevicePath to a + Target ID and LUN does not exist. + +--*/ +; + +EFI_STATUS +EFIAPI +AtapiScsiPassThruResetChannel ( + IN EFI_SCSI_PASS_THRU_PROTOCOL *This + ) +/*++ + +Routine Description: + + Resets a SCSI channel.This operation resets all the + SCSI devices connected to the SCSI channel. + +Arguments: + + This - Protocol instance pointer. + +Returns: + + EFI_SUCCESS - The SCSI channel was reset. + EFI_UNSUPPORTED - The SCSI channel does not support + a channel reset operation. + EFI_DEVICE_ERROR - A device error occurred while + attempting to reset the SCSI channel. + EFI_TIMEOUT - A timeout occurred while attempting + to reset the SCSI channel. + +--*/ +; + +EFI_STATUS +EFIAPI +AtapiScsiPassThruResetTarget ( + IN EFI_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT32 Target, + IN UINT64 Lun + ) +/*++ + +Routine Description: + + Resets a SCSI device that is connected to a SCSI channel. + +Arguments: + + This - Protocol instance pointer. + Target - The Target ID of the SCSI device to reset. + Lun - The LUN of the SCSI device to reset. + +Returns: + + EFI_SUCCESS - The SCSI device specified by Target and + Lun was reset. + EFI_UNSUPPORTED - The SCSI channel does not support a target + reset operation. + EFI_INVALID_PARAMETER - Target or Lun are invalid. + EFI_DEVICE_ERROR - A device error occurred while attempting + to reset the SCSI device specified by Target + and Lun. + EFI_TIMEOUT - A timeout occurred while attempting to reset + the SCSI device specified by Target and Lun. + +--*/ +; + +EFI_STATUS +EFIAPI +AtapiExtScsiPassThruFunction ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ) +/*++ + +Routine Description: + + Implements EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() function. + +Arguments: + + This: The EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + Target: The Target ID of the ATAPI device to send the SCSI + Request Packet. To ATAPI devices attached on an IDE + Channel, Target ID 0 indicates Master device;Target + ID 1 indicates Slave device. + Lun: The LUN of the ATAPI device to send the SCSI Request + Packet. To the ATAPI device, Lun is always 0. + Packet: The SCSI Request Packet to send to the ATAPI device + specified by Target and Lun. + Event: If non-blocking I/O is not supported then Event is ignored, + and blocking I/O is performed. + If Event is NULL, then blocking I/O is performed. + If Event is not NULL and non blocking I/O is supported, + then non-blocking I/O is performed, and Event will be signaled + when the SCSI Request Packet completes. + +Returns: + + EFI_STATUS + +--*/ +; + +EFI_STATUS +EFIAPI +AtapiExtScsiPassThruGetNextTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target, + IN OUT UINT64 *Lun + ) +/*++ + +Routine Description: + + Used to retrieve the list of legal Target IDs for SCSI devices + on a SCSI channel. + +Arguments: + + This - Protocol instance pointer. + Target - On input, a pointer to the Target ID of a SCSI + device present on the SCSI channel. On output, + a pointer to the Target ID of the next SCSI device + present on a SCSI channel. An input value of + 0xFFFFFFFF retrieves the Target ID of the first + SCSI device present on a SCSI channel. + Lun - On input, a pointer to the LUN of a SCSI device + present on the SCSI channel. On output, a pointer + to the LUN of the next SCSI device present on + a SCSI channel. +Returns: + + EFI_SUCCESS - The Target ID and Lun of the next SCSI device + on the SCSI channel was returned in Target and Lun. + EFI_NOT_FOUND - There are no more SCSI devices on this SCSI channel. + EFI_INVALID_PARAMETER - Target is not 0xFFFFFFFF,and Target and Lun were not + returned on a previous call to GetNextDevice(). + +--*/ +; + +EFI_STATUS +EFIAPI +AtapiExtScsiPassThruBuildDevicePath ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ) +/*++ + +Routine Description: + + Used to allocate and build a device path node for a SCSI device + on a SCSI channel. Would not build device path for a SCSI Host Controller. + +Arguments: + + This - Protocol instance pointer. + Target - The Target ID of the SCSI device for which + a device path node is to be allocated and built. + Lun - The LUN of the SCSI device for which a device + path node is to be allocated and built. + DevicePath - A pointer to a single device path node that + describes the SCSI device specified by + Target and Lun. This function is responsible + for allocating the buffer DevicePath with the boot + service AllocatePool(). It is the caller's + responsibility to free DevicePath when the caller + is finished with DevicePath. + Returns: + EFI_SUCCESS - The device path node that describes the SCSI device + specified by Target and Lun was allocated and + returned in DevicePath. + EFI_NOT_FOUND - The SCSI devices specified by Target and Lun does + not exist on the SCSI channel. + EFI_INVALID_PARAMETER - DevicePath is NULL. + EFI_OUT_OF_RESOURCES - There are not enough resources to allocate + DevicePath. + +--*/ +; + +EFI_STATUS +EFIAPI +AtapiExtScsiPassThruGetTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT8 **Target, + OUT UINT64 *Lun + ) +/*++ + +Routine Description: + + Used to translate a device path node to a Target ID and LUN. + +Arguments: + + This - Protocol instance pointer. + DevicePath - A pointer to the device path node that + describes a SCSI device on the SCSI channel. + Target - A pointer to the Target ID of a SCSI device + on the SCSI channel. + Lun - A pointer to the LUN of a SCSI device on + the SCSI channel. +Returns: + + EFI_SUCCESS - DevicePath was successfully translated to a + Target ID and LUN, and they were returned + in Target and Lun. + EFI_INVALID_PARAMETER - DevicePath/Target/Lun is NULL. + EFI_UNSUPPORTED - This driver does not support the device path + node type in DevicePath. + EFI_NOT_FOUND - A valid translation from DevicePath to a + Target ID and LUN does not exist. + +--*/ +; + +EFI_STATUS +EFIAPI +AtapiExtScsiPassThruResetChannel ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This + ) +/*++ + +Routine Description: + + Resets a SCSI channel.This operation resets all the + SCSI devices connected to the SCSI channel. + +Arguments: + + This - Protocol instance pointer. + +Returns: + + EFI_SUCCESS - The SCSI channel was reset. + EFI_UNSUPPORTED - The SCSI channel does not support + a channel reset operation. + EFI_DEVICE_ERROR - A device error occurred while + attempting to reset the SCSI channel. + EFI_TIMEOUT - A timeout occurred while attempting + to reset the SCSI channel. + +--*/ +; + +EFI_STATUS +EFIAPI +AtapiExtScsiPassThruResetTarget ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun + ) +/*++ + +Routine Description: + + Resets a SCSI device that is connected to a SCSI channel. + +Arguments: + + This - Protocol instance pointer. + Target - The Target ID of the SCSI device to reset. + Lun - The LUN of the SCSI device to reset. + +Returns: + + EFI_SUCCESS - The SCSI device specified by Target and + Lun was reset. + EFI_UNSUPPORTED - The SCSI channel does not support a target + reset operation. + EFI_INVALID_PARAMETER - Target or Lun are invalid. + EFI_DEVICE_ERROR - A device error occurred while attempting + to reset the SCSI device specified by Target + and Lun. + EFI_TIMEOUT - A timeout occurred while attempting to reset + the SCSI device specified by Target and Lun. + +--*/ +; + +EFI_STATUS +EFIAPI +AtapiExtScsiPassThruGetNextTarget ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target + ) +/*++ + +Routine Description: + Used to retrieve the list of legal Target IDs for SCSI devices + on a SCSI channel. + +Arguments: + This - Protocol instance pointer. + Target - On input, a pointer to the Target ID of a SCSI + device present on the SCSI channel. On output, + a pointer to the Target ID of the next SCSI device + present on a SCSI channel. An input value of + 0xFFFFFFFF retrieves the Target ID of the first + SCSI device present on a SCSI channel. + Lun - On input, a pointer to the LUN of a SCSI device + present on the SCSI channel. On output, a pointer + to the LUN of the next SCSI device present on + a SCSI channel. + +Returns: + EFI_SUCCESS - The Target ID and Lun of the next SCSI device + on the SCSI channel was returned in Target and Lun. + EFI_NOT_FOUND - There are no more SCSI devices on this SCSI channel. + EFI_INVALID_PARAMETER - Target is not 0xFFFFFFFF,and Target and Lun were not + returned on a previous call to GetNextDevice(). + +--*/ +; + +EFI_STATUS +CheckSCSIRequestPacket ( + EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +/*++ + +Routine Description: + + Checks the parameters in the SCSI Request Packet to make sure + they are valid for a SCSI Pass Thru request. + +Arguments: + + Packet - The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET + +Returns: + + EFI_STATUS + +--*/ +; + +EFI_STATUS +SubmitBlockingIoCommand ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT32 Target, + EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +/*++ + +Routine Description: + + Performs blocking I/O request. + +Arguments: + + AtapiScsiPrivate: Private data structure for the specified channel. + Target: The Target ID of the ATAPI device to send the SCSI + Request Packet. To ATAPI devices attached on an IDE + Channel, Target ID 0 indicates Master device;Target + ID 1 indicates Slave device. + Packet: The SCSI Request Packet to send to the ATAPI device + specified by Target. + + Returns: EFI_STATUS + +--*/ +; + +BOOLEAN +IsCommandValid ( + EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) + /*++ + +Routine Description: + + Checks the requested SCSI command: + Is it supported by this driver? + Is the Data transfer direction reasonable? + +Arguments: + + Packet - The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET + +Returns: + + EFI_STATUS + +--*/ +; + +EFI_STATUS +CheckExtSCSIRequestPacket ( + EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +/*++ + +Routine Description: + + Checks the parameters in the SCSI Request Packet to make sure + they are valid for a SCSI Pass Thru request. + +Arguments: + + Packet - The pointer of EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET + +Returns: + + EFI_STATUS + +--*/ +; + + +BOOLEAN +IsExtCommandValid ( + EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +/*++ + +Routine Description: + + Checks the requested SCSI command: + Is it supported by this driver? + Is the Data transfer direction reasonable? + +Arguments: + + Packet - The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET + +Returns: + + EFI_STATUS + +--*/ +; + +EFI_STATUS +SubmitExtBlockingIoCommand ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT8 Target, + EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +/*++ + +Routine Description: + + Performs blocking I/O request. + +Arguments: + + AtapiScsiPrivate: Private data structure for the specified channel. + Target: The Target ID of the ATAPI device to send the SCSI + Request Packet. To ATAPI devices attached on an IDE + Channel, Target ID 0 indicates Master device;Target + ID 1 indicates Slave device. + Packet: The SCSI Request Packet to send to the ATAPI device + specified by Target. + + Returns: EFI_STATUS + +--*/ +; + +EFI_STATUS +RequestSenseCommand ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT32 Target, + UINT64 Timeout, + VOID *SenseData, + UINT8 *SenseDataLength + ) +/*++ + +Routine Description: + + Submit request sense command + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + Target - The target ID + Timeout - The time to complete the command + SenseData - The buffer to fill in sense data + SenseDataLength - The length of buffer + +Returns: + + EFI_STATUS + +--*/ +; + +EFI_STATUS +AtapiPacketCommand ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT32 Target, + UINT8 *PacketCommand, + VOID *Buffer, + UINT32 *ByteCount, + DATA_DIRECTION Direction, + UINT64 TimeOutInMicroSeconds + ) +/*++ + +Routine Description: + + Submits ATAPI command packet to the specified ATAPI device. + +Arguments: + + AtapiScsiPrivate: Private data structure for the specified channel. + Target: The Target ID of the ATAPI device to send the SCSI + Request Packet. To ATAPI devices attached on an IDE + Channel, Target ID 0 indicates Master device;Target + ID 1 indicates Slave device. + PacketCommand: Points to the ATAPI command packet. + Buffer: Points to the transferred data. + ByteCount: When input,indicates the buffer size; when output, + indicates the actually transferred data size. + Direction: Indicates the data transfer direction. + TimeoutInMicroSeconds: + The timeout, in micro second units, to use for the + execution of this ATAPI command. + A TimeoutInMicroSeconds value of 0 means that + this function will wait indefinitely for the ATAPI + command to execute. + If TimeoutInMicroSeconds is greater than zero, then + this function will return EFI_TIMEOUT if the time + required to execute the ATAPI command is greater + than TimeoutInMicroSeconds. + +Returns: + + EFI_STATUS + +--*/ +; + + +UINT8 +ReadPortB ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT16 Port + ) +/*++ + +Routine Description: + + Read one byte from a specified I/O port. + +Arguments: + + PciIo - The pointer of EFI_PCI_IO_PROTOCOL + Port - IO port + +Returns: + + A byte read out + +--*/ +; + + +UINT16 +ReadPortW ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT16 Port + ) +/*++ + +Routine Description: + + Read one word from a specified I/O port. + +Arguments: + + PciIo - The pointer of EFI_PCI_IO_PROTOCOL + Port - IO port + +Returns: + + A word read out + +--*/ +; + + +VOID +WritePortB ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT16 Port, + IN UINT8 Data + ) +/*++ + +Routine Description: + + Write one byte to a specified I/O port. + +Arguments: + + PciIo - The pointer of EFI_PCI_IO_PROTOCOL + Port - IO port + Data - The data to write + +Returns: + + NONE + +--*/ +; + + +VOID +WritePortW ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT16 Port, + IN UINT16 Data + ) +/*++ + +Routine Description: + + Write one word to a specified I/O port. + +Arguments: + + PciIo - The pointer of EFI_PCI_IO_PROTOCOL + Port - IO port + Data - The data to write + +Returns: + + NONE + +--*/ +; + +EFI_STATUS +StatusDRQClear ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT64 TimeOutInMicroSeconds + ) +/*++ + +Routine Description: + + Check whether DRQ is clear in the Status Register. (BSY must also be cleared) + If TimeoutInMicroSeconds is zero, this routine should wait infinitely for + DRQ clear. Otherwise, it will return EFI_TIMEOUT when specified time is + elapsed. + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + TimeoutInMicroSeconds - The time to wait for + +Returns: + + EFI_STATUS + +--*/ +; + +EFI_STATUS +AltStatusDRQClear ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT64 TimeOutInMicroSeconds + ) +/*++ + +Routine Description: + + Check whether DRQ is clear in the Alternate Status Register. + (BSY must also be cleared).If TimeoutInMicroSeconds is zero, this routine should + wait infinitely for DRQ clear. Otherwise, it will return EFI_TIMEOUT when specified time is + elapsed. + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + TimeoutInMicroSeconds - The time to wait for + +Returns: + + EFI_STATUS + +--*/ +; + +EFI_STATUS +StatusDRQReady ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT64 TimeOutInMicroSeconds + ) +/*++ + +Routine Description: + + Check whether DRQ is ready in the Status Register. (BSY must also be cleared) + If TimeoutInMicroSeconds is zero, this routine should wait infinitely for + DRQ ready. Otherwise, it will return EFI_TIMEOUT when specified time is + elapsed. + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + TimeoutInMicroSeconds - The time to wait for + +Returns: + + EFI_STATUS + +--*/ +; + +EFI_STATUS +AltStatusDRQReady ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT64 TimeOutInMicroSeconds + ) +/*++ + +Routine Description: + + Check whether DRQ is ready in the Alternate Status Register. + (BSY must also be cleared) + If TimeoutInMicroSeconds is zero, this routine should wait infinitely for + DRQ ready. Otherwise, it will return EFI_TIMEOUT when specified time is + elapsed. + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + TimeoutInMicroSeconds - The time to wait for + +Returns: + + EFI_STATUS + +--*/ +; + +EFI_STATUS +StatusWaitForBSYClear ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT64 TimeoutInMicroSeconds + ) +/*++ + +Routine Description: + + Check whether BSY is clear in the Status Register. + If TimeoutInMicroSeconds is zero, this routine should wait infinitely for + BSY clear. Otherwise, it will return EFI_TIMEOUT when specified time is + elapsed. + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + TimeoutInMicroSeconds - The time to wait for + +Returns: + + EFI_STATUS + +--*/ +; + +EFI_STATUS +AltStatusWaitForBSYClear ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT64 TimeoutInMicroSeconds + ) +/*++ + +Routine Description: + + Check whether BSY is clear in the Alternate Status Register. + If TimeoutInMicroSeconds is zero, this routine should wait infinitely for + BSY clear. Otherwise, it will return EFI_TIMEOUT when specified time is + elapsed. + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + TimeoutInMicroSeconds - The time to wait for + +Returns: + + EFI_STATUS + +--*/ +; + +EFI_STATUS +StatusDRDYReady ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT64 TimeoutInMicroSeconds + ) +/*++ + +Routine Description: + + Check whether DRDY is ready in the Status Register. + (BSY must also be cleared) + If TimeoutInMicroSeconds is zero, this routine should wait infinitely for + DRDY ready. Otherwise, it will return EFI_TIMEOUT when specified time is + elapsed. + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + TimeoutInMicroSeconds - The time to wait for + +Returns: + + EFI_STATUS + +--*/ +; + +EFI_STATUS +AltStatusDRDYReady ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT64 TimeoutInMicroSeconds + ) +/*++ + +Routine Description: + + Check whether DRDY is ready in the Alternate Status Register. + (BSY must also be cleared) + If TimeoutInMicroSeconds is zero, this routine should wait infinitely for + DRDY ready. Otherwise, it will return EFI_TIMEOUT when specified time is + elapsed. + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + TimeoutInMicroSeconds - The time to wait for + +Returns: + + EFI_STATUS + +--*/ +; + +EFI_STATUS +AtapiPassThruPioReadWriteData ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + UINT16 *Buffer, + UINT32 *ByteCount, + DATA_DIRECTION Direction, + UINT64 TimeOutInMicroSeconds + ) +/*++ + +Routine Description: + + Performs data transfer between ATAPI device and host after the + ATAPI command packet is sent. + +Arguments: + + AtapiScsiPrivate: Private data structure for the specified channel. + Buffer: Points to the transferred data. + ByteCount: When input,indicates the buffer size; when output, + indicates the actually transferred data size. + Direction: Indicates the data transfer direction. + TimeoutInMicroSeconds: + The timeout, in micro second units, to use for the + execution of this ATAPI command. + A TimeoutInMicroSeconds value of 0 means that + this function will wait indefinitely for the ATAPI + command to execute. + If TimeoutInMicroSeconds is greater than zero, then + this function will return EFI_TIMEOUT if the time + required to execute the ATAPI command is greater + than TimeoutInMicroSeconds. + Returns: + + EFI_STATUS + +--*/ +; + +EFI_STATUS +AtapiPassThruCheckErrorStatus ( + ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate + ) +/*++ + +Routine Description: + + Check Error Register for Error Information. + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + +Returns: + + EFI_STATUS + +--*/ +; + + +EFI_STATUS +GetIdeRegistersBaseAddr ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + OUT IDE_REGISTERS_BASE_ADDR *IdeRegsBaseAddr + ) +/*++ + +Routine Description: + Get IDE IO port registers' base addresses by mode. In 'Compatibility' mode, + use fixed addresses. In Native-PCI mode, get base addresses from BARs in + the PCI IDE controller's Configuration Space. + +Arguments: + PciIo - Pointer to the EFI_PCI_IO_PROTOCOL instance + IdeRegsBaseAddr - Pointer to IDE_REGISTERS_BASE_ADDR to + receive IDE IO port registers' base addresses + +Returns: + + EFI_STATUS + +--*/ +; + + +VOID +InitAtapiIoPortRegisters ( + IN ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, + IN IDE_REGISTERS_BASE_ADDR *IdeRegsBaseAddr + ) +/*++ + +Routine Description: + + Initialize each Channel's Base Address of CommandBlock and ControlBlock. + +Arguments: + + AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV + IdeRegsBaseAddr - The pointer of IDE_REGISTERS_BASE_ADDR + +Returns: + + None + +--*/ +; + +/** + Installs Scsi Pass Thru and/or Ext Scsi Pass Thru + protocols based on feature flags. + + @param Controller The controller handle to + install these protocols on. + @param AtapiScsiPrivate A pointer to the protocol private + data structure. + + @retval EFI_SUCCESS The installation succeeds. + @retval other The installation fails. + +**/ +EFI_STATUS +InstallScsiPassThruProtocols ( + IN EFI_HANDLE *ControllerHandle, + IN ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate + ); + +#endif diff --git a/Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThruDxe.inf b/Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThruDxe.inf new file mode 100644 index 0000000000..750136275a --- /dev/null +++ b/Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThruDxe.inf @@ -0,0 +1,70 @@ +## @file +# Description file for the Atapi Pass Thru driver. +# +# This driver simulates SCSI devices with Atapi devices to test the SCSI io +# protocol. +# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = AtapiPassThruDxe + FILE_GUID = E49061CE-99A7-41d3-AB3A-36E5CFBAD63E + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = InitializeAtapiPassThru + + PCI_VENDOR_ID = 0x8086 + PCI_DEVICE_ID = 0x2921 + PCI_CLASS_CODE = 0x010100 + PCI_REVISION = 0x0003 + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# +# DRIVER_BINDING = gAtapiScsiPassThruDriverBinding +# COMPONENT_NAME = gAtapiScsiPassThruComponentName +# + +[Sources] + DriverSupportedEfiVersion.c + ComponentName.c + AtapiPassThru.c + AtapiPassThru.h + + +[Packages] + MdePkg/MdePkg.dec + OptionRomPkg/OptionRomPkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + UefiLib + BaseLib + UefiDriverEntryPoint + DebugLib + DevicePathLib + + +[Protocols] + gEfiScsiPassThruProtocolGuid # PROTOCOL BY_START + gEfiExtScsiPassThruProtocolGuid # PROTOCOL BY_START + gEfiPciIoProtocolGuid # PROTOCOL TO_START + gEfiDriverSupportedEfiVersionProtocolGuid # PROTOCOL ALWAYS_PRODUCED + +[FeaturePcd] + gOptionRomPkgTokenSpaceGuid.PcdSupportScsiPassThru + gOptionRomPkgTokenSpaceGuid.PcdSupportExtScsiPassThru + +[Pcd] + gOptionRomPkgTokenSpaceGuid.PcdDriverSupportedEfiVersion + diff --git a/Drivers/OptionRomPkg/AtapiPassThruDxe/ComponentName.c b/Drivers/OptionRomPkg/AtapiPassThruDxe/ComponentName.c new file mode 100644 index 0000000000..007cb5f195 --- /dev/null +++ b/Drivers/OptionRomPkg/AtapiPassThruDxe/ComponentName.c @@ -0,0 +1,169 @@ +/** @file + Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + + Module Name: ComponentName.c + +**/ +#include "AtapiPassThru.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gAtapiScsiPassThruComponentName = { + AtapiScsiPassThruComponentNameGetDriverName, + AtapiScsiPassThruComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gAtapiScsiPassThruComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) AtapiScsiPassThruComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) AtapiScsiPassThruComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mAtapiScsiPassThruDriverNameTable[] = { + { "eng;en", (CHAR16 *) L"ATAPI SCSI Pass Thru Driver" }, + { NULL , NULL } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +AtapiScsiPassThruComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mAtapiScsiPassThruDriverNameTable, + DriverName, + (BOOLEAN)(This == &gAtapiScsiPassThruComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +AtapiScsiPassThruComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/Drivers/OptionRomPkg/AtapiPassThruDxe/DriverSupportedEfiVersion.c b/Drivers/OptionRomPkg/AtapiPassThruDxe/DriverSupportedEfiVersion.c new file mode 100644 index 0000000000..84a9badad2 --- /dev/null +++ b/Drivers/OptionRomPkg/AtapiPassThruDxe/DriverSupportedEfiVersion.c @@ -0,0 +1,14 @@ +/** @file + Copyright (c) 2007, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + + Module Name: DriverSupportEfiVersion.c + +**/ +#include "AtapiPassThru.h" + +EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL gAtapiScsiPassThruDriverSupportedEfiVersion = { + sizeof (EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL), // Size of Protocol structure. + 0 // Version number to be filled at start up. +}; + diff --git a/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/CompatibleDevices.txt b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/CompatibleDevices.txt new file mode 100644 index 0000000000..1ec1cce0d1 --- /dev/null +++ b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/CompatibleDevices.txt @@ -0,0 +1,5 @@ +The following devices have been confirmed to work with the USB Serial Driver: + +Brand Model Name Product Name Vendor ID Device ID +Gearmo USA_FTDI-36 USB to RS-232 0x0403 0x6001 +Sabrent CB-FTDI 0x0403 0x6001 \ No newline at end of file diff --git a/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ComponentName.c b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ComponentName.c new file mode 100644 index 0000000000..d15abf5090 --- /dev/null +++ b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ComponentName.c @@ -0,0 +1,218 @@ +/** @file + UEFI Component Name(2) protocol implementation for USB Serial driver. + +Copyright (c) 2004 - 2013, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "FtdiUsbSerialDriver.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gUsbSerialComponentName = { + (EFI_COMPONENT_NAME_GET_DRIVER_NAME) UsbSerialComponentNameGetDriverName, + (EFI_COMPONENT_NAME_GET_CONTROLLER_NAME) UsbSerialComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUsbSerialComponentName2 = { + UsbSerialComponentNameGetDriverName, + UsbSerialComponentNameGetControllerName, + "en" +}; + +// +// Driver name string table +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUsbSerialDriverNameTable[] = { + { "eng;en", L"FTDI-232 USB Serial Driver" }, + { NULL , NULL } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + @param DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UsbSerialComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mUsbSerialDriverNameTable, + DriverName, + (BOOLEAN)(This == &gUsbSerialComponentName2) + ); +} + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + @param ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + @param ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + @param Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + @param ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UsbSerialComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + USB_SER_DEV *UsbSerDev; + EFI_SERIAL_IO_PROTOCOL *SerialIo; + EFI_USB_IO_PROTOCOL *UsbIoProtocol; + + // + // This is a device driver, so ChildHandle must be NULL. + // + if (ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + // + // Check Controller's handle + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiUsbIoProtocolGuid, + (VOID **) &UsbIoProtocol, + gUsbSerialDriverBinding.DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (!EFI_ERROR (Status)) { + gBS->CloseProtocol ( + ControllerHandle, + &gEfiUsbIoProtocolGuid, + gUsbSerialDriverBinding.DriverBindingHandle, + ControllerHandle + ); + + return EFI_UNSUPPORTED; + } + + if (Status != EFI_ALREADY_STARTED) { + return EFI_UNSUPPORTED; + } + // + // Get the device context + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiSerialIoProtocolGuid, + (VOID **) &SerialIo, + gUsbSerialDriverBinding.DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + UsbSerDev = USB_SER_DEV_FROM_THIS (SerialIo); + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + UsbSerDev->ControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gUsbSerialComponentName2) + ); +} diff --git a/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.c b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.c new file mode 100644 index 0000000000..ac09fae014 --- /dev/null +++ b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.c @@ -0,0 +1,2580 @@ +/** @file + USB Serial Driver that manages USB to Serial and produces Serial IO Protocol. + +Copyright (c) 2004 - 2013, Intel Corporation. All rights reserved. +Portions Copyright 2012 Ashley DeSimone +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +// + +// Tested with VEND_ID 0x0403, DEVICE_ID 0x6001 +// +// Driver starts the device with the following values: +// 115200, No parity, 8 data bits, 1 stop bit, No Flow control +// + +#include "FtdiUsbSerialDriver.h" + +// +// Table of supported devices. This is the device information that this +// driver was developed with. Add other FTDI devices as needed. +// +USB_DEVICE gUSBDeviceList[] = { + {VID_FTDI, DID_FTDI_FT232}, + {0,0} +}; + +// +// USB Serial Driver Global Variables +// +EFI_DRIVER_BINDING_PROTOCOL gUsbSerialDriverBinding = { + UsbSerialDriverBindingSupported, + UsbSerialDriverBindingStart, + UsbSerialDriverBindingStop, + 0xa, + NULL, + NULL +}; + +// +// Table with the nearest power of 2 for the numbers 0-15 +// +UINT8 gRoundedPowersOf2[16] = { 0, 2, 2, 4, 4, 4, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16 }; + +/** + Check to see if the device path node is the Flow control node + + @param[in] FlowControl The device path node to be checked + + @retval TRUE It is the flow control node + @retval FALSE It is not the flow control node + +**/ +BOOLEAN +IsUartFlowControlNode ( + IN UART_FLOW_CONTROL_DEVICE_PATH *FlowControl + ) +{ + return (BOOLEAN) ( + (DevicePathType (FlowControl) == MESSAGING_DEVICE_PATH) && + (DevicePathSubType (FlowControl) == MSG_VENDOR_DP) && + (CompareGuid (&FlowControl->Guid, &gEfiUartDevicePathGuid)) + ); +} + +/** + Checks the device path to see if it contains flow control. + + @param[in] DevicePath The device path to be checked + + @retval TRUE It contains flow control + @retval FALSE It does not contain flow control + +**/ +BOOLEAN +ContainsFlowControl ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + while (!IsDevicePathEnd (DevicePath)) { + if (IsUartFlowControlNode ((UART_FLOW_CONTROL_DEVICE_PATH *) DevicePath)) { + return TRUE; + } + DevicePath = NextDevicePathNode (DevicePath); + } + return FALSE; +} + +/** + Transfer the data between the device and host. + + This function transfers the data between the device and host. + BOT transfer is composed of three phases: Command, Data, and Status. + This is the Data phase. + + @param UsbBot[in] The USB BOT device + @param DataDir[in] The direction of the data + @param Data[in, out] The buffer to hold data + @param TransLen[in, out] The expected length of the data + @param Timeout[in] The time to wait the command to complete + + @retval EFI_SUCCESS The data is transferred + @retval EFI_SUCCESS No data to transfer + @retval EFI_NOT_READY The device return NAK to the transfer + @retval Others Failed to transfer data + +**/ +EFI_STATUS +UsbSerialDataTransfer ( + IN USB_SER_DEV *UsbBot, + IN EFI_USB_DATA_DIRECTION DataDir, + IN OUT VOID *Data, + IN OUT UINTN *TransLen, + IN UINT32 Timeout + ) +{ + EFI_USB_ENDPOINT_DESCRIPTOR *Endpoint; + EFI_STATUS Status; + UINT32 Result; + + // + // If no data to transfer, just return EFI_SUCCESS. + // + if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) { + return EFI_SUCCESS; + } + + // + // Select the endpoint then issue the transfer + // + if (DataDir == EfiUsbDataIn) { + Endpoint = &UsbBot->InEndpointDescriptor; + } else { + Endpoint = &UsbBot->OutEndpointDescriptor; + } + + Result = 0; + Status = UsbBot->UsbIo->UsbBulkTransfer ( + UsbBot->UsbIo, + Endpoint->EndpointAddress, + Data, + TransLen, + Timeout, + &Result + ); + if (EFI_ERROR (Status)) { + if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) { + Status = EFI_NOT_READY; + } else { + UsbBot->Shutdown = TRUE; // Fixes infinite loop in older EFI + } + return Status; + } + return Status; +} + +/** + Sets the status values of the Usb Serial Device. + + @param UsbSerialDevice[in] Handle to the Usb Serial Device to set the status + for + @param StatusBuffer[in] Buffer holding the status values + + @retval EFI_SUCCESS The status values were read and set correctly + +**/ +EFI_STATUS +EFIAPI +SetStatusInternal ( + IN USB_SER_DEV *UsbSerialDevice, + IN UINT8 *StatusBuffer + ) +{ + UINT8 Msr; + + Msr = (StatusBuffer[0] & MSR_MASK); + + // + // set the Status values to disabled + // + UsbSerialDevice->StatusValues.CtsState = FALSE; + UsbSerialDevice->StatusValues.DsrState = FALSE; + UsbSerialDevice->StatusValues.RiState = FALSE; + UsbSerialDevice->StatusValues.SdState = FALSE; + + // + // Check the values from the status buffer and set the appropriate status + // values to enabled + // + if ((Msr & CTS_MASK) == CTS_MASK) { + UsbSerialDevice->StatusValues.CtsState = TRUE; + } + if ((Msr & DSR_MASK) == DSR_MASK) { + UsbSerialDevice->StatusValues.DsrState = TRUE; + } + if ((Msr & RI_MASK) == RI_MASK) { + UsbSerialDevice->StatusValues.RiState = TRUE; + } + if ((Msr & SD_MASK) == SD_MASK) { + UsbSerialDevice->StatusValues.SdState = TRUE; + } + return EFI_SUCCESS; +} + +/** + Initiates a read operation on the Usb Serial Device. + + @param UsbSerialDevice[in] Handle to the USB device to read + @param BufferSize[in, out] On input, the size of the Buffer. On output, + the amount of data returned in Buffer. + Setting this to zero will initiate a read + and store all data returned in the internal + buffer. + @param Buffer [out] The buffer to return the data into. + + @retval EFI_SUCCESS The data was read. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_TIMEOUT The data write was stopped due to a timeout. + +**/ +EFI_STATUS +EFIAPI +ReadDataFromUsb ( + IN USB_SER_DEV *UsbSerialDevice, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + UINTN ReadBufferSize; + UINT8 *ReadBuffer; + UINTN Index; + EFI_TPL Tpl; + UINT8 StatusBuffer[2]; // buffer to store the status bytes + + ReadBufferSize = 512; + ReadBuffer = &(UsbSerialDevice->ReadBuffer[0]); + + if (UsbSerialDevice->Shutdown) { + return EFI_DEVICE_ERROR; + } + + Tpl = gBS->RaiseTPL (TPL_NOTIFY); + + Status = UsbSerialDataTransfer ( + UsbSerialDevice, + EfiUsbDataIn, + ReadBuffer, + &ReadBufferSize, + FTDI_TIMEOUT*2 //Padded because timers won't be exactly aligned + ); + if (EFI_ERROR (Status)) { + gBS->RestoreTPL (Tpl); + if (Status == EFI_TIMEOUT) { + return EFI_TIMEOUT; + } else { + return EFI_DEVICE_ERROR; + } + } + + // + // Store the status bytes in the status buffer + // + for (Index = 0; Index < 2; Index++) {//only the first 2 bytes are status bytes + StatusBuffer[Index] = ReadBuffer[Index]; + } + // + // update the statusvalue field of the usbserialdevice + // + Status = SetStatusInternal (UsbSerialDevice, StatusBuffer); + if (Status != EFI_SUCCESS) { + } + + // + // Store the read data in the read buffer, start at 2 to ignore status bytes + // + for (Index = 2; Index < ReadBufferSize; Index++) { + if (((UsbSerialDevice->DataBufferTail + 1) % SW_FIFO_DEPTH) == UsbSerialDevice->DataBufferHead) { + break; + } + if (ReadBuffer[Index] == 0x00) { + // + // This is null, do not add + // + } else { + UsbSerialDevice->DataBuffer[UsbSerialDevice->DataBufferTail] = ReadBuffer[Index]; + UsbSerialDevice->DataBufferTail = (UsbSerialDevice->DataBufferTail + 1) % SW_FIFO_DEPTH; + } + } + + // + // Read characters out of the buffer to satisfy caller's request. + // + for (Index = 0; Index < *BufferSize; Index++) { + if (UsbSerialDevice->DataBufferHead == UsbSerialDevice->DataBufferTail) { + break; + } + // + // Still have characters in the buffer to return + // + ((UINT8 *)Buffer)[Index] = UsbSerialDevice->DataBuffer[UsbSerialDevice->DataBufferHead]; + UsbSerialDevice->DataBufferHead = (UsbSerialDevice->DataBufferHead + 1) % SW_FIFO_DEPTH; + } + // + // Return actual number of bytes returned. + // + *BufferSize = Index; + gBS->RestoreTPL (Tpl); + return EFI_SUCCESS; +} + +/** + Sets the initial status values of the Usb Serial Device by reading the status + bytes from the device. + + @param UsbSerialDevice[in] Handle to the Usb Serial Device that needs its + initial status values set + + @retval EFI_SUCCESS The status bytes were read successfully and the + initial status values were set correctly + @retval EFI_TIMEOUT The read of the status bytes was stopped due to a + timeout + @retval EFI_DEVICE_ERROR The device reported an error during the read of + the status bytes + +**/ +EFI_STATUS +EFIAPI +SetInitialStatus ( + IN USB_SER_DEV *UsbSerialDevice + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + EFI_TPL Tpl; + UINT8 StatusBuffer[2]; + + Status = EFI_UNSUPPORTED; + BufferSize = sizeof (StatusBuffer); + + if (UsbSerialDevice->Shutdown) { + return EFI_DEVICE_ERROR; + } + + Tpl = gBS->RaiseTPL (TPL_NOTIFY); + + Status = UsbSerialDataTransfer ( + UsbSerialDevice, + EfiUsbDataIn, + StatusBuffer, + &BufferSize, + 40 //Slightly more than 2x the FTDI polling frequency to make sure that data will be returned + ); + + Status = SetStatusInternal (UsbSerialDevice, StatusBuffer); + + gBS->RestoreTPL (Tpl); + + return Status; +} + +/** + UsbSerialDriverCheckInput. + attempts to read data in from the device periodically, stores any read data + and updates the control attributes. + + @param Event[in] + @param Context[in]....The current instance of the USB serial device + +**/ +VOID +EFIAPI +UsbSerialDriverCheckInput ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UINTN BufferSize; + USB_SER_DEV *UsbSerialDevice; + + UsbSerialDevice = (USB_SER_DEV*)Context; + + if (UsbSerialDevice->DataBufferHead == UsbSerialDevice->DataBufferTail) { + // + // Data buffer is empty, try to read from device + // + BufferSize = 0; + ReadDataFromUsb (UsbSerialDevice, &BufferSize, NULL); + if (UsbSerialDevice->DataBufferHead == UsbSerialDevice->DataBufferTail) { + // + // Data buffer still has no data, set the EFI_SERIAL_INPUT_BUFFER_EMPTY + // flag + // + UsbSerialDevice->ControlBits |= EFI_SERIAL_INPUT_BUFFER_EMPTY; + } else { + // + // Read has returned some data, clear the EFI_SERIAL_INPUT_BUFFER_EMPTY + // flag + // + UsbSerialDevice->ControlBits &= ~(EFI_SERIAL_INPUT_BUFFER_EMPTY); + } + } else { + // + // Data buffer has data, no read attempt required + // + UsbSerialDevice->ControlBits &= ~(EFI_SERIAL_INPUT_BUFFER_EMPTY); + } +} + +/** + Encodes the baud rate into the format expected by the Ftdi device. + + @param BaudRate[in] The baudrate to be set on the device + @param EncodedBaudRate[out] The baud rate encoded in the format + expected by the Ftdi device + + @return EFI_SUCCESS Baudrate encoding was calculated + successfully + @return EFI_INVALID_PARAMETER An invalid value of BaudRate was received + +**/ +EFI_STATUS +EFIAPI +EncodeBaudRateForFtdi ( + IN UINT64 BaudRate, + OUT UINT16 *EncodedBaudRate + ) +{ + UINT32 Divisor; + UINT32 AdjustedFrequency; + UINT16 Result; + + // + // Check to make sure we won't get an integer overflow + // + if ((BaudRate < 178) || ( BaudRate > ((FTDI_UART_FREQUENCY * 100) / 97))) { + return EFI_INVALID_PARAMETER; + } + + // + // Baud Rates of 2000000 and 3000000 are special cases + // + if ((BaudRate >= FTDI_SPECIAL_CASE_300_MIN) && (BaudRate <= FTDI_SPECIAL_CASE_300_MAX)) { + *EncodedBaudRate = 0; + return EFI_SUCCESS; + } + if ((BaudRate >= FTDI_SPECIAL_CASE_200_MIN) && (BaudRate <= FTDI_SPECIAL_CASE_200_MAX)) { + *EncodedBaudRate = 1; + return EFI_SUCCESS; + } + + // + // Compute divisor + // + Divisor = (FTDI_UART_FREQUENCY << 4) / (UINT32)BaudRate; + + // + // Round the last 4 bits to the nearest power of 2 + // + Divisor = (Divisor & ~(0xF)) + (gRoundedPowersOf2[Divisor & 0xF]); + + // + // Check to make sure computed divisor is within + // the min and max that FTDI controller will accept + // + if (Divisor < FTDI_MIN_DIVISOR) { + Divisor = FTDI_MIN_DIVISOR; + } else if (Divisor > FTDI_MAX_DIVISOR) { + Divisor = FTDI_MAX_DIVISOR; + } + + // + // Check to make sure the frequency that the FTDI chip will need to + // generate to attain the requested Baud Rate is within 3% of the + // 3MHz clock frequency that the FTDI chip runs at. + // + // (3MHz * 1600) / 103 = 46601941 + // (3MHz * 1600) / 97 = 49484536 + // + AdjustedFrequency = (((UINT32)BaudRate) * Divisor); + if ((AdjustedFrequency < FTDI_MIN_FREQUENCY) || (AdjustedFrequency > FTDI_MAX_FREQUENCY)) { + return EFI_INVALID_PARAMETER; + } + + // + // Encode the Divisor into the format FTDI expects + // + Result = (UINT16)(Divisor >> 4); + if ((Divisor & 0x8) != 0) { + Result |= 0x4000; + } else if ((Divisor & 0x4) != 0) { + Result |= 0x8000; + } else if ((Divisor & 0x2) != 0) { + Result |= 0xC000; + } + + *EncodedBaudRate = Result; + return EFI_SUCCESS; +} + +/** + Uses USB I/O to check whether the device is a USB Serial device. + + @param UsbIo[in] Pointer to a USB I/O protocol instance. + + @retval TRUE Device is a USB Serial device. + @retval FALSE Device is a not USB Serial device. + +**/ +BOOLEAN +IsUsbSerial ( + IN EFI_USB_IO_PROTOCOL *UsbIo + ) +{ + EFI_STATUS Status; + EFI_USB_DEVICE_DESCRIPTOR DeviceDescriptor; + CHAR16 *StrMfg; + BOOLEAN Found; + UINT32 Index; + + // + // Get the default device descriptor + // + Status = UsbIo->UsbGetDeviceDescriptor ( + UsbIo, + &DeviceDescriptor + ); + if (EFI_ERROR (Status)) { + return FALSE; + } + + Found = FALSE; + Index = 0; + while (gUSBDeviceList[Index].VendorId != 0 && + gUSBDeviceList[Index].DeviceId != 0 && + !Found ) { + if (DeviceDescriptor.IdProduct == gUSBDeviceList[Index].DeviceId && + DeviceDescriptor.IdVendor == gUSBDeviceList[Index].VendorId ){ + // + // Checks to see if a string descriptor can be pulled from the device in + // the selected language. If not False is returned indicating that this + // is not a Usb Serial Device that can be managegd by this driver + // + StrMfg = NULL; + Status = UsbIo->UsbGetStringDescriptor ( + UsbIo, + USB_US_LANG_ID, // LANGID selector, should make this + // more robust to verify lang support + // for device + DeviceDescriptor.StrManufacturer, + &StrMfg + ); + if (StrMfg != NULL) { + FreePool (StrMfg); + } + if (EFI_ERROR (Status)) { + return FALSE; + } + return TRUE; + } + Index++; + } + return FALSE; +} + +/** + Internal function that sets the Data Bits, Stop Bits and Parity values on the + Usb Serial Device with a single usb control transfer. + + @param UsbIo[in] Usb Io Protocol instance pointer + @param DataBits[in] The data bits value to be set on the Usb + Serial Device + @param Parity[in] The parity type that will be set on the Usb + Serial Device + @param StopBits[in] The stop bits type that will be set on the + Usb Serial Device + @param LastSettings[in] A pointer to the Usb Serial Device's + PREVIOUS_ATTRIBUTES item + + @retval EFI_SUCCESS The data items were correctly set on the + USB Serial Device + @retval EFI_INVALID_PARAMETER An invalid data parameter or an invalid + combination or parameters was used + @retval EFI_DEVICE_ERROR The device is not functioning correctly and + the data values were unable to be set + +**/ +EFI_STATUS +EFIAPI +SetDataInternal ( + IN EFI_USB_IO_PROTOCOL *UsbIo, + IN UINT8 DataBits, + IN EFI_PARITY_TYPE Parity, + IN EFI_STOP_BITS_TYPE StopBits, + IN PREVIOUS_ATTRIBUTES *LastSettings + ) +{ + EFI_STATUS Status; + EFI_USB_DEVICE_REQUEST DevReq; + UINT32 ReturnValue; + UINT8 ConfigurationValue; + + // + // Since data bits settings of 6,7,8 cannot be set with a stop bits setting of + // 1.5 check to see if this happens when the values of last settings are used + // + if ((DataBits == 0) && (StopBits == OneFiveStopBits)) { + if ((LastSettings->DataBits == 6) || (LastSettings->DataBits == 7) || (LastSettings->DataBits == 8)) { + return EFI_INVALID_PARAMETER; + } + } else if ((StopBits == DefaultStopBits) && ((DataBits == 6) || (DataBits == 7) || (DataBits == 8))) { + if (LastSettings->StopBits == OneFiveStopBits) { + return EFI_INVALID_PARAMETER; + } + } else if ((DataBits == 0) && (StopBits == DefaultStopBits)) { + if (LastSettings->StopBits == OneFiveStopBits) { + if ((LastSettings->DataBits == 6) || (LastSettings->DataBits == 7) || (LastSettings->DataBits == 8)) { + return EFI_INVALID_PARAMETER; + } + } + } + + // + // set the DevReq.Value for the usb control transfer to the correct value + // based on the seleceted number of data bits if there is an invalid number of + // data bits requested return EFI_INVALID_PARAMETER + // + if (((DataBits < 5 ) || (DataBits > 8)) && (DataBits != 0)) { + return EFI_INVALID_PARAMETER; + } + if (DataBits == 0) { + // + // use the value of LastDataBits + // + DevReq.Value = SET_DATA_BITS (LastSettings->DataBits); + } else { + // + // use the value of DataBits + // + DevReq.Value = SET_DATA_BITS (DataBits); + } + + // + // Set Parity + // + if (Parity == DefaultParity) { + Parity = LastSettings->Parity; + } + + if (Parity == NoParity) { + DevReq.Value |= SET_PARITY_NONE; + } else if (Parity == EvenParity) { + DevReq.Value |= SET_PARITY_EVEN; + } else if (Parity == OddParity){ + DevReq.Value |= SET_PARITY_ODD; + } else if (Parity == MarkParity) { + DevReq.Value |= SET_PARITY_MARK; + } else if (Parity == SpaceParity) { + DevReq.Value |= SET_PARITY_SPACE; + } + + // + // Set Stop Bits + // + if (StopBits == DefaultStopBits) { + StopBits = LastSettings->StopBits; + } + + if (StopBits == OneStopBit) { + DevReq.Value |= SET_STOP_BITS_1; + } else if (StopBits == OneFiveStopBits) { + DevReq.Value |= SET_STOP_BITS_15; + } else if (StopBits == TwoStopBits) { + DevReq.Value |= SET_STOP_BITS_2; + } + + // + // set the rest of the DevReq parameters and perform the usb control transfer + // to set the data bits on the device + // + DevReq.Request = FTDI_COMMAND_SET_DATA; + DevReq.RequestType = USB_REQ_TYPE_VENDOR; + DevReq.Index = FTDI_PORT_IDENTIFIER; + DevReq.Length = 0; // indicates that there is no data phase in this request + + Status = UsbIo->UsbControlTransfer ( + UsbIo, + &DevReq, + EfiUsbDataOut, + WDR_SHORT_TIMEOUT, + &ConfigurationValue, + 1, + &ReturnValue + ); + if (EFI_ERROR (Status)) { + goto StatusError; + } + return Status; + +StatusError: + if ((Status != EFI_INVALID_PARAMETER) || (Status != EFI_DEVICE_ERROR)) { + return EFI_DEVICE_ERROR; + } else { + return Status; + } +} + +/** + Internal function that sets the baudrate on the Usb Serial Device. + + @param UsbIo[in] Usb Io Protocol instance pointer + @param BaudRate[in] The baudrate value to be set on the device. + If this value is 0 the value of LastBaudRate + will be used instead + @param LastBaudRate[in] The baud rate value that was previously set + on the Usb Serial Device + + @retval EFI_SUCCESS The baudrate was set succesfully + @retval EFI_INVALID_PARAMETER An invalid baudrate was used + @retval EFI_DEVICE_ERROR The device is not functioning correctly and + the baudrate was unable to be set + +**/ +EFI_STATUS +EFIAPI +SetBaudRateInternal ( + IN EFI_USB_IO_PROTOCOL *UsbIo, + IN UINT64 BaudRate, + IN UINT64 LastBaudRate + ) +{ + EFI_STATUS Status; + EFI_USB_DEVICE_REQUEST DevReq; + UINT32 ReturnValue; + UINT8 ConfigurationValue; + UINT16 EncodedBaudRate; + EFI_TPL Tpl; + + Tpl = gBS->RaiseTPL(TPL_NOTIFY); + + // + // set the value of DevReq.Value based on the value of BaudRate + // if 0 is selected as baud rate use the value of LastBaudRate + // + if (BaudRate == 0) { + Status = EncodeBaudRateForFtdi (LastBaudRate, &EncodedBaudRate); + if (EFI_ERROR (Status)) { + gBS->RestoreTPL (Tpl); + // + // EncodeBaudRateForFtdi returns EFI_INVALID_PARAMETER when not + // succesfull + // + return Status; + } + DevReq.Value = EncodedBaudRate; + } else { + Status = EncodeBaudRateForFtdi (BaudRate, &EncodedBaudRate); + if (EFI_ERROR (Status)) { + gBS->RestoreTPL (Tpl); + // + // EncodeBaudRateForFtdi returns EFI_INVALID_PARAMETER when not + // successfull + // + return Status; + } + DevReq.Value = EncodedBaudRate; + } + + // + // set the remaining parameters of DevReq and perform the usb control transfer + // to set the device + // + DevReq.Request = FTDI_COMMAND_SET_BAUDRATE; + DevReq.RequestType = USB_REQ_TYPE_VENDOR; + DevReq.Index = FTDI_PORT_IDENTIFIER; + DevReq.Length = 0; // indicates that there is no data phase in this request + + Status = UsbIo->UsbControlTransfer ( + UsbIo, + &DevReq, + EfiUsbDataOut, + WDR_SHORT_TIMEOUT, + &ConfigurationValue, + 1, + &ReturnValue + ); + if (EFI_ERROR (Status)) { + goto StatusError; + } + gBS->RestoreTPL (Tpl); + return Status; + +StatusError: + gBS->RestoreTPL (Tpl); + if ((Status != EFI_INVALID_PARAMETER) || (Status != EFI_DEVICE_ERROR)) { + return EFI_DEVICE_ERROR; + } else { + return Status; + } +} + +/** + Sets the baud rate, receive FIFO depth, transmit/receice time out, parity, + data bits, and stop bits on a serial device. + + @param UsbSerialDevice[in] Pointer to the current instance of the USB Serial + Device. + @param BaudRate[in] The requested baud rate. A BaudRate value of 0 + will use the device's default interface speed. + @param ReveiveFifoDepth[in] The requested depth of the FIFO on the receive + side of the serial interface. A ReceiveFifoDepth + value of 0 will use the device's default FIFO + depth. + @param Timeout[in] The requested time out for a single character in + microseconds.This timeout applies to both the + transmit and receive side of the interface.A + Timeout value of 0 will use the device's default + time out value. + @param Parity[in] The type of parity to use on this serial device. + A Parity value of DefaultParity will use the + device's default parity value. + @param DataBits[in] The number of data bits to use on the serial + device. A DataBits value of 0 will use the + device's default data bit setting. + @param StopBits[in] The number of stop bits to use on this serial + device. A StopBits value of DefaultStopBits will + use the device's default number of stop bits. + + @retval EFI_SUCCESS The attributes were set + @retval EFI_DEVICE_ERROR The attributes were not able to be set + +**/ +EFI_STATUS +EFIAPI +SetAttributesInternal ( + IN USB_SER_DEV *UsbSerialDevice, + IN UINT64 BaudRate, + IN UINT32 ReceiveFifoDepth, + IN UINT32 Timeout, + IN EFI_PARITY_TYPE Parity, + IN UINT8 DataBits, + IN EFI_STOP_BITS_TYPE StopBits + ) +{ + EFI_STATUS Status; + EFI_TPL Tpl; + UART_DEVICE_PATH *Uart; + EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath; + + Status = EFI_UNSUPPORTED; + Tpl = gBS->RaiseTPL(TPL_NOTIFY); + Uart = NULL; + + // + // check for invalid combinations of parameters + // + if (((DataBits >= 6) && (DataBits <= 8)) && (StopBits == OneFiveStopBits)) { + return EFI_INVALID_PARAMETER; + } + + // + // set data bits, parity and stop bits + // + Status = SetDataInternal ( + UsbSerialDevice->UsbIo, + DataBits, + Parity, + StopBits, + &(UsbSerialDevice->LastSettings) + ); + if (EFI_ERROR (Status)) { + goto StatusError; + } + // + // set baudrate + // + Status = SetBaudRateInternal ( + UsbSerialDevice->UsbIo, + BaudRate, + UsbSerialDevice->LastSettings.BaudRate + ); + if (EFI_ERROR (Status)){ + goto StatusError; + } + + // + // update the values of UsbSerialDevice->LastSettings and UsbSerialDevice->SerialIo.Mode + // + if (BaudRate == 0) { + UsbSerialDevice->LastSettings.BaudRate = UsbSerialDevice->LastSettings.BaudRate; + UsbSerialDevice->SerialIo.Mode->BaudRate = UsbSerialDevice->LastSettings.BaudRate; + } else { + UsbSerialDevice->LastSettings.BaudRate = BaudRate; + UsbSerialDevice->SerialIo.Mode->BaudRate = BaudRate; + } + + UsbSerialDevice->LastSettings.Timeout = FTDI_TIMEOUT; + UsbSerialDevice->LastSettings.ReceiveFifoDepth = FTDI_MAX_RECEIVE_FIFO_DEPTH; + + if (Parity == DefaultParity) { + UsbSerialDevice->LastSettings.Parity = UsbSerialDevice->LastSettings.Parity; + UsbSerialDevice->SerialIo.Mode->Parity = UsbSerialDevice->LastSettings.Parity; + } else { + UsbSerialDevice->LastSettings.Parity = Parity; + UsbSerialDevice->SerialIo.Mode->Parity = Parity; + } + if (DataBits == 0) { + UsbSerialDevice->LastSettings.DataBits = UsbSerialDevice->LastSettings.DataBits; + UsbSerialDevice->SerialIo.Mode->DataBits = UsbSerialDevice->LastSettings.DataBits; + } else { + UsbSerialDevice->LastSettings.DataBits = DataBits; + UsbSerialDevice->SerialIo.Mode->DataBits = DataBits; + } + if (StopBits == DefaultStopBits) { + UsbSerialDevice->LastSettings.StopBits = UsbSerialDevice->LastSettings.StopBits; + UsbSerialDevice->SerialIo.Mode->StopBits = UsbSerialDevice->LastSettings.StopBits; + } else { + UsbSerialDevice->LastSettings.StopBits = StopBits; + UsbSerialDevice->SerialIo.Mode->StopBits = StopBits; + } + + // + // See if the device path node has changed + // + if (UsbSerialDevice->UartDevicePath.BaudRate == BaudRate && + UsbSerialDevice->UartDevicePath.DataBits == DataBits && + UsbSerialDevice->UartDevicePath.StopBits == StopBits && + UsbSerialDevice->UartDevicePath.Parity == Parity + ) { + gBS->RestoreTPL (Tpl); + return EFI_SUCCESS; + } + + // + // Update the device path + // + UsbSerialDevice->UartDevicePath.BaudRate = BaudRate; + UsbSerialDevice->UartDevicePath.DataBits = DataBits; + UsbSerialDevice->UartDevicePath.StopBits = (UINT8) StopBits; + UsbSerialDevice->UartDevicePath.Parity = (UINT8) Parity; + + Status = EFI_SUCCESS; + if (UsbSerialDevice->ControllerHandle != NULL) { + RemainingDevicePath = UsbSerialDevice->DevicePath; + while (!IsDevicePathEnd (RemainingDevicePath)) { + Uart = (UART_DEVICE_PATH *) NextDevicePathNode (RemainingDevicePath); + if (Uart->Header.Type == MESSAGING_DEVICE_PATH && + Uart->Header.SubType == MSG_UART_DP && + sizeof (UART_DEVICE_PATH) == DevicePathNodeLength ((EFI_DEVICE_PATH *) Uart)) { + Uart->BaudRate = BaudRate; + Uart->DataBits = DataBits; + Uart->StopBits = (UINT8)StopBits; + Uart->Parity = (UINT8) Parity; + break; + } + RemainingDevicePath = NextDevicePathNode (RemainingDevicePath); + } + } + + gBS->RestoreTPL (Tpl); + return Status; + +StatusError: + gBS->RestoreTPL (Tpl); + if ((Status != EFI_INVALID_PARAMETER) || (Status != EFI_DEVICE_ERROR)) { + return EFI_DEVICE_ERROR; + } else { + return Status; + } +} + +/** + Internal function that performs a Usb Control Transfer to set the flow control + on the Usb Serial Device. + + @param UsbIo[in] Usb Io Protocol instance pointer + @param FlowControlEnable[in] Data on the Enable/Disable status of Flow + Control on the Usb Serial Device + + @retval EFI_SUCCESS The flow control was set on the Usb Serial + device + @retval EFI_INVALID_PARAMETER An invalid flow control value was used + @retval EFI_EFI_UNSUPPORTED The operation is not supported + @retval EFI_DEVICE_ERROR The device is not functioning correctly + +**/ +EFI_STATUS +EFIAPI +SetFlowControlInternal ( + IN EFI_USB_IO_PROTOCOL *UsbIo, + IN BOOLEAN FlowControlEnable + ) +{ + EFI_STATUS Status; + EFI_USB_DEVICE_REQUEST DevReq; + UINT32 ReturnValue; + UINT8 ConfigurationValue; + + // + // set DevReq.Value based on the value of FlowControlEnable + // + if (!FlowControlEnable) { + DevReq.Value = NO_FLOW_CTRL; + } + if (FlowControlEnable) { + DevReq.Value = XON_XOFF_CTRL; + } + // + // set the remaining DevReq parameters and perform the usb control transfer to + // set the flow control on the device + // + DevReq.Request = FTDI_COMMAND_SET_FLOW_CTRL; + DevReq.RequestType = USB_REQ_TYPE_VENDOR; + DevReq.Index = FTDI_PORT_IDENTIFIER; + DevReq.Length = 0; // indicates that this transfer has no data phase + Status = UsbIo->UsbControlTransfer ( + UsbIo, + &DevReq, + EfiUsbDataOut, + WDR_TIMEOUT, + &ConfigurationValue, + 1, + &ReturnValue + ); + if (EFI_ERROR (Status)) { + goto StatusError; + } + + return Status; + +StatusError: + if ((Status != EFI_INVALID_PARAMETER) || + (Status != EFI_DEVICE_ERROR) || + (Status != EFI_UNSUPPORTED) ) { + return EFI_DEVICE_ERROR; + } else { + return Status; + } +} + +/** + Internal function that performs a Usb Control Transfer to set the Dtr value on + the Usb Serial Device. + + @param UsbIo[in] Usb Io Protocol instance pointer + @param DtrEnable[in] Data on the Enable/Disable status of the + Dtr for the Usb Serial Device + + @retval EFI_SUCCESS The Dtr value was set on the Usb Serial + Device + @retval EFI_INVALID_PARAMETER An invalid Dtr value was used + @retval EFI_UNSUPPORTED The operation is not supported + @retval EFI_DEVICE_ERROR The device is not functioning correctly + +**/ +EFI_STATUS +EFIAPI +SetDtrInternal ( + IN EFI_USB_IO_PROTOCOL *UsbIo, + IN BOOLEAN DtrEnable + ) +{ + EFI_STATUS Status; + EFI_USB_DEVICE_REQUEST DevReq; + UINT32 ReturnValue; + UINT8 ConfigurationValue; + + // + // set the value of DevReq.Value based on the value of DtrEnable + // + if (!DtrEnable) { + DevReq.Value = SET_DTR_LOW; + } + if (DtrEnable) { + DevReq.Value = SET_DTR_HIGH; + } + // + // set the remaining attributes of DevReq and perform the usb control transfer + // to set the device + // + DevReq.Request = FTDI_COMMAND_MODEM_CTRL; + DevReq.RequestType = USB_REQ_TYPE_VENDOR; + DevReq.Index = FTDI_PORT_IDENTIFIER; + DevReq.Length = 0; // indicates that there is no data phase in this transfer + + Status = UsbIo->UsbControlTransfer ( + UsbIo, + &DevReq, + EfiUsbDataOut, + WDR_TIMEOUT, + &ConfigurationValue, + 1, + &ReturnValue + ); + if (EFI_ERROR (Status)) { + goto StatusError; + } + return Status; + +StatusError: + if ((Status != EFI_INVALID_PARAMETER) || + (Status != EFI_DEVICE_ERROR) || + (Status != EFI_UNSUPPORTED) ) { + return EFI_DEVICE_ERROR; + } else { + return Status; + } +} + +/** + Internal function that performs a Usb Control Transfer to set the Dtr value on + the Usb Serial Device. + + @param UsbIo[in] Usb Io Protocol instance pointer + @param RtsEnable[in] Data on the Enable/Disable status of the + Rts for the Usb Serial Device + + @retval EFI_SUCCESS The Rts value was set on the Usb Serial + Device + @retval EFI_INVALID_PARAMETER An invalid Rts value was used + @retval EFI_UNSUPPORTED The operation is not supported + @retval EFI_DEVICE_ERROR The device is not functioning correctly + +**/ +EFI_STATUS +EFIAPI +SetRtsInternal ( + IN EFI_USB_IO_PROTOCOL *UsbIo, + IN BOOLEAN RtsEnable + ) +{ + EFI_STATUS Status; + EFI_USB_DEVICE_REQUEST DevReq; + UINT32 ReturnValue; + UINT8 ConfigurationValue; + + // + // set DevReq.Value based on the value of RtsEnable + // + if (!RtsEnable) { + DevReq.Value = SET_RTS_LOW; + } + if (RtsEnable) { + DevReq.Value = SET_RTS_HIGH; + } + + // + // set the remaining parameters of DevReq and perform the usb control transfer + // to set the values on the device + // + DevReq.Request = FTDI_COMMAND_MODEM_CTRL; + DevReq.RequestType = USB_REQ_TYPE_VENDOR; + DevReq.Index = FTDI_PORT_IDENTIFIER; + DevReq.Length = 0; // indicates that there is no data phase in this request + + Status = UsbIo->UsbControlTransfer ( + UsbIo, + &DevReq, + EfiUsbDataOut, + WDR_TIMEOUT, + &ConfigurationValue, + 1, + &ReturnValue + ); + if (EFI_ERROR (Status)) { + goto StatusError; + } + + return Status; + +StatusError: + if ((Status != EFI_INVALID_PARAMETER) || + (Status != EFI_DEVICE_ERROR) || + (Status != EFI_UNSUPPORTED) ) { + return EFI_DEVICE_ERROR; + } else { + return Status; + } +} + +/** + Internal function that checks for valid control values and sets the control + bits on the Usb Serial Device. + + @param UsbSerialDevice[in] Handle to the Usb Serial Device whose + control bits are being set + @param Control[in] The control value passed to the function + that contains the values of the control + bits that are being set + + @retval EFI_SUCCESS The control bits were set on the Usb Serial + Device + @retval EFI_INVALID_PARAMETER An invalid control value was encountered + @retval EFI_EFI_UNSUPPORTED The operation is not supported + @retval EFI_DEVICE_ERROR The device is not functioning correctly + +**/ +EFI_STATUS +EFIAPI +SetControlBitsInternal ( + IN USB_SER_DEV *UsbSerialDevice, + IN CONTROL_BITS *Control + ) +{ + EFI_STATUS Status; + UART_FLOW_CONTROL_DEVICE_PATH *FlowControl; + EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath; + + // + // check for invalid control parameters hardware and software loopback enabled + // must always be set to FALSE + // + Control->HardwareLoopBack = FALSE; + Control->SoftwareLoopBack = FALSE; + + // + // set hardware flow control + // + Status = SetFlowControlInternal ( + UsbSerialDevice->UsbIo, + Control->HardwareFlowControl + ); + if (EFI_ERROR (Status)) { + goto StatusError; + } + + // + // set Dtr state + // + Status = SetDtrInternal (UsbSerialDevice->UsbIo, Control->DtrState); + if (EFI_ERROR (Status)) { + goto StatusError; + } + + // + // set Rts state + // + Status = SetRtsInternal (UsbSerialDevice->UsbIo, Control->RtsState); + if (EFI_ERROR (Status)){ + goto StatusError; + } + + // + // update the remaining control values for UsbSerialDevice->ControlValues + // + UsbSerialDevice->ControlValues.DtrState = Control->DtrState; + UsbSerialDevice->ControlValues.RtsState = Control->RtsState; + UsbSerialDevice->ControlValues.HardwareFlowControl = Control->HardwareFlowControl; + UsbSerialDevice->ControlValues.HardwareLoopBack = FALSE; + UsbSerialDevice->ControlValues.SoftwareLoopBack = FALSE; + + Status = EFI_SUCCESS; + // + // Update the device path to have the correct flow control values + // + if (UsbSerialDevice->ControllerHandle != NULL) { + RemainingDevicePath = UsbSerialDevice->DevicePath; + while (!IsDevicePathEnd (RemainingDevicePath)) { + FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (RemainingDevicePath); + if (FlowControl->Header.Type == MESSAGING_DEVICE_PATH && + FlowControl->Header.SubType == MSG_VENDOR_DP && + sizeof (UART_FLOW_CONTROL_DEVICE_PATH) == DevicePathNodeLength ((EFI_DEVICE_PATH *) FlowControl)){ + if (UsbSerialDevice->ControlValues.HardwareFlowControl == TRUE) { + FlowControl->FlowControlMap = UART_FLOW_CONTROL_HARDWARE; + } else if (UsbSerialDevice->ControlValues.HardwareFlowControl == FALSE) { + FlowControl->FlowControlMap = 0; + } + break; + } + RemainingDevicePath = NextDevicePathNode (RemainingDevicePath); + } + } + + return Status; + +StatusError: + if ((Status != EFI_INVALID_PARAMETER) || + (Status != EFI_DEVICE_ERROR) || + (Status != EFI_UNSUPPORTED) ) { + return EFI_DEVICE_ERROR; + } else { + return Status; + } +} + +/** + Internal function that calculates the Control value used by GetControlBits() + based on the status and control values of the Usb Serial Device. + + @param UsbSerialDevice[in] Handle to the Usb Serial Devie whose status + and control values are being used to set + Control + @param Control[out] On output the formated value of Control + that has been calculated based on the + control and status values of the Usb Serial + Device + + @retval EFI_SUCCESS The value of Control was successfully + calculated + +**/ +EFI_STATUS +EFIAPI +GetControlBitsInternal ( + IN USB_SER_DEV *UsbSerialDevice, + OUT UINT32 *Control + ) +{ + *Control = 0; + + // + // Check the values of UsbSerialDevice->Status Values and modify control + // accordingly these values correspond to the modem status register + // + if (UsbSerialDevice->StatusValues.CtsState) { + *Control |= EFI_SERIAL_CLEAR_TO_SEND; + } + if (UsbSerialDevice->StatusValues.DsrState) { + *Control |= EFI_SERIAL_DATA_SET_READY; + } + if (UsbSerialDevice->StatusValues.RiState) { + *Control |= EFI_SERIAL_RING_INDICATE; + } + if (UsbSerialDevice->StatusValues.SdState) { + *Control |= EFI_SERIAL_CARRIER_DETECT; + } + + // + // check the values of UsbSerialDevice->ControlValues and modify control + // accordingly these values correspond to the values of the Modem Control + // Register + // + if (UsbSerialDevice->ControlValues.DtrState) { + *Control |= EFI_SERIAL_DATA_TERMINAL_READY; + } + if (UsbSerialDevice->ControlValues.RtsState) { + *Control |= EFI_SERIAL_REQUEST_TO_SEND; + } + if (UsbSerialDevice->ControlValues.HardwareLoopBack) { + *Control |= EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE; + } + if (UsbSerialDevice->ControlValues.HardwareFlowControl) { + *Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE; + } + // + // check if the buffer is empty since only one is being used if it is empty + // set both the receive and transmit buffers to empty + // + if (UsbSerialDevice->DataBufferHead == UsbSerialDevice->DataBufferTail) { + *Control |= EFI_SERIAL_OUTPUT_BUFFER_EMPTY; + *Control |= EFI_SERIAL_INPUT_BUFFER_EMPTY; + } + // + // check for software loopback enable in UsbSerialDevice->ControlValues + // + if (UsbSerialDevice->ControlValues.SoftwareLoopBack) { + *Control |= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE; + } + + return EFI_SUCCESS; +} + +/** + Resets the USB Serial Device + + This function is the internal method for resetting the device and is called by + SerialReset() + + @param UsbSerialDevice[in] A pointer to the USB Serial device + + @retval EFI_SUCCESS The device was reset + @retval EFI_DEVICE_ERROR The device could not be reset + +**/ +EFI_STATUS +EFIAPI +ResetInternal ( + IN USB_SER_DEV *UsbSerialDevice + ) +{ + EFI_STATUS Status; + EFI_USB_DEVICE_REQUEST DevReq; + UINT8 ConfigurationValue; + UINT32 ReturnValue; + + DevReq.Request = FTDI_COMMAND_RESET_PORT; + DevReq.RequestType = USB_REQ_TYPE_VENDOR; + DevReq.Value = RESET_PORT_PURGE_RX; + DevReq.Index = FTDI_PORT_IDENTIFIER; + DevReq.Length = 0; //indicates that there is not data phase in this request + + Status = UsbSerialDevice->UsbIo->UsbControlTransfer ( + UsbSerialDevice->UsbIo, + &DevReq, + EfiUsbDataIn, + WDR_TIMEOUT, + &ConfigurationValue, + 1, + &ReturnValue + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + DevReq.Request = FTDI_COMMAND_RESET_PORT; + DevReq.RequestType = USB_REQ_TYPE_VENDOR; + DevReq.Value = RESET_PORT_PURGE_TX; + DevReq.Index = FTDI_PORT_IDENTIFIER; + DevReq.Length = 0; //indicates that there is no data phase in this request + + Status = UsbSerialDevice->UsbIo->UsbControlTransfer ( + UsbSerialDevice->UsbIo, + &DevReq, + EfiUsbDataIn, + WDR_TIMEOUT, + &ConfigurationValue, + 1, + &ReturnValue + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + return Status; +} + +/** + Entrypoint of USB Serial Driver. + + This function is the entrypoint of USB Serial Driver. It installs + Driver Binding Protocols together with Component Name Protocols. + + @param ImageHandle[in] The firmware allocated handle for the EFI image. + @param SystemTable[in] A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + +**/ +EFI_STATUS +EFIAPI +FtdiUsbSerialEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gUsbSerialDriverBinding, + ImageHandle, + &gUsbSerialComponentName, + &gUsbSerialComponentName2 + ); + ASSERT_EFI_ERROR (Status); + return EFI_SUCCESS; +} + +/** + Unload function for the Usb Serial Driver. + + @param ImageHandle[in] The allocated handle for the EFI image + + @retval EFI_SUCCESS The driver was unloaded successfully +**/ +EFI_STATUS +EFIAPI +FtdiUsbSerialUnload ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + EFI_HANDLE *HandleBuffer; + UINTN HandleCount; + UINTN Index; + + // + // Retrieve all handles in the handle database + // + Status = gBS->LocateHandleBuffer ( + AllHandles, + NULL, + NULL, + &HandleCount, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Disconnect the driver from the handles in the handle database + // + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->DisconnectController ( + HandleBuffer[Index], + gImageHandle, + NULL + ); + } + + // + // Free the handle array + // + FreePool (HandleBuffer); + + // + // Uninstall protocols installed by the driver in its entrypoint + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiDriverBindingProtocolGuid, + &gUsbSerialDriverBinding, + &gEfiComponentNameProtocolGuid, + &gUsbSerialComponentName, + &gEfiComponentName2ProtocolGuid, + &gUsbSerialComponentName2, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +/** + Check whether USB Serial driver supports this device. + + @param This[in] The USB Serial driver binding protocol. + @param Controller[in] The controller handle to check. + @param RemainingDevicePath[in] The remaining device path. + + @retval EFI_SUCCESS The driver supports this controller. + @retval other This device isn't supported. + +**/ +EFI_STATUS +EFIAPI +UsbSerialDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_USB_IO_PROTOCOL *UsbIo; + UART_DEVICE_PATH *UartNode; + UART_FLOW_CONTROL_DEVICE_PATH *FlowControlNode; + UINTN Index; + UINTN EntryCount; + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer; + BOOLEAN HasFlowControl; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + + if (RemainingDevicePath != NULL) { + if (!IsDevicePathEnd (RemainingDevicePath)) { + Status = EFI_UNSUPPORTED; + UartNode = (UART_DEVICE_PATH *) NextDevicePathNode (RemainingDevicePath); + if (UartNode->Header.Type != MESSAGING_DEVICE_PATH || + UartNode->Header.SubType != MSG_UART_DP || + sizeof (UART_DEVICE_PATH) != DevicePathNodeLength ((EFI_DEVICE_PATH *) UartNode)) { + goto Error; + } + FlowControlNode = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (UartNode); + if ((ReadUnaligned32 (&FlowControlNode->FlowControlMap) & ~UART_FLOW_CONTROL_HARDWARE) != 0) { + goto Error; + } + } + } + + // + // Check if USB I/O Protocol is attached on the controller handle. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + (VOID **) &UsbIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) { + if (RemainingDevicePath == NULL || IsDevicePathEnd (RemainingDevicePath)) { + return EFI_SUCCESS; + } + Status = gBS->OpenProtocolInformation ( + Controller, + &gEfiUsbIoProtocolGuid, + &OpenInfoBuffer, + &EntryCount + ); + if (EFI_ERROR (Status)) { + return Status; + } + for (Index = 0; Index < EntryCount; Index++) { + if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) { + Status = gBS->OpenProtocol ( + OpenInfoBuffer[Index].ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + HasFlowControl = ContainsFlowControl (RemainingDevicePath); + if (HasFlowControl ^ ContainsFlowControl (DevicePath)) { + Status = EFI_UNSUPPORTED; + } + } + break; + } + } + FreePool (OpenInfoBuffer); + return Status; + } + + if (EFI_ERROR (Status)) { + return Status; + } + + gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Use the USB I/O Protocol interface to check whether Controller is + // a USB Serial device that can be managed by this driver. + // + Status = EFI_SUCCESS; + + if (!IsUsbSerial (UsbIo)) { + Status = EFI_UNSUPPORTED; + goto Error; + } + +Error: + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return Status; +} + +/** + Starts the USB Serial device with this driver. + + This function produces initializes the USB Serial device and + produces the Serial IO Protocol. + + @param This[in] The USB Serial driver binding instance. + @param Controller[in] Handle of device to bind driver to. + @param RemainingDevicePath[in] Optional parameter use to pick a specific + child device to start. + + @retval EFI_SUCCESS The controller is controlled by the usb USB + Serial driver. + @retval EFI_UNSUPPORTED No interrupt endpoint can be found. + @retval Other This controller cannot be started. + +**/ +EFI_STATUS +EFIAPI +UsbSerialDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_USB_IO_PROTOCOL *UsbIo; + USB_SER_DEV *UsbSerialDevice; + UINT8 EndpointNumber; + EFI_USB_ENDPOINT_DESCRIPTOR EndpointDescriptor; + UINT8 Index; + BOOLEAN FoundIn; + BOOLEAN FoundOut; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer; + UINTN EntryCount; + EFI_SERIAL_IO_PROTOCOL *SerialIo; + UART_DEVICE_PATH *Uart; + UART_FLOW_CONTROL_DEVICE_PATH *FlowControl; + UINT32 Control; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; + + UsbSerialDevice = AllocateZeroPool (sizeof (USB_SER_DEV)); + ASSERT (UsbSerialDevice != NULL); + + // + // Get the Parent Device path + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { + goto ErrorExit1; + } + + // + // Open USB I/O Protocol + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + (VOID **) &UsbIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { + goto ErrorExit1; + } + + if (Status == EFI_ALREADY_STARTED) { + if (RemainingDevicePath == NULL || IsDevicePathEnd (RemainingDevicePath)) { + FreePool (UsbSerialDevice); + return EFI_SUCCESS; + } + + // + // Check to see if a child handle exists + // + Status = gBS->OpenProtocolInformation ( + Controller, + &gEfiSerialIoProtocolGuid, + &OpenInfoBuffer, + &EntryCount + ); + if (EFI_ERROR (Status)) { + goto ErrorExit1; + } + + Status = EFI_ALREADY_STARTED; + for (Index = 0; Index < EntryCount; Index++) { + if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) { + Status = gBS->OpenProtocol ( + OpenInfoBuffer[Index].ControllerHandle, + &gEfiSerialIoProtocolGuid, + (VOID **) &SerialIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + } + if (!EFI_ERROR (Status)) { + Uart = (UART_DEVICE_PATH *) RemainingDevicePath; + Status = SerialIo->SetAttributes ( + SerialIo, + Uart->BaudRate, + SerialIo->Mode->ReceiveFifoDepth, + SerialIo->Mode->Timeout, + (EFI_PARITY_TYPE) Uart->Parity, + Uart->DataBits, + (EFI_STOP_BITS_TYPE) Uart->StopBits + ); + FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (Uart); + if (!EFI_ERROR (Status) && IsUartFlowControlNode (FlowControl)) { + Status = SerialIo->GetControl ( + SerialIo, + &Control + ); + if (!EFI_ERROR (Status)) { + if (ReadUnaligned32 (&FlowControl->FlowControlMap) == UART_FLOW_CONTROL_HARDWARE) { + Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE; + } else { + Control &= ~EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE; + } + // + // Clear bits that are not allowed to be passed to SetControl + // + Control &= (EFI_SERIAL_REQUEST_TO_SEND | + EFI_SERIAL_DATA_TERMINAL_READY | + EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE | + EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE | + EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE); + Status = SerialIo->SetControl (SerialIo, Control); + } + } + } + break; + } + } + FreePool (OpenInfoBuffer); + return Status; + } + + if (RemainingDevicePath != NULL) { + if (IsDevicePathEnd (RemainingDevicePath)) { + return EFI_SUCCESS; + } + } + + UsbSerialDevice->UsbIo = UsbIo; + + // + // Get interface & endpoint descriptor + // + UsbIo->UsbGetInterfaceDescriptor ( + UsbIo, + &UsbSerialDevice->InterfaceDescriptor + ); + + EndpointNumber = UsbSerialDevice->InterfaceDescriptor.NumEndpoints; + + // + // Traverse endpoints to find the IN and OUT endpoints that will send and + // receive data. + // + FoundIn = FALSE; + FoundOut = FALSE; + for (Index = 0; Index < EndpointNumber; Index++) { + + Status = UsbIo->UsbGetEndpointDescriptor ( + UsbIo, + Index, + &EndpointDescriptor + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (EndpointDescriptor.EndpointAddress == FTDI_ENDPOINT_ADDRESS_OUT) { + // + // Set the Out endpoint device + // + CopyMem ( + &UsbSerialDevice->OutEndpointDescriptor, + &EndpointDescriptor, + sizeof(EndpointDescriptor) + ); + FoundOut = TRUE; + } + + if (EndpointDescriptor.EndpointAddress == FTDI_ENDPOINT_ADDRESS_IN) { + // + // Set the In endpoint device + // + CopyMem ( + &UsbSerialDevice->InEndpointDescriptor, + &EndpointDescriptor, + sizeof(EndpointDescriptor) + ); + FoundIn = TRUE; + } + } + + if (!FoundIn || !FoundOut) { + // + // No interrupt endpoint found, then return unsupported. + // + Status = EFI_UNSUPPORTED; + goto ErrorExit; + } + // + // set the initial values of UsbSerialDevice->LastSettings to the default + // values + // + UsbSerialDevice->LastSettings.BaudRate = 115200; + UsbSerialDevice->LastSettings.DataBits = 8; + UsbSerialDevice->LastSettings.Parity = NoParity; + UsbSerialDevice->LastSettings.ReceiveFifoDepth = FTDI_MAX_RECEIVE_FIFO_DEPTH; + UsbSerialDevice->LastSettings.StopBits = OneStopBit; + UsbSerialDevice->LastSettings.Timeout = FTDI_TIMEOUT; + + // + // set the initial values of UsbSerialDevice->ControlValues + // + UsbSerialDevice->ControlValues.DtrState = FALSE; + UsbSerialDevice->ControlValues.RtsState = FALSE; + UsbSerialDevice->ControlValues.HardwareFlowControl = FALSE; + UsbSerialDevice->ControlValues.HardwareLoopBack = FALSE; + UsbSerialDevice->ControlValues.SoftwareLoopBack = FALSE; + + // + // set the values of UsbSerialDevice->UartDevicePath + // + UsbSerialDevice->UartDevicePath.Header.Type = MESSAGING_DEVICE_PATH; + UsbSerialDevice->UartDevicePath.Header.SubType = MSG_UART_DP; + UsbSerialDevice->UartDevicePath.Header.Length[0] = (UINT8) (sizeof (UART_DEVICE_PATH)); + UsbSerialDevice->UartDevicePath.Header.Length[1] = (UINT8) ((sizeof (UART_DEVICE_PATH)) >> 8); + + // + // set the values of UsbSerialDevice->FlowControlDevicePath + UsbSerialDevice->FlowControlDevicePath.Header.Type = MESSAGING_DEVICE_PATH; + UsbSerialDevice->FlowControlDevicePath.Header.SubType = MSG_VENDOR_DP; + UsbSerialDevice->FlowControlDevicePath.Header.Length[0] = (UINT8) (sizeof (UART_FLOW_CONTROL_DEVICE_PATH)); + UsbSerialDevice->FlowControlDevicePath.Header.Length[1] = (UINT8) ((sizeof (UART_FLOW_CONTROL_DEVICE_PATH)) >> 8); + UsbSerialDevice->FlowControlDevicePath.FlowControlMap = 0; + + Status = SetAttributesInternal ( + UsbSerialDevice, + UsbSerialDevice->LastSettings.BaudRate, + UsbSerialDevice->LastSettings.ReceiveFifoDepth, + UsbSerialDevice->LastSettings.Timeout, + UsbSerialDevice->LastSettings.Parity, + UsbSerialDevice->LastSettings.DataBits, + UsbSerialDevice->LastSettings.StopBits + ); + + ASSERT_EFI_ERROR (Status); + + Status = SetControlBitsInternal ( + UsbSerialDevice, + &(UsbSerialDevice->ControlValues) + ); + + ASSERT_EFI_ERROR (Status); + + // + // Publish Serial GUID and protocol + // + + UsbSerialDevice->Signature = USB_SER_DEV_SIGNATURE; + UsbSerialDevice->SerialIo.Reset = SerialReset; + UsbSerialDevice->SerialIo.SetControl = SetControlBits; + UsbSerialDevice->SerialIo.SetAttributes = SetAttributes; + UsbSerialDevice->SerialIo.GetControl = GetControlBits; + UsbSerialDevice->SerialIo.Read = ReadSerialIo; + UsbSerialDevice->SerialIo.Write = WriteSerialIo; + + // + // Set the static Serial IO modes that will display when running + // "sermode" within the UEFI shell. + // + + UsbSerialDevice->SerialIo.Mode->Timeout = 0; + UsbSerialDevice->SerialIo.Mode->BaudRate = 115200; + UsbSerialDevice->SerialIo.Mode->DataBits = 8; + UsbSerialDevice->SerialIo.Mode->Parity = 1; + UsbSerialDevice->SerialIo.Mode->StopBits = 1; + + UsbSerialDevice->ParentDevicePath = ParentDevicePath; + UsbSerialDevice->ControllerHandle = NULL; + FlowControl = NULL; + + // + // Allocate space for the receive buffer + // + UsbSerialDevice->DataBuffer = AllocateZeroPool (SW_FIFO_DEPTH); + + // + // Initialize data buffer pointers. + // Head==Tail = true means buffer is empty. + // + UsbSerialDevice->DataBufferHead = 0; + UsbSerialDevice->DataBufferTail = 0; + + UsbSerialDevice->ControllerNameTable = NULL; + AddUnicodeString2 ( + "eng", + gUsbSerialComponentName.SupportedLanguages, + &UsbSerialDevice->ControllerNameTable, + L"FTDI USB Serial Adapter", + TRUE + ); + AddUnicodeString2 ( + "en", + gUsbSerialComponentName2.SupportedLanguages, + &UsbSerialDevice->ControllerNameTable, + L"FTDI USB Serial Adapter", + FALSE + ); + + Status = SetInitialStatus (UsbSerialDevice); + ASSERT_EFI_ERROR (Status); + + // + // Create a polling loop to check for input + // + + gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + UsbSerialDriverCheckInput, + UsbSerialDevice, + &(UsbSerialDevice->PollingLoop) + ); + // + // add code to set trigger time based on baud rate + // setting to 0.5s for now + // + gBS->SetTimer ( + UsbSerialDevice->PollingLoop, + TimerPeriodic, + EFI_TIMER_PERIOD_MILLISECONDS (500) + ); + + // + // Check if the remaining device path is null. If it is not null change the settings + // of the device to match those on the device path + // + if (RemainingDevicePath != NULL) { + CopyMem ( + &UsbSerialDevice->UartDevicePath, + RemainingDevicePath, + sizeof (UART_DEVICE_PATH) + ); + FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (RemainingDevicePath); + if (IsUartFlowControlNode (FlowControl)) { + UsbSerialDevice->FlowControlDevicePath.FlowControlMap = ReadUnaligned32 (&FlowControl->FlowControlMap); + } else { + FlowControl = NULL; + } + } + + // + // Build the device path by appending the UART node to the parent device path + // + UsbSerialDevice->DevicePath = AppendDevicePathNode ( + ParentDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &UsbSerialDevice->UartDevicePath + ); + // + // Continue building the device path by appending the flow control node + // + TempDevicePath = UsbSerialDevice->DevicePath; + UsbSerialDevice->DevicePath = AppendDevicePathNode ( + TempDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &UsbSerialDevice->FlowControlDevicePath + ); + FreePool (TempDevicePath); + + if (UsbSerialDevice->DevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ErrorExit; + } + + // + // Install protocol interfaces for the device + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &UsbSerialDevice->ControllerHandle, + &gEfiDevicePathProtocolGuid, + UsbSerialDevice->DevicePath, + &gEfiSerialIoProtocolGuid, + &UsbSerialDevice->SerialIo, + NULL + ); + if (EFI_ERROR (Status)){ + goto ErrorExit; + } + + // + // Open for child device + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + (VOID **) &UsbIo, + This->DriverBindingHandle, + UsbSerialDevice->ControllerHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + UsbSerialDevice->Shutdown = FALSE; + + return EFI_SUCCESS; + +ErrorExit: + // + // Error handler + // + + Status = gBS->UninstallMultipleProtocolInterfaces ( + Controller, + &gEfiSerialIoProtocolGuid, + &UsbSerialDevice->SerialIo, + NULL + ); + if (EFI_ERROR (Status)) { + goto ErrorExit1; + } + + FreePool (UsbSerialDevice->DataBuffer); + FreePool (UsbSerialDevice); + + UsbSerialDevice = NULL; + gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + +ErrorExit1: + return Status; +} + +/** + Stop the USB Serial device handled by this driver. + + @param This[in] The USB Serial driver binding protocol. + @param Controller[in] The controller to release. + @param NumberOfChildren[in] The number of handles in ChildHandleBuffer. + @param ChildHandleBuffer[in] The array of child handle. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_UNSUPPORTED Serial IO Protocol is not installed on + Controller. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a + device error. + @retval Others Fail to uninstall protocols attached on the + device. + +**/ +EFI_STATUS +EFIAPI +UsbSerialDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_SERIAL_IO_PROTOCOL *SerialIo; + EFI_USB_IO_PROTOCOL *UsbIo; + USB_SER_DEV *UsbSerialDevice; + UINTN Index; + BOOLEAN AllChildrenStopped; + + Status = EFI_SUCCESS; + UsbSerialDevice = NULL; + + if (NumberOfChildren == 0) { + // + // Close the driver + // + Status = gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + Status = gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return Status; + } + + AllChildrenStopped = TRUE; + + for (Index = 0; Index < NumberOfChildren ;Index++) { + Status = gBS->OpenProtocol ( + ChildHandleBuffer[Index], + &gEfiSerialIoProtocolGuid, + (VOID **) &SerialIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (Status == EFI_SUCCESS) {//!EFI_ERROR (Status)) { + UsbSerialDevice = USB_SER_DEV_FROM_THIS (SerialIo); + Status = gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + This->DriverBindingHandle, + ChildHandleBuffer[Index] + ); + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandleBuffer[Index], + &gEfiDevicePathProtocolGuid, + UsbSerialDevice->DevicePath, + &gEfiSerialIoProtocolGuid, + &UsbSerialDevice->SerialIo, + NULL + ); + + if (EFI_ERROR (Status)) { + gBS->OpenProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + (VOID **) &UsbIo, + This->DriverBindingHandle, + ChildHandleBuffer[Index], + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } else { + if (UsbSerialDevice->DevicePath != NULL) { + gBS->FreePool (UsbSerialDevice->DevicePath); + } + gBS->SetTimer ( + UsbSerialDevice->PollingLoop, + TimerCancel, + 0 + ); + gBS->CloseEvent (UsbSerialDevice->PollingLoop); + UsbSerialDevice->Shutdown = TRUE; + FreeUnicodeStringTable (UsbSerialDevice->ControllerNameTable); + FreePool (UsbSerialDevice->DataBuffer); + FreePool (UsbSerialDevice); + } + } + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + } + } + + if (!AllChildrenStopped) { + return EFI_DEVICE_ERROR; + } + return EFI_SUCCESS; +} + +// +// Serial IO Member Functions +// + +/** + Reset the serial device. + + @param This[in] Protocol instance pointer. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The serial device could not be reset. + +**/ +EFI_STATUS +EFIAPI +SerialReset ( + IN EFI_SERIAL_IO_PROTOCOL *This + ) +{ + EFI_STATUS Status; + USB_SER_DEV *UsbSerialDevice; + + UsbSerialDevice = USB_SER_DEV_FROM_THIS (This); + Status = ResetInternal (UsbSerialDevice); + if (EFI_ERROR (Status)){ + return EFI_DEVICE_ERROR; + } + return Status; +} + +/** + Set the control bits on a serial device. + + @param This[in] Protocol instance pointer. + @param Control[in] Set the bits of Control that are settable. + + @retval EFI_SUCCESS The new control bits were set on the serial device. + @retval EFI_UNSUPPORTED The serial device does not support this operation. + @retval EFI_DEVICE_ERROR The serial device is not functioning correctly. + +**/ +EFI_STATUS +EFIAPI +SetControlBits ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN UINT32 Control + ) +{ + EFI_STATUS Status; + USB_SER_DEV *UsbSerialDevice; + CONTROL_BITS ControlBits; + + UsbSerialDevice = USB_SER_DEV_FROM_THIS (This); + + // + // check for invalid control parameters + // + if ((Control & (~(EFI_SERIAL_REQUEST_TO_SEND | + EFI_SERIAL_DATA_TERMINAL_READY | + EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE | + EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE | + EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE))) != 0 ) { + return EFI_UNSUPPORTED; + } + + // + // check the control parameters and set the correct setting for + // the paramerts of ControlBits + // both loopback enables are always set to FALSE + // + ControlBits.HardwareLoopBack = FALSE; + ControlBits.SoftwareLoopBack = FALSE; + // + // check for hardware flow control + // + if ((Control & EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) == EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) { + ControlBits.HardwareFlowControl = TRUE; + } else { + ControlBits.HardwareFlowControl = FALSE; + } + // + // check for DTR enabled + // + if ((Control & EFI_SERIAL_DATA_TERMINAL_READY) == EFI_SERIAL_DATA_TERMINAL_READY) { + ControlBits.DtrState = TRUE; + } else { + ControlBits.DtrState = FALSE; + } + // + // check for RTS enabled + // + if ((Control & EFI_SERIAL_REQUEST_TO_SEND) == EFI_SERIAL_REQUEST_TO_SEND) { + ControlBits.RtsState = TRUE; + } else { + ControlBits.RtsState = FALSE; + } + + // + // set the control values with a call to SetControlBitsInternal() + // + Status = SetControlBitsInternal (UsbSerialDevice, &ControlBits); + + return Status; +} + +/** + calls SetAttributesInternal() to set the baud rate, receive FIFO depth, + transmit/receive time out, parity, data buts, and stop bits on a serial + device. + + @param This[in] Protocol instance pointer. + @param BaudRate[in] The requested baud rate. A BaudRate value of 0 + will use the device's default interface speed. + @param ReveiveFifoDepth[in] The requested depth of the FIFO on the receive + side of the serial interface. A ReceiveFifoDepth + value of 0 will use the device's default FIFO + depth. + @param Timeout[in] The requested time out for a single character in + microseconds.This timeout applies to both the + transmit and receive side of the interface. A + Timeout value of 0 will use the device's default + time out value. + @param Parity[in] The type of parity to use on this serial device. + A Parity value of DefaultParity will use the + device's default parity value. + @param DataBits[in] The number of data bits to use on the serial + device. A DataBit vaule of 0 will use the + device's default data bit setting. + @param StopBits[in] The number of stop bits to use on this serial + device. A StopBits value of DefaultStopBits will + use the device's default number of stop bits. + + @retval EFI_SUCCESS The attributes were set + @retval EFI_DEVICE_ERROR The attributes were not able to be + +**/ +EFI_STATUS +EFIAPI +SetAttributes ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN UINT64 BaudRate, + IN UINT32 ReceiveFifoDepth, + IN UINT32 Timeout, + IN EFI_PARITY_TYPE Parity, + IN UINT8 DataBits, + IN EFI_STOP_BITS_TYPE StopBits + ) +{ + + EFI_STATUS Status; + USB_SER_DEV *UsbSerialDevice; + + UsbSerialDevice = USB_SER_DEV_FROM_THIS (This); + + Status = SetAttributesInternal ( + UsbSerialDevice, + BaudRate, + ReceiveFifoDepth, + Timeout, + Parity, + DataBits, + StopBits + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return Status; +} + + +/** + Retrieves the status of the control bits on a serial device. + + @param This[in] Protocol instance pointer. + @param Control[out] A pointer to return the current Control signals + from the serial device. + + @retval EFI_SUCCESS The control bits were read from the serial + device. + @retval EFI_DEVICE_ERROR The serial device is not functioning correctly. + +**/ +EFI_STATUS +EFIAPI +GetControlBits ( + IN EFI_SERIAL_IO_PROTOCOL *This, + OUT UINT32 *Control + ) +{ + USB_SER_DEV *UsbSerialDevice; + EFI_STATUS Status; + + UsbSerialDevice = USB_SER_DEV_FROM_THIS (This); + + *Control = 0; + + Status = GetControlBitsInternal (UsbSerialDevice, Control); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + return Status; +} + +/** + Reads data from a serial device. + + @param This[in] Protocol instance pointer. + @param BufferSize[in, out] On input, the size of the Buffer. On output, + the amount of data returned in Buffer. + @param Buffer[out] The buffer to return the data into. + + @retval EFI_SUCCESS The data was read. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_TIMEOUT The data write was stopped due to a timeout. + +**/ +EFI_STATUS +EFIAPI +ReadSerialIo ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + UINTN Index; + UINTN RemainingCallerBufferSize; + USB_SER_DEV *UsbSerialDevice; + EFI_STATUS Status; + + + if (*BufferSize == 0) { + return EFI_SUCCESS; + } + + if (Buffer == NULL) { + return EFI_DEVICE_ERROR; + } + + Status = EFI_SUCCESS; + UsbSerialDevice = USB_SER_DEV_FROM_THIS (This); + + // + // Clear out any data that we already have in our internal buffer + // + for (Index = 0; Index < *BufferSize; Index++) { + if (UsbSerialDevice->DataBufferHead == UsbSerialDevice->DataBufferTail) { + break; + } + + // + // Still have characters in the buffer to return + // + ((UINT8 *)Buffer)[Index] = UsbSerialDevice->DataBuffer[UsbSerialDevice->DataBufferHead]; + UsbSerialDevice->DataBufferHead = (UsbSerialDevice->DataBufferHead + 1) % SW_FIFO_DEPTH; + } + + // + // If we haven't filled the caller's buffer using data that we already had on + // hand We need to generate an additional USB request to try and fill the + // caller's buffer + // + if (Index != *BufferSize) { + RemainingCallerBufferSize = *BufferSize - Index; + Status = ReadDataFromUsb ( + UsbSerialDevice, + &RemainingCallerBufferSize, + (VOID *)(((CHAR8 *)Buffer) + Index) + ); + if (!EFI_ERROR (Status)) { + *BufferSize = RemainingCallerBufferSize + Index; + } else { + *BufferSize = Index; + } + } + + if (UsbSerialDevice->DataBufferHead == UsbSerialDevice->DataBufferTail) { + // + // Data buffer has no data, set the EFI_SERIAL_INPUT_BUFFER_EMPTY flag + // + UsbSerialDevice->ControlBits |= EFI_SERIAL_INPUT_BUFFER_EMPTY; + } else { + // + // There is some leftover data, clear EFI_SERIAL_INPUT_BUFFER_EMPTY flag + // + UsbSerialDevice->ControlBits &= ~(EFI_SERIAL_INPUT_BUFFER_EMPTY); + } + return Status; +} + +/** + Writes data to a serial device. + + @param This[in] Protocol instance pointer. + @param BufferSize[in, out] On input, the size of the Buffer. On output, + the amount of data actually written. + @param Buffer[in] The buffer of data to write + + @retval EFI_SUCCESS The data was written. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_TIMEOUT The data write was stopped due to a timeout. + +**/ +EFI_STATUS +EFIAPI +WriteSerialIo ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + USB_SER_DEV *UsbSerialDevice; + EFI_TPL Tpl; + + UsbSerialDevice = USB_SER_DEV_FROM_THIS (This); + + if (UsbSerialDevice->Shutdown) { + return EFI_DEVICE_ERROR; + } + + Tpl = gBS->RaiseTPL (TPL_NOTIFY); + + Status = UsbSerialDataTransfer ( + UsbSerialDevice, + EfiUsbDataOut, + Buffer, + BufferSize, + FTDI_TIMEOUT + ); + + gBS->RestoreTPL (Tpl); + if (EFI_ERROR (Status)) { + if (Status == EFI_TIMEOUT){ + return Status; + } else { + return EFI_DEVICE_ERROR; + } + } + + return EFI_SUCCESS; +} diff --git a/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.h b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.h new file mode 100644 index 0000000000..6048923d6f --- /dev/null +++ b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.h @@ -0,0 +1,589 @@ +/** @file + Header file for USB Serial Driver's Data Structures. + +Copyright (c) 2004 - 2013, Intel Corporation. All rights reserved. +Portions Copyright 2012 Ashley DeSimone +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _FTDI_USB_SERIAL_DRIVER_H_ +#define _FTDI_USB_SERIAL_DRIVER_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// +// US English LangID +// +#define USB_US_LANG_ID 0x0409 + +// +// Supported Vendor Ids +// +#define VID_FTDI 0x0403 + +// +// Supported product ids +// +#define DID_FTDI_FT232 0x6001 + +// +// FTDI Commands +// +#define FTDI_COMMAND_RESET_PORT 0 +#define FTDI_COMMAND_MODEM_CTRL 1 +#define FTDI_COMMAND_SET_FLOW_CTRL 2 +#define FTDI_COMMAND_SET_BAUDRATE 3 +#define FTDI_COMMAND_SET_DATA 4 +#define FTDI_COMMAND_GET_MODEM_STATUS 5 +#define FTDI_COMMAND_SET_EVENT_CHAR 6 +#define FTDI_COMMAND_SET_ERROR_CHAR 7 +#define FTDI_COMMAND_SET_LATENCY_TIMER 9 +#define FTDI_COMMAND_GET_LATENCY_TIMER 10 + +// +// FTDI_PORT_IDENTIFIER +// Used in the usb control transfers that issue FTDI commands as the index value. +// +#define FTDI_PORT_IDENTIFIER 0x1 // For FTDI USB serial adapter the port + // identifier is always 1. + +// +// RESET_PORT +// +#define RESET_PORT_RESET 0x0 // Purges RX and TX, clears DTR and RTS sets + // flow control to none, disables event + // trigger, sets the event char to 0x0d and + // does nothing to baudrate or data settings +#define RESET_PORT_PURGE_RX 0x1 +#define RESET_PORT_PURGE_TX 0x2 + +// +// SET_FLOW_CONTROL +// +#define NO_FLOW_CTRL 0x0 +#define XON_XOFF_CTRL 0x4 + +// +// SET_BAUD_RATE +// To set baud rate, one must calculate an encoding of the baud rate from +// UINT32 to UINT16.See EncodeBaudRateForFtdi() for details +// +#define FTDI_UART_FREQUENCY 3000000 +#define FTDI_MIN_DIVISOR 0x20 +#define FTDI_MAX_DIVISOR 0x3FFF8 +// +// Special case baudrate values +// 300,000 and 200,000 are special cases for calculating the encoded baudrate +// +#define FTDI_SPECIAL_CASE_300_MIN (3000000 * 100) / 103 // minimum adjusted + // value for 300,000 +#define FTDI_SPECIAL_CASE_300_MAX (3000000 * 100) / 97 // maximum adjusted + // value for 300,000 +#define FTDI_SPECIAL_CASE_200_MIN (2000000 * 100) / 103 // minimum adjusted + // value for 200,000 +#define FTDI_SPECIAL_CASE_200_MAX (2000000 * 100) / 97 // maximum adjusted + // value for 200,000 +// +// Min and max frequency values that the FTDI chip can attain +//.all generated frequencies must be between these values +// +#define FTDI_MIN_FREQUENCY 46601941 // (3MHz * 1600) / 103 = 46601941 +#define FTDI_MAX_FREQUENCY 49484536 // (3MHz * 1600) / 97 = 49484536 + +// +// SET_DATA_BITS +// +#define SET_DATA_BITS(n) (n) + +// +// SET_PARITY +// +#define SET_PARITY_NONE 0x0 +#define SET_PARITY_ODD BIT8 // (0x1 << 8) +#define SET_PARITY_EVEN BIT9 // (0x2 << 8) +#define SET_PARITY_MARK BIT9 | BIT8 // (0x3 << 8) +#define SET_PARITY_SPACE BIT10 // (0x4 << 8) + +// +// SET_STOP_BITS +// +#define SET_STOP_BITS_1 0x0 +#define SET_STOP_BITS_15 BIT11 // (0x1 << 11) +#define SET_STOP_BITS_2 BIT12 // (0x2 << 11) + +// +// SET_MODEM_CTRL +// SET_DTR_HIGH = (1 | (1 << 8)), SET_DTR_LOW = (0 | (1 << 8) +// SET_RTS_HIGH = (2 | (2 << 8)), SET_RTS_LOW = (0 | (2 << 8) +// +#define SET_DTR_HIGH (BIT8 | BIT0) +#define SET_DTR_LOW (BIT8) +#define SET_RTS_HIGH (BIT9 | BIT1) +#define SET_RTS_LOW (BIT9) + +// +// MODEM_STATUS +// +#define CTS_MASK BIT4 +#define DSR_MASK BIT5 +#define RI_MASK BIT6 +#define SD_MASK BIT7 +#define MSR_MASK (CTS_MASK | DSR_MASK | RI_MASK | SD_MASK) + +// +// Macro used to check for USB transfer errors +// +#define USB_IS_ERROR(Result, Error) (((Result) & (Error)) != 0) + +// +// USB request timeouts +// +#define WDR_TIMEOUT 5000 // default urb timeout in ms +#define WDR_SHORT_TIMEOUT 1000 // shorter urb timeout in ms + +// +// FTDI timeout +// +#define FTDI_TIMEOUT 16 + +// +// FTDI FIFO depth +// +#define FTDI_MAX_RECEIVE_FIFO_DEPTH 384 + +// +// FTDI Endpoint Descriptors +// +#define FTDI_ENDPOINT_ADDRESS_IN 0x81 //the endpoint address for the in enpoint generated by the device +#define FTDI_ENDPOINT_ADDRESS_OUT 0x02 //the endpoint address for the out endpoint generated by the device + +// +// Max buffer size for USB transfers +// +#define SW_FIFO_DEPTH 1024 + +// +// struct to define a usb device as a vendor and product id pair +// +typedef struct { + UINTN VendorId; + UINTN DeviceId; +} USB_DEVICE; + +// +//struct to describe the control bits of the device +//true indicates enabled +//false indicates disabled +// +typedef struct { + BOOLEAN HardwareFlowControl; + BOOLEAN DtrState; + BOOLEAN RtsState; + BOOLEAN HardwareLoopBack; + BOOLEAN SoftwareLoopBack; +} CONTROL_BITS; + +// +//struct to describe the status bits of the device +//true indicates enabled +//false indicated disabled +// +typedef struct { + BOOLEAN CtsState; + BOOLEAN DsrState; + BOOLEAN RiState; + BOOLEAN SdState; +} STATUS_BITS; + +// +// Structure to describe the last attributes of the Usb Serial device +// +typedef struct { + UINT64 BaudRate; + UINT32 ReceiveFifoDepth; + UINT32 Timeout; + EFI_PARITY_TYPE Parity; + UINT8 DataBits; + EFI_STOP_BITS_TYPE StopBits; +} PREVIOUS_ATTRIBUTES; + +// +// Structure to describe USB serial device +// +#define USB_SER_DEV_SIGNATURE SIGNATURE_32 ('u', 's', 'b', 's') + +typedef struct { + UINTN Signature; + EFI_HANDLE ControllerHandle; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + UART_DEVICE_PATH UartDevicePath; + UART_FLOW_CONTROL_DEVICE_PATH FlowControlDevicePath; + EFI_USB_IO_PROTOCOL *UsbIo; + EFI_USB_INTERFACE_DESCRIPTOR InterfaceDescriptor; + EFI_USB_ENDPOINT_DESCRIPTOR InEndpointDescriptor; + EFI_USB_ENDPOINT_DESCRIPTOR OutEndpointDescriptor; + EFI_UNICODE_STRING_TABLE *ControllerNameTable; + UINT32 DataBufferHead; + UINT32 DataBufferTail; + UINT8 *DataBuffer; + EFI_SERIAL_IO_PROTOCOL SerialIo; + BOOLEAN Shutdown; + EFI_EVENT PollingLoop; + UINT32 ControlBits; + PREVIOUS_ATTRIBUTES LastSettings; + CONTROL_BITS ControlValues; + STATUS_BITS StatusValues; + UINT8 ReadBuffer[512]; +} USB_SER_DEV; + +#define USB_SER_DEV_FROM_THIS(a) \ + CR(a, USB_SER_DEV, SerialIo, USB_SER_DEV_SIGNATURE) + +// +// Global Variables +// +extern EFI_DRIVER_BINDING_PROTOCOL gUsbSerialDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gUsbSerialComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gUsbSerialComponentName2; + +// +// Functions of Driver Binding Protocol +// +/** + Check whether USB Serial driver supports this device. + + @param This[in] The USB Serial driver binding protocol. + @param Controller[in] The controller handle to check. + @param RemainingDevicePath[in] The remaining device path. + + @retval EFI_SUCCESS The driver supports this controller. + @retval other This device isn't supported. + +**/ +EFI_STATUS +EFIAPI +UsbSerialDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Starts the Serial device with this driver. + + This function produces Serial IO Protocol and initializes the USB + Serial device to manage this USB Serial device. + + @param This[in] The USB Serial driver binding instance. + @param Controller[in] Handle of device to bind driver to. + @param RemainingDevicePath[in] Optional parameter use to pick a specific + child device to start. + + @retval EFI_SUCCESS The controller is controlled by the USB + Serial driver. + @retval EFI_UNSUPPORTED No interrupt endpoint can be found. + @retval Other This controller cannot be started. + +**/ +EFI_STATUS +EFIAPI +UsbSerialDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stop the USB Serial device handled by this driver. + + @param This[in] The USB Serial driver binding protocol. + @param Controller[in] The controller to release. + @param NumberOfChildren[in] The number of handles in ChildHandleBuffer. + @param ChildHandleBuffer[in] The array of child handle. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_UNSUPPORTED Simple Text In Protocol or Simple Text In Ex + Protocol is not installed on Controller. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a + device error. + @retval Others Fail to uninstall protocols attached on the + device. + +**/ +EFI_STATUS +EFIAPI +UsbSerialDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// Serial IO Member Functions +// + +/** + Writes data to a serial device. + + @param This[in] Protocol instance pointer. + @param BufferSize[in, out] On input, the size of the Buffer. On output, + the amount of data actually written. + @param Buffer[in] The buffer of data to write + + @retval EFI_SUCCESS The data was written. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_TIMEOUT The data write was stopped due to a timeout. + +**/ +EFI_STATUS +EFIAPI +WriteSerialIo ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ); + +/** + Reads data from a serial device. + + @param This[in] Protocol instance pointer. + @param BufferSize[in, out] On input, the size of the Buffer. On output, + the amount of data returned in Buffer. + @param Buffer[out] The buffer to return the data into. + + @retval EFI_SUCCESS The data was read. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_TIMEOUT The data write was stopped due to a timeout. + +**/ +EFI_STATUS +EFIAPI +ReadSerialIo ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ); + +/** + Retrieves the status of the control bits on a serial device. + + @param This[in] Protocol instance pointer. + @param Control[out] A pointer to return the current Control signals + from the serial device. + + @retval EFI_SUCCESS The control bits were read from the serial + device. + @retval EFI_DEVICE_ERROR The serial device is not functioning correctly. + +**/ +EFI_STATUS +EFIAPI +GetControlBits ( + IN EFI_SERIAL_IO_PROTOCOL *This, + OUT UINT32 *Control + ); + +/** + Set the control bits on a serial device. + + @param This[in] Protocol instance pointer. + @param Control[in] Set the bits of Control that are settable. + + @retval EFI_SUCCESS The new control bits were set on the serial device. + @retval EFI_UNSUPPORTED The serial device does not support this operation. + @retval EFI_DEVICE_ERROR The serial device is not functioning correctly. + +**/ +EFI_STATUS +EFIAPI +SetControlBits ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN UINT32 Control + ); + +/** + Calls SetAttributesInternal() to set the baud rate, receive FIFO depth, + transmit/receice time out, parity, data buts, and stop bits on a serial device. + + @param This[in] Protocol instance pointer. + @param BaudRate[in] The requested baud rate. A BaudRate value of 0 + will use the device's default interface speed. + @param ReveiveFifoDepth[in] The requested depth of the FIFO on the receive + side of the serial interface. A ReceiveFifoDepth + value of 0 will use the device's default FIFO + depth. + @param Timeout[in] The requested time out for a single character in + microseconds.This timeout applies to both the + transmit and receive side of the interface.A + Timeout value of 0 will use the device's default + time out value. + @param Parity[in] The type of parity to use on this serial device.A + Parity value of DefaultParity will use the + device's default parity value. + @param DataBits[in] The number of data bits to use on the serial + device. A DataBits value of 0 will use the + device's default data bit setting. + @param StopBits[in] The number of stop bits to use on this serial + device. A StopBits value of DefaultStopBits will + use the device's default number of stop bits. + + @retval EFI_SUCCESS The attributes were set + @retval EFI_DEVICE_ERROR The attributes were not able to be + +**/ +EFI_STATUS +EFIAPI +SetAttributes ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN UINT64 BaudRate, + IN UINT32 ReceiveFifoDepth, + IN UINT32 Timeout, + IN EFI_PARITY_TYPE Parity, + IN UINT8 DataBits, + IN EFI_STOP_BITS_TYPE StopBits + ); + +/** + Reset the serial device. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The serial device could not be reset. + +**/ +EFI_STATUS +EFIAPI +SerialReset ( + IN EFI_SERIAL_IO_PROTOCOL *This + ); + +// +// EFI Component Name Functions +// + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL + or EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller + is requesting, and it must match one of the + languages specified in SupportedLanguages. + The number of languages supported by a + driver is up to the driver writer. Language + is specified in RFC 4646 or ISO 639-2 + language code format. + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified + by This and the language specified by + Language was returned in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not + support the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UsbSerialComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL + or EFI_COMPONENT_NAME_PROTOCOL instance. + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to + be returned. + @param ChildHandle[in] The handle of the child controller to + retrieve the name of. This is an optional + parameter that may be NULL. It will be NULL + for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the + name of the bus controller. It will not be + NULL for a bus driver that wishes to + retrieve the name of a child controller. + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller + is requesting, and it must match one of the + languages specified in SupportedLanguages. + The number of languages supported by a + driver is up to the driver writer. Language + is specified in RFC 4646 or ISO 639-2 + language code format. + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle + and ChildHandle in the language specified + by Language from the point of view of the + driver specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable + name in the language specified by Language + for the driver specified by This was + returned in DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a + valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not + currently managing the controller specified + by ControllerHandle and ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not + support the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UsbSerialComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +#endif diff --git a/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDxe.inf b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDxe.inf new file mode 100644 index 0000000000..67c1d36470 --- /dev/null +++ b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDxe.inf @@ -0,0 +1,55 @@ +## @file +# USB Serial Driver that manages USB Serial device and produces Serial IO +# Protocol. +# +# USB Serial Driver consumes USB I/O Protocol and Device Path Protocol, and +# produces Serial IO Protocol on USB Serial devices. +# It manages the USB Serial device via USB Bulk Transfer of USB I/O Protocol. +# This module refers to following specifications: +# 1. UEFI Specification, v2.1 +# +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = FtdiUsbSerialDxe + FILE_GUID = A8154B55-2021-4D40-AE81-2E23A02dCC46 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = FtdiUsbSerialEntryPoint + UNLOAD_IMAGE = FtdiUsbSerialUnload + +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + FtdiUsbSerialDriver.c + FtdiUsbSerialDriver.h + ComponentName.c + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + BaseMemoryLib + DebugLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiLib + DevicePathLib + +[Guids] + gEfiUartDevicePathGuid + +[Protocols] + ## TO_START + ## BY_START + gEfiDevicePathProtocolGuid + gEfiUsbIoProtocolGuid ## TO_START + gEfiSerialIoProtocolGuid ## BY_START diff --git a/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ReadMe.txt b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ReadMe.txt new file mode 100644 index 0000000000..d8ca227a41 --- /dev/null +++ b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ReadMe.txt @@ -0,0 +1,32 @@ + +=== FTDI USB SERIAL OVERVIEW === + +This is a bus driver that enables the EfiSerialIoProtocol interface +for FTDI8U232AM based USB-to-Serial adapters. + +=== STATUS === + +Serial Input: Functional on real hardware. +Serial Output: Functional on real hardware. + +Operating Modes: Currently the user is able to change all operating modes +except timeout and FIFO depth. +The default operating mode is: + Baudrate: 115200 + Parity: None + Flow Control: None + Data Bits: 8 + Stop Bits: 1 +Notes: + Data Bits setting of 6,7,8 can not be combined with a Stop Bits setting of 1.5 + + At baudrates less than 9600 some of the characters may be transmitted incorrectly. + +=== COMPATIBILITY === + +Tested with: +An FTDI8U232AM based USB-To-Serial adapter, the UEFI Shell, and the SerialTest application +using a PuTTY Terminal + +See CompatibleDevices.txt for a list of devices which have been confirmed to work with this +driver. \ No newline at end of file diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.c b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.c new file mode 100644 index 0000000000..c9329f506d --- /dev/null +++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.c @@ -0,0 +1,1318 @@ +/** @file + Implement the interface to the AX88772 Ethernet controller. + + This module implements the interface to the ASIX AX88772 + USB to Ethernet MAC with integrated 10/100 PHY. Note that this implementation + only supports the integrated PHY since no other test cases were available. + + Copyright (c) 2011, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ax88772.h" + + +/** + Compute the CRC + + @param [in] pMacAddress Address of a six byte buffer to containing the MAC address. + + @returns The CRC-32 value associated with this MAC address + +**/ +UINT32 +Ax88772Crc ( + IN UINT8 * pMacAddress + ) +{ + UINT32 BitNumber; + INT32 Carry; + INT32 Crc; + UINT32 Data; + UINT8 * pEnd; + + DBG_ENTER ( ); + + // + // Walk the MAC address + // + Crc = -1; + pEnd = &pMacAddress[ PXE_HWADDR_LEN_ETHER ]; + while ( pEnd > pMacAddress ) { + Data = *pMacAddress++; + + + // + // CRC32: x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1 + // + // 1 0000 0100 1100 0001 0001 1101 1011 0111 + // + for ( BitNumber = 0; 8 > BitNumber; BitNumber++ ) { + Carry = (( Crc >> 31 ) & 1 ) ^ ( Data & 1 ); + Crc <<= 1; + if ( 0 != Carry ) { + Crc ^= 0x04c11db7; + } + Data >>= 1; + } + } + + // + // Return the CRC value + // + DBG_EXIT_HEX ( Crc ); + return (UINT32) Crc; +} + + +/** + Get the MAC address + + This routine calls ::Ax88772UsbCommand to request the MAC + address from the network adapter. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [out] pMacAddress Address of a six byte buffer to receive the MAC address. + + @retval EFI_SUCCESS The MAC address is available. + @retval other The MAC address is not valid. + +**/ +EFI_STATUS +Ax88772MacAddressGet ( + IN NIC_DEVICE * pNicDevice, + OUT UINT8 * pMacAddress + ) +{ + USB_DEVICE_REQUEST SetupMsg; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Set the register address. + // + SetupMsg.RequestType = USB_ENDPOINT_DIR_IN + | USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_MAC_ADDRESS_READ; + SetupMsg.Value = 0; + SetupMsg.Index = 0; + SetupMsg.Length = PXE_HWADDR_LEN_ETHER; + + // + // Read the PHY register + // + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + pMacAddress ); + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Set the MAC address + + This routine calls ::Ax88772UsbCommand to set the MAC address + in the network adapter. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] pMacAddress Address of a six byte buffer to containing the new MAC address. + + @retval EFI_SUCCESS The MAC address was set. + @retval other The MAC address was not set. + +**/ +EFI_STATUS +Ax88772MacAddressSet ( + IN NIC_DEVICE * pNicDevice, + IN UINT8 * pMacAddress + ) +{ + USB_DEVICE_REQUEST SetupMsg; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Set the register address. + // + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_MAC_ADDRESS_WRITE; + SetupMsg.Value = 0; + SetupMsg.Index = 0; + SetupMsg.Length = PXE_HWADDR_LEN_ETHER; + + // + // Read the PHY register + // + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + pMacAddress ); + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Clear the multicast hash table + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + +**/ +VOID +Ax88772MulticastClear ( + IN NIC_DEVICE * pNicDevice + ) +{ + DBG_ENTER ( ); + + // + // Clear the multicast hash table + // + pNicDevice->MulticastHash[0] = 0; + pNicDevice->MulticastHash[1] = 0; + + DBG_EXIT ( ); +} + + +/** + Enable a multicast address in the multicast hash table + + This routine calls ::Ax88772Crc to compute the hash bit for + this MAC address. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] pMacAddress Address of a six byte buffer to containing the MAC address. + +**/ +VOID +Ax88772MulticastSet ( + IN NIC_DEVICE * pNicDevice, + IN UINT8 * pMacAddress + ) +{ + UINT32 BitNumber; + UINT32 Crc; + UINT32 Mask; + + DBG_ENTER ( ); + + // + // Compute the CRC on the destination address + // + Crc = Ax88772Crc ( pMacAddress ); + + // + // Set the bit corresponding to the destination address + // + BitNumber = Crc >> 26; + if ( 32 > BitNumber ) { + Mask = 1 << BitNumber; + pNicDevice->MulticastHash[0] |= Mask; + } + else { + Mask = 1 << ( BitNumber - 32 ); + pNicDevice->MulticastHash[1] |= Mask; + } + + // + // Display the multicast address + // + DEBUG (( DEBUG_RX_MULTICAST | DEBUG_INFO, + "Enable multicast: 0x%02x-%02x-%02x-%02x-%02x-%02x, CRC: 0x%08x, Bit number: 0x%02x\r\n", + pMacAddress[0], + pMacAddress[1], + pMacAddress[2], + pMacAddress[3], + pMacAddress[4], + pMacAddress[5], + Crc, + BitNumber )); + + DBG_EXIT ( ); +} + + +/** + Start the link negotiation + + This routine calls ::Ax88772PhyWrite to start the PHY's link + negotiation. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + + @retval EFI_SUCCESS The link negotiation was started. + @retval other Failed to start the link negotiation. + +**/ +EFI_STATUS +Ax88772NegotiateLinkStart ( + IN NIC_DEVICE * pNicDevice + ) +{ + UINT16 Control; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Set the supported capabilities. + // + Status = Ax88772PhyWrite ( pNicDevice, + PHY_ANAR, + AN_CSMA_CD + | AN_TX_FDX | AN_TX_HDX + | AN_10_FDX | AN_10_HDX ); + if ( !EFI_ERROR ( Status )) { + // + // Set the link speed and duplex + // + Control = BMCR_AUTONEGOTIATION_ENABLE + | BMCR_RESTART_AUTONEGOTIATION; + if ( pNicDevice->b100Mbps ) { + Control |= BMCR_100MBPS; + } + if ( pNicDevice->bFullDuplex ) { + Control |= BMCR_FULL_DUPLEX; + } + Status = Ax88772PhyWrite ( pNicDevice, PHY_BMCR, Control ); + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Complete the negotiation of the PHY link + + This routine calls ::Ax88772PhyRead to determine if the + link negotiation is complete. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in, out] pPollCount Address of number of times this routine was polled + @param [out] pbComplete Address of boolean to receive complate status. + @param [out] pbLinkUp Address of boolean to receive link status, TRUE=up. + @param [out] pbHiSpeed Address of boolean to receive link speed, TRUE=100Mbps. + @param [out] pbFullDuplex Address of boolean to receive link duplex, TRUE=full. + + @retval EFI_SUCCESS The MAC address is available. + @retval other The MAC address is not valid. + +**/ +EFI_STATUS +Ax88772NegotiateLinkComplete ( + IN NIC_DEVICE * pNicDevice, + IN OUT UINTN * pPollCount, + OUT BOOLEAN * pbComplete, + OUT BOOLEAN * pbLinkUp, + OUT BOOLEAN * pbHiSpeed, + OUT BOOLEAN * pbFullDuplex + ) +{ + UINT16 Mask; + UINT16 PhyData; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Determine if the link is up. + // + *pbComplete = FALSE; + + // + // Get the link status + // + Status = Ax88772PhyRead ( pNicDevice, + PHY_BMSR, + &PhyData ); + if ( !EFI_ERROR ( Status )) { + // + // Determine if the autonegotiation is complete. + // + *pbLinkUp = (BOOLEAN)( 0 != ( PhyData & BMSR_LINKST )); + *pbComplete = *pbLinkUp; + if ( 0 != *pbComplete ) { + // + // Get the partners capabilities. + // + Status = Ax88772PhyRead ( pNicDevice, + PHY_ANLPAR, + &PhyData ); + if ( !EFI_ERROR ( Status )) { + // + // Autonegotiation is complete + // Determine the link speed. + // + *pbHiSpeed = (BOOLEAN)( 0 != ( PhyData & ( AN_TX_FDX | AN_TX_HDX ))); + + // + // Determine the link duplex. + // + Mask = ( *pbHiSpeed ) ? AN_TX_FDX : AN_10_FDX; + *pbFullDuplex = (BOOLEAN)( 0 != ( PhyData & Mask )); + } + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Read a register from the PHY + + This routine calls ::Ax88772UsbCommand to read a PHY register. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] RegisterAddress Number of the register to read. + @param [in, out] pPhyData Address of a buffer to receive the PHY register value + + @retval EFI_SUCCESS The PHY data is available. + @retval other The PHY data is not valid. + +**/ +EFI_STATUS +Ax88772PhyRead ( + IN NIC_DEVICE * pNicDevice, + IN UINT8 RegisterAddress, + IN OUT UINT16 * pPhyData + ) +{ + USB_DEVICE_REQUEST SetupMsg; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Request access to the PHY + // + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_PHY_ACCESS_SOFTWARE; + SetupMsg.Value = 0; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + if ( !EFI_ERROR ( Status )) { + // + // Read the PHY register address. + // + SetupMsg.RequestType = USB_ENDPOINT_DIR_IN + | USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_PHY_REG_READ; + SetupMsg.Value = pNicDevice->PhyId; + SetupMsg.Index = RegisterAddress; + SetupMsg.Length = sizeof ( *pPhyData ); + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + pPhyData ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_PHY | DEBUG_INFO, + "PHY %d: 0x%02x --> 0x%04x\r\n", + pNicDevice->PhyId, + RegisterAddress, + *pPhyData )); + + // + // Release the PHY to the hardware + // + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_PHY_ACCESS_HARDWARE; + SetupMsg.Value = 0; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + } + } + + // + // Return the operation status. + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Write to a PHY register + + This routine calls ::Ax88772UsbCommand to write a PHY register. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] RegisterAddress Number of the register to read. + @param [in] PhyData Address of a buffer to receive the PHY register value + + @retval EFI_SUCCESS The PHY data was written. + @retval other Failed to wwrite the PHY register. + +**/ +EFI_STATUS +Ax88772PhyWrite ( + IN NIC_DEVICE * pNicDevice, + IN UINT8 RegisterAddress, + IN UINT16 PhyData + ) +{ + USB_DEVICE_REQUEST SetupMsg; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Request access to the PHY + // + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_PHY_ACCESS_SOFTWARE; + SetupMsg.Value = 0; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + if ( !EFI_ERROR ( Status )) { + // + // Write the PHY register + // + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_PHY_REG_WRITE; + SetupMsg.Value = pNicDevice->PhyId; + SetupMsg.Index = RegisterAddress; + SetupMsg.Length = sizeof ( PhyData ); + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + &PhyData ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_PHY | DEBUG_INFO, + "PHY %d: 0x%02x <-- 0x%04x\r\n", + pNicDevice->PhyId, + RegisterAddress, + PhyData )); + + // + // Release the PHY to the hardware + // + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_PHY_ACCESS_HARDWARE; + SetupMsg.Value = 0; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + } + } + + // + // Return the operation status. + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Reset the AX88772 + + This routine uses ::Ax88772UsbCommand to reset the network + adapter. This routine also uses ::Ax88772PhyWrite to reset + the PHY. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + + @retval EFI_SUCCESS The MAC address is available. + @retval other The MAC address is not valid. + +**/ +EFI_STATUS +Ax88772Reset ( + IN NIC_DEVICE * pNicDevice + ) +{ + USB_DEVICE_REQUEST SetupMsg; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Turn off the MAC + // + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_RX_CONTROL_WRITE; + SetupMsg.Value = 0; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_PHY | DEBUG_RX_BROADCAST | DEBUG_RX_MULTICAST + | DEBUG_RX_UNICAST | DEBUG_TX | DEBUG_INFO, + "MAC reset\r\n" )); + + // + // The link is now idle + // + pNicDevice->bLinkIdle = TRUE; + + // + // Delay for a bit + // + gBS->Stall ( RESET_MSEC ); + + // + // Select the internal PHY + // + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_PHY_SELECT; + SetupMsg.Value = SPHY_PSEL; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + if ( !EFI_ERROR ( Status )) { + // + // Delay for a bit + // + gBS->Stall ( PHY_RESET_MSEC ); + + // + // Clear the internal PHY reset + // + SetupMsg.Request = CMD_RESET; + SetupMsg.Value = SRR_IPRL | SRR_PRL; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + if ( !EFI_ERROR ( Status )) { + // + // Reset the PHY + // + Status = Ax88772PhyWrite ( pNicDevice, + PHY_BMCR, + BMCR_RESET ); + if ( !EFI_ERROR ( Status )) { + // + // Set the gaps + // + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_GAPS_WRITE; + SetupMsg.Value = 0x0c15; + SetupMsg.Index = 0x0e; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + } + } + } + } + + // + // Return the operation status. + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +VOID +FillPkt2Queue ( + IN NIC_DEVICE * pNicDevice, + IN UINTN BufLength) +{ + + UINT16 * pLength; + UINT16 * pLengthBar; + UINT8* pData; + UINT32 offset; + RX_TX_PACKET * pRxPacket; + EFI_STATUS Status; + + for ( offset = 0; offset < BufLength; ){ + pLength = (UINT16*) (pNicDevice->pBulkInBuff + offset); + pLengthBar = (UINT16*) (pNicDevice->pBulkInBuff + offset +2); + + *pLength &= 0x7ff; + *pLengthBar &= 0x7ff; + *pLengthBar |= 0xf800; + + if ((*pLength ^ *pLengthBar ) != 0xFFFF) { + DEBUG (( EFI_D_ERROR , "Pkt length error. BufLength = %d\n", BufLength)); + return; + } + + pRxPacket = pNicDevice->pRxFree; + if ( NULL == pRxPacket ) { + Status = gBS->AllocatePool ( EfiRuntimeServicesData, + sizeof( RX_TX_PACKET ), + (VOID **) &pRxPacket ); + if ( !EFI_ERROR ( Status )) { + // + // Add this packet to the free packet list + // + pNicDevice->pRxFree = pRxPacket; + pRxPacket->pNext = NULL; + } + else { + // + // Use the discard packet buffer + // + //pRxPacket = &Packet; + } + } + + + pData = pNicDevice->pBulkInBuff + offset + 4; + pRxPacket->Length = *pLength; + pRxPacket->LengthBar = *(UINT16*) (pNicDevice->pBulkInBuff + offset +2); + CopyMem (&pRxPacket->Data[0], pData, *pLength); + //DEBUG((DEBUG_INFO, "Packet [%d]\n", *pLength)); + + pNicDevice->pRxFree = pRxPacket->pNext; + pRxPacket->pNext = NULL; + + if ( NULL == pNicDevice->pRxTail ) { + pNicDevice->pRxHead = pRxPacket; + } + else { + pNicDevice->pRxTail->pNext = pRxPacket; + } + pNicDevice->pRxTail = pRxPacket; + offset += (*pLength + 4); + + } +} + + + +/** + Receive a frame from the network. + + This routine polls the USB receive interface for a packet. If a packet + is available, this routine adds the receive packet to the list of + pending receive packets. + + This routine calls ::Ax88772NegotiateLinkComplete to verify + that the link is up. This routine also calls ::SN_Reset to + reset the network adapter when necessary. Finally this + routine attempts to receive one or more packets from the + network adapter. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] bUpdateLink TRUE = Update link status + +**/ +VOID +Ax88772Rx ( + IN NIC_DEVICE * pNicDevice, + IN BOOLEAN bUpdateLink + ) +{ + BOOLEAN bFullDuplex; + BOOLEAN bLinkUp; + BOOLEAN bRxPacket; + BOOLEAN bSpeed100; + UINTN LengthInBytes; + RX_TX_PACKET Packet; + RX_TX_PACKET * pRxPacket; + EFI_USB_IO_PROTOCOL *pUsbIo; + EFI_STATUS Status; + EFI_TPL TplPrevious; + UINT32 TransferStatus; + + // + // Synchronize with Ax88772Timer + // + VERIFY_TPL ( TPL_AX88772 ); + TplPrevious = gBS->RaiseTPL ( TPL_AX88772 ); + DEBUG (( DEBUG_TPL | DEBUG_INFO, + "%d: TPL\r\n", + TPL_AX88772 )); + + // + // Get the link status + // + if ( bUpdateLink ) { + bLinkUp = pNicDevice->bLinkUp; + bSpeed100 = pNicDevice->b100Mbps; + bFullDuplex = pNicDevice->bFullDuplex; + Status = Ax88772NegotiateLinkComplete ( pNicDevice, + &pNicDevice->PollCount, + &pNicDevice->bComplete, + &pNicDevice->bLinkUp, + &pNicDevice->b100Mbps, + &pNicDevice->bFullDuplex ); + + // + // Determine if the autonegotiation is complete + // + if ( pNicDevice->bComplete ) { + if ( pNicDevice->bLinkUp ) { + if (( bSpeed100 && ( !pNicDevice->b100Mbps )) + || (( !bSpeed100 ) && pNicDevice->b100Mbps ) + || ( bFullDuplex && ( !pNicDevice->bFullDuplex )) + || (( !bFullDuplex ) && pNicDevice->bFullDuplex )) { + pNicDevice->PollCount = 0; + DEBUG (( DEBUG_LINK | DEBUG_INFO, + "Reset to establish proper link setup: %d Mbps, %s duplex\r\n", + pNicDevice->b100Mbps ? 100 : 10, + pNicDevice->bFullDuplex ? L"Full" : L"Half" )); + Status = SN_Reset ( &pNicDevice->SimpleNetwork, FALSE ); + } + if (( !bLinkUp ) && pNicDevice->bLinkUp ) { + // + // Display the autonegotiation status + // + DEBUG (( DEBUG_LINK | DEBUG_INFO, + "Link: Up, %d Mbps, %s duplex\r\n", + pNicDevice->b100Mbps ? 100 : 10, + pNicDevice->bFullDuplex ? L"Full" : L"Half" )); + } + } + } + + // + // Update the link status + // + if ( bLinkUp && ( !pNicDevice->bLinkUp )) { + DEBUG (( DEBUG_LINK | DEBUG_INFO, "Link: Down\r\n" )); + } + } + + // + // Loop until all the packets are emptied from the receiver + // + do { + bRxPacket = FALSE; + + // + // Locate a packet for use + // + pRxPacket = pNicDevice->pRxFree; + LengthInBytes = MAX_BULKIN_SIZE; + if ( NULL == pRxPacket ) { + Status = gBS->AllocatePool ( EfiRuntimeServicesData, + sizeof ( *pRxPacket ), + (VOID **) &pRxPacket ); + if ( !EFI_ERROR ( Status )) { + // + // Add this packet to the free packet list + // + pNicDevice->pRxFree = pRxPacket; + pRxPacket->pNext = NULL; + } + else { + // + // Use the discard packet buffer + // + pRxPacket = &Packet; + } + } + + // + // Attempt to receive a packet + // + SetMem (&pNicDevice->pBulkInBuff[0], MAX_BULKIN_SIZE, 0); + pUsbIo = pNicDevice->pUsbIo; + Status = pUsbIo->UsbBulkTransfer ( pUsbIo, + USB_ENDPOINT_DIR_IN | BULK_IN_ENDPOINT, + &pNicDevice->pBulkInBuff[0], + &LengthInBytes, + 2, + &TransferStatus ); + if ( LengthInBytes > 0 ) { + FillPkt2Queue(pNicDevice, LengthInBytes); + } + pRxPacket = pNicDevice->pRxHead; + if (( !EFI_ERROR ( Status )) + && ( 0 < pRxPacket->Length ) + && ( pRxPacket->Length <= sizeof ( pRxPacket->Data )) + && ( LengthInBytes > 0)) { + + // + // Determine if the packet should be received + // + bRxPacket = TRUE; + LengthInBytes = pRxPacket->Length; + pNicDevice->bLinkIdle = FALSE; + if ( pNicDevice->pRxFree == pRxPacket ) { + // + // Display the received packet + // + if ( 0 != ( pRxPacket->Data[0] & 1 )) { + if (( 0xff == pRxPacket->Data[0]) + && ( 0xff == pRxPacket->Data[1]) + && ( 0xff == pRxPacket->Data[2]) + && ( 0xff == pRxPacket->Data[3]) + && ( 0xff == pRxPacket->Data[4]) + && ( 0xff == pRxPacket->Data[5])) { + DEBUG (( DEBUG_RX_BROADCAST | DEBUG_INFO, + "RX: %02x-%02x-%02x-%02x-%02x-%02x %02x-%02x-%02x-%02x-%02x-%02x %02x-%02x %d bytes\r\n", + pRxPacket->Data[0], + pRxPacket->Data[1], + pRxPacket->Data[2], + pRxPacket->Data[3], + pRxPacket->Data[4], + pRxPacket->Data[5], + pRxPacket->Data[6], + pRxPacket->Data[7], + pRxPacket->Data[8], + pRxPacket->Data[9], + pRxPacket->Data[10], + pRxPacket->Data[11], + pRxPacket->Data[12], + pRxPacket->Data[13], + LengthInBytes )); + } + else { + DEBUG (( DEBUG_RX_MULTICAST | DEBUG_INFO, + "RX: %02x-%02x-%02x-%02x-%02x-%02x %02x-%02x-%02x-%02x-%02x-%02x %02x-%02x %d bytes\r\n", + pRxPacket->Data[0], + pRxPacket->Data[1], + pRxPacket->Data[2], + pRxPacket->Data[3], + pRxPacket->Data[4], + pRxPacket->Data[5], + pRxPacket->Data[6], + pRxPacket->Data[7], + pRxPacket->Data[8], + pRxPacket->Data[9], + pRxPacket->Data[10], + pRxPacket->Data[11], + pRxPacket->Data[12], + pRxPacket->Data[13], + LengthInBytes )); + } + } + else { + DEBUG (( DEBUG_RX_UNICAST | DEBUG_INFO, + "RX: %02x-%02x-%02x-%02x-%02x-%02x %02x-%02x-%02x-%02x-%02x-%02x %02x-%02x %d bytes\r\n", + pRxPacket->Data[0], + pRxPacket->Data[1], + pRxPacket->Data[2], + pRxPacket->Data[3], + pRxPacket->Data[4], + pRxPacket->Data[5], + pRxPacket->Data[6], + pRxPacket->Data[7], + pRxPacket->Data[8], + pRxPacket->Data[9], + pRxPacket->Data[10], + pRxPacket->Data[11], + pRxPacket->Data[12], + pRxPacket->Data[13], + LengthInBytes )); + } + + } + else { + // + // Error, not enough buffers for this packet, discard packet + // + DEBUG (( DEBUG_WARN | DEBUG_INFO, + "WARNING - No buffer, discarding RX packet: %02x-%02x-%02x-%02x-%02x-%02x %02x-%02x-%02x-%02x-%02x-%02x %02x-%02x %d bytes\r\n", + pRxPacket->Data[0], + pRxPacket->Data[1], + pRxPacket->Data[2], + pRxPacket->Data[3], + pRxPacket->Data[4], + pRxPacket->Data[5], + pRxPacket->Data[6], + pRxPacket->Data[7], + pRxPacket->Data[8], + pRxPacket->Data[9], + pRxPacket->Data[10], + pRxPacket->Data[11], + pRxPacket->Data[12], + pRxPacket->Data[13], + LengthInBytes )); + } + } + }while ( bRxPacket ); + + // + // Release the synchronization withhe Ax88772Timer + // + gBS->RestoreTPL ( TplPrevious ); + DEBUG (( DEBUG_TPL | DEBUG_INFO, + "%d: TPL\r\n", + TplPrevious )); +} + + +/** + Enable or disable the receiver + + This routine calls ::Ax88772UsbCommand to update the + receiver state. This routine also calls ::Ax88772MacAddressSet + to establish the MAC address for the network adapter. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] RxFilter Simple network RX filter mask value + + @retval EFI_SUCCESS The MAC address was set. + @retval other The MAC address was not set. + +**/ +EFI_STATUS +Ax88772RxControl ( + IN NIC_DEVICE * pNicDevice, + IN UINT32 RxFilter + ) +{ + UINT16 MediumStatus; + INT32 MulticastHash[2]; + UINT16 RxControl; + USB_DEVICE_REQUEST SetupMsg; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Disable all multicast + // + MulticastHash[0] = 0; + MulticastHash[1] = 0; + + // + // Enable the receiver if something is to be received + // + Status = EFI_SUCCESS; + RxControl = RXC_SO | RXC_MFB_16384; + if ( 0 != RxFilter ) { + // + // Enable the receiver + // + SetupMsg.RequestType = USB_ENDPOINT_DIR_IN + | USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_MEDIUM_STATUS_READ; + SetupMsg.Value = 0; + SetupMsg.Index = 0; + SetupMsg.Length = sizeof ( MediumStatus ); + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + &MediumStatus ); + if ( !EFI_ERROR ( Status )) { + if ( 0 == ( MediumStatus & MS_RE )) { + MediumStatus |= MS_RE | MS_ONE; + if ( pNicDevice->bFullDuplex ) { + MediumStatus |= MS_TFC | MS_RFC; + } + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_MEDIUM_STATUS_WRITE; + SetupMsg.Value = MediumStatus; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DEBUG_INFO, + "ERROR - Failed to enable receiver, Status: %r\r\n", + Status )); + } + } + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_INFO, + "ERROR - Failed to read receiver status, Status: %r\r\n", + Status )); + } + + // + // Enable multicast if requested + // + if ( 0 != ( RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST )) { + RxControl |= RXC_AM; + MulticastHash[0] = pNicDevice->MulticastHash[0]; + MulticastHash[1] = pNicDevice->MulticastHash[1]; + } + + // + // Enable all multicast if requested + // + if ( 0 != ( RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST )) { + RxControl |= RXC_AMALL; + MulticastHash[0] = -1; + MulticastHash[1] = -1; + } + + // + // Enable broadcast if requested + // + if ( 0 != ( RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST )) { + RxControl |= RXC_AB; + } + + // + // Enable promiscuous mode if requested + // + if ( 0 != ( RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS )) { + RxControl |= RXC_PRO; + MulticastHash[0] = -1; + MulticastHash[1] = -1; + } + } + + // + // Update the MAC address + // + if ( !EFI_ERROR ( Status )) { + Status = Ax88772MacAddressSet ( pNicDevice, &pNicDevice->SimpleNetworkData.CurrentAddress.Addr[0]); + } + + // + // Update the receiver control + // + if ( !EFI_ERROR ( Status )) { + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_RX_CONTROL_WRITE; + SetupMsg.Value = RxControl; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_RX_BROADCAST | DEBUG_RX_MULTICAST | DEBUG_RX_UNICAST | DEBUG_INFO, + "RxControl: 0x%04x\r\n", + RxControl )); + + // + // Update the multicast hash table + // + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_MULTICAST_HASH_WRITE; + SetupMsg.Value = 0; + SetupMsg.Index = 0; + SetupMsg.Length = sizeof ( pNicDevice ->MulticastHash ); + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + &pNicDevice->MulticastHash ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_RX_MULTICAST | DEBUG_INFO, + "Multicast Hash: 0x%02x %02x %02x %02x %02x %02x %02x %02x\r\n", + (UINT8) MulticastHash[0], + (UINT8)( MulticastHash[0] >> 8 ), + (UINT8)( MulticastHash[0] >> 16 ), + (UINT8)( MulticastHash[0] >> 24 ), + (UINT8) MulticastHash[1], + (UINT8)( MulticastHash[1] >> 8 ), + (UINT8)( MulticastHash[1] >> 16 ), + (UINT8)( MulticastHash[1] >> 24 ))); + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_INFO, + "ERROR - Failed to update multicast hash table, Status: %r\r\n", + Status )); + } + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_INFO, + "ERROR - Failed to set receiver control, Status: %r\r\n", + Status )); + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Read an SROM location + + This routine calls ::Ax88772UsbCommand to read data from the + SROM. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] Address SROM address + @param [out] pData Buffer to receive the data + + @retval EFI_SUCCESS The read was successful + @retval other The read failed + +**/ +EFI_STATUS +Ax88772SromRead ( + IN NIC_DEVICE * pNicDevice, + IN UINT32 Address, + OUT UINT16 * pData + ) +{ + USB_DEVICE_REQUEST SetupMsg; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Read a value from the SROM + // + SetupMsg.RequestType = USB_ENDPOINT_DIR_IN + | USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_SROM_READ; + SetupMsg.Value = (UINT16) Address; + SetupMsg.Index = 0; + SetupMsg.Length = sizeof ( *pData ); + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + pData ); + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + This routine is called at a regular interval to poll for + receive packets. + + This routine polls the link state and gets any receive packets + by calling ::Ax88772Rx. + + @param [in] Event Timer event + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + +**/ +VOID +Ax88772Timer ( + IN EFI_EVENT Event, + IN NIC_DEVICE * pNicDevice + ) +{ + // + // Use explicit DEBUG messages since the output frequency is too + // high for DEBUG_INFO to keep up and have spare cycles for the + // shell + // + DEBUG (( DEBUG_TIMER, "Entering Ax88772Timer\r\n" )); + + // + // Poll the link state and get any receive packets + // + Ax88772Rx ( pNicDevice, FALSE ); + + DEBUG (( DEBUG_TIMER, "Exiting Ax88772Timer\r\n" )); +} + + +/** + Send a command to the USB device. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] pRequest Pointer to the request structure + @param [in, out] pBuffer Data buffer address + + @retval EFI_SUCCESS The USB transfer was successful + @retval other The USB transfer failed + +**/ +EFI_STATUS +Ax88772UsbCommand ( + IN NIC_DEVICE * pNicDevice, + IN USB_DEVICE_REQUEST * pRequest, + IN OUT VOID * pBuffer + ) +{ + UINT32 CmdStatus; + EFI_USB_DATA_DIRECTION Direction; + EFI_USB_IO_PROTOCOL * pUsbIo; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Determine the transfer direction + // + Direction = EfiUsbNoData; + if ( 0 != pRequest->Length ) { + Direction = ( 0 != ( pRequest->RequestType & USB_ENDPOINT_DIR_IN )) + ? EfiUsbDataIn : EfiUsbDataOut; + } + + // + // Issue the command + // + pUsbIo = pNicDevice->pUsbIo; + Status = pUsbIo->UsbControlTransfer ( pUsbIo, + pRequest, + Direction, + USB_BUS_TIMEOUT, + pBuffer, + pRequest->Length, + &CmdStatus ); + + // + // Determine the operation status + // + if ( !EFI_ERROR ( Status )) { + Status = CmdStatus; + } + else { + // + // Display any errors + // + DEBUG (( DEBUG_INFO, + "Ax88772UsbCommand - Status: %r\n", + Status )); + + // + // Only use status values associated with the Simple Network protocol + // + if ( EFI_TIMEOUT == Status ) { + Status = EFI_DEVICE_ERROR; + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.h b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.h new file mode 100644 index 0000000000..8840a4f464 --- /dev/null +++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.h @@ -0,0 +1,969 @@ +/** @file + Definitions for ASIX AX88772 Ethernet adapter. + + Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _AX88772_H_ +#define _AX88772_H_ + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ +// +//Too many output debug info hangs system in Debug tip +// +//#if defined(_MSC_VER) /* Handle Microsoft VC++ compiler specifics. */ +//#define DBG_ENTER() DEBUG (( DEBUG_INFO, "Entering " __FUNCTION__ "\n" )) ///< Display routine entry +//#define DBG_EXIT() DEBUG (( DEBUG_INFO, "Exiting " __FUNCTION__ "\n" )) ///< Display routine exit +//#define DBG_EXIT_DEC(Status) DEBUG (( DEBUG_INFO, "Exiting " __FUNCTION__ ", Status: %d\n", Status )) ///< Display routine exit with decimal value +//#define DBG_EXIT_HEX(Status) DEBUG (( DEBUG_INFO, "Exiting " __FUNCTION__ ", Status: 0x%08x\n", Status )) ///< Display routine exit with hex value +//#define DBG_EXIT_STATUS(Status) DEBUG (( DEBUG_INFO, "Exiting " __FUNCTION__ ", Status: %r\n", Status )) ///< Display routine exit with status value +//#define DBG_EXIT_TF(Status) DEBUG (( DEBUG_INFO, "Exiting " __FUNCTION__ ", returning %s\n", (FALSE == Status) ? L"FALSE" : L"TRUE" )) ///< Display routine with TRUE/FALSE value +//#else // _MSC_VER +#define DBG_ENTER() ///< Display routine entry +#define DBG_EXIT() ///< Display routine exit +#define DBG_EXIT_DEC(Status) ///< Display routine exit with decimal value +#define DBG_EXIT_HEX(Status) ///< Display routine exit with hex value +#define DBG_EXIT_STATUS(Status) ///< Display routine exit with status value +#define DBG_EXIT_TF(Status) ///< Display routine with TRUE/FALSE value +//#endif // _MSC_VER + +#define USB_IS_IN_ENDPOINT(EndPointAddr) (((EndPointAddr) & BIT7) != 0) ///< Return TRUE/FALSE for IN direction +#define USB_IS_OUT_ENDPOINT(EndPointAddr) (((EndPointAddr) & BIT7) == 0) ///< Return TRUE/FALSE for OUT direction +#define USB_IS_BULK_ENDPOINT(Attribute) (((Attribute) & (BIT0 | BIT1)) == USB_ENDPOINT_BULK) ///< Return TRUE/FALSE for BULK type +#define USB_IS_INTERRUPT_ENDPOINT(Attribute) (((Attribute) & (BIT0 | BIT1)) == USB_ENDPOINT_INTERRUPT) ///< Return TRUE/FALSE for INTERRUPT type + +//------------------------------------------------------------------------------ +// Constants +//------------------------------------------------------------------------------ + +#define DEBUG_RX_BROADCAST 0x40000000 ///< Display RX broadcast messages +#define DEBUG_RX_MULTICAST 0x20000000 ///< Display RX multicast messages +#define DEBUG_RX_UNICAST 0x10000000 ///< Display RX unicast messages +#define DEBUG_MAC_ADDRESS 0x08000000 ///< Display the MAC address +#define DEBUG_LINK 0x04000000 ///< Display the link status +#define DEBUG_TX 0x02000000 ///< Display the TX messages +#define DEBUG_PHY 0x01000000 ///< Display the PHY register values +#define DEBUG_SROM 0x00800000 ///< Display the SROM contents +#define DEBUG_TIMER 0x00400000 ///< Display the timer routine entry/exit +#define DEBUG_TPL 0x00200000 ///< Display the timer routine entry/exit + +#define AX88772_MAX_PKT_SIZE ( 2048 - 4 ) ///< Maximum packet size +#define ETHERNET_HEADER_SIZE sizeof ( ETHERNET_HEADER ) ///< Size in bytes of the Ethernet header +#define MIN_ETHERNET_PKT_SIZE 60 ///< Minimum packet size including Ethernet header +#define MAX_ETHERNET_PKT_SIZE 1500 ///< Ethernet spec 3.1.1: Minimum packet size +#define MAX_BULKIN_SIZE 2048 ///< Maximum size of one UsbBulk + + +#define USB_NETWORK_CLASS 0x09 ///< USB Network class code +#define USB_BUS_TIMEOUT 1000 ///< USB timeout in milliseconds + +#define TIMER_MSEC 20 ///< Polling interval for the NIC +#define TPL_AX88772 TPL_CALLBACK ///< TPL for routine synchronization + +/** + Verify new TPL value + + This macro which is enabled when debug is enabled verifies that + the new TPL value is >= the current TPL value. +**/ +#ifdef VERIFY_TPL +#undef VERIFY_TPL +#endif // VERIFY_TPL + +#if !defined(MDEPKG_NDEBUG) + +#define VERIFY_TPL(tpl) \ +{ \ + EFI_TPL PreviousTpl; \ + \ + PreviousTpl = gBS->RaiseTPL ( TPL_HIGH_LEVEL ); \ + gBS->RestoreTPL ( PreviousTpl ); \ + if ( PreviousTpl > tpl ) { \ + DEBUG (( DEBUG_ERROR, "Current TPL: %d, New TPL: %d\r\n", PreviousTpl, tpl )); \ + ASSERT ( PreviousTpl <= tpl ); \ + } \ +} + +#else // MDEPKG_NDEBUG + +#define VERIFY_TPL(tpl) + +#endif // MDEPKG_NDEBUG + +//------------------------------------------------------------------------------ +// Hardware Definition +//------------------------------------------------------------------------------ + +#define DEV_SIGNATURE SIGNATURE_32 ('A','X','8','8') ///< Signature of data structures in memory + +#define VENDOR_ID 0x0b95 ///< Vendor ID for Asix +#define PRODUCT_ID 0x7720 ///< Product ID for the AX88772 USB 10/100 Ethernet controller + +#define RESET_MSEC 1000 ///< Reset duration +#define PHY_RESET_MSEC 500 ///< PHY reset duration + +// +// RX Control register +// + +#define RXC_PRO 0x0001 ///< Receive all packets +#define RXC_AMALL 0x0002 ///< Receive all multicast packets +#define RXC_SEP 0x0004 ///< Save error packets +#define RXC_AB 0x0008 ///< Receive broadcast packets +#define RXC_AM 0x0010 ///< Use multicast destination address hash table +#define RXC_AP 0x0020 ///< Accept physical address from Multicast Filter +#define RXC_SO 0x0080 ///< Start operation +#define RXC_MFB 0x0300 ///< Maximum frame burst +#define RXC_MFB_2048 0 ///< Maximum frame size: 2048 bytes +#define RXC_MFB_4096 0x0100 ///< Maximum frame size: 4096 bytes +#define RXC_MFB_8192 0x0200 ///< Maximum frame size: 8192 bytes +#define RXC_MFB_16384 0x0300 ///< Maximum frame size: 16384 bytes + +// +// Medium Status register +// + +#define MS_FD 0x0002 ///< Full duplex +#define MS_ONE 0x0004 ///< Must be one +#define MS_RFC 0x0010 ///< RX flow control enable +#define MS_TFC 0x0020 ///< TX flow control enable +#define MS_PF 0x0080 ///< Pause frame enable +#define MS_RE 0x0100 ///< Receive enable +#define MS_PS 0x0200 ///< Port speed 1=100, 0=10 Mbps +#define MS_SBP 0x0800 ///< Stop back pressure +#define MS_SM 0x1000 ///< Super MAC support + +// +// Software PHY Select register +// + +#define SPHY_PSEL 0x01 ///< Select internal PHY +#define SPHY_ASEL 0x02 ///< 1=Auto select, 0=Manual select + +// +// Software Reset register +// + +#define SRR_RR 0x01 ///< Clear receive frame length error +#define SRR_RT 0x02 ///< Clear transmit frame length error +#define SRR_PRTE 0x04 ///< External PHY reset pin tri-state enable +#define SRR_PRL 0x08 ///< External PHY reset pin level +#define SRR_BZ 0x10 ///< Force Bulk to return zero length packet +#define SRR_IPRL 0x20 ///< Internal PHY reset control +#define SRR_IPPD 0x40 ///< Internal PHY power down + +// +// PHY ID values +// + +#define PHY_ID_INTERNAL 0x0010 ///< Internal PHY + +// +// USB Commands +// + +#define CMD_PHY_ACCESS_SOFTWARE 0x06 ///< Software in control of PHY +#define CMD_PHY_REG_READ 0x07 ///< Read PHY register, Value: PHY, Index: Register, Data: Register value +#define CMD_PHY_REG_WRITE 0x08 ///< Write PHY register, Value: PHY, Index: Register, Data: New 16-bit value +#define CMD_PHY_ACCESS_HARDWARE 0x0a ///< Hardware in control of PHY +#define CMD_SROM_READ 0x0b ///< Read SROM register: Value: Address, Data: Value +#define CMD_RX_CONTROL_WRITE 0x10 ///< Set the RX control register, Value: New value +#define CMD_GAPS_WRITE 0x12 ///< Write the gaps register, Value: New value +#define CMD_MAC_ADDRESS_READ 0x13 ///< Read the MAC address, Data: 6 byte MAC address +#define CMD_MAC_ADDRESS_WRITE 0x14 ///< Set the MAC address, Data: New 6 byte MAC address +#define CMD_MULTICAST_HASH_WRITE 0x16 ///< Write the multicast hash table, Data: New 8 byte value +#define CMD_MEDIUM_STATUS_READ 0x1a ///< Read medium status register, Data: Register value +#define CMD_MEDIUM_STATUS_WRITE 0x1b ///< Write medium status register, Value: New value +#define CMD_RESET 0x20 ///< Reset register, Value: New value +#define CMD_PHY_SELECT 0x22 ///< PHY select register, Value: New value + +//------------------------------ +// USB Endpoints +//------------------------------ + +#define CONTROL_ENDPOINT 0 ///< Control endpoint +#define INTERRUPT_ENDPOINT 1 ///< Interrupt endpoint +#define BULK_IN_ENDPOINT 2 ///< Receive endpoint +#define BULK_OUT_ENDPOINT 3 ///< Transmit endpoint + +//------------------------------ +// PHY Registers +//------------------------------ + +#define PHY_BMCR 0 ///< Control register +#define PHY_BMSR 1 ///< Status register +#define PHY_ANAR 4 ///< Autonegotiation advertisement register +#define PHY_ANLPAR 5 ///< Autonegotiation link parter ability register +#define PHY_ANER 6 ///< Autonegotiation expansion register + +// BMCR - Register 0 + +#define BMCR_RESET 0x8000 ///< 1 = Reset the PHY, bit clears after reset +#define BMCR_LOOPBACK 0x4000 ///< 1 = Loopback enabled +#define BMCR_100MBPS 0x2000 ///< 100 Mbits/Sec +#define BMCR_10MBPS 0 ///< 10 Mbits/Sec +#define BMCR_AUTONEGOTIATION_ENABLE 0x1000 ///< 1 = Enable autonegotiation +#define BMCR_POWER_DOWN 0x0800 ///< 1 = Power down +#define BMCR_ISOLATE 0x0400 ///< 0 = Isolate PHY +#define BMCR_RESTART_AUTONEGOTIATION 0x0200 ///< 1 = Restart autonegotiation +#define BMCR_FULL_DUPLEX 0x0100 ///< Full duplex operation +#define BMCR_HALF_DUPLEX 0 ///< Half duplex operation +#define BMCR_COLLISION_TEST 0x0080 ///< 1 = Collision test enabled + +// BSMR - Register 1 + +#define BMSR_100BASET4 0x8000 ///< 1 = 100BASE-T4 mode +#define BMSR_100BASETX_FDX 0x4000 ///< 1 = 100BASE-TX full duplex +#define BMSR_100BASETX_HDX 0x2000 ///< 1 = 100BASE-TX half duplex +#define BMSR_10BASET_FDX 0x1000 ///< 1 = 10BASE-T full duplex +#define BMSR_10BASET_HDX 0x0800 ///< 1 = 10BASE-T half duplex +#define BMSR_MF 0x0040 ///< 1 = PHY accepts frames with preamble suppressed +#define BMSR_AUTONEG_CMPLT 0x0020 ///< 1 = Autonegotiation complete +#define BMSR_RF 0x0010 ///< 1 = Remote fault +#define BMSR_AUTONEG 0x0008 ///< 1 = Able to perform autonegotiation +#define BMSR_LINKST 0x0004 ///< 1 = Link up +#define BMSR_JABBER_DETECT 0x0002 ///< 1 = jabber condition detected +#define BMSR_EXTENDED_CAPABILITY 0x0001 ///< 1 = Extended register capable + +// ANAR and ANLPAR Registers 4, 5 + +#define AN_NP 0x8000 ///< 1 = Next page available +#define AN_ACK 0x4000 ///< 1 = Link partner acknowledged +#define AN_RF 0x2000 ///< 1 = Remote fault indicated by link partner +#define AN_FCS 0x0400 ///< 1 = Flow control ability +#define AN_T4 0x0200 ///< 1 = 100BASE-T4 support +#define AN_TX_FDX 0x0100 ///< 1 = 100BASE-TX Full duplex +#define AN_TX_HDX 0x0080 ///< 1 = 100BASE-TX support +#define AN_10_FDX 0x0040 ///< 1 = 10BASE-T Full duplex +#define AN_10_HDX 0x0020 ///< 1 = 10BASE-T support +#define AN_CSMA_CD 0x0001 ///< 1 = IEEE 802.3 CSMA/CD support + +//------------------------------------------------------------------------------ +// Data Types +//------------------------------------------------------------------------------ + +/** + Ethernet header layout + + IEEE 802.3-2002 Part 3 specification, section 3.1.1. +**/ +#pragma pack(1) +typedef struct { + UINT8 dest_addr[PXE_HWADDR_LEN_ETHER]; ///< Destination LAN address + UINT8 src_addr[PXE_HWADDR_LEN_ETHER]; ///< Source LAN address + UINT16 type; ///< Protocol or length +} ETHERNET_HEADER; +#pragma pack() + +/** + Receive and Transmit packet structure +**/ +#pragma pack(1) +typedef struct _RX_TX_PACKET { + struct _RX_TX_PACKET * pNext; ///< Next receive packet + UINT16 Length; ///< Packet length + UINT16 LengthBar; ///< Complement of the length + UINT8 Data[ AX88772_MAX_PKT_SIZE ]; ///< Received packet data +} RX_TX_PACKET; +#pragma pack() + +/** + AX88772 control structure + + The driver uses this structure to manage the Asix AX88772 10/100 + Ethernet controller. +**/ +typedef struct { + UINTN Signature; ///< Structure identification + + // + // USB data + // + EFI_HANDLE Controller; ///< Controller handle + EFI_USB_IO_PROTOCOL * pUsbIo; ///< USB driver interface + + // + // Simple network protocol data + // + EFI_SIMPLE_NETWORK_PROTOCOL SimpleNetwork; ///< Driver's network stack interface + EFI_SIMPLE_NETWORK_MODE SimpleNetworkData; ///< Data for simple network + + // + // Ethernet controller data + // + BOOLEAN bInitialized; ///< Controller initialized + VOID * pTxBuffer; ///< Last transmit buffer + UINT16 PhyId; ///< PHY ID + + // + // Link state + // + BOOLEAN b100Mbps; ///< Current link speed, FALSE = 10 Mbps + BOOLEAN bComplete; ///< Current state of auto-negotiation + BOOLEAN bFullDuplex; ///< Current duplex + BOOLEAN bLinkUp; ///< Current link state + BOOLEAN bLinkIdle; ///< TRUE = No received traffic + EFI_EVENT Timer; ///< Timer to monitor link state and receive packets + UINTN PollCount; ///< Number of times the autonegotiation status was polled + + // + // Receive buffer list + // + RX_TX_PACKET * pRxHead; ///< Head of receive packet list + RX_TX_PACKET * pRxTail; ///< Tail of receive packet list + RX_TX_PACKET * pRxFree; ///< Free packet list + INT32 MulticastHash[2]; ///< Hash table for multicast destination addresses + UINT8 * pBulkInBuff; ///< Buffer for Usb Bulk +} NIC_DEVICE; + +#define DEV_FROM_SIMPLE_NETWORK(a) CR (a, NIC_DEVICE, SimpleNetwork, DEV_SIGNATURE) ///< Locate NIC_DEVICE from Simple Network Protocol + +//------------------------------------------------------------------------------ +// Simple Network Protocol +//------------------------------------------------------------------------------ + +/** + Reset the network adapter. + + Resets a network adapter and reinitializes it with the parameters that + were provided in the previous call to Initialize (). The transmit and + receive queues are cleared. Receive filters, the station address, the + statistics, and the multicast-IP-to-HW MAC addresses are not reset by + this call. + + This routine calls ::Ax88772Reset to perform the adapter specific + reset operation. This routine also starts the link negotiation + by calling ::Ax88772NegotiateLinkStart. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] bExtendedVerification Indicates that the driver may perform a more + exhaustive verification operation of the device + during reset. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Reset ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN BOOLEAN bExtendedVerification + ); + +/** + Initialize the simple network protocol. + + This routine calls ::Ax88772MacAddressGet to obtain the + MAC address. + + @param [in] pNicDevice NIC_DEVICE_INSTANCE pointer + + @retval EFI_SUCCESS Setup was successful + +**/ +EFI_STATUS +SN_Setup ( + IN NIC_DEVICE * pNicDevice + ); + +/** + This routine starts the network interface. + + @param [in] pSimpleNetwork Protocol instance pointer + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_ALREADY_STARTED The network interface was already started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Start ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork + ); + +/** + Set the MAC address. + + This function modifies or resets the current station address of a + network interface. If Reset is TRUE, then the current station address + is set ot the network interface's permanent address. If Reset if FALSE + then the current station address is changed to the address specified by + pNew. + + This routine calls ::Ax88772MacAddressSet to update the MAC address + in the network adapter. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] bReset Flag used to reset the station address to the + network interface's permanent address. + @param [in] pNew New station address to be used for the network + interface. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_StationAddress ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN BOOLEAN bReset, + IN EFI_MAC_ADDRESS * pNew + ); + +/** + This function resets or collects the statistics on a network interface. + If the size of the statistics table specified by StatisticsSize is not + big enough for all of the statistics that are collected by the network + interface, then a partial buffer of statistics is returned in + StatisticsTable. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] bReset Set to TRUE to reset the statistics for the network interface. + @param [in, out] pStatisticsSize On input the size, in bytes, of StatisticsTable. On output + the size, in bytes, of the resulting table of statistics. + @param [out] pStatisticsTable A pointer to the EFI_NETWORK_STATISTICS structure that + conains the statistics. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_BUFFER_TOO_SMALL The pStatisticsTable is NULL or the buffer is too small. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Statistics ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN BOOLEAN bReset, + IN OUT UINTN * pStatisticsSize, + OUT EFI_NETWORK_STATISTICS * pStatisticsTable + ); + +/** + This function stops a network interface. This call is only valid + if the network interface is in the started state. + + @param [in] pSimpleNetwork Protocol instance pointer + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Stop ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork + ); + +/** + This function releases the memory buffers assigned in the Initialize() call. + Pending transmits and receives are lost, and interrupts are cleared and disabled. + After this call, only Initialize() and Stop() calls may be used. + + @param [in] pSimpleNetwork Protocol instance pointer + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Shutdown ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork + ); + +/** + Send a packet over the network. + + This function places the packet specified by Header and Buffer on + the transmit queue. This function performs a non-blocking transmit + operation. When the transmit is complete, the buffer is returned + via the GetStatus() call. + + This routine calls ::Ax88772Rx to empty the network adapter of + receive packets. The routine then passes the transmit packet + to the network adapter. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] HeaderSize The size, in bytes, of the media header to be filled in by + the Transmit() function. If HeaderSize is non-zero, then + it must be equal to SimpleNetwork->Mode->MediaHeaderSize + and DestAddr and Protocol parameters must not be NULL. + @param [in] BufferSize The size, in bytes, of the entire packet (media header and + data) to be transmitted through the network interface. + @param [in] pBuffer A pointer to the packet (media header followed by data) to + to be transmitted. This parameter can not be NULL. If + HeaderSize is zero, then the media header is Buffer must + already be filled in by the caller. If HeaderSize is nonzero, + then the media header will be filled in by the Transmit() + function. + @param [in] pSrcAddr The source HW MAC address. If HeaderSize is zero, then + this parameter is ignored. If HeaderSize is nonzero and + SrcAddr is NULL, then SimpleNetwork->Mode->CurrentAddress + is used for the source HW MAC address. + @param [in] pDestAddr The destination HW MAC address. If HeaderSize is zero, then + this parameter is ignored. + @param [in] pProtocol The type of header to build. If HeaderSize is zero, then + this parameter is ignored. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_NOT_READY The network interface is too busy to accept this transmit request. + @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + +**/ +EFI_STATUS +EFIAPI +SN_Transmit ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN UINTN HeaderSize, + IN UINTN BufferSize, + IN VOID * pBuffer, + IN EFI_MAC_ADDRESS * pSrcAddr, + IN EFI_MAC_ADDRESS * pDestAddr, + IN UINT16 * pProtocol + ); + +//------------------------------------------------------------------------------ +// Support Routines +//------------------------------------------------------------------------------ + +/** + Get the MAC address + + This routine calls ::Ax88772UsbCommand to request the MAC + address from the network adapter. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [out] pMacAddress Address of a six byte buffer to receive the MAC address. + + @retval EFI_SUCCESS The MAC address is available. + @retval other The MAC address is not valid. + +**/ +EFI_STATUS +Ax88772MacAddressGet ( + IN NIC_DEVICE * pNicDevice, + OUT UINT8 * pMacAddress + ); + +/** + Set the MAC address + + This routine calls ::Ax88772UsbCommand to set the MAC address + in the network adapter. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] pMacAddress Address of a six byte buffer to containing the new MAC address. + + @retval EFI_SUCCESS The MAC address was set. + @retval other The MAC address was not set. + +**/ +EFI_STATUS +Ax88772MacAddressSet ( + IN NIC_DEVICE * pNicDevice, + IN UINT8 * pMacAddress + ); + +/** + Clear the multicast hash table + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + +**/ +VOID +Ax88772MulticastClear ( + IN NIC_DEVICE * pNicDevice + ); + +/** + Enable a multicast address in the multicast hash table + + This routine calls ::Ax88772Crc to compute the hash bit for + this MAC address. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] pMacAddress Address of a six byte buffer to containing the MAC address. + +**/ +VOID +Ax88772MulticastSet ( + IN NIC_DEVICE * pNicDevice, + IN UINT8 * pMacAddress + ); + +/** + Start the link negotiation + + This routine calls ::Ax88772PhyWrite to start the PHY's link + negotiation. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + + @retval EFI_SUCCESS The link negotiation was started. + @retval other Failed to start the link negotiation. + +**/ +EFI_STATUS +Ax88772NegotiateLinkStart ( + IN NIC_DEVICE * pNicDevice + ); + +/** + Complete the negotiation of the PHY link + + This routine calls ::Ax88772PhyRead to determine if the + link negotiation is complete. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in, out] pPollCount Address of number of times this routine was polled + @param [out] pbComplete Address of boolean to receive complate status. + @param [out] pbLinkUp Address of boolean to receive link status, TRUE=up. + @param [out] pbHiSpeed Address of boolean to receive link speed, TRUE=100Mbps. + @param [out] pbFullDuplex Address of boolean to receive link duplex, TRUE=full. + + @retval EFI_SUCCESS The MAC address is available. + @retval other The MAC address is not valid. + +**/ +EFI_STATUS +Ax88772NegotiateLinkComplete ( + IN NIC_DEVICE * pNicDevice, + IN OUT UINTN * pPollCount, + OUT BOOLEAN * pbComplete, + OUT BOOLEAN * pbLinkUp, + OUT BOOLEAN * pbHiSpeed, + OUT BOOLEAN * pbFullDuplex + ); + +/** + Read a register from the PHY + + This routine calls ::Ax88772UsbCommand to read a PHY register. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] RegisterAddress Number of the register to read. + @param [in, out] pPhyData Address of a buffer to receive the PHY register value + + @retval EFI_SUCCESS The PHY data is available. + @retval other The PHY data is not valid. + +**/ +EFI_STATUS +Ax88772PhyRead ( + IN NIC_DEVICE * pNicDevice, + IN UINT8 RegisterAddress, + IN OUT UINT16 * pPhyData + ); + +/** + Write to a PHY register + + This routine calls ::Ax88772UsbCommand to write a PHY register. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] RegisterAddress Number of the register to read. + @param [in] PhyData Address of a buffer to receive the PHY register value + + @retval EFI_SUCCESS The PHY data was written. + @retval other Failed to wwrite the PHY register. + +**/ +EFI_STATUS +Ax88772PhyWrite ( + IN NIC_DEVICE * pNicDevice, + IN UINT8 RegisterAddress, + IN UINT16 PhyData + ); + +/** + Reset the AX88772 + + This routine uses ::Ax88772UsbCommand to reset the network + adapter. This routine also uses ::Ax88772PhyWrite to reset + the PHY. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + + @retval EFI_SUCCESS The MAC address is available. + @retval other The MAC address is not valid. + +**/ +EFI_STATUS +Ax88772Reset ( + IN NIC_DEVICE * pNicDevice + ); + +/** + Receive a frame from the network. + + This routine polls the USB receive interface for a packet. If a packet + is available, this routine adds the receive packet to the list of + pending receive packets. + + This routine calls ::Ax88772NegotiateLinkComplete to verify + that the link is up. This routine also calls ::SN_Reset to + reset the network adapter when necessary. Finally this + routine attempts to receive one or more packets from the + network adapter. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] bUpdateLink TRUE = Update link status + +**/ +VOID +Ax88772Rx ( + IN NIC_DEVICE * pNicDevice, + IN BOOLEAN bUpdateLink + ); + +/** + Enable or disable the receiver + + This routine calls ::Ax88772UsbCommand to update the + receiver state. This routine also calls ::Ax88772MacAddressSet + to establish the MAC address for the network adapter. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] RxFilter Simple network RX filter mask value + + @retval EFI_SUCCESS The MAC address was set. + @retval other The MAC address was not set. + +**/ +EFI_STATUS +Ax88772RxControl ( + IN NIC_DEVICE * pNicDevice, + IN UINT32 RxFilter + ); + +/** + Read an SROM location + + This routine calls ::Ax88772UsbCommand to read data from the + SROM. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] Address SROM address + @param [out] pData Buffer to receive the data + + @retval EFI_SUCCESS The read was successful + @retval other The read failed + +**/ +EFI_STATUS +Ax88772SromRead ( + IN NIC_DEVICE * pNicDevice, + IN UINT32 Address, + OUT UINT16 * pData + ); + +/** + This routine is called at a regular interval to poll for + receive packets. + + This routine polls the link state and gets any receive packets + by calling ::Ax88772Rx. + + @param [in] Event Timer event + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + +**/ +VOID +Ax88772Timer ( + IN EFI_EVENT Event, + IN NIC_DEVICE * pNicDevice + ); + +/** + Send a command to the USB device. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] pRequest Pointer to the request structure + @param [in, out] pBuffer Data buffer address + + @retval EFI_SUCCESS The USB transfer was successful + @retval other The USB transfer failed + +**/ +EFI_STATUS +Ax88772UsbCommand ( + IN NIC_DEVICE * pNicDevice, + IN USB_DEVICE_REQUEST * pRequest, + IN OUT VOID * pBuffer + ); + +//------------------------------------------------------------------------------ +// EFI Component Name Protocol Support +//------------------------------------------------------------------------------ + +extern EFI_COMPONENT_NAME_PROTOCOL gComponentName; ///< Component name protocol declaration +extern EFI_COMPONENT_NAME2_PROTOCOL gComponentName2; ///< Component name 2 protocol declaration + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param [in] pThis A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + @param [in] pLanguage A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 3066 or ISO 639-2 language code format. + @param [out] ppDriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +GetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL * pThis, + IN CHAR8 * pLanguage, + OUT CHAR16 ** ppDriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param [in] pThis A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + @param [in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + @param [in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + @param [in] pLanguage A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 3066 or ISO 639-2 language code format. + @param [out] ppControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +GetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL * pThis, + IN EFI_HANDLE ControllerHandle, + IN OPTIONAL EFI_HANDLE ChildHandle, + IN CHAR8 * pLanguage, + OUT CHAR16 ** ppControllerName + ); + +//------------------------------------------------------------------------------ + +#endif // _AX88772_H_ diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.inf b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.inf new file mode 100644 index 0000000000..12e7ebc5a2 --- /dev/null +++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.inf @@ -0,0 +1,61 @@ +## @file +# Component description file for ASIX AX88772 USB/Ethernet driver. +# +# This module provides support for the ASIX AX88772 USB/Ethernet adapter. +# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010018 + BASE_NAME = Ax88772 + FILE_GUID = B15239D6-6A01-4808-A0F7-B7F20F073555 + MODULE_TYPE = DXE_RUNTIME_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = EntryPoint + +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources.common] + Ax88772.h + Ax88772.c + ComponentName.c + DriverBinding.c + SimpleNetwork.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiLib + UefiBootServicesTableLib + BaseMemoryLib + DebugLib + UefiRuntimeLib + UefiDriverEntryPoint + +[Protocols] + gEfiDevicePathProtocolGuid ## BY_START + gEfiSimpleNetworkProtocolGuid ## BY_START + gEfiUsbIoProtocolGuid ## TO_START + +[Depex] + gEfiBdsArchProtocolGuid AND + gEfiCpuArchProtocolGuid AND + gEfiMetronomeArchProtocolGuid AND + gEfiMonotonicCounterArchProtocolGuid AND + gEfiRealTimeClockArchProtocolGuid AND + gEfiResetArchProtocolGuid AND + gEfiRuntimeArchProtocolGuid AND + gEfiSecurityArchProtocolGuid AND + gEfiTimerArchProtocolGuid AND + gEfiVariableWriteArchProtocolGuid AND + gEfiVariableArchProtocolGuid AND + gEfiWatchdogTimerArchProtocolGuid diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/ComponentName.c b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/ComponentName.c new file mode 100644 index 0000000000..b6dce7e7cb --- /dev/null +++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/ComponentName.c @@ -0,0 +1,178 @@ +/** @file + UEFI Component Name(2) protocol implementation. + + Copyright (c) 2011, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ax88772.h" + +/** + EFI Component Name Protocol declaration +**/ +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gComponentName = { + GetDriverName, + GetControllerName, + "eng" +}; + +/** + EFI Component Name 2 Protocol declaration +**/ +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) GetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) GetControllerName, + "en" +}; + + +/** + Driver name table declaration +**/ +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE +mDriverNameTable[] = { + {"eng;en", L"AX88772 Ethernet Driver"}, + {NULL, NULL} +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param [in] pThis A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + @param [in] pLanguage A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 3066 or ISO 639-2 language code format. + @param [out] ppDriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +GetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL * pThis, + IN CHAR8 * pLanguage, + OUT CHAR16 ** ppDriverName + ) +{ + EFI_STATUS Status; + + DBG_ENTER ( ); + Status = LookupUnicodeString2 ( + pLanguage, + pThis->SupportedLanguages, + mDriverNameTable, + ppDriverName, + (BOOLEAN)(pThis == &gComponentName) + ); + DBG_EXIT_HEX ( Status ); + return Status; +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param [in] pThis A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + @param [in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + @param [in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + @param [in] pLanguage A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 3066 or ISO 639-2 language code format. + @param [out] ppControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +GetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL * pThis, + IN EFI_HANDLE ControllerHandle, + IN OPTIONAL EFI_HANDLE ChildHandle, + IN CHAR8 * pLanguage, + OUT CHAR16 ** ppControllerName + ) +{ + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Set the controller name + // + *ppControllerName = L"AX88772 10/100 Ethernet"; + Status = EFI_SUCCESS; + + // + // Return the operation status + // + DBG_EXIT_HEX ( Status ); + return Status; +} diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/DriverBinding.c b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/DriverBinding.c new file mode 100644 index 0000000000..5bcde4b211 --- /dev/null +++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/DriverBinding.c @@ -0,0 +1,507 @@ +/** @file + Implement the driver binding protocol for Asix AX88772 Ethernet driver. + + Copyright (c) 2011-2013, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ax88772.h" + +/** + Verify the controller type + + @param [in] pThis Protocol instance pointer. + @param [in] Controller Handle of device to test. + @param [in] pRemainingDevicePath Not used. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +DriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL * pThis, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL * pRemainingDevicePath + ) +{ + EFI_USB_DEVICE_DESCRIPTOR Device; + EFI_USB_IO_PROTOCOL * pUsbIo; + EFI_STATUS Status; + + // + // Connect to the USB stack + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + (VOID **) &pUsbIo, + pThis->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (!EFI_ERROR ( Status )) { + + // + // Get the interface descriptor to check the USB class and find a transport + // protocol handler. + // + Status = pUsbIo->UsbGetDeviceDescriptor ( pUsbIo, &Device ); + if (!EFI_ERROR ( Status )) { + + // + // Validate the adapter + // + if (( VENDOR_ID != Device.IdVendor ) + || ( PRODUCT_ID != Device.IdProduct )) { + Status = EFI_UNSUPPORTED; + } + } + + // + // Done with the USB stack + // + gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + pThis->DriverBindingHandle, + Controller + ); + } + + // + // Return the device supported status + // + return Status; +} + + +/** + Start this driver on Controller by opening UsbIo and DevicePath protocols. + Initialize PXE structures, create a copy of the Controller Device Path with the + NIC's MAC address appended to it, install the NetworkInterfaceIdentifier protocol + on the newly created Device Path. + + @param [in] pThis Protocol instance pointer. + @param [in] Controller Handle of device to work with. + @param [in] pRemainingDevicePath Not used, always produce all possible children. + + @retval EFI_SUCCESS This driver is added to Controller. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +DriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL * pThis, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL * pRemainingDevicePath + ) +{ + EFI_STATUS Status; + NIC_DEVICE * pNicDevice; + UINTN LengthInBytes; + + DBG_ENTER ( ); + + // + // Allocate the device structure + // + LengthInBytes = sizeof ( *pNicDevice ); + Status = gBS->AllocatePool ( + EfiRuntimeServicesData, + LengthInBytes, + (VOID **) &pNicDevice + ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_POOL | DEBUG_INIT, + "0x%08x: Allocate pNicDevice, %d bytes\r\n", + pNicDevice, + sizeof ( *pNicDevice ))); + + // + // Set the structure signature + // + ZeroMem ( pNicDevice, LengthInBytes ); + pNicDevice->Signature = DEV_SIGNATURE; + + // + // Connect to the USB I/O protocol + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + (VOID **) &pNicDevice->pUsbIo, + pThis->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if ( !EFI_ERROR ( Status )) { + // + // Allocate the necessary events + // + Status = gBS->CreateEvent ( EVT_TIMER, + TPL_AX88772, + (EFI_EVENT_NOTIFY)Ax88772Timer, + pNicDevice, + (VOID **)&pNicDevice->Timer ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "0x%08x: Allocated timer\r\n", + pNicDevice->Timer )); + + // + // Initialize the simple network protocol + // + pNicDevice->Controller = Controller; + SN_Setup ( pNicDevice ); + + // + // Start the timer + // + Status = gBS->SetTimer ( pNicDevice->Timer, + TimerPeriodic, + TIMER_MSEC ); + if ( !EFI_ERROR ( Status )) { + // + // Install both the simple network and device path protocols. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiCallerIdGuid, + pNicDevice, + &gEfiSimpleNetworkProtocolGuid, + &pNicDevice->SimpleNetwork, + NULL + ); + + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "Installed: gEfiCallerIdGuid on 0x%08x\r\n", + Controller )); + DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "Installed: gEfiSimpleNetworkProtocolGuid on 0x%08x\r\n", + Controller )); + DBG_EXIT_STATUS ( Status ); + return Status; + } + DEBUG (( DEBUG_ERROR | DEBUG_INIT | DEBUG_INFO, + "ERROR - Failed to install gEfiSimpleNetworkProtocol on 0x%08x\r\n", + Controller )); + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_INIT | DEBUG_INFO, + "ERROR - Failed to start the timer, Status: %r\r\n", + Status )); + } + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_INIT | DEBUG_INFO, + "ERROR - Failed to create timer event, Status: %r\r\n", + Status )); + } + + // + // Done with the USB stack + // + gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + pThis->DriverBindingHandle, + Controller + ); + } + + // + // Done with the device + // + gBS->FreePool ( pNicDevice ); + } + + // + // Display the driver start status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Stop this driver on Controller by removing NetworkInterfaceIdentifier protocol and + closing the DevicePath and PciIo protocols on Controller. + + @param [in] pThis Protocol instance pointer. + @param [in] Controller Handle of device to stop driver on. + @param [in] NumberOfChildren How many children need to be stopped. + @param [in] pChildHandleBuffer Not used. + + @retval EFI_SUCCESS This driver is removed Controller. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +DriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL * pThis, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE * pChildHandleBuffer + ) +{ + NIC_DEVICE * pNicDevice; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Determine if this driver is already attached + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiCallerIdGuid, + (VOID **) &pNicDevice, + pThis->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if ( !EFI_ERROR ( Status )) { + // + // AX88772 driver is no longer running on this device + // + gBS->UninstallMultipleProtocolInterfaces ( + Controller, + &gEfiSimpleNetworkProtocolGuid, + &pNicDevice->SimpleNetwork, + &gEfiCallerIdGuid, + pNicDevice, + NULL ); + DEBUG (( DEBUG_POOL | DEBUG_INIT, + "Removed: gEfiSimpleNetworkProtocolGuid from 0x%08x\r\n", + Controller )); + DEBUG (( DEBUG_POOL | DEBUG_INIT, + "Removed: gEfiCallerIdGuid from 0x%08x\r\n", + Controller )); + + // + // Stop the timer + // + if ( NULL != pNicDevice->Timer ) { + gBS->SetTimer ( pNicDevice->Timer, TimerCancel, 0 ); + gBS->CloseEvent ( pNicDevice->Timer ); + DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "0x%08x: Released timer\r\n", + pNicDevice->Timer )); + } + + // + // Done with the device context + // + DEBUG (( DEBUG_POOL | DEBUG_INIT, + "0x%08x: Free pNicDevice, %d bytes\r\n", + pNicDevice, + sizeof ( *pNicDevice ))); + gBS->FreePool ( pNicDevice ); + } + + // + // Return the shutdown status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Driver binding protocol declaration +**/ +EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = { + DriverSupported, + DriverStart, + DriverStop, + 0xa, + NULL, + NULL +}; + + +/** + Ax88772 driver unload routine. + + @param [in] ImageHandle Handle for the image. + + @retval EFI_SUCCESS Image may be unloaded + +**/ +EFI_STATUS +EFIAPI +DriverUnload ( + IN EFI_HANDLE ImageHandle + ) +{ + UINTN BufferSize; + UINTN Index; + UINTN Max; + EFI_HANDLE * pHandle; + EFI_STATUS Status; + + // + // Determine which devices are using this driver + // + BufferSize = 0; + pHandle = NULL; + Status = gBS->LocateHandle ( + ByProtocol, + &gEfiCallerIdGuid, + NULL, + &BufferSize, + NULL ); + if ( EFI_BUFFER_TOO_SMALL == Status ) { + for ( ; ; ) { + // + // One or more block IO devices are present + // + Status = gBS->AllocatePool ( + EfiRuntimeServicesData, + BufferSize, + (VOID **) &pHandle + ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "Insufficient memory, failed handle buffer allocation\r\n" )); + break; + } + + // + // Locate the block IO devices + // + Status = gBS->LocateHandle ( + ByProtocol, + &gEfiCallerIdGuid, + NULL, + &BufferSize, + pHandle ); + if ( EFI_ERROR ( Status )) { + // + // Error getting handles + // + DEBUG (( DEBUG_ERROR | DEBUG_INIT | DEBUG_INFO, + "Failure getting Telnet handles\r\n" )); + break; + } + + // + // Remove any use of the driver + // + Max = BufferSize / sizeof ( pHandle[ 0 ]); + for ( Index = 0; Max > Index; Index++ ) { + Status = DriverStop ( &gDriverBinding, + pHandle[ Index ], + 0, + NULL ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_WARN | DEBUG_INIT | DEBUG_INFO, + "WARNING - Failed to shutdown the driver on handle %08x\r\n", pHandle[ Index ])); + break; + } + } + break; + } + } + else { + if ( EFI_NOT_FOUND == Status ) { + // + // No devices were found + // + Status = EFI_SUCCESS; + } + } + + // + // Free the handle array + // + if ( NULL != pHandle ) { + gBS->FreePool ( pHandle ); + } + + // + // Remove the protocols installed by the EntryPoint routine. + // + if ( !EFI_ERROR ( Status )) { + gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiDriverBindingProtocolGuid, + &gDriverBinding, + &gEfiComponentNameProtocolGuid, + &gComponentName, + &gEfiComponentName2ProtocolGuid, + &gComponentName2, + NULL + ); + DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "Removed: gEfiComponentName2ProtocolGuid from 0x%08x\r\n", + ImageHandle )); + DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "Removed: gEfiComponentNameProtocolGuid from 0x%08x\r\n", + ImageHandle )); + DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "Removed: gEfiDriverBindingProtocolGuid from 0x%08x\r\n", + ImageHandle )); + } + + // + // Return the unload status + // + return Status; +} + + +/** +Ax88772 driver entry point. + +@param [in] ImageHandle Handle for the image. +@param [in] pSystemTable Address of the system table. + +@retval EFI_SUCCESS Image successfully loaded. + +**/ +EFI_STATUS +EFIAPI +EntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * pSystemTable + ) +{ + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Add the driver to the list of drivers + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + pSystemTable, + &gDriverBinding, + ImageHandle, + &gComponentName, + &gComponentName2 + ); + ASSERT_EFI_ERROR (Status); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "Installed: gEfiDriverBindingProtocolGuid on 0x%08x\r\n", + ImageHandle )); + DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "Installed: gEfiComponentNameProtocolGuid on 0x%08x\r\n", + ImageHandle )); + DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "Installed: gEfiComponentName2ProtocolGuid on 0x%08x\r\n", + ImageHandle )); + } + DBG_EXIT_STATUS ( Status ); + return Status; +} diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/SimpleNetwork.c b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/SimpleNetwork.c new file mode 100644 index 0000000000..0105d04f5d --- /dev/null +++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/SimpleNetwork.c @@ -0,0 +1,1503 @@ +/** @file + Provides the Simple Network functions. + + Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ax88772.h" + +/** + This function updates the filtering on the receiver. + + This support routine calls ::Ax88772MacAddressSet to update + the MAC address. This routine then rebuilds the multicast + hash by calling ::Ax88772MulticastClear and ::Ax88772MulticastSet. + Finally this routine enables the receiver by calling + ::Ax88772RxControl. + + @param [in] pSimpleNetwork Simple network mode pointer + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +ReceiveFilterUpdate ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork + ) +{ + EFI_SIMPLE_NETWORK_MODE * pMode; + NIC_DEVICE * pNicDevice; + EFI_STATUS Status; + UINT32 Index; + + DBG_ENTER ( ); + + // + // Set the MAC address + // + pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork ); + pMode = pSimpleNetwork->Mode; + Status = Ax88772MacAddressSet ( pNicDevice, + &pMode->CurrentAddress.Addr[0]); + if ( !EFI_ERROR ( Status )) { + // + // Clear the multicast hash table + // + Ax88772MulticastClear ( pNicDevice ); + + // + // Load the multicast hash table + // + if ( 0 != ( pMode->ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST )) { + for ( Index = 0; + ( !EFI_ERROR ( Status )) && ( Index < pMode->MCastFilterCount ); + Index++ ) { + // + // Enable the next multicast address + // + Ax88772MulticastSet ( pNicDevice, + &pMode->MCastFilter[ Index ].Addr[0]); + } + } + + // + // Enable the receiver + // + if ( !EFI_ERROR ( Status )) { + Status = Ax88772RxControl ( pNicDevice, pMode->ReceiveFilterSetting ); + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + This function updates the SNP driver status. + + This function gets the current interrupt and recycled transmit + buffer status from the network interface. The interrupt status + and the media status are returned as a bit mask in InterruptStatus. + If InterruptStatus is NULL, the interrupt status will not be read. + Upon successful return of the media status, the MediaPresent field + of EFI_SIMPLE_NETWORK_MODE will be updated to reflect any change + of media status. If TxBuf is not NULL, a recycled transmit buffer + address will be retrived. If a recycled transmit buffer address + is returned in TxBuf, then the buffer has been successfully + transmitted, and the status for that buffer is cleared. + + This function calls ::Ax88772Rx to update the media status and + queue any receive packets. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] pInterruptStatus A pointer to the bit mask of the current active interrupts. + If this is NULL, the interrupt status will not be read from + the device. If this is not NULL, the interrupt status will + be read from teh device. When the interrupt status is read, + it will also be cleared. Clearing the transmit interrupt + does not empty the recycled transmit buffer array. + @param [out] ppTxBuf Recycled transmit buffer address. The network interface will + not transmit if its internal recycled transmit buffer array is + full. Reading the transmit buffer does not clear the transmit + interrupt. If this is NULL, then the transmit buffer status + will not be read. If there are not transmit buffers to recycle + and TxBuf is not NULL, *TxBuf will be set to NULL. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + +**/ +EFI_STATUS +EFIAPI +SN_GetStatus ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + OUT UINT32 * pInterruptStatus, + OUT VOID ** ppTxBuf + ) +{ + BOOLEAN bLinkIdle; + EFI_SIMPLE_NETWORK_MODE * pMode; + NIC_DEVICE * pNicDevice; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Verify the parameters + // + if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) { + // + // Return the transmit buffer + // + pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork ); + if (( NULL != ppTxBuf ) && ( NULL != pNicDevice->pTxBuffer )) { + *ppTxBuf = pNicDevice->pTxBuffer; + pNicDevice->pTxBuffer = NULL; + } + + // + // Determine if interface is running + // + pMode = pSimpleNetwork->Mode; + if ( EfiSimpleNetworkStopped != pMode->State ) { + // + // Synchronize with Ax88772Timer + // + VERIFY_TPL ( TPL_AX88772 ); + TplPrevious = gBS->RaiseTPL ( TPL_AX88772 ); + + // + // Update the link status + // + bLinkIdle = pNicDevice->bLinkIdle; + pNicDevice->bLinkIdle = TRUE; + Ax88772Rx ( pNicDevice, bLinkIdle ); + pMode->MediaPresent = pNicDevice->bLinkUp; + + // + // Release the synchronization with Ax88772Timer + // + gBS->RestoreTPL ( TplPrevious ); + + // + // Return the interrupt status + // + if ( NULL != pInterruptStatus ) { + *pInterruptStatus = 0; + } + Status = EFI_SUCCESS; + } + else { + Status = EFI_NOT_STARTED; + } + } + else { + Status = EFI_INVALID_PARAMETER; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Resets the network adapter and allocates the transmit and receive buffers + required by the network interface; optionally, also requests allocation of + additional transmit and receive buffers. This routine must be called before + any other routine in the Simple Network protocol is called. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] ExtraRxBufferSize Size in bytes to add to the receive buffer allocation + @param [in] ExtraTxBufferSize Size in bytes to add to the transmit buffer allocation + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_OUT_OF_RESOURCES There was not enough memory for the transmit and receive buffers + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Initialize ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN UINTN ExtraRxBufferSize, + IN UINTN ExtraTxBufferSize + ) +{ + EFI_SIMPLE_NETWORK_MODE * pMode; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the parameters + // + if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) { + // + // Determine if the interface is already started + // + pMode = pSimpleNetwork->Mode; + if ( EfiSimpleNetworkStarted == pMode->State ) { + if (( 0 == ExtraRxBufferSize ) && ( 0 == ExtraTxBufferSize )) { + // + // Start the adapter + // + Status = SN_Reset ( pSimpleNetwork, FALSE ); + if ( !EFI_ERROR ( Status )) { + // + // Update the network state + // + pMode->State = EfiSimpleNetworkInitialized; + } + } + else { + Status = EFI_UNSUPPORTED; + } + } + else { + Status = EFI_NOT_STARTED; + } + } + else { + Status = EFI_INVALID_PARAMETER; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + This function converts a multicast IP address to a multicast HW MAC address + for all packet transactions. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] bIPv6 Set to TRUE if the multicast IP address is IPv6 [RFC2460]. + Set to FALSE if the multicast IP address is IPv4 [RFC 791]. + @param [in] pIP The multicast IP address that is to be converted to a + multicast HW MAC address. + @param [in] pMAC The multicast HW MAC address that is to be generated from IP. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_MCastIPtoMAC ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN BOOLEAN bIPv6, + IN EFI_IP_ADDRESS * pIP, + IN EFI_MAC_ADDRESS * pMAC + ) +{ + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // This is not currently supported + // + Status = EFI_UNSUPPORTED; + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + This function performs read and write operations on the NVRAM device + attached to a network interface. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] ReadWrite TRUE for read operations, FALSE for write operations. + @param [in] Offset Byte offset in the NVRAM device at which to start the + read or write operation. This must be a multiple of + NvRamAccessSize and less than NvRamSize. + @param [in] BufferSize The number of bytes to read or write from the NVRAM device. + This must also be a multiple of NvramAccessSize. + @param [in, out] pBuffer A pointer to the data buffer. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_NvData ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN BOOLEAN ReadWrite, + IN UINTN Offset, + IN UINTN BufferSize, + IN OUT VOID * pBuffer + ) +{ + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // This is not currently supported + // + Status = EFI_UNSUPPORTED; + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Attempt to receive a packet from the network adapter. + + This function retrieves one packet from the receive queue of the network + interface. If there are no packets on the receive queue, then EFI_NOT_READY + will be returned. If there is a packet on the receive queue, and the size + of the packet is smaller than BufferSize, then the contents of the packet + will be placed in Buffer, and BufferSize will be udpated with the actual + size of the packet. In addition, if SrcAddr, DestAddr, and Protocol are + not NULL, then these values will be extracted from the media header and + returned. If BufferSize is smaller than the received packet, then the + size of the receive packet will be placed in BufferSize and + EFI_BUFFER_TOO_SMALL will be returned. + + This routine calls ::Ax88772Rx to update the media status and + empty the network adapter of receive packets. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [out] pHeaderSize The size, in bytes, of the media header to be filled in by + the Transmit() function. If HeaderSize is non-zero, then + it must be equal to SimpleNetwork->Mode->MediaHeaderSize + and DestAddr and Protocol parameters must not be NULL. + @param [out] pBufferSize The size, in bytes, of the entire packet (media header and + data) to be transmitted through the network interface. + @param [out] pBuffer A pointer to the packet (media header followed by data) to + to be transmitted. This parameter can not be NULL. If + HeaderSize is zero, then the media header is Buffer must + already be filled in by the caller. If HeaderSize is nonzero, + then the media header will be filled in by the Transmit() + function. + @param [out] pSrcAddr The source HW MAC address. If HeaderSize is zero, then + this parameter is ignored. If HeaderSize is nonzero and + SrcAddr is NULL, then SimpleNetwork->Mode->CurrentAddress + is used for the source HW MAC address. + @param [out] pDestAddr The destination HW MAC address. If HeaderSize is zero, then + this parameter is ignored. + @param [out] pProtocol The type of header to build. If HeaderSize is zero, then + this parameter is ignored. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_NOT_READY No packets have been received on the network interface. + @retval EFI_BUFFER_TOO_SMALL The packet is larger than BufferSize bytes. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + +**/ +EFI_STATUS +EFIAPI +SN_Receive ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + OUT UINTN * pHeaderSize, + OUT UINTN * pBufferSize, + OUT VOID * pBuffer, + OUT EFI_MAC_ADDRESS * pSrcAddr, + OUT EFI_MAC_ADDRESS * pDestAddr, + OUT UINT16 * pProtocol + ) +{ + ETHERNET_HEADER * pHeader; + EFI_SIMPLE_NETWORK_MODE * pMode; + NIC_DEVICE * pNicDevice; + RX_TX_PACKET * pRxPacket; + EFI_STATUS Status; + EFI_TPL TplPrevious; + UINT16 Type; + + DBG_ENTER ( ); + + // + // Verify the parameters + // + if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) { + // + // The interface must be running + // + pMode = pSimpleNetwork->Mode; + if ( EfiSimpleNetworkInitialized == pMode->State ) { + // + // Synchronize with Ax88772Timer + // + VERIFY_TPL ( TPL_AX88772 ); + TplPrevious = gBS->RaiseTPL ( TPL_AX88772 ); + + // + // Update the link status + // + pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork ); + Ax88772Rx ( pNicDevice, FALSE ); + pMode->MediaPresent = pNicDevice->bLinkUp; + if ( pMode->MediaPresent ) { + // + // Attempt to receive a packet + // + pRxPacket = pNicDevice->pRxHead; + if ( NULL != pRxPacket ) { + pNicDevice->pRxHead = pRxPacket->pNext; + if ( NULL == pNicDevice->pRxHead ) { + pNicDevice->pRxTail = NULL; + } + + // + // Copy the received packet into the receive buffer + // + *pBufferSize = pRxPacket->Length; + CopyMem ( pBuffer, &pRxPacket->Data[0], pRxPacket->Length ); + pHeader = (ETHERNET_HEADER *) &pRxPacket->Data[0]; + if ( NULL != pHeaderSize ) { + *pHeaderSize = sizeof ( *pHeader ); + } + if ( NULL != pDestAddr ) { + CopyMem ( pDestAddr, &pHeader->dest_addr, PXE_HWADDR_LEN_ETHER ); + } + if ( NULL != pSrcAddr ) { + CopyMem ( pSrcAddr, &pHeader->src_addr, PXE_HWADDR_LEN_ETHER ); + } + if ( NULL != pProtocol ) { + Type = pHeader->type; + Type = (UINT16)(( Type >> 8 ) | ( Type << 8 )); + *pProtocol = Type; + } + Status = EFI_SUCCESS; + } + else { + // + // No receive packets available + // + Status = EFI_NOT_READY; + } + } + else { + // + // Link no up + // + Status = EFI_NOT_READY; + } + + // + // Release the synchronization with Ax88772Timer + // + gBS->RestoreTPL ( TplPrevious ); + } + else { + Status = EFI_NOT_STARTED; + } + } + else { + Status = EFI_INVALID_PARAMETER; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + This function is used to enable and disable the hardware and software receive + filters for the underlying network device. + + The receive filter change is broken down into three steps: + + 1. The filter mask bits that are set (ON) in the Enable parameter + are added to the current receive filter settings. + + 2. The filter mask bits that are set (ON) in the Disable parameter + are subtracted from the updated receive filter settins. + + 3. If the resulting filter settigns is not supported by the hardware + a more liberal setting is selected. + + If the same bits are set in the Enable and Disable parameters, then the bits + in the Disable parameter takes precedence. + + If the ResetMCastFilter parameter is TRUE, then the multicast address list + filter is disabled (irregardless of what other multicast bits are set in + the enable and Disable parameters). The SNP->Mode->MCastFilterCount field + is set to zero. The SNP->Mode->MCastFilter contents are undefined. + + After enableing or disabling receive filter settings, software should + verify the new settings by checking the SNP->Mode->ReceeiveFilterSettings, + SNP->Mode->MCastFilterCount and SNP->Mode->MCastFilter fields. + + Note: Some network drivers and/or devices will automatically promote + receive filter settings if the requested setting can not be honored. + For example, if a request for four multicast addresses is made and + the underlying hardware only supports two multicast addresses the + driver might set the promiscuous or promiscuous multicast receive filters + instead. The receiving software is responsible for discarding any extra + packets that get through the hardware receive filters. + + If ResetMCastFilter is TRUE, then the multicast receive filter list + on the network interface will be reset to the default multicast receive + filter list. If ResetMCastFilter is FALSE, and this network interface + allows the multicast receive filter list to be modified, then the + MCastFilterCnt and MCastFilter are used to update the current multicast + receive filter list. The modified receive filter list settings can be + found in the MCastFilter field of EFI_SIMPLE_NETWORK_MODE. + + This routine calls ::ReceiveFilterUpdate to update the receive + state in the network adapter. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] Enable A bit mask of receive filters to enable on the network interface. + @param [in] Disable A bit mask of receive filters to disable on the network interface. + For backward compatibility with EFI 1.1 platforms, the + EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST bit must be set + when the ResetMCastFilter parameter is TRUE. + @param [in] bResetMCastFilter Set to TRUE to reset the contents of the multicast receive + filters on the network interface to their default values. + @param [in] MCastFilterCnt Number of multicast HW MAC address in the new MCastFilter list. + This value must be less than or equal to the MaxMCastFilterCnt + field of EFI_SIMPLE_NETWORK_MODE. This field is optional if + ResetMCastFilter is TRUE. + @param [in] pMCastFilter A pointer to a list of new multicast receive filter HW MAC + addresses. This list will replace any existing multicast + HW MAC address list. This field is optional if ResetMCastFilter + is TRUE. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_ReceiveFilters ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN UINT32 Enable, + IN UINT32 Disable, + IN BOOLEAN bResetMCastFilter, + IN UINTN MCastFilterCnt, + IN EFI_MAC_ADDRESS * pMCastFilter + ) +{ + EFI_SIMPLE_NETWORK_MODE * pMode; + EFI_MAC_ADDRESS * pMulticastAddress; + EFI_MAC_ADDRESS * pTableEnd; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the parameters + // + Status = EFI_INVALID_PARAMETER; + if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) { + pMode = pSimpleNetwork->Mode; + + // + // Update the multicast list if necessary + // + if ( !bResetMCastFilter ) { + if ( 0 != MCastFilterCnt ) { + if (( MAX_MCAST_FILTER_CNT >= MCastFilterCnt ) + && ( NULL != pMCastFilter )) { + // + // Verify the multicast addresses + // + pMulticastAddress = pMCastFilter; + pTableEnd = pMulticastAddress + MCastFilterCnt; + while ( pTableEnd > pMulticastAddress ) { + // + // The first digit of the multicast address must have the LSB set + // + if ( 0 == ( pMulticastAddress->Addr[0] & 1 )) { + // + // Invalid multicast address + // + break; + } + pMulticastAddress += 1; + } + if ( pTableEnd == pMulticastAddress ) { + // + // Update the multicast filter list. + // + CopyMem (&pMode->MCastFilter[0], + pMCastFilter, + MCastFilterCnt * sizeof ( *pMCastFilter )); + Status = EFI_SUCCESS; + } + } + } + else { + Status = EFI_SUCCESS; + } + } + else { + // + // No multicast address list is specified + // + MCastFilterCnt = 0; + Status = EFI_SUCCESS; + } + if ( !EFI_ERROR ( Status )) { + // + // The parameters are valid! + // + pMode->ReceiveFilterSetting |= Enable; + pMode->ReceiveFilterSetting &= ~Disable; + pMode->MCastFilterCount = (UINT32)MCastFilterCnt; + + // + // Update the receive filters in the adapter + // + Status = ReceiveFilterUpdate ( pSimpleNetwork ); + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Reset the network adapter. + + Resets a network adapter and reinitializes it with the parameters that + were provided in the previous call to Initialize (). The transmit and + receive queues are cleared. Receive filters, the station address, the + statistics, and the multicast-IP-to-HW MAC addresses are not reset by + this call. + + This routine calls ::Ax88772Reset to perform the adapter specific + reset operation. This routine also starts the link negotiation + by calling ::Ax88772NegotiateLinkStart. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] bExtendedVerification Indicates that the driver may perform a more + exhaustive verification operation of the device + during reset. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Reset ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN BOOLEAN bExtendedVerification + ) +{ + EFI_SIMPLE_NETWORK_MODE * pMode; + NIC_DEVICE * pNicDevice; + RX_TX_PACKET * pRxPacket; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Verify the parameters + // + if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) { + // + // Synchronize with Ax88772Timer + // + VERIFY_TPL ( TPL_AX88772 ); + TplPrevious = gBS->RaiseTPL ( TPL_AX88772 ); + + // + // Update the device state + // + pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork ); + pNicDevice->bComplete = FALSE; + pNicDevice->bLinkUp = FALSE; + + pMode = pSimpleNetwork->Mode; + pMode->MediaPresent = FALSE; + + // + // Discard any received packets + // + while ( NULL != pNicDevice->pRxHead ) { + // + // Remove the packet from the received packet list + // + pRxPacket = pNicDevice->pRxHead; + pNicDevice->pRxHead = pRxPacket->pNext; + + // + // Queue the packet to the free list + // + pRxPacket->pNext = pNicDevice->pRxFree; + pNicDevice->pRxFree = pRxPacket; + } + pNicDevice->pRxTail = NULL; + + // + // Reset the device + // + Status = Ax88772Reset ( pNicDevice ); + if ( !EFI_ERROR ( Status )) { + // + // Update the receive filters in the adapter + // + Status = ReceiveFilterUpdate ( pSimpleNetwork ); + + // + // Try to get a connection to the network + // + if ( !EFI_ERROR ( Status )) { + // + // Start the autonegotiation + // + Status = Ax88772NegotiateLinkStart ( pNicDevice ); + } + } + + // + // Release the synchronization with Ax88772Timer + // + gBS->RestoreTPL ( TplPrevious ); + } + else { + Status = EFI_INVALID_PARAMETER; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Initialize the simple network protocol. + + This routine calls ::Ax88772MacAddressGet to obtain the + MAC address. + + @param [in] pNicDevice NIC_DEVICE_INSTANCE pointer + + @retval EFI_SUCCESS Setup was successful + +**/ +EFI_STATUS +SN_Setup ( + IN NIC_DEVICE * pNicDevice + ) +{ + EFI_SIMPLE_NETWORK_MODE * pMode; + EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Initialize the simple network protocol + // + pSimpleNetwork = &pNicDevice->SimpleNetwork; + pSimpleNetwork->Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION; + pSimpleNetwork->Start = (EFI_SIMPLE_NETWORK_START)SN_Start; + pSimpleNetwork->Stop = (EFI_SIMPLE_NETWORK_STOP)SN_Stop; + pSimpleNetwork->Initialize = (EFI_SIMPLE_NETWORK_INITIALIZE)SN_Initialize; + pSimpleNetwork->Reset = (EFI_SIMPLE_NETWORK_RESET)SN_Reset; + pSimpleNetwork->Shutdown = (EFI_SIMPLE_NETWORK_SHUTDOWN)SN_Shutdown; + pSimpleNetwork->ReceiveFilters = (EFI_SIMPLE_NETWORK_RECEIVE_FILTERS)SN_ReceiveFilters; + pSimpleNetwork->StationAddress = (EFI_SIMPLE_NETWORK_STATION_ADDRESS)SN_StationAddress; + pSimpleNetwork->Statistics = (EFI_SIMPLE_NETWORK_STATISTICS)SN_Statistics; + pSimpleNetwork->MCastIpToMac = (EFI_SIMPLE_NETWORK_MCAST_IP_TO_MAC)SN_MCastIPtoMAC; + pSimpleNetwork->NvData = (EFI_SIMPLE_NETWORK_NVDATA)SN_NvData; + pSimpleNetwork->GetStatus = (EFI_SIMPLE_NETWORK_GET_STATUS)SN_GetStatus; + pSimpleNetwork->Transmit = (EFI_SIMPLE_NETWORK_TRANSMIT)SN_Transmit; + pSimpleNetwork->Receive = (EFI_SIMPLE_NETWORK_RECEIVE)SN_Receive; + pSimpleNetwork->WaitForPacket = NULL; + pMode = &pNicDevice->SimpleNetworkData; + pSimpleNetwork->Mode = pMode; + + pMode->State = EfiSimpleNetworkStopped; + pMode->HwAddressSize = PXE_HWADDR_LEN_ETHER; + pMode->MediaHeaderSize = sizeof ( ETHERNET_HEADER ); + pMode->MaxPacketSize = MAX_ETHERNET_PKT_SIZE; + pMode->NvRamSize = 0; + pMode->NvRamAccessSize = 0; + pMode->ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST + | EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST + | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST + | EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS + | EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST; + pMode->ReceiveFilterSetting = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST + | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST; + pMode->MaxMCastFilterCount = MAX_MCAST_FILTER_CNT; + pMode->MCastFilterCount = 0; + SetMem ( &pMode->BroadcastAddress, + PXE_HWADDR_LEN_ETHER, + 0xff ); + pMode->IfType = EfiNetworkInterfaceUndi; + pMode->MacAddressChangeable = TRUE; + pMode->MultipleTxSupported = TRUE; + pMode->MediaPresentSupported = TRUE; + pMode->MediaPresent = FALSE; + + // + // Read the MAC address + // + pNicDevice->PhyId = PHY_ID_INTERNAL; + pNicDevice->b100Mbps = TRUE; + pNicDevice->bFullDuplex = TRUE; + + Status = gBS->AllocatePool ( EfiRuntimeServicesData, + MAX_BULKIN_SIZE, + (VOID **) &pNicDevice->pBulkInBuff); + if ( EFI_ERROR(Status)) { + DEBUG (( EFI_D_ERROR, "Memory are not enough\n")); + return Status; + } + + Status = Ax88772MacAddressGet ( + pNicDevice, + &pMode->PermanentAddress.Addr[0]); + if ( !EFI_ERROR ( Status )) { + // + // Display the MAC address + // + DEBUG (( DEBUG_MAC_ADDRESS | DEBUG_INFO, + "MAC: %02x-%02x-%02x-%02x-%02x-%02x\n", + pMode->PermanentAddress.Addr[0], + pMode->PermanentAddress.Addr[1], + pMode->PermanentAddress.Addr[2], + pMode->PermanentAddress.Addr[3], + pMode->PermanentAddress.Addr[4], + pMode->PermanentAddress.Addr[5])); + + // + // Use the hardware address as the current address + // + CopyMem ( &pMode->CurrentAddress, + &pMode->PermanentAddress, + PXE_HWADDR_LEN_ETHER ); + } + + // + // Return the setup status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + This routine starts the network interface. + + @param [in] pSimpleNetwork Protocol instance pointer + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_ALREADY_STARTED The network interface was already started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Start ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork + ) +{ + NIC_DEVICE * pNicDevice; + EFI_SIMPLE_NETWORK_MODE * pMode; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the parameters + // + Status = EFI_INVALID_PARAMETER; + if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) { + pMode = pSimpleNetwork->Mode; + if ( EfiSimpleNetworkStopped == pMode->State ) { + // + // Initialize the mode structure + // NVRAM access is not supported + // + ZeroMem ( pMode, sizeof ( *pMode )); + + pMode->State = EfiSimpleNetworkStarted; + pMode->HwAddressSize = PXE_HWADDR_LEN_ETHER; + pMode->MediaHeaderSize = sizeof ( ETHERNET_HEADER ); + pMode->MaxPacketSize = MAX_ETHERNET_PKT_SIZE; + pMode->ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST + | EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST + | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST + | EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS + | EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST; + pMode->ReceiveFilterSetting = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST; + pMode->MaxMCastFilterCount = MAX_MCAST_FILTER_CNT; + pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork ); + Status = Ax88772MacAddressGet ( pNicDevice, &pMode->PermanentAddress.Addr[0]); + CopyMem ( &pMode->CurrentAddress, + &pMode->PermanentAddress, + sizeof ( pMode->CurrentAddress )); + pMode->BroadcastAddress.Addr[0] = 0xff; + pMode->BroadcastAddress.Addr[1] = 0xff; + pMode->BroadcastAddress.Addr[2] = 0xff; + pMode->BroadcastAddress.Addr[3] = 0xff; + pMode->BroadcastAddress.Addr[4] = 0xff; + pMode->BroadcastAddress.Addr[5] = 0xff; + pMode->IfType = 1; + pMode->MacAddressChangeable = TRUE; + pMode->MultipleTxSupported = TRUE; + pMode->MediaPresentSupported = TRUE; + pMode->MediaPresent = FALSE; + } + else { + Status = EFI_ALREADY_STARTED; + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Set the MAC address. + + This function modifies or resets the current station address of a + network interface. If Reset is TRUE, then the current station address + is set ot the network interface's permanent address. If Reset if FALSE + then the current station address is changed to the address specified by + pNew. + + This routine calls ::Ax88772MacAddressSet to update the MAC address + in the network adapter. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] bReset Flag used to reset the station address to the + network interface's permanent address. + @param [in] pNew New station address to be used for the network + interface. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_StationAddress ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN BOOLEAN bReset, + IN EFI_MAC_ADDRESS * pNew + ) +{ + NIC_DEVICE * pNicDevice; + EFI_SIMPLE_NETWORK_MODE * pMode; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the parameters + // + if (( NULL != pSimpleNetwork ) + && ( NULL != pSimpleNetwork->Mode ) + && (( !bReset ) || ( bReset && ( NULL != pNew )))) { + // + // Verify that the adapter is already started + // + pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork ); + pMode = pSimpleNetwork->Mode; + if ( EfiSimpleNetworkStarted == pMode->State ) { + // + // Determine the adapter MAC address + // + if ( bReset ) { + // + // Use the permanent address + // + CopyMem ( &pMode->CurrentAddress, + &pMode->PermanentAddress, + sizeof ( pMode->CurrentAddress )); + } + else { + // + // Use the specified address + // + CopyMem ( &pMode->CurrentAddress, + pNew, + sizeof ( pMode->CurrentAddress )); + } + + // + // Update the address on the adapter + // + Status = Ax88772MacAddressSet ( pNicDevice, &pMode->CurrentAddress.Addr[0]); + } + else { + Status = EFI_NOT_STARTED; + } + } + else { + Status = EFI_INVALID_PARAMETER; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + This function resets or collects the statistics on a network interface. + If the size of the statistics table specified by StatisticsSize is not + big enough for all of the statistics that are collected by the network + interface, then a partial buffer of statistics is returned in + StatisticsTable. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] bReset Set to TRUE to reset the statistics for the network interface. + @param [in, out] pStatisticsSize On input the size, in bytes, of StatisticsTable. On output + the size, in bytes, of the resulting table of statistics. + @param [out] pStatisticsTable A pointer to the EFI_NETWORK_STATISTICS structure that + conains the statistics. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_BUFFER_TOO_SMALL The pStatisticsTable is NULL or the buffer is too small. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Statistics ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN BOOLEAN bReset, + IN OUT UINTN * pStatisticsSize, + OUT EFI_NETWORK_STATISTICS * pStatisticsTable + ) +{ + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // This is not currently supported + // + Status = EFI_UNSUPPORTED; + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + This function stops a network interface. This call is only valid + if the network interface is in the started state. + + @param [in] pSimpleNetwork Protocol instance pointer + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Stop ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork + ) +{ + EFI_SIMPLE_NETWORK_MODE * pMode; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the parameters + // + if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) { + // + // Determine if the interface is started + // + pMode = pSimpleNetwork->Mode; + if ( EfiSimpleNetworkStopped != pMode->State ) { + if ( EfiSimpleNetworkStarted == pMode->State ) { + // + // Release the resources acquired in SN_Start + // + + // + // Mark the adapter as stopped + // + pMode->State = EfiSimpleNetworkStopped; + Status = EFI_SUCCESS; + } + else { + Status = EFI_UNSUPPORTED; + } + } + else { + Status = EFI_NOT_STARTED; + } + } + else { + Status = EFI_INVALID_PARAMETER; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + This function releases the memory buffers assigned in the Initialize() call. + Pending transmits and receives are lost, and interrupts are cleared and disabled. + After this call, only Initialize() and Stop() calls may be used. + + @param [in] pSimpleNetwork Protocol instance pointer + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Shutdown ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork + ) +{ + EFI_SIMPLE_NETWORK_MODE * pMode; + UINT32 RxFilter; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the parameters + // + if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) { + // + // Determine if the interface is already started + // + pMode = pSimpleNetwork->Mode; + if ( EfiSimpleNetworkInitialized == pMode->State ) { + // + // Stop the adapter + // + RxFilter = pMode->ReceiveFilterSetting; + pMode->ReceiveFilterSetting = 0; + Status = SN_Reset ( pSimpleNetwork, FALSE ); + pMode->ReceiveFilterSetting = RxFilter; + if ( !EFI_ERROR ( Status )) { + // + // Release the resources acquired by SN_Initialize + // + + // + // Update the network state + // + pMode->State = EfiSimpleNetworkStarted; + } + } + else { + Status = EFI_NOT_STARTED; + } + } + else { + Status = EFI_INVALID_PARAMETER; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Send a packet over the network. + + This function places the packet specified by Header and Buffer on + the transmit queue. This function performs a non-blocking transmit + operation. When the transmit is complete, the buffer is returned + via the GetStatus() call. + + This routine calls ::Ax88772Rx to empty the network adapter of + receive packets. The routine then passes the transmit packet + to the network adapter. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] HeaderSize The size, in bytes, of the media header to be filled in by + the Transmit() function. If HeaderSize is non-zero, then + it must be equal to SimpleNetwork->Mode->MediaHeaderSize + and DestAddr and Protocol parameters must not be NULL. + @param [in] BufferSize The size, in bytes, of the entire packet (media header and + data) to be transmitted through the network interface. + @param [in] pBuffer A pointer to the packet (media header followed by data) to + to be transmitted. This parameter can not be NULL. If + HeaderSize is zero, then the media header is Buffer must + already be filled in by the caller. If HeaderSize is nonzero, + then the media header will be filled in by the Transmit() + function. + @param [in] pSrcAddr The source HW MAC address. If HeaderSize is zero, then + this parameter is ignored. If HeaderSize is nonzero and + SrcAddr is NULL, then SimpleNetwork->Mode->CurrentAddress + is used for the source HW MAC address. + @param [in] pDestAddr The destination HW MAC address. If HeaderSize is zero, then + this parameter is ignored. + @param [in] pProtocol The type of header to build. If HeaderSize is zero, then + this parameter is ignored. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_NOT_READY The network interface is too busy to accept this transmit request. + @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + +**/ +EFI_STATUS +EFIAPI +SN_Transmit ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN UINTN HeaderSize, + IN UINTN BufferSize, + IN VOID * pBuffer, + IN EFI_MAC_ADDRESS * pSrcAddr, + IN EFI_MAC_ADDRESS * pDestAddr, + IN UINT16 * pProtocol + ) +{ + RX_TX_PACKET Packet; + ETHERNET_HEADER * pHeader; + EFI_SIMPLE_NETWORK_MODE * pMode; + NIC_DEVICE * pNicDevice; + EFI_USB_IO_PROTOCOL * pUsbIo; + EFI_STATUS Status; + EFI_TPL TplPrevious; + UINTN TransferLength; + UINT32 TransferStatus; + UINT16 Type; + + DBG_ENTER ( ); + + // + // Verify the parameters + // + if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) { + // + // The interface must be running + // + pMode = pSimpleNetwork->Mode; + if ( EfiSimpleNetworkInitialized == pMode->State ) { + // + // Synchronize with Ax88772Timer + // + VERIFY_TPL ( TPL_AX88772 ); + TplPrevious = gBS->RaiseTPL ( TPL_AX88772 ); + + // + // Update the link status + // + pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork ); + + // + //No need to call receive to receive packet + // + //Ax88772Rx ( pNicDevice, FALSE ); + pMode->MediaPresent = pNicDevice->bLinkUp; + + // + // Release the synchronization with Ax88772Timer + // + gBS->RestoreTPL ( TplPrevious ); + if ( pMode->MediaPresent ) { + // + // Copy the packet into the USB buffer + // + CopyMem ( &Packet.Data[0], pBuffer, BufferSize ); + Packet.Length = (UINT16) BufferSize; + + // + // Transmit the packet + // + pHeader = (ETHERNET_HEADER *) &Packet.Data[0]; + if ( 0 != HeaderSize ) { + if ( NULL != pDestAddr ) { + CopyMem ( &pHeader->dest_addr, pDestAddr, PXE_HWADDR_LEN_ETHER ); + } + if ( NULL != pSrcAddr ) { + CopyMem ( &pHeader->src_addr, pSrcAddr, PXE_HWADDR_LEN_ETHER ); + } + else { + CopyMem ( &pHeader->src_addr, &pMode->CurrentAddress.Addr[0], PXE_HWADDR_LEN_ETHER ); + } + if ( NULL != pProtocol ) { + Type = *pProtocol; + } + else { + Type = Packet.Length; + } + Type = (UINT16)(( Type >> 8 ) | ( Type << 8 )); + pHeader->type = Type; + } + if ( Packet.Length < MIN_ETHERNET_PKT_SIZE ) { + Packet.Length = MIN_ETHERNET_PKT_SIZE; + ZeroMem ( &Packet.Data[ BufferSize ], + Packet.Length - BufferSize ); + } + DEBUG (( DEBUG_TX | DEBUG_INFO, + "TX: %02x-%02x-%02x-%02x-%02x-%02x %02x-%02x-%02x-%02x-%02x-%02x %02x-%02x %d bytes\r\n", + Packet.Data[0], + Packet.Data[1], + Packet.Data[2], + Packet.Data[3], + Packet.Data[4], + Packet.Data[5], + Packet.Data[6], + Packet.Data[7], + Packet.Data[8], + Packet.Data[9], + Packet.Data[10], + Packet.Data[11], + Packet.Data[12], + Packet.Data[13], + Packet.Length )); + Packet.LengthBar = ~Packet.Length; + TransferLength = sizeof ( Packet.Length ) + + sizeof ( Packet.LengthBar ) + + Packet.Length; + + // + // Work around USB bus driver bug where a timeout set by receive + // succeeds but the timeout expires immediately after, causing the + // transmit operation to timeout. + // + pUsbIo = pNicDevice->pUsbIo; + Status = pUsbIo->UsbBulkTransfer ( pUsbIo, + BULK_OUT_ENDPOINT, + &Packet.Length, + &TransferLength, + 0xfffffffe, + &TransferStatus ); + if ( !EFI_ERROR ( Status )) { + Status = TransferStatus; + } + if (( !EFI_ERROR ( Status )) + && ( TransferLength != (UINTN)( Packet.Length + 4 ))) { + Status = EFI_WARN_WRITE_FAILURE; + } + if ( EFI_SUCCESS == Status ) { + pNicDevice->pTxBuffer = pBuffer; + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_INFO, + "Ax88772 USB transmit error, TransferLength: %d, Status: %r\r\n", + sizeof ( Packet.Length ) + Packet.Length, + Status )); + // + // Reset the controller to fix the error + // + if ( EFI_DEVICE_ERROR == Status ) { + SN_Reset ( pSimpleNetwork, FALSE ); + } + } + } + else { + // + // No packets available. + // + Status = EFI_NOT_READY; + } + } + else { + Status = EFI_NOT_STARTED; + } + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_INFO, + "Ax88772 invalid transmit parameter\r\n" + " 0x%08x: HeaderSize\r\n" + " 0x%08x: BufferSize\r\n" + " 0x%08x: Buffer\r\n" + " 0x%08x: SrcAddr\r\n" + " 0x%08x: DestAddr\r\n" + " 0x%04x: Protocol\r\n", + HeaderSize, + BufferSize, + pBuffer, + pSrcAddr, + pDestAddr, + pProtocol )); + Status = EFI_INVALID_PARAMETER; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772.c b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772.c new file mode 100644 index 0000000000..12684a6bd1 --- /dev/null +++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772.c @@ -0,0 +1,875 @@ +/** @file + Implement the interface to the AX88772 Ethernet controller. + + This module implements the interface to the ASIX AX88772 + USB to Ethernet MAC with integrated 10/100 PHY. Note that this implementation + only supports the integrated PHY since no other test cases were available. + + Copyright (c) 2011, Intel Corporation. All rights reserved. + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ax88772.h" + + +/** + Compute the CRC + + @param [in] pMacAddress Address of a six byte buffer to containing the MAC address. + + @returns The CRC-32 value associated with this MAC address + +**/ +UINT32 +Ax88772Crc ( + IN UINT8 * pMacAddress + ) +{ + UINT32 BitNumber; + INT32 Carry; + INT32 Crc; + UINT32 Data; + UINT8 * pEnd; + + // + // Walk the MAC address + // + Crc = -1; + pEnd = &pMacAddress[ PXE_HWADDR_LEN_ETHER ]; + while ( pEnd > pMacAddress ) { + Data = *pMacAddress++; + // + // CRC32: x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1 + // + // 1 0000 0100 1100 0001 0001 1101 1011 0111 + // + for ( BitNumber = 0; 8 > BitNumber; BitNumber++ ) { + Carry = (( Crc >> 31 ) & 1 ) ^ ( Data & 1 ); + Crc <<= 1; + if ( 0 != Carry ) { + Crc ^= 0x04c11db7; + } + Data >>= 1; + } + } + // + // Return the CRC value + // + return (UINT32) Crc; +} + + +/** + Get the MAC address + + This routine calls ::Ax88772UsbCommand to request the MAC + address from the network adapter. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [out] pMacAddress Address of a six byte buffer to receive the MAC address. + + @retval EFI_SUCCESS The MAC address is available. + @retval other The MAC address is not valid. + +**/ +EFI_STATUS +Ax88772MacAddressGet ( + IN NIC_DEVICE * pNicDevice, + OUT UINT8 * pMacAddress + ) +{ + USB_DEVICE_REQUEST SetupMsg; + EFI_STATUS Status; + + // + // Set the register address. + // + SetupMsg.RequestType = USB_ENDPOINT_DIR_IN + | USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_MAC_ADDRESS_READ; + SetupMsg.Value = 0; + SetupMsg.Index = 0; + SetupMsg.Length = PXE_HWADDR_LEN_ETHER; + + // + // Read the PHY register + // + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + pMacAddress ); + return Status; +} + + +/** + Set the MAC address + + This routine calls ::Ax88772UsbCommand to set the MAC address + in the network adapter. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] pMacAddress Address of a six byte buffer to containing the new MAC address. + + @retval EFI_SUCCESS The MAC address was set. + @retval other The MAC address was not set. + +**/ +EFI_STATUS +Ax88772MacAddressSet ( + IN NIC_DEVICE * pNicDevice, + IN UINT8 * pMacAddress + ) +{ + USB_DEVICE_REQUEST SetupMsg; + EFI_STATUS Status; + + // + // Set the register address. + // + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_MAC_ADDRESS_WRITE; + SetupMsg.Value = 0; + SetupMsg.Index = 0; + SetupMsg.Length = PXE_HWADDR_LEN_ETHER; + + // + // Read the PHY register + // + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + pMacAddress ); + return Status; +} + +/** + Clear the multicast hash table + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + +**/ +VOID +Ax88772MulticastClear ( + IN NIC_DEVICE * pNicDevice + ) +{ + int i = 0; + // + // Clear the multicast hash table + // + for ( i = 0 ; i < 8 ; i ++ ) + pNicDevice->MulticastHash[0] = 0; +} + +/** + Enable a multicast address in the multicast hash table + + This routine calls ::Ax88772Crc to compute the hash bit for + this MAC address. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] pMacAddress Address of a six byte buffer to containing the MAC address. + +**/ +VOID +Ax88772MulticastSet ( + IN NIC_DEVICE * pNicDevice, + IN UINT8 * pMacAddress + ) +{ + UINT32 Crc; + + // + // Compute the CRC on the destination address + // + Crc = Ax88772Crc ( pMacAddress ) >> 26; + + // + // Set the bit corresponding to the destination address + // + pNicDevice->MulticastHash [ Crc >> 3 ] |= ( 1<< (Crc& 7)); +} + +/** + Start the link negotiation + + This routine calls ::Ax88772PhyWrite to start the PHY's link + negotiation. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + + @retval EFI_SUCCESS The link negotiation was started. + @retval other Failed to start the link negotiation. + +**/ +EFI_STATUS +Ax88772NegotiateLinkStart ( + IN NIC_DEVICE * pNicDevice + ) +{ + UINT16 Control; + EFI_STATUS Status; + int i; + // + // Set the supported capabilities. + // + Status = Ax88772PhyWrite ( pNicDevice, + PHY_ANAR, + AN_CSMA_CD + | AN_TX_FDX | AN_TX_HDX + | AN_10_FDX | AN_10_HDX ); + if ( !EFI_ERROR ( Status )) { + // + // Set the link speed and duplex + // + Control = BMCR_AUTONEGOTIATION_ENABLE + | BMCR_RESTART_AUTONEGOTIATION; + if ( pNicDevice->b100Mbps ) { + Control |= BMCR_100MBPS; + } + if ( pNicDevice->bFullDuplex ) { + Control |= BMCR_FULL_DUPLEX; + } + Status = Ax88772PhyWrite ( pNicDevice, PHY_BMCR, Control ); + } + + if (!EFI_ERROR(Status)) { + i = 0; + do { + + if (pNicDevice->bComplete && pNicDevice->bLinkUp) { + pNicDevice->SimpleNetwork.Mode->MediaPresent + = pNicDevice->bLinkUp & pNicDevice->bComplete; + break; + } + else { + gBS->Stall(AUTONEG_DELAY); + Status = Ax88772NegotiateLinkComplete ( pNicDevice, + &pNicDevice->PollCount, + &pNicDevice->bComplete, + &pNicDevice->bLinkUp, + &pNicDevice->b100Mbps, + &pNicDevice->bFullDuplex ); + i++; + } + }while(!pNicDevice->bLinkUp && i < AUTONEG_POLLCNT); + } + return Status; +} + + +/** + Complete the negotiation of the PHY link + + This routine calls ::Ax88772PhyRead to determine if the + link negotiation is complete. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in, out] pPollCount Address of number of times this routine was polled + @param [out] pbComplete Address of boolean to receive complate status. + @param [out] pbLinkUp Address of boolean to receive link status, TRUE=up. + @param [out] pbHiSpeed Address of boolean to receive link speed, TRUE=100Mbps. + @param [out] pbFullDuplex Address of boolean to receive link duplex, TRUE=full. + + @retval EFI_SUCCESS The MAC address is available. + @retval other The MAC address is not valid. + +**/ +EFI_STATUS +Ax88772NegotiateLinkComplete ( + IN NIC_DEVICE * pNicDevice, + IN OUT UINTN * pPollCount, + OUT BOOLEAN * pbComplete, + OUT BOOLEAN * pbLinkUp, + OUT BOOLEAN * pbHiSpeed, + OUT BOOLEAN * pbFullDuplex + ) +{ + UINT16 Mask; + UINT16 PhyData; + EFI_STATUS Status; + + // + // Determine if the link is up. + // + *pbComplete = FALSE; + + // + // Get the link status + // + Status = Ax88772PhyRead ( pNicDevice, + PHY_BMSR, + &PhyData ); + + if ( !EFI_ERROR ( Status )) { + *pbLinkUp = (BOOLEAN)( 0 != ( PhyData & BMSR_LINKST )); + if ( 0 == *pbLinkUp ) { + DEBUG (( EFI_D_INFO, "Link Down\n" )); + } + else { + *pbComplete = (BOOLEAN)( 0 != ( PhyData & 0x20 )); + if ( 0 == *pbComplete ) { + DEBUG (( EFI_D_INFO, "Autoneg is not yet Complete\n" )); + } + else { + Status = Ax88772PhyRead ( pNicDevice, + PHY_ANLPAR, + &PhyData ); + if ( !EFI_ERROR ( Status )) { + // + // Autonegotiation is complete + // Determine the link speed. + // + *pbHiSpeed = (BOOLEAN)( 0 != ( PhyData & ( AN_TX_FDX | AN_TX_HDX ))); + + // + // Determine the link duplex. + // + Mask = ( *pbHiSpeed ) ? AN_TX_FDX : AN_10_FDX; + *pbFullDuplex = (BOOLEAN)( 0 != ( PhyData & Mask )); + } + } + } + } + else { + DEBUG (( EFI_D_ERROR, "Failed to read BMCR\n" )); + } + return Status; +} + + +/** + Read a register from the PHY + + This routine calls ::Ax88772UsbCommand to read a PHY register. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] RegisterAddress Number of the register to read. + @param [in, out] pPhyData Address of a buffer to receive the PHY register value + + @retval EFI_SUCCESS The PHY data is available. + @retval other The PHY data is not valid. + +**/ +EFI_STATUS +Ax88772PhyRead ( + IN NIC_DEVICE * pNicDevice, + IN UINT8 RegisterAddress, + IN OUT UINT16 * pPhyData + ) +{ + USB_DEVICE_REQUEST SetupMsg; + EFI_STATUS Status; + + // + // Request access to the PHY + // + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_PHY_ACCESS_SOFTWARE; + SetupMsg.Value = 0; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + if ( !EFI_ERROR ( Status )) { + // + // Read the PHY register address. + // + SetupMsg.RequestType = USB_ENDPOINT_DIR_IN + | USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_PHY_REG_READ; + SetupMsg.Value = pNicDevice->PhyId; + SetupMsg.Index = RegisterAddress; + SetupMsg.Length = sizeof ( *pPhyData ); + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + pPhyData ); + if ( !EFI_ERROR ( Status )) { + + // + // Release the PHY to the hardware + // + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_PHY_ACCESS_HARDWARE; + SetupMsg.Value = 0; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + } + } + return Status; +} + + +/** + Write to a PHY register + + This routine calls ::Ax88772UsbCommand to write a PHY register. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] RegisterAddress Number of the register to read. + @param [in] PhyData Address of a buffer to receive the PHY register value + + @retval EFI_SUCCESS The PHY data was written. + @retval other Failed to wwrite the PHY register. + +**/ +EFI_STATUS +Ax88772PhyWrite ( + IN NIC_DEVICE * pNicDevice, + IN UINT8 RegisterAddress, + IN UINT16 PhyData + ) +{ + USB_DEVICE_REQUEST SetupMsg; + EFI_STATUS Status; + + // + // Request access to the PHY + // + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_PHY_ACCESS_SOFTWARE; + SetupMsg.Value = 0; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + if ( !EFI_ERROR ( Status )) { + // + // Write the PHY register + // + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_PHY_REG_WRITE; + SetupMsg.Value = pNicDevice->PhyId; + SetupMsg.Index = RegisterAddress; + SetupMsg.Length = sizeof ( PhyData ); + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + &PhyData ); + if ( !EFI_ERROR ( Status )) { + + // + // Release the PHY to the hardware + // + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_PHY_ACCESS_HARDWARE; + SetupMsg.Value = 0; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + } + } + + return Status; +} + + +/** + Reset the AX88772 + + This routine uses ::Ax88772UsbCommand to reset the network + adapter. This routine also uses ::Ax88772PhyWrite to reset + the PHY. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + + @retval EFI_SUCCESS The MAC address is available. + @retval other The MAC address is not valid. + +**/ +EFI_STATUS +Ax88772Reset ( + IN NIC_DEVICE * pNicDevice + ) +{ + USB_DEVICE_REQUEST SetupMsg; + EFI_STATUS Status; + + EFI_USB_IO_PROTOCOL *pUsbIo; + EFI_USB_DEVICE_DESCRIPTOR Device; + + pUsbIo = pNicDevice->pUsbIo; + Status = pUsbIo->UsbGetDeviceDescriptor ( pUsbIo, &Device ); + + if (EFI_ERROR(Status)) goto err; + + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_PHY_ACCESS_HARDWARE; + SetupMsg.Value = 0; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + + if (EFI_ERROR(Status)) goto err; + + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_PHY_SELECT; + SetupMsg.Value = SPHY_PSEL; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + + if (EFI_ERROR(Status)) goto err; + + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_RESET; + SetupMsg.Value = SRR_IPRL ; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + + if (EFI_ERROR(Status)) goto err; + + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_RESET; + SetupMsg.Value = SRR_IPPD | SRR_IPRL ; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + + gBS->Stall ( 200000 ); + + if (EFI_ERROR(Status)) goto err; + + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_RESET; + SetupMsg.Value = SRR_IPRL ; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + + gBS->Stall ( 200000 ); + + if (EFI_ERROR(Status)) goto err; + + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_RESET; + SetupMsg.Value = 0; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + + if (EFI_ERROR(Status)) goto err; + + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_PHY_SELECT; + SetupMsg.Value = SPHY_PSEL; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + + if (EFI_ERROR(Status)) goto err; + + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_RESET; + SetupMsg.Value = SRR_IPRL | SRR_BZ | SRR_BZTYPE; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + + if (EFI_ERROR(Status)) goto err; + + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_RX_CONTROL_WRITE; + SetupMsg.Value = 0; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + + if (EFI_ERROR(Status)) goto err; + + if (pNicDevice->Flags != FLAG_TYPE_AX88772) { + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_RXQTC; + SetupMsg.Value = 0x8000; + SetupMsg.Index = 0x8001; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + } + +err: + return Status; +} + +/** + Enable or disable the receiver + + This routine calls ::Ax88772UsbCommand to update the + receiver state. This routine also calls ::Ax88772MacAddressSet + to establish the MAC address for the network adapter. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] RxFilter Simple network RX filter mask value + + @retval EFI_SUCCESS The MAC address was set. + @retval other The MAC address was not set. + +**/ +EFI_STATUS +Ax88772RxControl ( + IN NIC_DEVICE * pNicDevice, + IN UINT32 RxFilter + ) +{ + UINT16 MediumStatus; + UINT16 RxControl; + USB_DEVICE_REQUEST SetupMsg; + EFI_STATUS Status; + EFI_USB_IO_PROTOCOL *pUsbIo; + EFI_USB_DEVICE_DESCRIPTOR Device; + + pUsbIo = pNicDevice->pUsbIo; + Status = pUsbIo->UsbGetDeviceDescriptor ( pUsbIo, &Device ); + + if (EFI_ERROR(Status)) { + DEBUG (( EFI_D_ERROR, "Failed to get device descriptor\n" )); + return Status; + } + + // + // Enable the receiver if something is to be received + // + + if ( 0 != RxFilter ) { + // + // Enable the receiver + // + SetupMsg.RequestType = USB_ENDPOINT_DIR_IN + | USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_MEDIUM_STATUS_READ; + SetupMsg.Value = 0; + SetupMsg.Index = 0; + SetupMsg.Length = sizeof ( MediumStatus ); + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + &MediumStatus ); + if ( !EFI_ERROR ( Status )) { + if ( 0 == ( MediumStatus & MS_RE )) { + MediumStatus |= MS_RE | MS_ONE; + + if ( pNicDevice->bFullDuplex ) + MediumStatus |= MS_TFC | MS_RFC | MS_FD; + else + MediumStatus &= ~(MS_TFC | MS_RFC | MS_FD); + + if ( pNicDevice->b100Mbps ) + MediumStatus |= MS_PS; + else + MediumStatus &= ~MS_PS; + + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_MEDIUM_STATUS_WRITE; + SetupMsg.Value = MediumStatus; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + if ( EFI_ERROR ( Status )) { + DEBUG (( EFI_D_ERROR, "Failed to enable receiver, Status: %r\r\n", + Status )); + } + } + } + else { + DEBUG (( EFI_D_ERROR, "Failed to read receiver status, Status: %r\r\n", + Status )); + } + } + + RxControl = RXC_SO | RXC_RH1M; + // + // Enable multicast if requested + // + if ( 0 != ( RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST )) { + RxControl |= RXC_AM; + // + // Update the multicast hash table + // + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_MULTICAST_HASH_WRITE; + SetupMsg.Value = 0; + SetupMsg.Index = 0; + SetupMsg.Length = sizeof ( pNicDevice ->MulticastHash ); + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + &pNicDevice->MulticastHash ); + } + // + // Enable all multicast if requested + // + if ( 0 != ( RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST )) { + RxControl |= RXC_AMALL; + } + + // + // Enable broadcast if requested + // + if ( 0 != ( RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST )) { + RxControl |= RXC_AB; + } + + // + // Enable promiscuous mode if requested + // + if ( 0 != ( RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS )) { + RxControl |= RXC_PRO; + } + + // + // Update the receiver control + // + if (pNicDevice->CurRxControl != RxControl) { + SetupMsg.RequestType = USB_REQ_TYPE_VENDOR + | USB_TARGET_DEVICE; + SetupMsg.Request = CMD_RX_CONTROL_WRITE; + SetupMsg.Value = RxControl; + SetupMsg.Index = 0; + SetupMsg.Length = 0; + Status = Ax88772UsbCommand ( pNicDevice, + &SetupMsg, + NULL ); + if ( !EFI_ERROR ( Status )) { + pNicDevice->CurRxControl = RxControl; + + } + else { + DEBUG (( EFI_D_ERROR, "ERROR - Failed to set receiver control, Status: %r\r\n", + Status )); + } + } + return Status; +} + + +/** + Read an SROM location + + This routine calls ::Ax88772UsbCommand to read data from the + SROM. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] Address SROM address + @param [out] pData Buffer to receive the data + + @retval EFI_SUCCESS The read was successful + @retval other The read failed + +**/ +EFI_STATUS +Ax88772SromRead ( + IN NIC_DEVICE * pNicDevice, + IN UINT32 Address, + OUT UINT16 * pData + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Send a command to the USB device. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] pRequest Pointer to the request structure + @param [in, out] pBuffer Data buffer address + + @retval EFI_SUCCESS The USB transfer was successful + @retval other The USB transfer failed + +**/ +EFI_STATUS +Ax88772UsbCommand ( + IN NIC_DEVICE * pNicDevice, + IN USB_DEVICE_REQUEST * pRequest, + IN OUT VOID * pBuffer + ) +{ + UINT32 CmdStatus; + EFI_USB_DATA_DIRECTION Direction; + EFI_USB_IO_PROTOCOL * pUsbIo; + EFI_STATUS Status; + + // + // Determine the transfer direction + // + Direction = EfiUsbNoData; + if ( 0 != pRequest->Length ) { + Direction = ( 0 != ( pRequest->RequestType & USB_ENDPOINT_DIR_IN )) + ? EfiUsbDataIn : EfiUsbDataOut; + } + + // + // Issue the command + // + pUsbIo = pNicDevice->pUsbIo; + Status = pUsbIo->UsbControlTransfer ( pUsbIo, + pRequest, + Direction, + USB_BUS_TIMEOUT, + pBuffer, + pRequest->Length, + &CmdStatus ); + // + // Determine the operation status + // + if ( !EFI_ERROR ( Status )) { + Status = CmdStatus; + } + else { + // + // Only use status values associated with the Simple Network protocol + // + if ( EFI_TIMEOUT == Status ) { + Status = EFI_DEVICE_ERROR; + } + } + return Status; +} + diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772.h b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772.h new file mode 100644 index 0000000000..365929489b --- /dev/null +++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772.h @@ -0,0 +1,1026 @@ +/** @file + Definitions for ASIX AX88772 Ethernet adapter. + + Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved. + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _AX88772_H_ +#define _AX88772_H_ + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define MAX_QUEUE_SIZE 50 +#define MAX_BULKIN_SIZE 16384 +#define HW_HDR_LENGTH 8 + + +#define MAX_LINKIDLE_THRESHOLD 20000 + + + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +#if defined(_MSC_VER) /* Handle Microsoft VC++ compiler specifics. */ +#define DBG_ENTER() DEBUG (( 0xffffffff, "Entering " __FUNCTION__ "\n" )) ///< Display routine entry +#define DBG_EXIT() DEBUG (( 0xffffffff, "Exiting " __FUNCTION__ "\n" )) ///< Display routine exit +#define DBG_EXIT_DEC(Status) DEBUG (( 0xffffffff, "Exiting " __FUNCTION__ ", Status: %d\n", Status )) ///< Display routine exit with decimal value +#define DBG_EXIT_HEX(Status) DEBUG (( 0xffffffff, "Exiting " __FUNCTION__ ", Status: 0x%08x\n", Status )) ///< Display routine exit with hex value +#define DBG_EXIT_STATUS(Status) DEBUG (( 0xffffffff, "Exiting " __FUNCTION__ ", Status: %r\n", Status )) ///< Display routine exit with status value +#define DBG_EXIT_TF(Status) DEBUG (( 0xffffffff, "Exiting " __FUNCTION__ ", returning %s\n", (FALSE == Status) ? L"FALSE" : L"TRUE" )) ///< Display routine with TRUE/FALSE value +#else // _MSC_VER +#define DBG_ENTER() ///< Display routine entry +#define DBG_EXIT() ///< Display routine exit +#define DBG_EXIT_DEC(Status) ///< Display routine exit with decimal value +#define DBG_EXIT_HEX(Status) ///< Display routine exit with hex value +#define DBG_EXIT_STATUS(Status) ///< Display routine exit with status value +#define DBG_EXIT_TF(Status) ///< Display routine with TRUE/FALSE value +#endif // _MSC_VER + +#define USB_IS_IN_ENDPOINT(EndPointAddr) (((EndPointAddr) & BIT7) != 0) ///< Return TRUE/FALSE for IN direction +#define USB_IS_OUT_ENDPOINT(EndPointAddr) (((EndPointAddr) & BIT7) == 0) ///< Return TRUE/FALSE for OUT direction +#define USB_IS_BULK_ENDPOINT(Attribute) (((Attribute) & (BIT0 | BIT1)) == USB_ENDPOINT_BULK) ///< Return TRUE/FALSE for BULK type +#define USB_IS_INTERRUPT_ENDPOINT(Attribute) (((Attribute) & (BIT0 | BIT1)) == USB_ENDPOINT_INTERRUPT) ///< Return TRUE/FALSE for INTERRUPT type + + +#define PRINT(_L_STR) (gST->ConOut->OutputString(gST->ConOut,(_L_STR))) +//------------------------------------------------------------------------------ +// Constants +//------------------------------------------------------------------------------ + +#define DEBUG_RX_BROADCAST 0x40000000 ///< Display RX broadcast messages +#define DEBUG_RX_MULTICAST 0x20000000 ///< Display RX multicast messages +#define DEBUG_RX_UNICAST 0x10000000 ///< Display RX unicast messages +#define DEBUG_MAC_ADDRESS 0x08000000 ///< Display the MAC address +#define DEBUG_LINK 0x04000000 ///< Display the link status +#define DEBUG_TX 0x02000000 ///< Display the TX messages +#define DEBUG_PHY 0x01000000 ///< Display the PHY register values +#define DEBUG_SROM 0x00800000 ///< Display the SROM contents +#define DEBUG_TIMER 0x00400000 ///< Display the timer routine entry/exit +#define DEBUG_TPL 0x00200000 ///< Display the timer routine entry/exit + +#define AX88772_MAX_PKT_SIZE 2048 ///< Maximum packet size + +#define ETHERNET_HEADER_SIZE sizeof ( ETHERNET_HEADER ) ///< Size in bytes of the Ethernet header +#define MIN_ETHERNET_PKT_SIZE 60 ///< Minimum packet size including Ethernet header +#define MAX_ETHERNET_PKT_SIZE 1500 ///< Ethernet spec 3.1.1: Minimum packet size + +#define USB_NETWORK_CLASS 0x09 ///< USB Network class code +#define USB_BUS_TIMEOUT 1000 ///< USB timeout in milliseconds + +#define TIMER_MSEC 20 ///< Polling interval for the NIC +//#define TPL_AX88772 TPL_CALLBACK ///< TPL for routine synchronization + +#define HC_DEBUG 0 +#define BULKIN_TIMEOUT 20 +#define AUTONEG_DELAY 500000 +#define AUTONEG_POLLCNT 20 + +/** + Verify new TPL value + + This macro which is enabled when debug is enabled verifies that + the new TPL value is >= the current TPL value. +**/ +#ifdef VERIFY_TPL +#undef VERIFY_TPL +#endif // VERIFY_TPL + +#if !defined(MDEPKG_NDEBUG) + +#define VERIFY_TPL(tpl) \ +{ \ + EFI_TPL PreviousTpl; \ + \ + PreviousTpl = gBS->RaiseTPL ( TPL_HIGH_LEVEL ); \ + gBS->RestoreTPL ( PreviousTpl ); \ + if ( PreviousTpl > tpl ) { \ + DEBUG (( DEBUG_ERROR, "Current TPL: %d, New TPL: %d\r\n", PreviousTpl, tpl )); \ + ASSERT ( PreviousTpl <= tpl ); \ + } \ +} + +#else // MDEPKG_NDEBUG + +#define VERIFY_TPL(tpl) + +#endif // MDEPKG_NDEBUG + +//------------------------------------------------------------------------------ +// Hardware Definition +//------------------------------------------------------------------------------ + +#define FreeQueueSize 10 + +#define DEV_SIGNATURE SIGNATURE_32 ('A','X','8','8') ///< Signature of data structures in memory + +#define RESET_MSEC 1000 ///< Reset duration +#define PHY_RESET_MSEC 500 ///< PHY reset duration + +// +// RX Control register +// + +#define RXC_PRO 0x0001 ///< Receive all packets +#define RXC_AMALL 0x0002 ///< Receive all multicast packets +#define RXC_SEP 0x0004 ///< Save error packets +#define RXC_AB 0x0008 ///< Receive broadcast packets +#define RXC_AM 0x0010 ///< Use multicast destination address hash table +#define RXC_AP 0x0020 ///< Accept physical address from Multicast Filter +#define RXC_SO 0x0080 ///< Start operation +#define RXC_MFB 0x0300 ///< Maximum frame burst +#define RXC_MFB_2048 0 ///< Maximum frame size: 2048 bytes +#define RXC_MFB_4096 0x0100 ///< Maximum frame size: 4096 bytes +#define RXC_MFB_8192 0x0200 ///< Maximum frame size: 8192 bytes +#define RXC_MFB_16384 0x0300 ///< Maximum frame size: 16384 bytes + +/*Freddy*/ +#define RXC_RH1M 0x0100 ///< Rx header 1 +#define RXC_RH2M 0x0200 ///< Rx header 2 +#define RXC_RH3M 0x0400 ///< Rx header 3 +/*Freddy*/ + +// +// Medium Status register +// + +#define MS_FD 0x0002 ///< Full duplex +#define MS_ONE 0x0004 ///< Must be one +#define MS_RFC 0x0010 ///< RX flow control enable +#define MS_TFC 0x0020 ///< TX flow control enable +#define MS_PF 0x0080 ///< Pause frame enable +#define MS_RE 0x0100 ///< Receive enable +#define MS_PS 0x0200 ///< Port speed 1=100, 0=10 Mbps +#define MS_SBP 0x0800 ///< Stop back pressure +#define MS_SM 0x1000 ///< Super MAC support + +// +// Software PHY Select register +// + +#define SPHY_PSEL (1 << 0) ///< Select internal PHY +#define SPHY_SSMII (1 << 2) +#define SPHY_SSEN (1 << 4) +#define SPHY_ASEL 0x02 ///< 1=Auto select, 0=Manual select + +// +// Software Reset register +// + +#define SRR_RR 0x01 ///< Clear receive frame length error +#define SRR_RT 0x02 ///< Clear transmit frame length error +#define SRR_BZTYPE 0x04 ///< External PHY reset pin tri-state enable +#define SRR_PRL 0x08 ///< External PHY reset pin level +#define SRR_BZ 0x10 ///< Force Bulk to return zero length packet +#define SRR_IPRL 0x20 ///< Internal PHY reset control +#define SRR_IPPD 0x40 ///< Internal PHY power down + +// +// PHY ID values +// + +#define PHY_ID_INTERNAL 0x0010 ///< Internal PHY + +// +// USB Commands +// + +#define CMD_PHY_ACCESS_SOFTWARE 0x06 ///< Software in control of PHY +#define CMD_PHY_REG_READ 0x07 ///< Read PHY register, Value: PHY, Index: Register, Data: Register value +#define CMD_PHY_REG_WRITE 0x08 ///< Write PHY register, Value: PHY, Index: Register, Data: New 16-bit value +#define CMD_PHY_ACCESS_HARDWARE 0x0a ///< Hardware in control of PHY +#define CMD_SROM_READ 0x0b ///< Read SROM register: Value: Address, Data: Value +#define CMD_RX_CONTROL_WRITE 0x10 ///< Set the RX control register, Value: New value +#define CMD_GAPS_WRITE 0x12 ///< Write the gaps register, Value: New value +#define CMD_MAC_ADDRESS_READ 0x13 ///< Read the MAC address, Data: 6 byte MAC address +#define CMD_MAC_ADDRESS_WRITE 0x14 ///< Set the MAC address, Data: New 6 byte MAC address +#define CMD_MULTICAST_HASH_WRITE 0x16 ///< Write the multicast hash table, Data: New 8 byte value +#define CMD_MULTICAST_HASH_READ 0x16 ///< Read the multicast hash table +#define CMD_MEDIUM_STATUS_READ 0x1a ///< Read medium status register, Data: Register value +#define CMD_MEDIUM_STATUS_WRITE 0x1b ///< Write medium status register, Value: New value +#define CMD_WRITE_GPIOS 0x1f +#define CMD_RESET 0x20 ///< Reset register, Value: New value +#define CMD_PHY_SELECT 0x22 ///< PHY select register, Value: New value + +/*Freddy*/ +#define CMD_RXQTC 0x2a ///< RX Queue Cascade Threshold Control Register +/*Freddy*/ + +//------------------------------ +// USB Endpoints +//------------------------------ + +#define CONTROL_ENDPOINT 0 ///< Control endpoint +#define INTERRUPT_ENDPOINT 1 ///< Interrupt endpoint +#define BULK_IN_ENDPOINT 2 ///< Receive endpoint +#define BULK_OUT_ENDPOINT 3 ///< Transmit endpoint + +//------------------------------ +// PHY Registers +//------------------------------ + +#define PHY_BMCR 0 ///< Control register +#define PHY_BMSR 1 ///< Status register +#define PHY_ANAR 4 ///< Autonegotiation advertisement register +#define PHY_ANLPAR 5 ///< Autonegotiation link parter ability register +#define PHY_ANER 6 ///< Autonegotiation expansion register + +// BMCR - Register 0 + +#define BMCR_RESET 0x8000 ///< 1 = Reset the PHY, bit clears after reset +#define BMCR_LOOPBACK 0x4000 ///< 1 = Loopback enabled +#define BMCR_100MBPS 0x2000 ///< 100 Mbits/Sec +#define BMCR_10MBPS 0 ///< 10 Mbits/Sec +#define BMCR_AUTONEGOTIATION_ENABLE 0x1000 ///< 1 = Enable autonegotiation +#define BMCR_POWER_DOWN 0x0800 ///< 1 = Power down +#define BMCR_ISOLATE 0x0400 ///< 0 = Isolate PHY +#define BMCR_RESTART_AUTONEGOTIATION 0x0200 ///< 1 = Restart autonegotiation +#define BMCR_FULL_DUPLEX 0x0100 ///< Full duplex operation +#define BMCR_HALF_DUPLEX 0 ///< Half duplex operation +#define BMCR_COLLISION_TEST 0x0080 ///< 1 = Collision test enabled + +// BSMR - Register 1 + +#define BMSR_100BASET4 0x8000 ///< 1 = 100BASE-T4 mode +#define BMSR_100BASETX_FDX 0x4000 ///< 1 = 100BASE-TX full duplex +#define BMSR_100BASETX_HDX 0x2000 ///< 1 = 100BASE-TX half duplex +#define BMSR_10BASET_FDX 0x1000 ///< 1 = 10BASE-T full duplex +#define BMSR_10BASET_HDX 0x0800 ///< 1 = 10BASE-T half duplex +#define BMSR_MF 0x0040 ///< 1 = PHY accepts frames with preamble suppressed +#define BMSR_AUTONEG_CMPLT 0x0020 ///< 1 = Autonegotiation complete +#define BMSR_RF 0x0010 ///< 1 = Remote fault +#define BMSR_AUTONEG 0x0008 ///< 1 = Able to perform autonegotiation +#define BMSR_LINKST 0x0004 ///< 1 = Link up +#define BMSR_JABBER_DETECT 0x0002 ///< 1 = jabber condition detected +#define BMSR_EXTENDED_CAPABILITY 0x0001 ///< 1 = Extended register capable + +// ANAR and ANLPAR Registers 4, 5 + +#define AN_NP 0x8000 ///< 1 = Next page available +#define AN_ACK 0x4000 ///< 1 = Link partner acknowledged +#define AN_RF 0x2000 ///< 1 = Remote fault indicated by link partner +#define AN_FCS 0x0400 ///< 1 = Flow control ability +#define AN_T4 0x0200 ///< 1 = 100BASE-T4 support +#define AN_TX_FDX 0x0100 ///< 1 = 100BASE-TX Full duplex +#define AN_TX_HDX 0x0080 ///< 1 = 100BASE-TX support +#define AN_10_FDX 0x0040 ///< 1 = 10BASE-T Full duplex +#define AN_10_HDX 0x0020 ///< 1 = 10BASE-T support +#define AN_CSMA_CD 0x0001 ///< 1 = IEEE 802.3 CSMA/CD support + +// asix_flags defines +#define FLAG_NONE 0 +#define FLAG_TYPE_AX88172 BIT0 +#define FLAG_TYPE_AX88772 BIT1 +#define FLAG_TYPE_AX88772B BIT2 +#define FLAG_EEPROM_MAC BIT3 // initial mac address in eeprom + +//------------------------------------------------------------------------------ +// Data Types +//------------------------------------------------------------------------------ + +typedef struct { + UINT16 VendorId; + UINT16 ProductId; + INT32 Flags; +}ASIX_DONGLE; + +/** + Ethernet header layout + + IEEE 802.3-2002 Part 3 specification, section 3.1.1. +**/ +#pragma pack(1) +typedef struct { + UINT8 dest_addr[PXE_HWADDR_LEN_ETHER]; ///< Destination LAN address + UINT8 src_addr[PXE_HWADDR_LEN_ETHER]; ///< Source LAN address + UINT16 type; ///< Protocol or length +} ETHERNET_HEADER; +#pragma pack() + +/** + Receive and Transmit packet structure +**/ +#pragma pack(1) +typedef struct _RX_TX_PACKET { + struct _RX_TX_PACKET * pNext; ///< Next receive packet + UINT16 Length; ///< Packet length + UINT16 LengthBar; ///< Complement of the length + UINT8 Data[ AX88772_MAX_PKT_SIZE ]; ///< Received packet data +} RX_TX_PACKET; +#pragma pack() + + +#pragma pack(1) +typedef struct _RX_PKT { + struct _RX_PKT *pNext; + BOOLEAN f_Used; + UINT16 Length; + UINT8 Data [AX88772_MAX_PKT_SIZE] ; +} RX_PKT; +#pragma pack() + +/** + AX88772 control structure + + The driver uses this structure to manage the Asix AX88772 10/100 + Ethernet controller. +**/ +typedef struct { + UINTN Signature; ///< Structure identification + + // + // USB data + // + EFI_HANDLE Controller; ///< Controller handle + EFI_USB_IO_PROTOCOL * pUsbIo; ///< USB driver interface + + // + // Simple network protocol data + // + EFI_SIMPLE_NETWORK_PROTOCOL SimpleNetwork; ///< Driver's network stack interface + EFI_SIMPLE_NETWORK_PROTOCOL SimpleNetwork_Backup; + EFI_SIMPLE_NETWORK_MODE SimpleNetworkData; ///< Data for simple network + + // + // Ethernet controller data + // + BOOLEAN bInitialized; ///< Controller initialized + VOID * pTxBuffer; ///< Last transmit buffer + UINT16 PhyId; ///< PHY ID + + // + // Link state + // + BOOLEAN b100Mbps; ///< Current link speed, FALSE = 10 Mbps + BOOLEAN bComplete; ///< Current state of auto-negotiation + BOOLEAN bFullDuplex; ///< Current duplex + BOOLEAN bLinkUp; ///< Current link state + UINTN LinkIdleCnt; + UINTN PollCount; ///< Number of times the autonegotiation status was polled + UINT16 CurRxControl; + // + // Receive buffer list + // + RX_TX_PACKET * pRxTest; + RX_TX_PACKET * pTxTest; + + INT8 MulticastHash[8]; + EFI_MAC_ADDRESS MAC; + BOOLEAN bHavePkt; + + EFI_DEVICE_PATH_PROTOCOL *MyDevPath; + + EFI_DRIVER_BINDING_PROTOCOL * DrvBind; + + RX_PKT * QueueHead; + RX_PKT * pNextFill; + RX_PKT * pFirstFill; + UINTN PktCntInQueue; + UINT8 * pBulkInBuff; + + INT32 Flags; + +} NIC_DEVICE; + +#define DEV_FROM_SIMPLE_NETWORK(a) CR (a, NIC_DEVICE, SimpleNetwork, DEV_SIGNATURE) ///< Locate NIC_DEVICE from Simple Network Protocol + +//------------------------------------------------------------------------------ +// Simple Network Protocol +//------------------------------------------------------------------------------ + +/** + Reset the network adapter. + + Resets a network adapter and reinitializes it with the parameters that + were provided in the previous call to Initialize (). The transmit and + receive queues are cleared. Receive filters, the station address, the + statistics, and the multicast-IP-to-HW MAC addresses are not reset by + this call. + + This routine calls ::Ax88772Reset to perform the adapter specific + reset operation. This routine also starts the link negotiation + by calling ::Ax88772NegotiateLinkStart. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] bExtendedVerification Indicates that the driver may perform a more + exhaustive verification operation of the device + during reset. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Reset ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN BOOLEAN bExtendedVerification + ); + +/** + Initialize the simple network protocol. + + This routine calls ::Ax88772MacAddressGet to obtain the + MAC address. + + @param [in] pNicDevice NIC_DEVICE_INSTANCE pointer + + @retval EFI_SUCCESS Setup was successful + +**/ +EFI_STATUS +SN_Setup ( + IN NIC_DEVICE * pNicDevice + ); + +/** + This routine starts the network interface. + + @param [in] pSimpleNetwork Protocol instance pointer + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_ALREADY_STARTED The network interface was already started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Start ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork + ); + +/** + Set the MAC address. + + This function modifies or resets the current station address of a + network interface. If Reset is TRUE, then the current station address + is set ot the network interface's permanent address. If Reset if FALSE + then the current station address is changed to the address specified by + pNew. + + This routine calls ::Ax88772MacAddressSet to update the MAC address + in the network adapter. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] bReset Flag used to reset the station address to the + network interface's permanent address. + @param [in] pNew New station address to be used for the network + interface. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_StationAddress ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN BOOLEAN bReset, + IN EFI_MAC_ADDRESS * pNew + ); + +/** + This function resets or collects the statistics on a network interface. + If the size of the statistics table specified by StatisticsSize is not + big enough for all of the statistics that are collected by the network + interface, then a partial buffer of statistics is returned in + StatisticsTable. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] bReset Set to TRUE to reset the statistics for the network interface. + @param [in, out] pStatisticsSize On input the size, in bytes, of StatisticsTable. On output + the size, in bytes, of the resulting table of statistics. + @param [out] pStatisticsTable A pointer to the EFI_NETWORK_STATISTICS structure that + conains the statistics. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_BUFFER_TOO_SMALL The pStatisticsTable is NULL or the buffer is too small. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Statistics ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN BOOLEAN bReset, + IN OUT UINTN * pStatisticsSize, + OUT EFI_NETWORK_STATISTICS * pStatisticsTable + ); + +/** + This function stops a network interface. This call is only valid + if the network interface is in the started state. + + @param [in] pSimpleNetwork Protocol instance pointer + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Stop ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork + ); + +/** + This function releases the memory buffers assigned in the Initialize() call. + Pending transmits and receives are lost, and interrupts are cleared and disabled. + After this call, only Initialize() and Stop() calls may be used. + + @param [in] pSimpleNetwork Protocol instance pointer + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Shutdown ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork + ); + +/** + Send a packet over the network. + + This function places the packet specified by Header and Buffer on + the transmit queue. This function performs a non-blocking transmit + operation. When the transmit is complete, the buffer is returned + via the GetStatus() call. + + This routine calls ::Ax88772Rx to empty the network adapter of + receive packets. The routine then passes the transmit packet + to the network adapter. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] HeaderSize The size, in bytes, of the media header to be filled in by + the Transmit() function. If HeaderSize is non-zero, then + it must be equal to SimpleNetwork->Mode->MediaHeaderSize + and DestAddr and Protocol parameters must not be NULL. + @param [in] BufferSize The size, in bytes, of the entire packet (media header and + data) to be transmitted through the network interface. + @param [in] pBuffer A pointer to the packet (media header followed by data) to + to be transmitted. This parameter can not be NULL. If + HeaderSize is zero, then the media header is Buffer must + already be filled in by the caller. If HeaderSize is nonzero, + then the media header will be filled in by the Transmit() + function. + @param [in] pSrcAddr The source HW MAC address. If HeaderSize is zero, then + this parameter is ignored. If HeaderSize is nonzero and + SrcAddr is NULL, then SimpleNetwork->Mode->CurrentAddress + is used for the source HW MAC address. + @param [in] pDestAddr The destination HW MAC address. If HeaderSize is zero, then + this parameter is ignored. + @param [in] pProtocol The type of header to build. If HeaderSize is zero, then + this parameter is ignored. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_NOT_READY The network interface is too busy to accept this transmit request. + @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + +**/ +EFI_STATUS +EFIAPI +SN_Transmit ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN UINTN HeaderSize, + IN UINTN BufferSize, + IN VOID * pBuffer, + IN EFI_MAC_ADDRESS * pSrcAddr, + IN EFI_MAC_ADDRESS * pDestAddr, + IN UINT16 * pProtocol + ); + +//------------------------------------------------------------------------------ +// Support Routines +//------------------------------------------------------------------------------ + +/** + Get the MAC address + + This routine calls ::Ax88772UsbCommand to request the MAC + address from the network adapter. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [out] pMacAddress Address of a six byte buffer to receive the MAC address. + + @retval EFI_SUCCESS The MAC address is available. + @retval other The MAC address is not valid. + +**/ +EFI_STATUS +Ax88772MacAddressGet ( + IN NIC_DEVICE * pNicDevice, + OUT UINT8 * pMacAddress + ); + +/** + Set the MAC address + + This routine calls ::Ax88772UsbCommand to set the MAC address + in the network adapter. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] pMacAddress Address of a six byte buffer to containing the new MAC address. + + @retval EFI_SUCCESS The MAC address was set. + @retval other The MAC address was not set. + +**/ +EFI_STATUS +Ax88772MacAddressSet ( + IN NIC_DEVICE * pNicDevice, + IN UINT8 * pMacAddress + ); + +/** + Clear the multicast hash table + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + +**/ +VOID +Ax88772MulticastClear ( + IN NIC_DEVICE * pNicDevice + ); + +/** + Enable a multicast address in the multicast hash table + + This routine calls ::Ax88772Crc to compute the hash bit for + this MAC address. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] pMacAddress Address of a six byte buffer to containing the MAC address. + +**/ +VOID +Ax88772MulticastSet ( + IN NIC_DEVICE * pNicDevice, + IN UINT8 * pMacAddress + ); + +/** + Start the link negotiation + + This routine calls ::Ax88772PhyWrite to start the PHY's link + negotiation. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + + @retval EFI_SUCCESS The link negotiation was started. + @retval other Failed to start the link negotiation. + +**/ +EFI_STATUS +Ax88772NegotiateLinkStart ( + IN NIC_DEVICE * pNicDevice + ); + +/** + Complete the negotiation of the PHY link + + This routine calls ::Ax88772PhyRead to determine if the + link negotiation is complete. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in, out] pPollCount Address of number of times this routine was polled + @param [out] pbComplete Address of boolean to receive complate status. + @param [out] pbLinkUp Address of boolean to receive link status, TRUE=up. + @param [out] pbHiSpeed Address of boolean to receive link speed, TRUE=100Mbps. + @param [out] pbFullDuplex Address of boolean to receive link duplex, TRUE=full. + + @retval EFI_SUCCESS The MAC address is available. + @retval other The MAC address is not valid. + +**/ +EFI_STATUS +Ax88772NegotiateLinkComplete ( + IN NIC_DEVICE * pNicDevice, + IN OUT UINTN * pPollCount, + OUT BOOLEAN * pbComplete, + OUT BOOLEAN * pbLinkUp, + OUT BOOLEAN * pbHiSpeed, + OUT BOOLEAN * pbFullDuplex + ); + +/** + Read a register from the PHY + + This routine calls ::Ax88772UsbCommand to read a PHY register. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] RegisterAddress Number of the register to read. + @param [in, out] pPhyData Address of a buffer to receive the PHY register value + + @retval EFI_SUCCESS The PHY data is available. + @retval other The PHY data is not valid. + +**/ +EFI_STATUS +Ax88772PhyRead ( + IN NIC_DEVICE * pNicDevice, + IN UINT8 RegisterAddress, + IN OUT UINT16 * pPhyData + ); + +/** + Write to a PHY register + + This routine calls ::Ax88772UsbCommand to write a PHY register. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] RegisterAddress Number of the register to read. + @param [in] PhyData Address of a buffer to receive the PHY register value + + @retval EFI_SUCCESS The PHY data was written. + @retval other Failed to wwrite the PHY register. + +**/ +EFI_STATUS +Ax88772PhyWrite ( + IN NIC_DEVICE * pNicDevice, + IN UINT8 RegisterAddress, + IN UINT16 PhyData + ); + +/** + Reset the AX88772 + + This routine uses ::Ax88772UsbCommand to reset the network + adapter. This routine also uses ::Ax88772PhyWrite to reset + the PHY. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + + @retval EFI_SUCCESS The MAC address is available. + @retval other The MAC address is not valid. + +**/ +EFI_STATUS +Ax88772Reset ( + IN NIC_DEVICE * pNicDevice + ); + +VOID +Ax88772ChkLink ( + IN NIC_DEVICE * pNicDevice, + IN BOOLEAN bUpdateLink + ); + +/** + Receive a frame from the network. + + This routine polls the USB receive interface for a packet. If a packet + is available, this routine adds the receive packet to the list of + pending receive packets. + + This routine calls ::Ax88772NegotiateLinkComplete to verify + that the link is up. This routine also calls ::SN_Reset to + reset the network adapter when necessary. Finally this + routine attempts to receive one or more packets from the + network adapter. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] bUpdateLink TRUE = Update link status + +**/ +VOID +Ax88772Rx ( + IN NIC_DEVICE * pNicDevice, + IN BOOLEAN bUpdateLink + ); + +/** + Enable or disable the receiver + + This routine calls ::Ax88772UsbCommand to update the + receiver state. This routine also calls ::Ax88772MacAddressSet + to establish the MAC address for the network adapter. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] RxFilter Simple network RX filter mask value + + @retval EFI_SUCCESS The MAC address was set. + @retval other The MAC address was not set. + +**/ +EFI_STATUS +Ax88772RxControl ( + IN NIC_DEVICE * pNicDevice, + IN UINT32 RxFilter + ); + +/** + Read an SROM location + + This routine calls ::Ax88772UsbCommand to read data from the + SROM. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] Address SROM address + @param [out] pData Buffer to receive the data + + @retval EFI_SUCCESS The read was successful + @retval other The read failed + +**/ +EFI_STATUS +Ax88772SromRead ( + IN NIC_DEVICE * pNicDevice, + IN UINT32 Address, + OUT UINT16 * pData + ); + +/** + Send a command to the USB device. + + @param [in] pNicDevice Pointer to the NIC_DEVICE structure + @param [in] pRequest Pointer to the request structure + @param [in, out] pBuffer Data buffer address + + @retval EFI_SUCCESS The USB transfer was successful + @retval other The USB transfer failed + +**/ +EFI_STATUS +Ax88772UsbCommand ( + IN NIC_DEVICE * pNicDevice, + IN USB_DEVICE_REQUEST * pRequest, + IN OUT VOID * pBuffer + ); + +//------------------------------------------------------------------------------ +// EFI Component Name Protocol Support +//------------------------------------------------------------------------------ + +extern EFI_COMPONENT_NAME_PROTOCOL gComponentName; ///< Component name protocol declaration +extern EFI_COMPONENT_NAME2_PROTOCOL gComponentName2; ///< Component name 2 protocol declaration + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param [in] pThis A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + @param [in] pLanguage A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 3066 or ISO 639-2 language code format. + @param [out] ppDriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +GetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL * pThis, + IN CHAR8 * pLanguage, + OUT CHAR16 ** ppDriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param [in] pThis A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + @param [in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + @param [in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + @param [in] pLanguage A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 3066 or ISO 639-2 language code format. + @param [out] ppControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +GetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL * pThis, + IN EFI_HANDLE ControllerHandle, + IN OPTIONAL EFI_HANDLE ChildHandle, + IN CHAR8 * pLanguage, + OUT CHAR16 ** ppControllerName + ); + +VOID +FillPkt2Queue ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN UINTN BufLength); + +//------------------------------------------------------------------------------ + +#endif // _AX88772_H_ diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772b.inf b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772b.inf new file mode 100644 index 0000000000..60e43fd275 --- /dev/null +++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772b.inf @@ -0,0 +1,61 @@ +## @file +# Component description file for ASIX AX88772 USB/Ethernet driver. +# +# This module provides support for the ASIX AX88772 USB/Ethernet adapter. +# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010018 + BASE_NAME = Ax88772b + FILE_GUID = 95C8D770-E1A4-4422-B263-E32F14FD8186 + MODULE_TYPE = DXE_RUNTIME_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = EntryPoint + +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources.common] + Ax88772.h + Ax88772.c + ComponentName.c + DriverBinding.c + SimpleNetwork.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiLib + UefiBootServicesTableLib + BaseMemoryLib + DebugLib + UefiRuntimeLib + UefiDriverEntryPoint + +[Protocols] + gEfiDevicePathProtocolGuid ## BY_START + gEfiSimpleNetworkProtocolGuid ## BY_START + gEfiUsbIoProtocolGuid ## TO_START + +[Depex] + gEfiBdsArchProtocolGuid AND + gEfiCpuArchProtocolGuid AND + gEfiMetronomeArchProtocolGuid AND + gEfiMonotonicCounterArchProtocolGuid AND + gEfiRealTimeClockArchProtocolGuid AND + gEfiResetArchProtocolGuid AND + gEfiRuntimeArchProtocolGuid AND + gEfiSecurityArchProtocolGuid AND + gEfiTimerArchProtocolGuid AND + gEfiVariableWriteArchProtocolGuid AND + gEfiVariableArchProtocolGuid AND + gEfiWatchdogTimerArchProtocolGuid diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/ComponentName.c b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/ComponentName.c new file mode 100644 index 0000000000..76a732a7b0 --- /dev/null +++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/ComponentName.c @@ -0,0 +1,175 @@ +/** @file + UEFI Component Name(2) protocol implementation. + + Copyright (c) 2011, Intel Corporation. All rights reserved. + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ax88772.h" + +/** + EFI Component Name Protocol declaration +**/ +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gComponentName = { + GetDriverName, + GetControllerName, + "eng" +}; + +/** + EFI Component Name 2 Protocol declaration +**/ +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) GetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) GetControllerName, + "en" +}; + + +/** + Driver name table declaration +**/ +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE +mDriverNameTable[] = { + {"eng;en", L"ASIX AX88772B Ethernet Driver 1.0"}, + {NULL, NULL} +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param [in] pThis A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + @param [in] pLanguage A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 3066 or ISO 639-2 language code format. + @param [out] ppDriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +GetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL * pThis, + IN CHAR8 * pLanguage, + OUT CHAR16 ** ppDriverName + ) +{ + EFI_STATUS Status; + + Status = LookupUnicodeString2 ( + pLanguage, + pThis->SupportedLanguages, + mDriverNameTable, + ppDriverName, + (BOOLEAN)(pThis == &gComponentName) + ); + + return Status; +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param [in] pThis A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + @param [in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + @param [in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + @param [in] pLanguage A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 3066 or ISO 639-2 language code format. + @param [out] ppControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +GetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL * pThis, + IN EFI_HANDLE ControllerHandle, + IN OPTIONAL EFI_HANDLE ChildHandle, + IN CHAR8 * pLanguage, + OUT CHAR16 ** ppControllerName + ) +{ + EFI_STATUS Status; + + // + // Set the controller name + // + *ppControllerName = L"ASIX AX88772B USB Fast Ethernet Controller"; + Status = EFI_SUCCESS; + + // + // Return the operation status + // + + return Status; +} diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/DriverBinding.c b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/DriverBinding.c new file mode 100644 index 0000000000..2bec944140 --- /dev/null +++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/DriverBinding.c @@ -0,0 +1,696 @@ +/** @file + Implement the driver binding protocol for Asix AX88772 Ethernet driver. + + Copyright (c) 2011-2013, Intel Corporation. All rights reserved. + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ax88772.h" + +ASIX_DONGLE ASIX_DONGLES[] = { + { 0x05AC, 0x1402, FLAG_TYPE_AX88772 }, // Apple USB Ethernet Adapter + // ASIX 88772B + { 0x0B95, 0x772B, FLAG_TYPE_AX88772B | FLAG_EEPROM_MAC }, + { 0x0000, 0x0000, FLAG_NONE } // END - Do not remove +}; + +/** + Verify the controller type + + @param [in] pThis Protocol instance pointer. + @param [in] Controller Handle of device to test. + @param [in] pRemainingDevicePath Not used. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +DriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL * pThis, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL * pRemainingDevicePath + ) +{ + EFI_USB_DEVICE_DESCRIPTOR Device; + EFI_USB_IO_PROTOCOL * pUsbIo; + EFI_STATUS Status; + UINT32 Index; + + // + // Connect to the USB stack + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + (VOID **) &pUsbIo, + pThis->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (!EFI_ERROR ( Status )) { + + // + // Get the interface descriptor to check the USB class and find a transport + // protocol handler. + // + Status = pUsbIo->UsbGetDeviceDescriptor ( pUsbIo, &Device ); + if (EFI_ERROR ( Status )) { + Status = EFI_UNSUPPORTED; + } + else { + // + // Validate the adapter + // + for (Index = 0; ASIX_DONGLES[Index].VendorId != 0; Index++) { + if (ASIX_DONGLES[Index].VendorId == Device.IdVendor && + ASIX_DONGLES[Index].ProductId == Device.IdProduct) { + DEBUG ((EFI_D_INFO, "Found the AX88772B\r\n")); + break; + } + } + + if (ASIX_DONGLES[Index].VendorId == 0) + Status = EFI_UNSUPPORTED; + } + + // + // Done with the USB stack + // + gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + pThis->DriverBindingHandle, + Controller + ); + } + return Status; +} + + +/** + Start this driver on Controller by opening UsbIo and DevicePath protocols. + Initialize PXE structures, create a copy of the Controller Device Path with the + NIC's MAC address appended to it, install the NetworkInterfaceIdentifier protocol + on the newly created Device Path. + + @param [in] pThis Protocol instance pointer. + @param [in] Controller Handle of device to work with. + @param [in] pRemainingDevicePath Not used, always produce all possible children. + + @retval EFI_SUCCESS This driver is added to Controller. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +DriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL * pThis, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL * pRemainingDevicePath + ) +{ + + EFI_STATUS Status; + NIC_DEVICE *pNicDevice; + UINTN LengthInBytes; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath = NULL; + MAC_ADDR_DEVICE_PATH MacDeviceNode; + EFI_USB_DEVICE_DESCRIPTOR Device; + UINT32 Index; + + // + // Allocate the device structure + // + LengthInBytes = sizeof ( *pNicDevice ); + Status = gBS->AllocatePool ( + EfiRuntimeServicesData, + LengthInBytes, + (VOID **) &pNicDevice + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "gBS->AllocatePool:pNicDevice ERROR Status = %r\n", Status)); + goto EXIT; + } + + // + // Set the structure signature + // + ZeroMem ( pNicDevice, LengthInBytes ); + pNicDevice->Signature = DEV_SIGNATURE; + + Status = gBS->OpenProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + (VOID **) &pNicDevice->pUsbIo, + pThis->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "gBS->OpenProtocol:EFI_USB_IO_PROTOCOL ERROR Status = %r\n", Status)); + gBS->FreePool ( pNicDevice ); + goto EXIT; + } + + // + // Initialize the simple network protocol + // + Status = SN_Setup ( pNicDevice ); + + if (EFI_ERROR(Status)){ + DEBUG ((EFI_D_ERROR, "SN_Setup ERROR Status = %r\n", Status)); + gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + pThis->DriverBindingHandle, + Controller + ); + gBS->FreePool ( pNicDevice ); + goto EXIT; + } + + Status = pNicDevice->pUsbIo->UsbGetDeviceDescriptor ( pNicDevice->pUsbIo, &Device ); + if (EFI_ERROR ( Status )) { + gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + pThis->DriverBindingHandle, + Controller + ); + gBS->FreePool ( pNicDevice ); + goto EXIT; + } else { + // + // Validate the adapter + // + for (Index = 0; ASIX_DONGLES[Index].VendorId != 0; Index++) { + if (ASIX_DONGLES[Index].VendorId == Device.IdVendor && + ASIX_DONGLES[Index].ProductId == Device.IdProduct) { + break; + } + } + + if (ASIX_DONGLES[Index].VendorId == 0) { + gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + pThis->DriverBindingHandle, + Controller + ); + gBS->FreePool ( pNicDevice ); + goto EXIT; + } + + pNicDevice->Flags = ASIX_DONGLES[Index].Flags; + } + + // + // Set Device Path + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, + pThis->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "gBS->OpenProtocol:EFI_DEVICE_PATH_PROTOCOL error. Status = %r\n", + Status)); + gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + pThis->DriverBindingHandle, + Controller + ); + gBS->FreePool ( pNicDevice ); + goto EXIT; + } + + ZeroMem (&MacDeviceNode, sizeof (MAC_ADDR_DEVICE_PATH)); + MacDeviceNode.Header.Type = MESSAGING_DEVICE_PATH; + MacDeviceNode.Header.SubType = MSG_MAC_ADDR_DP; + + SetDevicePathNodeLength (&MacDeviceNode.Header, sizeof (MAC_ADDR_DEVICE_PATH)); + + CopyMem (&MacDeviceNode.MacAddress, + &pNicDevice->SimpleNetworkData.CurrentAddress, + PXE_HWADDR_LEN_ETHER); + + MacDeviceNode.IfType = pNicDevice->SimpleNetworkData.IfType; + + pNicDevice->MyDevPath = AppendDevicePathNode ( + ParentDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &MacDeviceNode + ); + + pNicDevice->Controller = NULL; + + // + // Install both the simple network and device path protocols. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &pNicDevice->Controller, + &gEfiCallerIdGuid, + pNicDevice, + &gEfiSimpleNetworkProtocolGuid, + &pNicDevice->SimpleNetwork, + &gEfiDevicePathProtocolGuid, + pNicDevice->MyDevPath, + NULL + ); + + if (EFI_ERROR(Status)){ + DEBUG ((EFI_D_ERROR, "gBS->InstallMultipleProtocolInterfaces error. Status = %r\n", + Status)); + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + pThis->DriverBindingHandle, + Controller); + gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + pThis->DriverBindingHandle, + Controller + ); + gBS->FreePool ( pNicDevice ); + goto EXIT; + } + + // + // Open For Child Device + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + (VOID **) &pNicDevice->pUsbIo, + pThis->DriverBindingHandle, + pNicDevice->Controller, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + if (EFI_ERROR(Status)){ + gBS->UninstallMultipleProtocolInterfaces ( + &pNicDevice->Controller, + &gEfiCallerIdGuid, + pNicDevice, + &gEfiSimpleNetworkProtocolGuid, + &pNicDevice->SimpleNetwork, + &gEfiDevicePathProtocolGuid, + pNicDevice->MyDevPath, + NULL + ); + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + pThis->DriverBindingHandle, + Controller); + gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + pThis->DriverBindingHandle, + Controller + ); + gBS->FreePool ( pNicDevice ); + } + +EXIT: + return Status; + +} + +/** + Stop this driver on Controller by removing NetworkInterfaceIdentifier protocol and + closing the DevicePath and PciIo protocols on Controller. + + @param [in] pThis Protocol instance pointer. + @param [in] Controller Handle of device to stop driver on. + @param [in] NumberOfChildren How many children need to be stopped. + @param [in] pChildHandleBuffer Not used. + + @retval EFI_SUCCESS This driver is removed Controller. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +DriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL * pThis, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE * ChildHandleBuffer + ) +{ + BOOLEAN AllChildrenStopped; + UINTN Index; + EFI_SIMPLE_NETWORK_PROTOCOL *SimpleNetwork; + EFI_STATUS Status = EFI_SUCCESS; + NIC_DEVICE *pNicDevice; + + // + // Complete all outstanding transactions to Controller. + // Don't allow any new transaction to Controller to be started. + // + if (NumberOfChildren == 0) { + + Status = gBS->OpenProtocol ( + Controller, + &gEfiSimpleNetworkProtocolGuid, + (VOID **) &SimpleNetwork, + pThis->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR(Status)) { + // + // This is a 2nd type handle(multi-lun root), it needs to close devicepath + // and usbio protocol. + // + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + pThis->DriverBindingHandle, + Controller + ); + gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + pThis->DriverBindingHandle, + Controller + ); + return EFI_SUCCESS; + } + + pNicDevice = DEV_FROM_SIMPLE_NETWORK ( SimpleNetwork ); + + Status = gBS->UninstallMultipleProtocolInterfaces ( + Controller, + &gEfiCallerIdGuid, + pNicDevice, + &gEfiSimpleNetworkProtocolGuid, + &pNicDevice->SimpleNetwork, + &gEfiDevicePathProtocolGuid, + pNicDevice->MyDevPath, + NULL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Close the bus driver + // + Status = gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + pThis->DriverBindingHandle, + Controller + ); + + if (EFI_ERROR(Status)){ + DEBUG ((EFI_D_ERROR, "driver stop: gBS->CloseProtocol:EfiDevicePathProtocol error. Status %r\n", Status)); + } + + Status = gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + pThis->DriverBindingHandle, + Controller + ); + + if (EFI_ERROR(Status)){ + DEBUG ((EFI_D_ERROR, "driver stop: gBS->CloseProtocol:EfiUsbIoProtocol error. Status %r\n", Status)); + } + return EFI_SUCCESS; + } + AllChildrenStopped = TRUE; + + for (Index = 0; Index < NumberOfChildren; Index++) { + + Status = gBS->OpenProtocol ( + ChildHandleBuffer[Index], + &gEfiSimpleNetworkProtocolGuid, + (VOID **) &SimpleNetwork, + pThis->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + DEBUG ((EFI_D_ERROR, "Fail to stop No.%d multi-lun child handle when opening SimpleNetwork\n", (UINT32)Index)); + continue; + } + + pNicDevice = DEV_FROM_SIMPLE_NETWORK ( SimpleNetwork ); + + gBS->CloseProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + pThis->DriverBindingHandle, + ChildHandleBuffer[Index] + ); + + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandleBuffer[Index], + &gEfiCallerIdGuid, + pNicDevice, + &gEfiSimpleNetworkProtocolGuid, + &pNicDevice->SimpleNetwork, + &gEfiDevicePathProtocolGuid, + pNicDevice->MyDevPath, + NULL + ); + + if (EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + Controller, + &gEfiUsbIoProtocolGuid, + (VOID **) &pNicDevice->pUsbIo, + pThis->DriverBindingHandle, + ChildHandleBuffer[Index], + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } + else { + int i; + RX_PKT * pCurr = pNicDevice->QueueHead; + RX_PKT * pFree; + + for ( i = 0 ; i < MAX_QUEUE_SIZE ; i++) { + if ( NULL != pCurr ) { + pFree = pCurr; + pCurr = pCurr->pNext; + gBS->FreePool (pFree); + } + } + + if ( NULL != pNicDevice->pRxTest) + gBS->FreePool (pNicDevice->pRxTest); + + if ( NULL != pNicDevice->pTxTest) + gBS->FreePool (pNicDevice->pTxTest); + + if ( NULL != pNicDevice->MyDevPath) + gBS->FreePool (pNicDevice->MyDevPath); + + if ( NULL != pNicDevice) + gBS->FreePool (pNicDevice); + } + } + + if (!AllChildrenStopped) { + return EFI_DEVICE_ERROR; + } + return EFI_SUCCESS; +} + + +/** + Driver binding protocol declaration +**/ +EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = { + DriverSupported, + DriverStart, + DriverStop, + 0xa, + NULL, + NULL +}; + + +/** + Ax88772 driver unload routine. + + @param [in] ImageHandle Handle for the image. + + @retval EFI_SUCCESS Image may be unloaded + +**/ +EFI_STATUS +EFIAPI +DriverUnload ( + IN EFI_HANDLE ImageHandle + ) +{ + UINTN BufferSize; + UINTN Index; + UINTN Max; + EFI_HANDLE * pHandle; + EFI_STATUS Status; + + // + // Determine which devices are using this driver + // + BufferSize = 0; + pHandle = NULL; + Status = gBS->LocateHandle ( + ByProtocol, + &gEfiCallerIdGuid, + NULL, + &BufferSize, + NULL ); + if ( EFI_BUFFER_TOO_SMALL == Status ) { + for ( ; ; ) { + // + // One or more block IO devices are present + // + Status = gBS->AllocatePool ( + EfiRuntimeServicesData, + BufferSize, + (VOID **) &pHandle + ); + if ( EFI_ERROR ( Status )) { + DEBUG ((EFI_D_ERROR, "Insufficient memory, failed handle buffer allocation\r\n")); + break; + } + + // + // Locate the block IO devices + // + Status = gBS->LocateHandle ( + ByProtocol, + &gEfiCallerIdGuid, + NULL, + &BufferSize, + pHandle ); + if ( EFI_ERROR ( Status )) { + // + // Error getting handles + // + break; + } + + // + // Remove any use of the driver + // + Max = BufferSize / sizeof ( pHandle[ 0 ]); + for ( Index = 0; Max > Index; Index++ ) { + Status = DriverStop ( &gDriverBinding, + pHandle[ Index ], + 0, + NULL ); + if ( EFI_ERROR ( Status )) { + DEBUG ((EFI_D_ERROR, "WARNING - Failed to shutdown the driver on handle %08x\r\n", pHandle[ Index ])); + break; + } + } + break; + } + } + else { + if ( EFI_NOT_FOUND == Status ) { + // + // No devices were found + // + Status = EFI_SUCCESS; + } + } + + // + // Free the handle array + // + if ( NULL != pHandle ) { + gBS->FreePool ( pHandle ); + } + + // + // Remove the protocols installed by the EntryPoint routine. + // + if ( !EFI_ERROR ( Status )) { + gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiDriverBindingProtocolGuid, + &gDriverBinding, + &gEfiComponentNameProtocolGuid, + &gComponentName, + &gEfiComponentName2ProtocolGuid, + &gComponentName2, + NULL + ); + + DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "Removed: gEfiComponentName2ProtocolGuid from 0x%08x\r\n", + ImageHandle )); + DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "Removed: gEfiComponentNameProtocolGuid from 0x%08x\r\n", + ImageHandle )); + DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, + "Removed: gEfiDriverBindingProtocolGuid from 0x%08x\r\n", + ImageHandle )); + + } + + return Status; +} + + +/** +Ax88772 driver entry point. + +@param [in] ImageHandle Handle for the image. +@param [in] pSystemTable Address of the system table. + +@retval EFI_SUCCESS Image successfully loaded. + +**/ +EFI_STATUS +EFIAPI +EntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE * pSystemTable + ) +{ + EFI_STATUS Status; + + // + // Add the driver to the list of drivers + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + pSystemTable, + &gDriverBinding, + ImageHandle, + &gComponentName, + &gComponentName2 + ); + if ( !EFI_ERROR ( Status )) { + DEBUG ((EFI_D_INFO, "Installed: gEfiDriverBindingProtocolGuid on 0x%08x\r\n", + ImageHandle)); + DEBUG ((EFI_D_INFO, "Installed: gEfiComponentNameProtocolGuid on 0x%08x\r\n", + ImageHandle)); + DEBUG ((EFI_D_INFO,"Installed: gEfiComponentName2ProtocolGuid on 0x%08x\r\n", + ImageHandle )); + + } + return Status; +} diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/SimpleNetwork.c b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/SimpleNetwork.c new file mode 100644 index 0000000000..76babedb20 --- /dev/null +++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/SimpleNetwork.c @@ -0,0 +1,1657 @@ +/** @file + Provides the Simple Network functions. + + Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved. + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ax88772.h" + +/** + This function updates the filtering on the receiver. + + This support routine calls ::Ax88772MacAddressSet to update + the MAC address. This routine then rebuilds the multicast + hash by calling ::Ax88772MulticastClear and ::Ax88772MulticastSet. + Finally this routine enables the receiver by calling + ::Ax88772RxControl. + + @param [in] pSimpleNetwork Simple network mode pointer + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +ReceiveFilterUpdate ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork + ) +{ + EFI_SIMPLE_NETWORK_MODE * pMode; + NIC_DEVICE * pNicDevice; + EFI_STATUS Status; + UINT32 Index; + + // + // Set the MAC address + // + pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork ); + pMode = pSimpleNetwork->Mode; + + // + // Clear the multicast hash table + // + Ax88772MulticastClear ( pNicDevice ); + + // + // Load the multicast hash table + // + if ( 0 != ( pMode->ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST )) { + for ( Index = 0; Index < pMode->MCastFilterCount; Index++ ) { + // + // Enable the next multicast address + // + Ax88772MulticastSet ( pNicDevice, + &pMode->MCastFilter[ Index ].Addr[0]); + } + } + + Status = Ax88772RxControl ( pNicDevice, pMode->ReceiveFilterSetting ); + + return Status; +} + + +/** + This function updates the SNP driver status. + + This function gets the current interrupt and recycled transmit + buffer status from the network interface. The interrupt status + and the media status are returned as a bit mask in InterruptStatus. + If InterruptStatus is NULL, the interrupt status will not be read. + Upon successful return of the media status, the MediaPresent field + of EFI_SIMPLE_NETWORK_MODE will be updated to reflect any change + of media status. If TxBuf is not NULL, a recycled transmit buffer + address will be retrived. If a recycled transmit buffer address + is returned in TxBuf, then the buffer has been successfully + transmitted, and the status for that buffer is cleared. + + This function calls ::Ax88772Rx to update the media status and + queue any receive packets. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] pInterruptStatus A pointer to the bit mask of the current active interrupts. + If this is NULL, the interrupt status will not be read from + the device. If this is not NULL, the interrupt status will + be read from teh device. When the interrupt status is read, + it will also be cleared. Clearing the transmit interrupt + does not empty the recycled transmit buffer array. + @param [out] ppTxBuf Recycled transmit buffer address. The network interface will + not transmit if its internal recycled transmit buffer array is + full. Reading the transmit buffer does not clear the transmit + interrupt. If this is NULL, then the transmit buffer status + will not be read. If there are not transmit buffers to recycle + and TxBuf is not NULL, *TxBuf will be set to NULL. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + +**/ +EFI_STATUS +EFIAPI +SN_GetStatus ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + OUT UINT32 * pInterruptStatus, + OUT VOID ** ppTxBuf + ) +{ + EFI_SIMPLE_NETWORK_MODE * pMode; + NIC_DEVICE * pNicDevice; + EFI_STATUS Status; + BOOLEAN bFullDuplex; + BOOLEAN bLinkUp; + BOOLEAN bSpeed100; + EFI_TPL TplPrevious; + + TplPrevious = gBS->RaiseTPL(TPL_CALLBACK); + // + // Verify the parameters + // + if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) { + // + // Return the transmit buffer + // + + pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork ); + if (( NULL != ppTxBuf ) && ( NULL != pNicDevice->pTxBuffer )) { + *ppTxBuf = pNicDevice->pTxBuffer; + pNicDevice->pTxBuffer = NULL; + } + + // + // Determine if interface is running + // + pMode = pSimpleNetwork->Mode; + if ( EfiSimpleNetworkInitialized == pMode->State ) { + + if ( pNicDevice->LinkIdleCnt > MAX_LINKIDLE_THRESHOLD) { + + bLinkUp = pNicDevice->bLinkUp; + bSpeed100 = pNicDevice->b100Mbps; + bFullDuplex = pNicDevice->bFullDuplex; + Status = Ax88772NegotiateLinkComplete ( pNicDevice, + &pNicDevice->PollCount, + &pNicDevice->bComplete, + &pNicDevice->bLinkUp, + &pNicDevice->b100Mbps, + &pNicDevice->bFullDuplex ); + + // + // Determine if the autonegotiation is complete + // + if ( pNicDevice->bComplete ) { + if ( pNicDevice->bLinkUp ) { + if (( bSpeed100 && ( !pNicDevice->b100Mbps )) + || (( !bSpeed100 ) && pNicDevice->b100Mbps ) + || ( bFullDuplex && ( !pNicDevice->bFullDuplex )) + || (( !bFullDuplex ) && pNicDevice->bFullDuplex )) { + pNicDevice->PollCount = 0; + DEBUG (( EFI_D_INFO , "Reset to establish proper link setup: %d Mbps, %a duplex\r\n", + pNicDevice->b100Mbps ? 100 : 10, pNicDevice->bFullDuplex ? "Full" : "Half")); + Status = SN_Reset ( &pNicDevice->SimpleNetwork, FALSE ); + } + if (( !bLinkUp ) && pNicDevice->bLinkUp ) { + // + // Display the autonegotiation status + // + DEBUG (( EFI_D_INFO , "Link: Up, %d Mbps, %a duplex\r\n", + pNicDevice->b100Mbps ? 100 : 10, pNicDevice->bFullDuplex ? "Full" : "Half")); + + } + pNicDevice->LinkIdleCnt = 0; + } + } + // + // Update the link status + // + if ( bLinkUp && ( !pNicDevice->bLinkUp )) { + DEBUG (( EFI_D_INFO , "Link: Down\r\n")); + } + } + + pMode->MediaPresent = pNicDevice->bLinkUp; + // + // Return the interrupt status + // + if ( NULL != pInterruptStatus ) { + *pInterruptStatus = 0; + } + Status = EFI_SUCCESS; + } + else { + if ( EfiSimpleNetworkStarted == pMode->State ) { + Status = EFI_DEVICE_ERROR; + } + else { + Status = EFI_NOT_STARTED; + } + } + + } + else { + Status = EFI_INVALID_PARAMETER; + } + gBS->RestoreTPL(TplPrevious) ; + + return Status; +} + + +/** + Resets the network adapter and allocates the transmit and receive buffers + required by the network interface; optionally, also requests allocation of + additional transmit and receive buffers. This routine must be called before + any other routine in the Simple Network protocol is called. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] ExtraRxBufferSize Size in bytes to add to the receive buffer allocation + @param [in] ExtraTxBufferSize Size in bytes to add to the transmit buffer allocation + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_OUT_OF_RESOURCES There was not enough memory for the transmit and receive buffers + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Initialize ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN UINTN ExtraRxBufferSize, + IN UINTN ExtraTxBufferSize + ) +{ + EFI_SIMPLE_NETWORK_MODE * pMode; + EFI_STATUS Status; + UINT32 TmpState; + EFI_TPL TplPrevious; + + TplPrevious = gBS->RaiseTPL (TPL_CALLBACK); + // + // Verify the parameters + // + if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) { + // + // Determine if the interface is already started + // + pMode = pSimpleNetwork->Mode; + if ( EfiSimpleNetworkStarted == pMode->State ) { + if (( 0 == ExtraRxBufferSize ) && ( 0 == ExtraTxBufferSize )) { + // + // Start the adapter + // + TmpState = pMode->State; + pMode->State = EfiSimpleNetworkInitialized; + Status = SN_Reset ( pSimpleNetwork, FALSE ); + if ( EFI_ERROR ( Status )) { + // + // Update the network state + // + pMode->State = TmpState; + DEBUG (( EFI_D_ERROR , "SN_reset failed\n")); + } + } + else { + DEBUG (( EFI_D_ERROR , "Increase ExtraRxBufferSize = %d ExtraTxBufferSize=%d\n", + ExtraRxBufferSize, ExtraTxBufferSize)); + Status = EFI_UNSUPPORTED; + } + } + else { + Status = EFI_NOT_STARTED; + } + } + else { + Status = EFI_INVALID_PARAMETER; + } + gBS->RestoreTPL (TplPrevious); + + return Status; +} + + +/** + This function converts a multicast IP address to a multicast HW MAC address + for all packet transactions. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] bIPv6 Set to TRUE if the multicast IP address is IPv6 [RFC2460]. + Set to FALSE if the multicast IP address is IPv4 [RFC 791]. + @param [in] pIP The multicast IP address that is to be converted to a + multicast HW MAC address. + @param [in] pMAC The multicast HW MAC address that is to be generated from IP. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_MCastIPtoMAC ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN BOOLEAN bIPv6, + IN EFI_IP_ADDRESS * pIP, + OUT EFI_MAC_ADDRESS * pMAC + ) +{ + EFI_STATUS Status; + EFI_TPL TplPrevious; + + TplPrevious = gBS->RaiseTPL(TPL_CALLBACK); + // + // Get pointer to SNP driver instance for *this. + // + if (pSimpleNetwork == NULL) { + gBS->RestoreTPL(TplPrevious); + return EFI_INVALID_PARAMETER; + } + + if (pIP == NULL || pMAC == NULL) { + gBS->RestoreTPL(TplPrevious); + return EFI_INVALID_PARAMETER; + } + + if (bIPv6){ + Status = EFI_UNSUPPORTED; + } + else { + // + // check if the ip given is a mcast IP + // + if ((pIP->v4.Addr[0] & 0xF0) != 0xE0) { + gBS->RestoreTPL(TplPrevious); + return EFI_INVALID_PARAMETER; + } + else { + if (pSimpleNetwork->Mode->State == EfiSimpleNetworkInitialized) + { + pMAC->Addr[0] = 0x01; + pMAC->Addr[1] = 0x00; + pMAC->Addr[2] = 0x5e; + pMAC->Addr[3] = (UINT8) (pIP->v4.Addr[1] & 0x7f); + pMAC->Addr[4] = (UINT8) pIP->v4.Addr[2]; + pMAC->Addr[5] = (UINT8) pIP->v4.Addr[3]; + Status = EFI_SUCCESS; + } + else if (pSimpleNetwork->Mode->State == EfiSimpleNetworkStarted) { + Status = EFI_DEVICE_ERROR; + } + else { + Status = EFI_NOT_STARTED; + } + gBS->RestoreTPL(TplPrevious); + } + } + return Status; +} + + +/** + This function performs read and write operations on the NVRAM device + attached to a network interface. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] ReadWrite TRUE for read operations, FALSE for write operations. + @param [in] Offset Byte offset in the NVRAM device at which to start the + read or write operation. This must be a multiple of + NvRamAccessSize and less than NvRamSize. + @param [in] BufferSize The number of bytes to read or write from the NVRAM device. + This must also be a multiple of NvramAccessSize. + @param [in, out] pBuffer A pointer to the data buffer. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_NvData ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN BOOLEAN ReadWrite, + IN UINTN Offset, + IN UINTN BufferSize, + IN OUT VOID * pBuffer + ) +{ + EFI_STATUS Status; + // + // This is not currently supported + // + Status = EFI_UNSUPPORTED; + return Status; +} + +VOID +FillPkt2Queue ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN UINTN BufLength) +{ + + UINT16 * pLength; + UINT16 * pLengthBar; + UINT8* pData; + UINT32 offset; + NIC_DEVICE * pNicDevice; + + pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork); + for ( offset = 0; offset < BufLength; ){ + pLength = (UINT16*) (pNicDevice->pBulkInBuff + offset); + pLengthBar = (UINT16*) (pNicDevice->pBulkInBuff + offset +2); + + *pLength &= 0x7ff; + *pLengthBar &= 0x7ff; + *pLengthBar |= 0xf800; + + if ((*pLength ^ *pLengthBar ) != 0xFFFF) { + DEBUG (( EFI_D_ERROR , "Pkt length error. BufLength = %d\n", BufLength)); + return; + } + + if (TRUE == pNicDevice->pNextFill->f_Used) { + return; + } + else { + pData = pNicDevice->pBulkInBuff + offset + 4; + pNicDevice->pNextFill->f_Used = TRUE; + pNicDevice->pNextFill->Length = *pLength; + CopyMem (&pNicDevice->pNextFill->Data[0], pData, *pLength); + + pNicDevice->pNextFill = pNicDevice->pNextFill->pNext; + offset += ((*pLength + HW_HDR_LENGTH - 1) &~3) + 1; + pNicDevice->PktCntInQueue++; + } + + } +} + +EFI_STATUS +EFIAPI +SN_Receive ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + OUT UINTN * pHeaderSize, + OUT UINTN * pBufferSize, + OUT VOID * pBuffer, + OUT EFI_MAC_ADDRESS * pSrcAddr, + OUT EFI_MAC_ADDRESS * pDestAddr, + OUT UINT16 * pProtocol + ) +{ + EFI_SIMPLE_NETWORK_MODE * pMode; + NIC_DEVICE * pNicDevice; + EFI_STATUS Status; + EFI_TPL TplPrevious; + UINT16 Type; + EFI_USB_IO_PROTOCOL *pUsbIo; + UINTN LengthInBytes; + UINT32 TransferStatus; + RX_PKT * pFirstFill; + TplPrevious = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Verify the parameters + // + if (( NULL != pSimpleNetwork ) && + ( NULL != pSimpleNetwork->Mode ) && + (NULL != pBufferSize) && + (NULL != pBuffer)) { + // + // The interface must be running + // + pMode = pSimpleNetwork->Mode; + if ( EfiSimpleNetworkInitialized == pMode->State ) { + // + // Update the link status + // + pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork ); + pNicDevice->LinkIdleCnt++; + pMode->MediaPresent = pNicDevice->bLinkUp; + + if ( pMode->MediaPresent && pNicDevice->bComplete) { + + + if (pNicDevice->PktCntInQueue != 0 ) { + DEBUG (( EFI_D_INFO, "pNicDevice->PktCntInQueue = %d\n", + pNicDevice->PktCntInQueue)); + } + + LengthInBytes = MAX_BULKIN_SIZE; + if (pNicDevice->PktCntInQueue == 0 ){ + // + // Attempt to do bulk in + // + SetMem (&pNicDevice->pBulkInBuff[0], 4, 0); + pUsbIo = pNicDevice->pUsbIo; + Status = pUsbIo->UsbBulkTransfer ( pUsbIo, + USB_ENDPOINT_DIR_IN | BULK_IN_ENDPOINT, + &pNicDevice->pBulkInBuff[0], + &LengthInBytes, + BULKIN_TIMEOUT, + &TransferStatus ); + + if (LengthInBytes != 0 && !EFI_ERROR(Status) && !EFI_ERROR(TransferStatus) ){ + FillPkt2Queue(pSimpleNetwork, LengthInBytes); + } + } + + pFirstFill = pNicDevice->pFirstFill; + + if (TRUE == pFirstFill->f_Used) { + ETHERNET_HEADER * pHeader; + pNicDevice->LinkIdleCnt = 0; + CopyMem (pBuffer, &pFirstFill->Data[0], pFirstFill->Length); + pHeader = (ETHERNET_HEADER *) &pFirstFill->Data[0]; + + DEBUG (( EFI_D_INFO, "RX: %02x-%02x-%02x-%02x-%02x-%02x " + "%02x-%02x-%02x-%02x-%02x-%02x %02x-%02x %d bytes\r\n", + pFirstFill->Data[0], + pFirstFill->Data[1], + pFirstFill->Data[2], + pFirstFill->Data[3], + pFirstFill->Data[4], + pFirstFill->Data[5], + pFirstFill->Data[6], + pFirstFill->Data[7], + pFirstFill->Data[8], + pFirstFill->Data[9], + pFirstFill->Data[10], + pFirstFill->Data[11], + pFirstFill->Data[12], + pFirstFill->Data[13], + pFirstFill->Length)); + + if ( NULL != pHeaderSize ) { + *pHeaderSize = sizeof ( *pHeader ); + } + if ( NULL != pDestAddr ) { + CopyMem ( pDestAddr, &pHeader->dest_addr, PXE_HWADDR_LEN_ETHER ); + } + if ( NULL != pSrcAddr ) { + CopyMem ( pSrcAddr, &pHeader->src_addr, PXE_HWADDR_LEN_ETHER ); + } + if ( NULL != pProtocol ) { + Type = pHeader->type; + Type = (UINT16)(( Type >> 8 ) | ( Type << 8 )); + *pProtocol = Type; + } + Status = EFI_SUCCESS; + if (*pBufferSize < pFirstFill->Length) { + DEBUG (( EFI_D_ERROR, "RX: Buffer was too small")); + Status = EFI_BUFFER_TOO_SMALL; + } + *pBufferSize = pFirstFill->Length; + pFirstFill->f_Used = FALSE; + pNicDevice->pFirstFill = pFirstFill->pNext; + pNicDevice->PktCntInQueue--; + } + else { + pNicDevice->LinkIdleCnt++; + Status = EFI_NOT_READY; + } + } + else { + // + // Link no up + // + pNicDevice->LinkIdleCnt++; + Status = EFI_NOT_READY; + } + + } + else { + if (EfiSimpleNetworkStarted == pMode->State) { + Status = EFI_DEVICE_ERROR; + } + else { + Status = EFI_NOT_STARTED; + } + } + } + else { + Status = EFI_INVALID_PARAMETER; + } + gBS->RestoreTPL (TplPrevious); + return Status; +} + +/** + This function is used to enable and disable the hardware and software receive + filters for the underlying network device. + + The receive filter change is broken down into three steps: + + 1. The filter mask bits that are set (ON) in the Enable parameter + are added to the current receive filter settings. + + 2. The filter mask bits that are set (ON) in the Disable parameter + are subtracted from the updated receive filter settins. + + 3. If the resulting filter settigns is not supported by the hardware + a more liberal setting is selected. + + If the same bits are set in the Enable and Disable parameters, then the bits + in the Disable parameter takes precedence. + + If the ResetMCastFilter parameter is TRUE, then the multicast address list + filter is disabled (irregardless of what other multicast bits are set in + the enable and Disable parameters). The SNP->Mode->MCastFilterCount field + is set to zero. The SNP->Mode->MCastFilter contents are undefined. + + After enableing or disabling receive filter settings, software should + verify the new settings by checking the SNP->Mode->ReceeiveFilterSettings, + SNP->Mode->MCastFilterCount and SNP->Mode->MCastFilter fields. + + Note: Some network drivers and/or devices will automatically promote + receive filter settings if the requested setting can not be honored. + For example, if a request for four multicast addresses is made and + the underlying hardware only supports two multicast addresses the + driver might set the promiscuous or promiscuous multicast receive filters + instead. The receiving software is responsible for discarding any extra + packets that get through the hardware receive filters. + + If ResetMCastFilter is TRUE, then the multicast receive filter list + on the network interface will be reset to the default multicast receive + filter list. If ResetMCastFilter is FALSE, and this network interface + allows the multicast receive filter list to be modified, then the + MCastFilterCnt and MCastFilter are used to update the current multicast + receive filter list. The modified receive filter list settings can be + found in the MCastFilter field of EFI_SIMPLE_NETWORK_MODE. + + This routine calls ::ReceiveFilterUpdate to update the receive + state in the network adapter. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] Enable A bit mask of receive filters to enable on the network interface. + @param [in] Disable A bit mask of receive filters to disable on the network interface. + For backward compatibility with EFI 1.1 platforms, the + EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST bit must be set + when the ResetMCastFilter parameter is TRUE. + @param [in] bResetMCastFilter Set to TRUE to reset the contents of the multicast receive + filters on the network interface to their default values. + @param [in] MCastFilterCnt Number of multicast HW MAC address in the new MCastFilter list. + This value must be less than or equal to the MaxMCastFilterCnt + field of EFI_SIMPLE_NETWORK_MODE. This field is optional if + ResetMCastFilter is TRUE. + @param [in] pMCastFilter A pointer to a list of new multicast receive filter HW MAC + addresses. This list will replace any existing multicast + HW MAC address list. This field is optional if ResetMCastFilter + is TRUE. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_ReceiveFilters ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN UINT32 Enable, + IN UINT32 Disable, +/* +#define EFI_SIMPLE_NETWORK_RECEIVE_UNICAST 0x01 +#define EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST 0x02 +#define EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST 0x04 +#define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS 0x08 +#define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST 0x10 +*/ + IN BOOLEAN bResetMCastFilter, + IN UINTN MCastFilterCnt, + IN EFI_MAC_ADDRESS * pMCastFilter + ) +{ + EFI_SIMPLE_NETWORK_MODE * pMode; + EFI_STATUS Status = EFI_SUCCESS; + EFI_TPL TplPrevious; + + TplPrevious = gBS->RaiseTPL(TPL_CALLBACK); + pMode = pSimpleNetwork->Mode; + + if (pSimpleNetwork == NULL) { + gBS->RestoreTPL(TplPrevious); + return EFI_INVALID_PARAMETER; + } + + switch (pMode->State) { + case EfiSimpleNetworkInitialized: + break; + case EfiSimpleNetworkStopped: + Status = EFI_NOT_STARTED; + gBS->RestoreTPL(TplPrevious); + return Status; + default: + Status = EFI_DEVICE_ERROR; + gBS->RestoreTPL(TplPrevious); + return Status; + } + + // + // check if we are asked to enable or disable something that the UNDI + // does not even support! + // + if (((Enable &~pMode->ReceiveFilterMask) != 0) || + ((Disable &~pMode->ReceiveFilterMask) != 0)) { + Status = EFI_INVALID_PARAMETER; + gBS->RestoreTPL(TplPrevious); + return Status; + } + + if (bResetMCastFilter) { + Disable |= (EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST & pMode->ReceiveFilterMask); + pMode->MCastFilterCount = 0; + if ( (0 == (pMode->ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST)) + && Enable == 0 && Disable == 2) { + gBS->RestoreTPL(TplPrevious); + return EFI_SUCCESS; + } + } + else { + if (MCastFilterCnt != 0) { + UINTN i; + EFI_MAC_ADDRESS * pMulticastAddress; + pMulticastAddress = pMCastFilter; + + if ((MCastFilterCnt > pMode->MaxMCastFilterCount) || + (pMCastFilter == NULL)) { + Status = EFI_INVALID_PARAMETER; + gBS->RestoreTPL(TplPrevious); + return Status; + } + + for ( i = 0 ; i < MCastFilterCnt ; i++ ) { + UINT8 tmp; + tmp = pMulticastAddress->Addr[0]; + if ( (tmp & 0x01) != 0x01 ) { + gBS->RestoreTPL(TplPrevious); + return EFI_INVALID_PARAMETER; + } + pMulticastAddress++; + } + + pMode->MCastFilterCount = (UINT32)MCastFilterCnt; + CopyMem (&pMode->MCastFilter[0], + pMCastFilter, + MCastFilterCnt * sizeof ( EFI_MAC_ADDRESS)); + } + } + + if (Enable == 0 && Disable == 0 && !bResetMCastFilter && MCastFilterCnt == 0) { + Status = EFI_SUCCESS; + gBS->RestoreTPL(TplPrevious); + return Status; + } + + if ((Enable & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0 && MCastFilterCnt == 0) { + Status = EFI_INVALID_PARAMETER; + gBS->RestoreTPL(TplPrevious); + return Status; + } + + pMode->ReceiveFilterSetting |= Enable; + pMode->ReceiveFilterSetting &= ~Disable; + Status = ReceiveFilterUpdate (pSimpleNetwork); + + if (EFI_DEVICE_ERROR == Status || EFI_INVALID_PARAMETER == Status) + Status = EFI_SUCCESS; + + gBS->RestoreTPL(TplPrevious); + return Status; +} + +/** + Reset the network adapter. + + Resets a network adapter and reinitializes it with the parameters that + were provided in the previous call to Initialize (). The transmit and + receive queues are cleared. Receive filters, the station address, the + statistics, and the multicast-IP-to-HW MAC addresses are not reset by + this call. + + This routine calls ::Ax88772Reset to perform the adapter specific + reset operation. This routine also starts the link negotiation + by calling ::Ax88772NegotiateLinkStart. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] bExtendedVerification Indicates that the driver may perform a more + exhaustive verification operation of the device + during reset. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Reset ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN BOOLEAN bExtendedVerification + ) +{ + EFI_SIMPLE_NETWORK_MODE * pMode; + NIC_DEVICE * pNicDevice; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + TplPrevious = gBS->RaiseTPL(TPL_CALLBACK); + // + // Verify the parameters + // + if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) { + pMode = pSimpleNetwork->Mode; + if ( EfiSimpleNetworkInitialized == pMode->State ) { + // + // Update the device state + // + pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork ); + pNicDevice->bComplete = FALSE; + pNicDevice->bLinkUp = FALSE; + pNicDevice->bHavePkt = FALSE; + pMode = pSimpleNetwork->Mode; + pMode->MediaPresent = FALSE; + + // + // Reset the device + // + Status = Ax88772Reset ( pNicDevice ); + if ( !EFI_ERROR ( Status )) { + // + // Update the receive filters in the adapter + // + Status = ReceiveFilterUpdate ( pSimpleNetwork ); + + // + // Try to get a connection to the network + // + if ( !EFI_ERROR ( Status )) { + // + // Start the autonegotiation + // + Status = Ax88772NegotiateLinkStart ( pNicDevice ); + } + } + } + else { + if (EfiSimpleNetworkStarted == pMode->State) { + Status = EFI_DEVICE_ERROR; + } + else { + Status = EFI_NOT_STARTED; + } + } + } + else { + Status = EFI_INVALID_PARAMETER; + } + gBS->RestoreTPL ( TplPrevious ); + return Status; +} + +/** + Initialize the simple network protocol. + + This routine calls ::Ax88772MacAddressGet to obtain the + MAC address. + + @param [in] pNicDevice NIC_DEVICE_INSTANCE pointer + + @retval EFI_SUCCESS Setup was successful + +**/ +EFI_STATUS +SN_Setup ( + IN NIC_DEVICE * pNicDevice + ) +{ + + + EFI_SIMPLE_NETWORK_MODE * pMode; + EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork; + EFI_STATUS Status; + RX_PKT * pCurr = NULL; + RX_PKT * pPrev = NULL; + + pSimpleNetwork = &pNicDevice->SimpleNetwork; + pSimpleNetwork->Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION; + pSimpleNetwork->Start = (EFI_SIMPLE_NETWORK_START)SN_Start; + pSimpleNetwork->Stop = (EFI_SIMPLE_NETWORK_STOP)SN_Stop; + pSimpleNetwork->Initialize = (EFI_SIMPLE_NETWORK_INITIALIZE)SN_Initialize; + pSimpleNetwork->Reset = (EFI_SIMPLE_NETWORK_RESET)SN_Reset; + pSimpleNetwork->Shutdown = (EFI_SIMPLE_NETWORK_SHUTDOWN)SN_Shutdown; + pSimpleNetwork->ReceiveFilters = (EFI_SIMPLE_NETWORK_RECEIVE_FILTERS)SN_ReceiveFilters; + pSimpleNetwork->StationAddress = (EFI_SIMPLE_NETWORK_STATION_ADDRESS)SN_StationAddress; + pSimpleNetwork->Statistics = (EFI_SIMPLE_NETWORK_STATISTICS)SN_Statistics; + pSimpleNetwork->MCastIpToMac = (EFI_SIMPLE_NETWORK_MCAST_IP_TO_MAC)SN_MCastIPtoMAC; + pSimpleNetwork->NvData = (EFI_SIMPLE_NETWORK_NVDATA)SN_NvData; + pSimpleNetwork->GetStatus = (EFI_SIMPLE_NETWORK_GET_STATUS)SN_GetStatus; + pSimpleNetwork->Transmit = (EFI_SIMPLE_NETWORK_TRANSMIT)SN_Transmit; + pSimpleNetwork->Receive = (EFI_SIMPLE_NETWORK_RECEIVE)SN_Receive; + pSimpleNetwork->WaitForPacket = NULL; + pMode = &pNicDevice->SimpleNetworkData; + pSimpleNetwork->Mode = pMode; + pMode->State = EfiSimpleNetworkStopped; + pMode->HwAddressSize = PXE_HWADDR_LEN_ETHER; + pMode->MediaHeaderSize = sizeof ( ETHERNET_HEADER ); + pMode->MaxPacketSize = MAX_ETHERNET_PKT_SIZE; + pMode->NvRamSize = 0; + pMode->NvRamAccessSize = 0; + pMode->ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST + | EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST + | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST + | EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS + | EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST; + pMode->ReceiveFilterSetting = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST + | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST; + pMode->MaxMCastFilterCount = MAX_MCAST_FILTER_CNT; + pMode->MCastFilterCount = 0; + SetMem ( &pMode->BroadcastAddress, + PXE_HWADDR_LEN_ETHER, + 0xff ); + pMode->IfType = EfiNetworkInterfaceUndi; + pMode->MacAddressChangeable = TRUE; + pMode->MultipleTxSupported = FALSE; + pMode->MediaPresentSupported = TRUE; + pMode->MediaPresent = FALSE; + pNicDevice->LinkIdleCnt = 0; + // + // Read the MAC address + // + pNicDevice->PhyId = PHY_ID_INTERNAL; + pNicDevice->b100Mbps = TRUE; + pNicDevice->bFullDuplex = TRUE; + + Status = Ax88772MacAddressGet ( + pNicDevice, + &pMode->PermanentAddress.Addr[0]); + + if ( !EFI_ERROR ( Status )) { + int i; + // + // Use the hardware address as the current address + // + + CopyMem ( &pMode->CurrentAddress, + &pMode->PermanentAddress, + PXE_HWADDR_LEN_ETHER ); + + CopyMem ( &pNicDevice->MAC, + &pMode->PermanentAddress, + PXE_HWADDR_LEN_ETHER ); + + pNicDevice->PktCntInQueue = 0; + + for ( i = 0 ; i < MAX_QUEUE_SIZE ; i++) { + Status = gBS->AllocatePool ( EfiRuntimeServicesData, + sizeof (RX_PKT), + (VOID **) &pCurr); + if ( EFI_ERROR(Status)) { + DEBUG (( EFI_D_ERROR, "Memory are not enough\n")); + return Status; + } + pCurr->f_Used = FALSE; + + if ( i ) { + pPrev->pNext = pCurr; + } + else { + pNicDevice->QueueHead = pCurr; + } + + if (MAX_QUEUE_SIZE - 1 == i) { + pCurr->pNext = pNicDevice->QueueHead; + } + + pPrev = pCurr; + } + + pNicDevice->pNextFill = pNicDevice->QueueHead; + pNicDevice->pFirstFill = pNicDevice->QueueHead; + + Status = gBS->AllocatePool (EfiRuntimeServicesData, + MAX_BULKIN_SIZE, + (VOID **) &pNicDevice->pBulkInBuff); + + if (EFI_ERROR(Status)) { + DEBUG (( EFI_D_ERROR, "gBS->AllocatePool for pBulkInBuff error. Status = %r\n", + Status)); + return Status; + } + } + else { + DEBUG (( EFI_D_ERROR, "Ax88772MacAddressGet error. Status = %r\n", Status)); + return Status; + } + + Status = gBS->AllocatePool ( EfiRuntimeServicesData, + sizeof ( RX_TX_PACKET ), + (VOID **) &pNicDevice->pRxTest ); + + if (EFI_ERROR (Status)) { + DEBUG (( EFI_D_ERROR, "gBS->AllocatePool:pNicDevice->pRxTest error. Status = %r\n", + Status)); + return Status; + } + + Status = gBS->AllocatePool ( EfiRuntimeServicesData, + sizeof ( RX_TX_PACKET ), + (VOID **) &pNicDevice->pTxTest ); + + if (EFI_ERROR (Status)) { + DEBUG (( EFI_D_ERROR, "gBS->AllocatePool:pNicDevice->pTxTest error. Status = %r\n", + Status)); + gBS->FreePool (pNicDevice->pRxTest); + } + + return Status; +} + + +/** + This routine starts the network interface. + + @param [in] pSimpleNetwork Protocol instance pointer + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_ALREADY_STARTED The network interface was already started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Start ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork + ) +{ + NIC_DEVICE * pNicDevice; + EFI_SIMPLE_NETWORK_MODE * pMode; + EFI_STATUS Status; + EFI_TPL TplPrevious; + int i = 0; + RX_PKT * pCurr = NULL; + + TplPrevious = gBS->RaiseTPL(TPL_CALLBACK); + // + // Verify the parameters + // + Status = EFI_INVALID_PARAMETER; + if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) { + pMode = pSimpleNetwork->Mode; + if ( EfiSimpleNetworkStopped == pMode->State ) { + // + // Initialize the mode structuref + // NVRAM access is not supported + // + ZeroMem ( pMode, sizeof ( *pMode )); + + pMode->State = EfiSimpleNetworkStarted; + pMode->HwAddressSize = PXE_HWADDR_LEN_ETHER; + pMode->MediaHeaderSize = sizeof ( ETHERNET_HEADER ); + pMode->MaxPacketSize = MAX_ETHERNET_PKT_SIZE; + pMode->ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST + | EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST + | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST + | EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS + | EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST; + pMode->ReceiveFilterSetting = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST; + pMode->MaxMCastFilterCount = MAX_MCAST_FILTER_CNT; + pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork ); + Status = Ax88772MacAddressGet ( pNicDevice, &pMode->PermanentAddress.Addr[0]); + CopyMem ( &pMode->CurrentAddress, + &pMode->PermanentAddress, + sizeof ( pMode->CurrentAddress )); + SetMem(&pMode->BroadcastAddress, PXE_HWADDR_LEN_ETHER, 0xff); + pMode->IfType = EfiNetworkInterfaceUndi; + pMode->MacAddressChangeable = TRUE; + pMode->MultipleTxSupported = FALSE; + pMode->MediaPresentSupported = TRUE; + pMode->MediaPresent = FALSE; + pNicDevice->PktCntInQueue = 0; + pNicDevice->pNextFill = pNicDevice->QueueHead; + pNicDevice->pFirstFill = pNicDevice->QueueHead; + pCurr = pNicDevice->QueueHead; + + for ( i = 0 ; i < MAX_QUEUE_SIZE ; i++) { + pCurr->f_Used = FALSE; + pCurr = pCurr->pNext; + } + + } + else { + Status = EFI_ALREADY_STARTED; + } + } + gBS->RestoreTPL ( TplPrevious ); + return Status; +} + + +/** + Set the MAC address. + + This function modifies or resets the current station address of a + network interface. If Reset is TRUE, then the current station address + is set ot the network interface's permanent address. If Reset if FALSE + then the current station address is changed to the address specified by + pNew. + + This routine calls ::Ax88772MacAddressSet to update the MAC address + in the network adapter. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] bReset Flag used to reset the station address to the + network interface's permanent address. + @param [in] pNew New station address to be used for the network + interface. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_StationAddress ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN BOOLEAN bReset, + IN EFI_MAC_ADDRESS * pNew + ) +{ + NIC_DEVICE * pNicDevice; + EFI_SIMPLE_NETWORK_MODE * pMode; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + TplPrevious = gBS->RaiseTPL(TPL_CALLBACK); + // + // Verify the parameters + // + if (( NULL != pSimpleNetwork ) + && ( NULL != pSimpleNetwork->Mode ) + && (( bReset ) || ( ( !bReset) && ( NULL != pNew )))) { + // + // Verify that the adapter is already started + // + pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork ); + pMode = pSimpleNetwork->Mode; + if ( EfiSimpleNetworkInitialized == pMode->State ) { + // + // Determine the adapter MAC address + // + if ( bReset ) { + // + // Use the permanent address + // + CopyMem ( &pMode->CurrentAddress, + &pMode->PermanentAddress, + sizeof ( pMode->CurrentAddress )); + } + else { + // + // Use the specified address + // + CopyMem ( &pMode->CurrentAddress, + pNew, + sizeof ( pMode->CurrentAddress )); + } + + // + // Update the address on the adapter + // + Status = Ax88772MacAddressSet ( pNicDevice, &pMode->CurrentAddress.Addr[0]); + } + else { + if (EfiSimpleNetworkStarted == pMode->State) { + Status = EFI_DEVICE_ERROR; + } + else { + Status = EFI_NOT_STARTED; + } + } + } + else { + Status = EFI_INVALID_PARAMETER; + } + gBS->RestoreTPL ( TplPrevious ); + return Status; +} + + +/** + This function resets or collects the statistics on a network interface. + If the size of the statistics table specified by StatisticsSize is not + big enough for all of the statistics that are collected by the network + interface, then a partial buffer of statistics is returned in + StatisticsTable. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] bReset Set to TRUE to reset the statistics for the network interface. + @param [in, out] pStatisticsSize On input the size, in bytes, of StatisticsTable. On output + the size, in bytes, of the resulting table of statistics. + @param [out] pStatisticsTable A pointer to the EFI_NETWORK_STATISTICS structure that + conains the statistics. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_BUFFER_TOO_SMALL The pStatisticsTable is NULL or the buffer is too small. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + + typedef struct { + UINT64 RxTotalFrames; + UINT64 RxGoodFrames; + UINT64 RxUndersizeFrames; + UINT64 RxOversizeFrames; + UINT64 RxDroppedFrames; + UINT64 RxUnicastFrames; + UINT64 RxBroadcastFrames; + UINT64 RxMulticastFrames; + UINT64 RxCrcErrorFrames; + UINT64 RxTotalBytes; + UINT64 TxTotalFrames; + UINT64 TxGoodFrames; + UINT64 TxUndersizeFrames; + UINT64 TxOversizeFrames; + UINT64 TxDroppedFrames; + UINT64 TxUnicastFrames; + UINT64 TxBroadcastFrames; + UINT64 TxMulticastFrames; + UINT64 TxCrcErrorFrames; + UINT64 TxTotalBytes; + UINT64 Collisions; + UINT64 UnsupportedProtocol; + } EFI_NETWORK_STATISTICS; +**/ +EFI_STATUS +EFIAPI +SN_Statistics ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN BOOLEAN bReset, + IN OUT UINTN * pStatisticsSize, + OUT EFI_NETWORK_STATISTICS * pStatisticsTable + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_MODE * pMode; + // + // Verify the prarameters + // + if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) { + pMode = pSimpleNetwork->Mode; + // + // Determine if the interface is started + // + if (EfiSimpleNetworkInitialized == pMode->State){ + // + // Determine if the StatisticsSize is big enough + // + if (sizeof (EFI_NETWORK_STATISTICS) <= *pStatisticsSize){ + if (bReset) { + Status = EFI_SUCCESS; + } + else { + Status = EFI_UNSUPPORTED; + } + } + else { + Status = EFI_BUFFER_TOO_SMALL; + } + } + else{ + if (EfiSimpleNetworkStarted == pMode->State) { + Status = EFI_DEVICE_ERROR; + } + else { + Status = EFI_NOT_STARTED; + } + } + } + else { + Status = EFI_INVALID_PARAMETER; + } + + return Status; +} + + +/** + This function stops a network interface. This call is only valid + if the network interface is in the started state. + + @param [in] pSimpleNetwork Protocol instance pointer + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Stop ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork + ) +{ + EFI_SIMPLE_NETWORK_MODE * pMode; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + TplPrevious = gBS->RaiseTPL(TPL_CALLBACK); + // + // Verify the parameters + // + if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) { + // + // Determine if the interface is started + // + pMode = pSimpleNetwork->Mode; + if ( EfiSimpleNetworkStarted == pMode->State ) { + pMode->State = EfiSimpleNetworkStopped; + Status = EFI_SUCCESS; + } + else { + Status = EFI_NOT_STARTED; + } + } + else { + Status = EFI_INVALID_PARAMETER; + } + + gBS->RestoreTPL ( TplPrevious ); + return Status; +} + + +/** + This function releases the memory buffers assigned in the Initialize() call. + Pending transmits and receives are lost, and interrupts are cleared and disabled. + After this call, only Initialize() and Stop() calls may be used. + + @param [in] pSimpleNetwork Protocol instance pointer + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED The increased buffer size feature is not supported. + +**/ +EFI_STATUS +EFIAPI +SN_Shutdown ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork + ) +{ + EFI_SIMPLE_NETWORK_MODE * pMode; + UINT32 RxFilter; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + TplPrevious = gBS->RaiseTPL(TPL_CALLBACK); + // + // Verify the parameters + // + if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) { + // + // Determine if the interface is already started + // + pMode = pSimpleNetwork->Mode; + if ( EfiSimpleNetworkInitialized == pMode->State ) { + // + // Stop the adapter + // + RxFilter = pMode->ReceiveFilterSetting; + pMode->ReceiveFilterSetting = 0; + Status = SN_Reset ( pSimpleNetwork, FALSE ); + pMode->ReceiveFilterSetting = RxFilter; + if ( !EFI_ERROR ( Status )) { + + // + // Update the network state + // + pMode->State = EfiSimpleNetworkStarted; + } + else if ( EFI_DEVICE_ERROR == Status ) { + pMode->State = EfiSimpleNetworkStopped; + } + } + else { + Status = EFI_NOT_STARTED; + } + } + else { + Status = EFI_INVALID_PARAMETER; + } + gBS->RestoreTPL ( TplPrevious ); + return Status; +} + + +/** + Send a packet over the network. + + This function places the packet specified by Header and Buffer on + the transmit queue. This function performs a non-blocking transmit + operation. When the transmit is complete, the buffer is returned + via the GetStatus() call. + + This routine calls ::Ax88772Rx to empty the network adapter of + receive packets. The routine then passes the transmit packet + to the network adapter. + + @param [in] pSimpleNetwork Protocol instance pointer + @param [in] HeaderSize The size, in bytes, of the media header to be filled in by + the Transmit() function. If HeaderSize is non-zero, then + it must be equal to SimpleNetwork->Mode->MediaHeaderSize + and DestAddr and Protocol parameters must not be NULL. + @param [in] BufferSize The size, in bytes, of the entire packet (media header and + data) to be transmitted through the network interface. + @param [in] pBuffer A pointer to the packet (media header followed by data) to + to be transmitted. This parameter can not be NULL. If + HeaderSize is zero, then the media header is Buffer must + already be filled in by the caller. If HeaderSize is nonzero, + then the media header will be filled in by the Transmit() + function. + @param [in] pSrcAddr The source HW MAC address. If HeaderSize is zero, then + this parameter is ignored. If HeaderSize is nonzero and + SrcAddr is NULL, then SimpleNetwork->Mode->CurrentAddress + is used for the source HW MAC address. + @param [in] pDestAddr The destination HW MAC address. If HeaderSize is zero, then + this parameter is ignored. + @param [in] pProtocol The type of header to build. If HeaderSize is zero, then + this parameter is ignored. + + @retval EFI_SUCCESS This operation was successful. + @retval EFI_NOT_STARTED The network interface was not started. + @retval EFI_NOT_READY The network interface is too busy to accept this transmit request. + @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small. + @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid + EFI_SIMPLE_NETWORK_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + +**/ +EFI_STATUS +EFIAPI +SN_Transmit ( + IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork, + IN UINTN HeaderSize, + IN UINTN BufferSize, + IN VOID * pBuffer, + IN EFI_MAC_ADDRESS * pSrcAddr, + IN EFI_MAC_ADDRESS * pDestAddr, + IN UINT16 * pProtocol + ) +{ + ETHERNET_HEADER * pHeader; + EFI_SIMPLE_NETWORK_MODE * pMode; + NIC_DEVICE * pNicDevice; + EFI_USB_IO_PROTOCOL * pUsbIo; + EFI_STATUS Status; + UINTN TransferLength; + UINT32 TransferStatus; + UINT16 Type; + EFI_TPL TplPrevious; + + TplPrevious = gBS->RaiseTPL(TPL_CALLBACK); + + // Verify the parameters + // + if (( NULL != pSimpleNetwork ) && + ( NULL != pSimpleNetwork->Mode ) && + ( NULL != pBuffer) && + ( (HeaderSize == 0) || ( (NULL != pDestAddr) && (NULL != pProtocol) ))) { + // + // The interface must be running + // + pMode = pSimpleNetwork->Mode; + // + // Verify parameter of HeaderSize + // + if ((HeaderSize == 0) || (HeaderSize == pMode->MediaHeaderSize)){ + // + // Determine if BufferSize is big enough + // + if (BufferSize >= pMode->MediaHeaderSize){ + if ( EfiSimpleNetworkInitialized == pMode->State ) { + // + // Update the link status + // + pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork ); + pMode->MediaPresent = pNicDevice->bLinkUp; + + // + // Release the synchronization with Ax88772Timer + // + if ( pMode->MediaPresent && pNicDevice->bComplete) { + // + // Copy the packet into the USB buffer + // + + CopyMem ( &pNicDevice->pTxTest->Data[0], pBuffer, BufferSize ); + pNicDevice->pTxTest->Length = (UINT16) BufferSize; + + // + // Transmit the packet + // + pHeader = (ETHERNET_HEADER *) &pNicDevice->pTxTest->Data[0]; + if ( 0 != HeaderSize ) { + if ( NULL != pDestAddr ) { + CopyMem ( &pHeader->dest_addr, pDestAddr, PXE_HWADDR_LEN_ETHER ); + } + if ( NULL != pSrcAddr ) { + CopyMem ( &pHeader->src_addr, pSrcAddr, PXE_HWADDR_LEN_ETHER ); + } + else { + CopyMem ( &pHeader->src_addr, &pMode->CurrentAddress.Addr[0], PXE_HWADDR_LEN_ETHER ); + } + if ( NULL != pProtocol ) { + Type = *pProtocol; + } + else { + Type = pNicDevice->pTxTest->Length; + } + Type = (UINT16)(( Type >> 8 ) | ( Type << 8 )); + pHeader->type = Type; + } + if ( pNicDevice->pTxTest->Length < MIN_ETHERNET_PKT_SIZE ) { + pNicDevice->pTxTest->Length = MIN_ETHERNET_PKT_SIZE; + ZeroMem ( &pNicDevice->pTxTest->Data[ BufferSize ], + pNicDevice->pTxTest->Length - BufferSize ); + } + + DEBUG ((EFI_D_INFO, "TX: %02x-%02x-%02x-%02x-%02x-%02x %02x-%02x-%02x-%02x-%02x-%02x" + " %02x-%02x %d bytes\r\n", + pNicDevice->pTxTest->Data[0], + pNicDevice->pTxTest->Data[1], + pNicDevice->pTxTest->Data[2], + pNicDevice->pTxTest->Data[3], + pNicDevice->pTxTest->Data[4], + pNicDevice->pTxTest->Data[5], + pNicDevice->pTxTest->Data[6], + pNicDevice->pTxTest->Data[7], + pNicDevice->pTxTest->Data[8], + pNicDevice->pTxTest->Data[9], + pNicDevice->pTxTest->Data[10], + pNicDevice->pTxTest->Data[11], + pNicDevice->pTxTest->Data[12], + pNicDevice->pTxTest->Data[13], + pNicDevice->pTxTest->Length )); + + pNicDevice->pTxTest->LengthBar = ~(pNicDevice->pTxTest->Length); + TransferLength = sizeof ( pNicDevice->pTxTest->Length ) + + sizeof ( pNicDevice->pTxTest->LengthBar ) + + pNicDevice->pTxTest->Length; + + if (TransferLength % 512 == 0 || TransferLength % 1024 == 0) + TransferLength +=4; + + // + // Work around USB bus driver bug where a timeout set by receive + // succeeds but the timeout expires immediately after, causing the + // transmit operation to timeout. + // + pUsbIo = pNicDevice->pUsbIo; + Status = pUsbIo->UsbBulkTransfer ( pUsbIo, + BULK_OUT_ENDPOINT, + &pNicDevice->pTxTest->Length, + &TransferLength, + 0xfffffffe, + &TransferStatus ); + if ( !EFI_ERROR ( Status )) { + Status = TransferStatus; + } + + if ( !EFI_ERROR ( Status )) { + pNicDevice->pTxBuffer = pBuffer; + } + else { + if ((TransferLength != (UINTN)( pNicDevice->pTxTest->Length + 4 )) && + (TransferLength != (UINTN)(( pNicDevice->pTxTest->Length + 4 ) + 4))) { + DEBUG ((EFI_D_INFO, "TransferLength didn't match Packet Length\n")); + } + // + // Reset the controller to fix the error + // + if ( EFI_DEVICE_ERROR == Status ) { + SN_Reset ( pSimpleNetwork, FALSE ); + } + Status = EFI_NOT_READY; + } + } + else { + // + // No packets available. + // + Status = EFI_NOT_READY; + } + + } + else { + if (EfiSimpleNetworkStarted == pMode->State) { + Status = EFI_DEVICE_ERROR; + } + else { + Status = EFI_NOT_STARTED ; + } + } + } + else { + Status = EFI_BUFFER_TOO_SMALL; + } + } + else { + Status = EFI_INVALID_PARAMETER; + } + } + else { + Status = EFI_INVALID_PARAMETER; + } + + gBS->RestoreTPL (TplPrevious); + + return Status; +} diff --git a/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430.c b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430.c new file mode 100644 index 0000000000..4e7830ea94 --- /dev/null +++ b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430.c @@ -0,0 +1,917 @@ +/** @file + Cirrus Logic 5430 Controller Driver. + This driver is a sample implementation of the UGA Draw and Graphics Output + Protocols for the Cirrus Logic 5430 family of PCI video controllers. + This driver is only usable in the EFI pre-boot environment. + This sample is intended to show how the UGA Draw and Graphics output Protocol + is able to function. + The UGA I/O Protocol is not implemented in this sample. + A fully compliant EFI UGA driver requires both + the UGA Draw and the UGA I/O Protocol. Please refer to Microsoft's + documentation on UGA for details on how to write a UGA driver that is able + to function both in the EFI pre-boot environment and from the OS runtime. + + Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +// +// Cirrus Logic 5430 Controller Driver +// +#include "CirrusLogic5430.h" + +EFI_DRIVER_BINDING_PROTOCOL gCirrusLogic5430DriverBinding = { + CirrusLogic5430ControllerDriverSupported, + CirrusLogic5430ControllerDriverStart, + CirrusLogic5430ControllerDriverStop, + 0x10, + NULL, + NULL +}; + +/// +/// Generic Attribute Controller Register Settings +/// +UINT8 AttributeController[21] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x41, 0x00, 0x0F, 0x00, 0x00 +}; + +/// +/// Generic Graphics Controller Register Settings +/// +UINT8 GraphicsController[9] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF +}; + +// +// 640 x 480 x 256 color @ 60 Hertz +// +UINT8 Crtc_640_480_256_60[28] = { + 0x5d, 0x4f, 0x50, 0x82, 0x53, 0x9f, 0x00, 0x3e, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe1, 0x83, 0xdf, 0x50, 0x00, 0xe7, 0x04, 0xe3, + 0xff, 0x00, 0x00, 0x22 +}; + +UINT16 Seq_640_480_256_60[15] = { + 0x0100, 0x0101, 0x0f02, 0x0003, 0x0e04, 0x1107, 0x0008, 0x4a0b, + 0x5b0c, 0x450d, 0x7e0e, 0x2b1b, 0x2f1c, 0x301d, 0x331e +}; + +// +// 800 x 600 x 256 color @ 60 Hertz +// +UINT8 Crtc_800_600_256_60[28] = { + 0x7F, 0x63, 0x64, 0x80, 0x6B, 0x1B, 0x72, 0xF0, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x58, 0x8C, 0x57, 0x64, 0x00, 0x5F, 0x91, 0xE3, + 0xFF, 0x00, 0x00, 0x22 +}; + +UINT16 Seq_800_600_256_60[15] = { + 0x0100, 0x0101, 0x0f02, 0x0003, 0x0e04, 0x1107, 0x0008, 0x4a0b, + 0x5b0c, 0x450d, 0x510e, 0x2b1b, 0x2f1c, 0x301d, 0x3a1e +}; + +// +// 1024 x 768 x 256 color @ 60 Hertz +// +UINT8 Crtc_1024_768_256_60[28] = { + 0xA3, 0x7F, 0x80, 0x86, 0x85, 0x96, 0x24, 0xFD, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x88, 0xFF, 0x80, 0x00, 0x00, 0x24, 0xE3, + 0xFF, 0x4A, 0x00, 0x22 +}; + +UINT16 Seq_1024_768_256_60[15] = { + 0x0100, 0x0101, 0x0f02, 0x0003, 0x0e04, 0x1107, 0x0008, 0x4a0b, + 0x5b0c, 0x450d, 0x760e, 0x2b1b, 0x2f1c, 0x301d, 0x341e +}; + +/// +/// Table of supported video modes +/// +CIRRUS_LOGIC_5430_VIDEO_MODES CirrusLogic5430VideoModes[] = { + { 640, 480, 8, 60, Crtc_640_480_256_60, Seq_640_480_256_60, 0xe3 }, + { 800, 600, 8, 60, Crtc_800_600_256_60, Seq_800_600_256_60, 0xef }, + { 1024, 768, 8, 60, Crtc_1024_768_256_60, Seq_1024_768_256_60, 0xef } +}; + + +/** + CirrusLogic5430ControllerDriverSupported + + TODO: This - add argument and description to function comment + TODO: Controller - add argument and description to function comment + TODO: RemainingDevicePath - add argument and description to function comment +**/ +EFI_STATUS +EFIAPI +CirrusLogic5430ControllerDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE00 Pci; + EFI_DEV_PATH *Node; + + // + // Open the PCI I/O Protocol + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Read the PCI Configuration Header from the PCI Device + // + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (Pci) / sizeof (UINT32), + &Pci + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = EFI_UNSUPPORTED; + // + // See if the I/O enable is on. Most systems only allow one VGA device to be turned on + // at a time, so see if this is one that is turned on. + // + // if (((Pci.Hdr.Command & 0x01) == 0x01)) { + // + // See if this is a Cirrus Logic PCI controller + // + if (Pci.Hdr.VendorId == CIRRUS_LOGIC_VENDOR_ID) { + // + // See if this is a 5430 or a 5446 PCI controller + // + if (Pci.Hdr.DeviceId == CIRRUS_LOGIC_5430_DEVICE_ID || + Pci.Hdr.DeviceId == CIRRUS_LOGIC_5430_ALTERNATE_DEVICE_ID || + Pci.Hdr.DeviceId == CIRRUS_LOGIC_5446_DEVICE_ID) { + + Status = EFI_SUCCESS; + // + // If this is an Intel 945 graphics controller, + // go further check RemainingDevicePath validation + // + if (RemainingDevicePath != NULL) { + Node = (EFI_DEV_PATH *) RemainingDevicePath; + // + // Check if RemainingDevicePath is the End of Device Path Node, + // if yes, return EFI_SUCCESS + // + if (!IsDevicePathEnd (Node)) { + // + // If RemainingDevicePath isn't the End of Device Path Node, + // check its validation + // + if (Node->DevPath.Type != ACPI_DEVICE_PATH || + Node->DevPath.SubType != ACPI_ADR_DP || + DevicePathNodeLength(&Node->DevPath) != sizeof(ACPI_ADR_DEVICE_PATH)) { + Status = EFI_UNSUPPORTED; + } + } + } + } + } + +Done: + // + // Close the PCI I/O Protocol + // + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + +/** + CirrusLogic5430ControllerDriverStart + + TODO: This - add argument and description to function comment + TODO: Controller - add argument and description to function comment + TODO: RemainingDevicePath - add argument and description to function comment +**/ +EFI_STATUS +EFIAPI +CirrusLogic5430ControllerDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private; + BOOLEAN PciAttributesSaved; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + ACPI_ADR_DEVICE_PATH AcpiDeviceNode; + UINT64 Supports; + + PciAttributesSaved = FALSE; + // + // Allocate Private context data for UGA Draw inteface. + // + Private = AllocateZeroPool (sizeof (CIRRUS_LOGIC_5430_PRIVATE_DATA)); + if (Private == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + // + // Set up context record + // + Private->Signature = CIRRUS_LOGIC_5430_PRIVATE_DATA_SIGNATURE; + Private->Handle = NULL; + + // + // Open PCI I/O Protocol + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &Private->PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Get supported PCI attributes + // + Status = Private->PciIo->Attributes ( + Private->PciIo, + EfiPciIoAttributeOperationSupported, + 0, + &Supports + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + Supports &= (EFI_PCI_IO_ATTRIBUTE_VGA_IO | EFI_PCI_IO_ATTRIBUTE_VGA_IO_16); + if (Supports == 0 || Supports == (EFI_PCI_IO_ATTRIBUTE_VGA_IO | EFI_PCI_IO_ATTRIBUTE_VGA_IO_16)) { + Status = EFI_UNSUPPORTED; + goto Error; + } + + // + // Save original PCI attributes + // + Status = Private->PciIo->Attributes ( + Private->PciIo, + EfiPciIoAttributeOperationGet, + 0, + &Private->OriginalPciAttributes + ); + + if (EFI_ERROR (Status)) { + goto Error; + } + PciAttributesSaved = TRUE; + + Status = Private->PciIo->Attributes ( + Private->PciIo, + EfiPciIoAttributeOperationEnable, + EFI_PCI_DEVICE_ENABLE | EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY | Supports, + NULL + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Get ParentDevicePath + // + Status = gBS->HandleProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + if (FeaturePcdGet (PcdSupportGop)) { + // + // Set Gop Device Path + // + if (RemainingDevicePath == NULL) { + ZeroMem (&AcpiDeviceNode, sizeof (ACPI_ADR_DEVICE_PATH)); + AcpiDeviceNode.Header.Type = ACPI_DEVICE_PATH; + AcpiDeviceNode.Header.SubType = ACPI_ADR_DP; + AcpiDeviceNode.ADR = ACPI_DISPLAY_ADR (1, 0, 0, 1, 0, ACPI_ADR_DISPLAY_TYPE_VGA, 0, 0); + SetDevicePathNodeLength (&AcpiDeviceNode.Header, sizeof (ACPI_ADR_DEVICE_PATH)); + + Private->GopDevicePath = AppendDevicePathNode ( + ParentDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &AcpiDeviceNode + ); + } else if (!IsDevicePathEnd (RemainingDevicePath)) { + // + // If RemainingDevicePath isn't the End of Device Path Node, + // only scan the specified device by RemainingDevicePath + // + Private->GopDevicePath = AppendDevicePathNode (ParentDevicePath, RemainingDevicePath); + } else { + // + // If RemainingDevicePath is the End of Device Path Node, + // don't create child device and return EFI_SUCCESS + // + Private->GopDevicePath = NULL; + } + + if (Private->GopDevicePath != NULL) { + // + // Creat child handle and device path protocol firstly + // + Private->Handle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Handle, + &gEfiDevicePathProtocolGuid, + Private->GopDevicePath, + NULL + ); + } + } + + // + // Construct video mode buffer + // + Status = CirrusLogic5430VideoModeSetup (Private); + if (EFI_ERROR (Status)) { + goto Error; + } + + if (FeaturePcdGet (PcdSupportUga)) { + // + // Start the UGA Draw software stack. + // + Status = CirrusLogic5430UgaDrawConstructor (Private); + ASSERT_EFI_ERROR (Status); + + Private->UgaDevicePath = ParentDevicePath; + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiUgaDrawProtocolGuid, + &Private->UgaDraw, + &gEfiDevicePathProtocolGuid, + Private->UgaDevicePath, + NULL + ); + + } else if (FeaturePcdGet (PcdSupportGop)) { + if (Private->GopDevicePath == NULL) { + // + // If RemainingDevicePath is the End of Device Path Node, + // don't create child device and return EFI_SUCCESS + // + Status = EFI_SUCCESS; + } else { + + // + // Start the GOP software stack. + // + Status = CirrusLogic5430GraphicsOutputConstructor (Private); + ASSERT_EFI_ERROR (Status); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Handle, + &gEfiGraphicsOutputProtocolGuid, + &Private->GraphicsOutput, + &gEfiEdidDiscoveredProtocolGuid, + &Private->EdidDiscovered, + &gEfiEdidActiveProtocolGuid, + &Private->EdidActive, + NULL + ); + } + } else { + // + // This driver must support eithor GOP or UGA or both. + // + ASSERT (FALSE); + Status = EFI_UNSUPPORTED; + } + + +Error: + if (EFI_ERROR (Status)) { + if (Private) { + if (Private->PciIo) { + if (PciAttributesSaved == TRUE) { + // + // Restore original PCI attributes + // + Private->PciIo->Attributes ( + Private->PciIo, + EfiPciIoAttributeOperationSet, + Private->OriginalPciAttributes, + NULL + ); + } + // + // Close the PCI I/O Protocol + // + gBS->CloseProtocol ( + Private->Handle, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Private->Handle + ); + } + + gBS->FreePool (Private); + } + } + + return Status; +} + +/** + CirrusLogic5430ControllerDriverStop + + TODO: This - add argument and description to function comment + TODO: Controller - add argument and description to function comment + TODO: NumberOfChildren - add argument and description to function comment + TODO: ChildHandleBuffer - add argument and description to function comment + TODO: EFI_SUCCESS - add return value to function comment +**/ +EFI_STATUS +EFIAPI +CirrusLogic5430ControllerDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_UGA_DRAW_PROTOCOL *UgaDraw; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + + EFI_STATUS Status; + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private; + + if (FeaturePcdGet (PcdSupportUga)) { + Status = gBS->OpenProtocol ( + Controller, + &gEfiUgaDrawProtocolGuid, + (VOID **) &UgaDraw, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Get our private context information + // + Private = CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_UGA_DRAW_THIS (UgaDraw); + CirrusLogic5430UgaDrawDestructor (Private); + + if (FeaturePcdGet (PcdSupportGop)) { + CirrusLogic5430GraphicsOutputDestructor (Private); + // + // Remove the UGA and GOP protocol interface from the system + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + Private->Handle, + &gEfiUgaDrawProtocolGuid, + &Private->UgaDraw, + &gEfiGraphicsOutputProtocolGuid, + &Private->GraphicsOutput, + NULL + ); + } else { + // + // Remove the UGA Draw interface from the system + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + Private->Handle, + &gEfiUgaDrawProtocolGuid, + &Private->UgaDraw, + NULL + ); + } + } else { + Status = gBS->OpenProtocol ( + Controller, + &gEfiGraphicsOutputProtocolGuid, + (VOID **) &GraphicsOutput, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get our private context information + // + Private = CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (GraphicsOutput); + + CirrusLogic5430GraphicsOutputDestructor (Private); + // + // Remove the GOP protocol interface from the system + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + Private->Handle, + &gEfiUgaDrawProtocolGuid, + &Private->UgaDraw, + &gEfiGraphicsOutputProtocolGuid, + &Private->GraphicsOutput, + NULL + ); + } + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Restore original PCI attributes + // + Private->PciIo->Attributes ( + Private->PciIo, + EfiPciIoAttributeOperationSet, + Private->OriginalPciAttributes, + NULL + ); + + // + // Close the PCI I/O Protocol + // + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Free our instance data + // + gBS->FreePool (Private); + + return EFI_SUCCESS; +} + +/** + CirrusLogic5430UgaDrawDestructor + + TODO: Private - add argument and description to function comment + TODO: EFI_SUCCESS - add return value to function comment +**/ +EFI_STATUS +CirrusLogic5430UgaDrawDestructor ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private + ) +{ + return EFI_SUCCESS; +} + +/** + TODO: Add function description + + @param Private TODO: add argument description + @param Address TODO: add argument description + @param Data TODO: add argument description + + TODO: add return values + +**/ +VOID +outb ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private, + UINTN Address, + UINT8 Data + ) +{ + Private->PciIo->Io.Write ( + Private->PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + Address, + 1, + &Data + ); +} + +/** + TODO: Add function description + + @param Private TODO: add argument description + @param Address TODO: add argument description + @param Data TODO: add argument description + + TODO: add return values + +**/ +VOID +outw ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private, + UINTN Address, + UINT16 Data + ) +{ + Private->PciIo->Io.Write ( + Private->PciIo, + EfiPciIoWidthUint16, + EFI_PCI_IO_PASS_THROUGH_BAR, + Address, + 1, + &Data + ); +} + +/** + TODO: Add function description + + @param Private TODO: add argument description + @param Address TODO: add argument description + + TODO: add return values + +**/ +UINT8 +inb ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private, + UINTN Address + ) +{ + UINT8 Data; + + Private->PciIo->Io.Read ( + Private->PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + Address, + 1, + &Data + ); + return Data; +} + +/** + TODO: Add function description + + @param Private TODO: add argument description + @param Address TODO: add argument description + + TODO: add return values + +**/ +UINT16 +inw ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private, + UINTN Address + ) +{ + UINT16 Data; + + Private->PciIo->Io.Read ( + Private->PciIo, + EfiPciIoWidthUint16, + EFI_PCI_IO_PASS_THROUGH_BAR, + Address, + 1, + &Data + ); + return Data; +} + +/** + TODO: Add function description + + @param Private TODO: add argument description + @param Index TODO: add argument description + @param Red TODO: add argument description + @param Green TODO: add argument description + @param Blue TODO: add argument description + + TODO: add return values + +**/ +VOID +SetPaletteColor ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private, + UINTN Index, + UINT8 Red, + UINT8 Green, + UINT8 Blue + ) +{ + outb (Private, PALETTE_INDEX_REGISTER, (UINT8) Index); + outb (Private, PALETTE_DATA_REGISTER, (UINT8) (Red >> 2)); + outb (Private, PALETTE_DATA_REGISTER, (UINT8) (Green >> 2)); + outb (Private, PALETTE_DATA_REGISTER, (UINT8) (Blue >> 2)); +} + +/** + TODO: Add function description + + @param Private TODO: add argument description + + TODO: add return values + +**/ +VOID +SetDefaultPalette ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private + ) +{ + UINTN Index; + UINTN RedIndex; + UINTN GreenIndex; + UINTN BlueIndex; + + Index = 0; + for (RedIndex = 0; RedIndex < 8; RedIndex++) { + for (GreenIndex = 0; GreenIndex < 8; GreenIndex++) { + for (BlueIndex = 0; BlueIndex < 4; BlueIndex++) { + SetPaletteColor (Private, Index, (UINT8) (RedIndex << 5), (UINT8) (GreenIndex << 5), (UINT8) (BlueIndex << 6)); + Index++; + } + } + } +} + +/** + TODO: Add function description + + @param Private TODO: add argument description + + TODO: add return values + +**/ +VOID +ClearScreen ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private + ) +{ + UINT32 Color; + + Color = 0; + Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthFillUint32, + 0, + 0, + 0x100000 >> 2, + &Color + ); +} + +/** + TODO: Add function description + + @param Private TODO: add argument description + + TODO: add return values + +**/ +VOID +DrawLogo ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private, + UINTN ScreenWidth, + UINTN ScreenHeight + ) +{ +} + +/** + TODO: Add function description + + @param Private TODO: add argument description + @param ModeData TODO: add argument description + + TODO: add return values + +**/ +VOID +InitializeGraphicsMode ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private, + CIRRUS_LOGIC_5430_VIDEO_MODES *ModeData + ) +{ + UINT8 Byte; + UINTN Index; + UINT16 DeviceId; + EFI_STATUS Status; + + Status = Private->PciIo->Pci.Read ( + Private->PciIo, + EfiPciIoWidthUint16, + PCI_DEVICE_ID_OFFSET, + 1, + &DeviceId + ); + // + // Read the PCI Configuration Header from the PCI Device + // + ASSERT_EFI_ERROR (Status); + + outw (Private, SEQ_ADDRESS_REGISTER, 0x1206); + outw (Private, SEQ_ADDRESS_REGISTER, 0x0012); + + for (Index = 0; Index < 15; Index++) { + outw (Private, SEQ_ADDRESS_REGISTER, ModeData->SeqSettings[Index]); + } + + if (DeviceId != CIRRUS_LOGIC_5446_DEVICE_ID) { + outb (Private, SEQ_ADDRESS_REGISTER, 0x0f); + Byte = (UINT8) ((inb (Private, SEQ_DATA_REGISTER) & 0xc7) ^ 0x30); + outb (Private, SEQ_DATA_REGISTER, Byte); + } + + outb (Private, MISC_OUTPUT_REGISTER, ModeData->MiscSetting); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0506); + outw (Private, SEQ_ADDRESS_REGISTER, 0x0300); + outw (Private, CRTC_ADDRESS_REGISTER, 0x2011); + + for (Index = 0; Index < 28; Index++) { + outw (Private, CRTC_ADDRESS_REGISTER, (UINT16) ((ModeData->CrtcSettings[Index] << 8) | Index)); + } + + for (Index = 0; Index < 9; Index++) { + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((GraphicsController[Index] << 8) | Index)); + } + + inb (Private, INPUT_STATUS_1_REGISTER); + + for (Index = 0; Index < 21; Index++) { + outb (Private, ATT_ADDRESS_REGISTER, (UINT8) Index); + outb (Private, ATT_ADDRESS_REGISTER, AttributeController[Index]); + } + + outb (Private, ATT_ADDRESS_REGISTER, 0x20); + + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0009); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x000a); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x000b); + outb (Private, DAC_PIXEL_MASK_REGISTER, 0xff); + + SetDefaultPalette (Private); + ClearScreen (Private); +} + +EFI_STATUS +EFIAPI +InitializeCirrusLogic5430 ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gCirrusLogic5430DriverBinding, + ImageHandle, + &gCirrusLogic5430ComponentName, + &gCirrusLogic5430ComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + // + // Install EFI Driver Supported EFI Version Protocol required for + // EFI drivers that are on PCI and other plug in cards. + // + gCirrusLogic5430DriverSupportedEfiVersion.FirmwareVersion = PcdGet32 (PcdDriverSupportedEfiVersion); + Status = gBS->InstallMultipleProtocolInterfaces ( + &ImageHandle, + &gEfiDriverSupportedEfiVersionProtocolGuid, + &gCirrusLogic5430DriverSupportedEfiVersion, + NULL + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430.h b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430.h new file mode 100644 index 0000000000..355f0418b3 --- /dev/null +++ b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430.h @@ -0,0 +1,432 @@ +/** @file + Cirrus Logic 5430 Controller Driver + + Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +// +// Cirrus Logic 5430 Controller Driver +// + +#ifndef _CIRRUS_LOGIC_5430_H_ +#define _CIRRUS_LOGIC_5430_H_ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +// +// Cirrus Logic 5430 PCI Configuration Header values +// +#define CIRRUS_LOGIC_VENDOR_ID 0x1013 +#define CIRRUS_LOGIC_5430_DEVICE_ID 0x00a8 +#define CIRRUS_LOGIC_5430_ALTERNATE_DEVICE_ID 0x00a0 +#define CIRRUS_LOGIC_5446_DEVICE_ID 0x00b8 + +// +// Cirrus Logic Graphical Mode Data +// +#define CIRRUS_LOGIC_5430_MODE_COUNT 3 + +typedef struct { + UINT32 ModeNumber; + UINT32 HorizontalResolution; + UINT32 VerticalResolution; + UINT32 ColorDepth; + UINT32 RefreshRate; +} CIRRUS_LOGIC_5430_MODE_DATA; + +#define PIXEL_RED_SHIFT 0 +#define PIXEL_GREEN_SHIFT 3 +#define PIXEL_BLUE_SHIFT 6 + +#define PIXEL_RED_MASK (BIT7 | BIT6 | BIT5) +#define PIXEL_GREEN_MASK (BIT4 | BIT3 | BIT2) +#define PIXEL_BLUE_MASK (BIT1 | BIT0) + +#define PIXEL_TO_COLOR_BYTE(pixel, mask, shift) ((UINT8) ((pixel & mask) << shift)) +#define PIXEL_TO_RED_BYTE(pixel) PIXEL_TO_COLOR_BYTE(pixel, PIXEL_RED_MASK, PIXEL_RED_SHIFT) +#define PIXEL_TO_GREEN_BYTE(pixel) PIXEL_TO_COLOR_BYTE(pixel, PIXEL_GREEN_MASK, PIXEL_GREEN_SHIFT) +#define PIXEL_TO_BLUE_BYTE(pixel) PIXEL_TO_COLOR_BYTE(pixel, PIXEL_BLUE_MASK, PIXEL_BLUE_SHIFT) + +#define RGB_BYTES_TO_PIXEL(Red, Green, Blue) \ + (UINT8) ( (((Red) >> PIXEL_RED_SHIFT) & PIXEL_RED_MASK) | \ + (((Green) >> PIXEL_GREEN_SHIFT) & PIXEL_GREEN_MASK) | \ + (((Blue) >> PIXEL_BLUE_SHIFT) & PIXEL_BLUE_MASK) ) + +#define GRAPHICS_OUTPUT_INVALIDE_MODE_NUMBER 0xffff + +// +// Cirrus Logic 5440 Private Data Structure +// +#define CIRRUS_LOGIC_5430_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('C', 'L', '5', '4') + +typedef struct { + UINT64 Signature; + EFI_HANDLE Handle; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT64 OriginalPciAttributes; + EFI_UGA_DRAW_PROTOCOL UgaDraw; + EFI_GRAPHICS_OUTPUT_PROTOCOL GraphicsOutput; + EFI_EDID_DISCOVERED_PROTOCOL EdidDiscovered; + EFI_EDID_ACTIVE_PROTOCOL EdidActive; + EFI_DEVICE_PATH_PROTOCOL *GopDevicePath; + EFI_DEVICE_PATH_PROTOCOL *UgaDevicePath; + UINTN CurrentMode; + UINTN MaxMode; + CIRRUS_LOGIC_5430_MODE_DATA ModeData[CIRRUS_LOGIC_5430_MODE_COUNT]; + UINT8 *LineBuffer; + BOOLEAN HardwareNeedsStarting; +} CIRRUS_LOGIC_5430_PRIVATE_DATA; + +/// +/// Video Mode structure +/// +typedef struct { + UINT32 Width; + UINT32 Height; + UINT32 ColorDepth; + UINT32 RefreshRate; + UINT8 *CrtcSettings; + UINT16 *SeqSettings; + UINT8 MiscSetting; +} CIRRUS_LOGIC_5430_VIDEO_MODES; + +#define CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_UGA_DRAW_THIS(a) \ + CR(a, CIRRUS_LOGIC_5430_PRIVATE_DATA, UgaDraw, CIRRUS_LOGIC_5430_PRIVATE_DATA_SIGNATURE) + +#define CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS(a) \ + CR(a, CIRRUS_LOGIC_5430_PRIVATE_DATA, GraphicsOutput, CIRRUS_LOGIC_5430_PRIVATE_DATA_SIGNATURE) + + +// +// Global Variables +// +extern UINT8 AttributeController[]; +extern UINT8 GraphicsController[]; +extern UINT8 Crtc_640_480_256_60[]; +extern UINT16 Seq_640_480_256_60[]; +extern UINT8 Crtc_800_600_256_60[]; +extern UINT16 Seq_800_600_256_60[]; +extern UINT8 Crtc_1024_768_256_60[]; +extern UINT16 Seq_1024_768_256_60[]; +extern CIRRUS_LOGIC_5430_VIDEO_MODES CirrusLogic5430VideoModes[]; +extern EFI_DRIVER_BINDING_PROTOCOL gCirrusLogic5430DriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gCirrusLogic5430ComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gCirrusLogic5430ComponentName2; +extern EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL gCirrusLogic5430DriverSupportedEfiVersion; + +// +// Io Registers defined by VGA +// +#define CRTC_ADDRESS_REGISTER 0x3d4 +#define CRTC_DATA_REGISTER 0x3d5 +#define SEQ_ADDRESS_REGISTER 0x3c4 +#define SEQ_DATA_REGISTER 0x3c5 +#define GRAPH_ADDRESS_REGISTER 0x3ce +#define GRAPH_DATA_REGISTER 0x3cf +#define ATT_ADDRESS_REGISTER 0x3c0 +#define MISC_OUTPUT_REGISTER 0x3c2 +#define INPUT_STATUS_1_REGISTER 0x3da +#define DAC_PIXEL_MASK_REGISTER 0x3c6 +#define PALETTE_INDEX_REGISTER 0x3c8 +#define PALETTE_DATA_REGISTER 0x3c9 + +// +// UGA Draw Hardware abstraction internal worker functions +// +EFI_STATUS +CirrusLogic5430UgaDrawConstructor ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private + ); + +EFI_STATUS +CirrusLogic5430UgaDrawDestructor ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private + ); + +// +// Graphics Output Hardware abstraction internal worker functions +// +EFI_STATUS +CirrusLogic5430GraphicsOutputConstructor ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private + ); + +EFI_STATUS +CirrusLogic5430GraphicsOutputDestructor ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private + ); + + +// +// EFI_DRIVER_BINDING_PROTOCOL Protocol Interface +// +/** + TODO: Add function description + + @param This TODO: add argument description + @param Controller TODO: add argument description + @param RemainingDevicePath TODO: add argument description + + TODO: add return values + +**/ +EFI_STATUS +EFIAPI +CirrusLogic5430ControllerDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + TODO: Add function description + + @param This TODO: add argument description + @param Controller TODO: add argument description + @param RemainingDevicePath TODO: add argument description + + TODO: add return values + +**/ +EFI_STATUS +EFIAPI +CirrusLogic5430ControllerDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + TODO: Add function description + + @param This TODO: add argument description + @param Controller TODO: add argument description + @param NumberOfChildren TODO: add argument description + @param ChildHandleBuffer TODO: add argument description + + TODO: add return values + +**/ +EFI_STATUS +EFIAPI +CirrusLogic5430ControllerDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +CirrusLogic5430ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +CirrusLogic5430ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +// +// Local Function Prototypes +// +VOID +InitializeGraphicsMode ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private, + CIRRUS_LOGIC_5430_VIDEO_MODES *ModeData + ); + +VOID +SetPaletteColor ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private, + UINTN Index, + UINT8 Red, + UINT8 Green, + UINT8 Blue + ); + +VOID +SetDefaultPalette ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private + ); + +VOID +DrawLogo ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private, + UINTN ScreenWidth, + UINTN ScreenHeight + ); + +VOID +outb ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private, + UINTN Address, + UINT8 Data + ); + +VOID +outw ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private, + UINTN Address, + UINT16 Data + ); + +UINT8 +inb ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private, + UINTN Address + ); + +UINT16 +inw ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private, + UINTN Address + ); + +EFI_STATUS +CirrusLogic5430VideoModeSetup ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private + ); + +#endif diff --git a/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430Dxe.inf b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430Dxe.inf new file mode 100644 index 0000000000..3e8b7b087f --- /dev/null +++ b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430Dxe.inf @@ -0,0 +1,84 @@ +## @file +# Component description file for CirrusLogic5430 module +# +# Cirrus Logic 5430 Controller Driver.This driver is a sample implementation +# of the UGA Draw Protocol for the Cirrus Logic 5430 family of PCI video controllers. +# This driver is only usable in the EFI pre-boot environment. This sample is +# intended to show how the UGA Draw Protocol is able to function. The UGA I/O +# Protocol is not implemented in this sample. A fully compliant EFI UGA driver +# requires both the UGA Draw and the UGA I/O Protocol. Please refer to Microsoft's +# documentation on UGA for details on how to write a UGA driver that is able +# to function both in the EFI pre-boot environment and from the OS runtime. +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CirrusLogic5430Dxe + FILE_GUID = 555F76EA-785F-40d7-9174-153C43636C68 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = InitializeCirrusLogic5430 + + PCI_VENDOR_ID = 0x1013 + PCI_DEVICE_ID = 0x00A8 + PCI_CLASS_CODE = 0x030000 + PCI_REVISION = 0x00 + PCI_COMPRESS = TRUE + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# +# DRIVER_BINDING = gCirrusLogic5430DriverBinding +# COMPONENT_NAME = gCirrusLogic5430ComponentName +# + +[Sources] + ComponentName.c + DriverSupportedEfiVersion.c + CirrusLogic5430UgaDraw.c + CirrusLogic5430GraphicsOutput.c + CirrusLogic5430.c + CirrusLogic5430.h + Edid.c + CirrusLogic5430I2c.h + CirrusLogic5430I2c.c + +[Packages] + MdePkg/MdePkg.dec + OptionRomPkg/OptionRomPkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + MemoryAllocationLib + UefiLib + UefiDriverEntryPoint + DebugLib + BaseMemoryLib + DevicePathLib + TimerLib + +[Protocols] + gEfiDriverSupportedEfiVersionProtocolGuid # PROTOCOL ALWAYS_PRODUCED + gEfiUgaDrawProtocolGuid # PROTOCOL BY_START + gEfiGraphicsOutputProtocolGuid # PROTOCOL BY_START + gEfiEdidDiscoveredProtocolGuid # PROTOCOL BY_START + gEfiEdidActiveProtocolGuid # PROTOCOL BY_START + gEfiDevicePathProtocolGuid # PROTOCOL BY_START + gEfiPciIoProtocolGuid # PROTOCOL TO_START + gEfiEdidOverrideProtocolGuid # PROTOCOL TO_START + + +[FeaturePcd] + gOptionRomPkgTokenSpaceGuid.PcdSupportGop + gOptionRomPkgTokenSpaceGuid.PcdSupportUga + +[Pcd] + gOptionRomPkgTokenSpaceGuid.PcdDriverSupportedEfiVersion diff --git a/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430GraphicsOutput.c b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430GraphicsOutput.c new file mode 100644 index 0000000000..b74d84b131 --- /dev/null +++ b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430GraphicsOutput.c @@ -0,0 +1,556 @@ +/** @file +Copyright (c) 2007 - 2012, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +Module Name: + + UefiCirrusLogic5430GraphicsOutput.c + +Abstract: + + This file produces the graphics abstration of Graphics Output Protocol. It is called by + CirrusLogic5430.c file which deals with the EFI 1.1 driver model. + This file just does graphics. + +**/ +#include "CirrusLogic5430.h" +#include + + +STATIC +VOID +CirrusLogic5430CompleteModeInfo ( + OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info + ) +{ + Info->Version = 0; + Info->PixelFormat = PixelBitMask; + Info->PixelInformation.RedMask = PIXEL_RED_MASK; + Info->PixelInformation.GreenMask = PIXEL_GREEN_MASK; + Info->PixelInformation.BlueMask = PIXEL_BLUE_MASK; + Info->PixelInformation.ReservedMask = 0; + Info->PixelsPerScanLine = Info->HorizontalResolution; +} + + +STATIC +EFI_STATUS +CirrusLogic5430CompleteModeData ( + IN CIRRUS_LOGIC_5430_PRIVATE_DATA *Private, + OUT EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode + ) +{ + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *FrameBufDesc; + + Info = Mode->Info; + CirrusLogic5430CompleteModeInfo (Info); + + Private->PciIo->GetBarAttributes ( + Private->PciIo, + 0, + NULL, + (VOID**) &FrameBufDesc + ); + + Mode->FrameBufferBase = FrameBufDesc->AddrRangeMin; + Mode->FrameBufferSize = Info->HorizontalResolution * Info->VerticalResolution; + + return EFI_SUCCESS; +} + + +// +// Graphics Output Protocol Member Functions +// +EFI_STATUS +EFIAPI +CirrusLogic5430GraphicsOutputQueryMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN UINT32 ModeNumber, + OUT UINTN *SizeOfInfo, + OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info + ) +/*++ + +Routine Description: + + Graphics Output protocol interface to query video mode + + Arguments: + This - Protocol instance pointer. + ModeNumber - The mode number to return information on. + Info - Caller allocated buffer that returns information about ModeNumber. + SizeOfInfo - A pointer to the size, in bytes, of the Info buffer. + + Returns: + EFI_SUCCESS - Mode information returned. + EFI_BUFFER_TOO_SMALL - The Info buffer was too small. + EFI_DEVICE_ERROR - A hardware error occurred trying to retrieve the video mode. + EFI_NOT_STARTED - Video display is not initialized. Call SetMode () + EFI_INVALID_PARAMETER - One of the input args was NULL. + +--*/ +{ + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private; + + Private = CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (This); + + if (Private->HardwareNeedsStarting) { + return EFI_NOT_STARTED; + } + + if (Info == NULL || SizeOfInfo == NULL || ModeNumber >= This->Mode->MaxMode) { + return EFI_INVALID_PARAMETER; + } + + *Info = AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION)); + if (*Info == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + *SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION); + + (*Info)->HorizontalResolution = Private->ModeData[ModeNumber].HorizontalResolution; + (*Info)->VerticalResolution = Private->ModeData[ModeNumber].VerticalResolution; + CirrusLogic5430CompleteModeInfo (*Info); + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +CirrusLogic5430GraphicsOutputSetMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN UINT32 ModeNumber + ) +/*++ + +Routine Description: + + Graphics Output protocol interface to set video mode + + Arguments: + This - Protocol instance pointer. + ModeNumber - The mode number to be set. + + Returns: + EFI_SUCCESS - Graphics mode was changed. + EFI_DEVICE_ERROR - The device had an error and could not complete the request. + EFI_UNSUPPORTED - ModeNumber is not supported by this device. + +--*/ +{ + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private; + CIRRUS_LOGIC_5430_MODE_DATA *ModeData; + + Private = CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (This); + + if (ModeNumber >= This->Mode->MaxMode) { + return EFI_UNSUPPORTED; + } + + ModeData = &Private->ModeData[ModeNumber]; + + if (Private->LineBuffer) { + gBS->FreePool (Private->LineBuffer); + } + + Private->LineBuffer = NULL; + Private->LineBuffer = AllocatePool (ModeData->HorizontalResolution); + if (Private->LineBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + InitializeGraphicsMode (Private, &CirrusLogic5430VideoModes[ModeData->ModeNumber]); + + This->Mode->Mode = ModeNumber; + This->Mode->Info->HorizontalResolution = ModeData->HorizontalResolution; + This->Mode->Info->VerticalResolution = ModeData->VerticalResolution; + This->Mode->SizeOfInfo = sizeof(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION); + + CirrusLogic5430CompleteModeData (Private, This->Mode); + + Private->HardwareNeedsStarting = FALSE; + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +CirrusLogic5430GraphicsOutputBlt ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL + IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta + ) +/*++ + +Routine Description: + + Graphics Output protocol instance to block transfer for CirrusLogic device + +Arguments: + + This - Pointer to Graphics Output protocol instance + BltBuffer - The data to transfer to screen + BltOperation - The operation to perform + SourceX - The X coordinate of the source for BltOperation + SourceY - The Y coordinate of the source for BltOperation + DestinationX - The X coordinate of the destination for BltOperation + DestinationY - The Y coordinate of the destination for BltOperation + Width - The width of a rectangle in the blt rectangle in pixels + Height - The height of a rectangle in the blt rectangle in pixels + Delta - Not used for EfiBltVideoFill and EfiBltVideoToVideo operation. + If a Delta of 0 is used, the entire BltBuffer will be operated on. + If a subrectangle of the BltBuffer is used, then Delta represents + the number of bytes in a row of the BltBuffer. + +Returns: + + EFI_INVALID_PARAMETER - Invalid parameter passed in + EFI_SUCCESS - Blt operation success + +--*/ +{ + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private; + EFI_TPL OriginalTPL; + UINTN DstY; + UINTN SrcY; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt; + UINTN X; + UINT8 Pixel; + UINT32 WidePixel; + UINTN ScreenWidth; + UINTN Offset; + UINTN SourceOffset; + UINT32 CurrentMode; + + Private = CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (This); + + if ((UINT32)BltOperation >= EfiGraphicsOutputBltOperationMax) { + return EFI_INVALID_PARAMETER; + } + + if (Width == 0 || Height == 0) { + return EFI_INVALID_PARAMETER; + } + + // + // If Delta is zero, then the entire BltBuffer is being used, so Delta + // is the number of bytes in each row of BltBuffer. Since BltBuffer is Width pixels size, + // the number of bytes in each row can be computed. + // + if (Delta == 0) { + Delta = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL); + } + + // + // We need to fill the Virtual Screen buffer with the blt data. + // The virtual screen is upside down, as the first row is the bootom row of + // the image. + // + + CurrentMode = This->Mode->Mode; + // + // Make sure the SourceX, SourceY, DestinationX, DestinationY, Width, and Height parameters + // are valid for the operation and the current screen geometry. + // + if (BltOperation == EfiBltVideoToBltBuffer) { + // + // Video to BltBuffer: Source is Video, destination is BltBuffer + // + if (SourceY + Height > Private->ModeData[CurrentMode].VerticalResolution) { + return EFI_INVALID_PARAMETER; + } + + if (SourceX + Width > Private->ModeData[CurrentMode].HorizontalResolution) { + return EFI_INVALID_PARAMETER; + } + } else { + // + // BltBuffer to Video: Source is BltBuffer, destination is Video + // + if (DestinationY + Height > Private->ModeData[CurrentMode].VerticalResolution) { + return EFI_INVALID_PARAMETER; + } + + if (DestinationX + Width > Private->ModeData[CurrentMode].HorizontalResolution) { + return EFI_INVALID_PARAMETER; + } + } + // + // We have to raise to TPL Notify, so we make an atomic write the frame buffer. + // We would not want a timer based event (Cursor, ...) to come in while we are + // doing this operation. + // + OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY); + + switch (BltOperation) { + case EfiBltVideoToBltBuffer: + // + // Video to BltBuffer: Source is Video, destination is BltBuffer + // + for (SrcY = SourceY, DstY = DestinationY; DstY < (Height + DestinationY); SrcY++, DstY++) { + + Offset = (SrcY * Private->ModeData[CurrentMode].HorizontalResolution) + SourceX; + if (((Offset & 0x03) == 0) && ((Width & 0x03) == 0)) { + Private->PciIo->Mem.Read ( + Private->PciIo, + EfiPciIoWidthUint32, + 0, + Offset, + Width >> 2, + Private->LineBuffer + ); + } else { + Private->PciIo->Mem.Read ( + Private->PciIo, + EfiPciIoWidthUint8, + 0, + Offset, + Width, + Private->LineBuffer + ); + } + + for (X = 0; X < Width; X++) { + Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) ((UINT8 *) BltBuffer + (DstY * Delta) + (DestinationX + X) * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); + + Blt->Red = PIXEL_TO_RED_BYTE (Private->LineBuffer[X]); + Blt->Green = PIXEL_TO_GREEN_BYTE (Private->LineBuffer[X]); + Blt->Blue = PIXEL_TO_BLUE_BYTE (Private->LineBuffer[X]); + } + } + break; + + case EfiBltVideoToVideo: + // + // Perform hardware acceleration for Video to Video operations + // + ScreenWidth = Private->ModeData[CurrentMode].HorizontalResolution; + SourceOffset = (SourceY * Private->ModeData[CurrentMode].HorizontalResolution) + (SourceX); + Offset = (DestinationY * Private->ModeData[CurrentMode].HorizontalResolution) + (DestinationX); + + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0000); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0010); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0012); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0014); + + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0001); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0011); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0013); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0015); + + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) (((Width << 8) & 0xff00) | 0x20)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((Width & 0xff00) | 0x21)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) (((Height << 8) & 0xff00) | 0x22)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((Height & 0xff00) | 0x23)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) (((ScreenWidth << 8) & 0xff00) | 0x24)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((ScreenWidth & 0xff00) | 0x25)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) (((ScreenWidth << 8) & 0xff00) | 0x26)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((ScreenWidth & 0xff00) | 0x27)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((Offset) << 8) & 0xff00) | 0x28)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((Offset) >> 0) & 0xff00) | 0x29)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((Offset) >> 8) & 0xff00) | 0x2a)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((SourceOffset) << 8) & 0xff00) | 0x2c)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((SourceOffset) >> 0) & 0xff00) | 0x2d)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((SourceOffset) >> 8) & 0xff00) | 0x2e)); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x002f); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0030); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0d32); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0033); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0034); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0035); + + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0231); + + outb (Private, GRAPH_ADDRESS_REGISTER, 0x31); + while ((inb (Private, GRAPH_DATA_REGISTER) & 0x01) == 0x01) + ; + break; + + case EfiBltVideoFill: + Blt = BltBuffer; + Pixel = RGB_BYTES_TO_PIXEL (Blt->Red, Blt->Green, Blt->Blue); + WidePixel = (Pixel << 8) | Pixel; + WidePixel = (WidePixel << 16) | WidePixel; + + if (DestinationX == 0 && Width == Private->ModeData[CurrentMode].HorizontalResolution) { + Offset = DestinationY * Private->ModeData[CurrentMode].HorizontalResolution; + if (((Offset & 0x03) == 0) && (((Width * Height) & 0x03) == 0)) { + Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthFillUint32, + 0, + Offset, + (Width * Height) >> 2, + &WidePixel + ); + } else { + Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthFillUint8, + 0, + Offset, + Width * Height, + &Pixel + ); + } + } else { + for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) { + Offset = (DstY * Private->ModeData[CurrentMode].HorizontalResolution) + DestinationX; + if (((Offset & 0x03) == 0) && ((Width & 0x03) == 0)) { + Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthFillUint32, + 0, + Offset, + Width >> 2, + &WidePixel + ); + } else { + Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthFillUint8, + 0, + Offset, + Width, + &Pixel + ); + } + } + } + break; + + case EfiBltBufferToVideo: + for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) { + + for (X = 0; X < Width; X++) { + Blt = + (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) ( + (UINT8 *) BltBuffer + + (SrcY * Delta) + + ((SourceX + X) * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)) + ); + Private->LineBuffer[X] = + RGB_BYTES_TO_PIXEL (Blt->Red, Blt->Green, Blt->Blue); + } + + Offset = (DstY * Private->ModeData[CurrentMode].HorizontalResolution) + DestinationX; + + if (((Offset & 0x03) == 0) && ((Width & 0x03) == 0)) { + Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthUint32, + 0, + Offset, + Width >> 2, + Private->LineBuffer + ); + } else { + Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthUint8, + 0, + Offset, + Width, + Private->LineBuffer + ); + } + } + break; + default: + ASSERT (FALSE); + } + + gBS->RestoreTPL (OriginalTPL); + + return EFI_SUCCESS; +} + +EFI_STATUS +CirrusLogic5430GraphicsOutputConstructor ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + + + GraphicsOutput = &Private->GraphicsOutput; + GraphicsOutput->QueryMode = CirrusLogic5430GraphicsOutputQueryMode; + GraphicsOutput->SetMode = CirrusLogic5430GraphicsOutputSetMode; + GraphicsOutput->Blt = CirrusLogic5430GraphicsOutputBlt; + + // + // Initialize the private data + // + Status = gBS->AllocatePool ( + EfiBootServicesData, + sizeof (EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE), + (VOID **) &Private->GraphicsOutput.Mode + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status = gBS->AllocatePool ( + EfiBootServicesData, + sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION), + (VOID **) &Private->GraphicsOutput.Mode->Info + ); + if (EFI_ERROR (Status)) { + return Status; + } + Private->GraphicsOutput.Mode->MaxMode = (UINT32) Private->MaxMode; + Private->GraphicsOutput.Mode->Mode = GRAPHICS_OUTPUT_INVALIDE_MODE_NUMBER; + Private->HardwareNeedsStarting = TRUE; + Private->LineBuffer = NULL; + + // + // Initialize the hardware + // + GraphicsOutput->SetMode (GraphicsOutput, 0); + ASSERT (Private->GraphicsOutput.Mode->Mode < CIRRUS_LOGIC_5430_MODE_COUNT); + DrawLogo ( + Private, + Private->ModeData[Private->GraphicsOutput.Mode->Mode].HorizontalResolution, + Private->ModeData[Private->GraphicsOutput.Mode->Mode].VerticalResolution + ); + + return EFI_SUCCESS; +} + +EFI_STATUS +CirrusLogic5430GraphicsOutputDestructor ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private + ) +/*++ + +Routine Description: + +Arguments: + +Returns: + + None + +--*/ +{ + if (Private->GraphicsOutput.Mode != NULL) { + if (Private->GraphicsOutput.Mode->Info != NULL) { + gBS->FreePool (Private->GraphicsOutput.Mode->Info); + } + gBS->FreePool (Private->GraphicsOutput.Mode); + } + + return EFI_SUCCESS; +} + + diff --git a/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430I2c.c b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430I2c.c new file mode 100644 index 0000000000..0cec8670b7 --- /dev/null +++ b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430I2c.c @@ -0,0 +1,427 @@ +/** @file + I2C Bus implementation upon CirrusLogic. + + Copyright (c) 2008 - 2009, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CirrusLogic5430.h" +#include "CirrusLogic5430I2c.h" + +#define SEQ_ADDRESS_REGISTER 0x3c4 +#define SEQ_DATA_REGISTER 0x3c5 + +#define I2C_CONTROL 0x08 +#define I2CDAT_IN 7 +#define I2CCLK_IN 2 +#define I2CDAT_OUT 1 +#define I2CCLK_OUT 0 + +#define I2C_BUS_SPEED 100 //100kbps + +/** + PCI I/O byte write function. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param Address The bit map of I2C Data or I2C Clock pins. + @param Data The date to write. + +**/ +VOID +I2cOutb ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINTN Address, + UINT8 Data + ) +{ + PciIo->Io.Write ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + Address, + 1, + &Data + ); +} +/** + PCI I/O byte read function. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param Address The bit map of I2C Data or I2C Clock pins. + + return byte value read from PCI I/O space. + +**/ +UINT8 +I2cInb ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINTN Address + ) +{ + UINT8 Data; + + PciIo->Io.Read ( + PciIo, + EfiPciIoWidthUint8, + EFI_PCI_IO_PASS_THROUGH_BAR, + Address, + 1, + &Data + ); + return Data; +} + +/** + Read status of I2C Data and I2C Clock Pins. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param Blt The bit map of I2C Data or I2C Clock pins. + + @retval 0 Low on I2C Data or I2C Clock Pin. + @retval 1 High on I2C Data or I2C Clock Pin. + +**/ +UINT8 +I2cPinRead ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 Bit + ) +{ + I2cOutb (PciIo, SEQ_ADDRESS_REGISTER, I2C_CONTROL); + return (UINT8) ((I2cInb (PciIo, SEQ_DATA_REGISTER) >> Bit ) & 0xfe); +} + + +/** + Set/Clear I2C Data and I2C Clock Pins. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param Blt The bit map to controller I2C Data or I2C Clock pins. + @param Value 1 or 0 stands for Set or Clear I2C Data and I2C Clock Pins. + +**/ +VOID +I2cPinWrite ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 Bit, + UINT8 Value + ) +{ + UINT8 Byte; + I2cOutb (PciIo, SEQ_ADDRESS_REGISTER, I2C_CONTROL); + Byte = (UINT8) (I2cInb (PciIo, SEQ_DATA_REGISTER) & (UINT8) ~(1 << Bit)) ; + Byte = (UINT8) (Byte | ((Value & 0x01) << Bit)); + I2cOutb (PciIo, SEQ_DATA_REGISTER, (UINT8) (Byte | 0x40)); + return; +} + +/** + Read/write delay acoording to I2C Bus Speed. + +**/ +VOID +I2cDelay ( + VOID + ) +{ + MicroSecondDelay (1000 / I2C_BUS_SPEED); +} + +/** + Write a 8-bit data onto I2C Data Pin. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param Data The byte data to write. + +**/ +VOID +I2cSendByte ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 Data + ) +{ + UINTN Index; + // + // Send byte data onto I2C Bus + // + for (Index = 0; Index < 8; Index --) { + I2cPinWrite (PciIo, I2CDAT_OUT, (UINT8) (Data >> (7 - Index))); + I2cPinWrite (PciIo, I2CCLK_OUT, 1); + I2cDelay (); + I2cPinWrite (PciIo, I2CCLK_OUT, 0); + } +} + +/** + Read a 8-bit data from I2C Data Pin. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + + Return the byte data read from I2C Data Pin. +**/ +UINT8 +I2cReceiveByte ( + EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + UINT8 Data; + UINTN Index; + + Data = 0; + // + // Read byte data from I2C Bus + // + for (Index = 0; Index < 8; Index --) { + I2cPinWrite (PciIo, I2CCLK_OUT, 1); + I2cDelay (); + Data = (UINT8) (Data << 1); + Data = (UINT8) (Data | I2cPinRead (PciIo, I2CDAT_IN)); + I2cPinWrite (PciIo, I2CCLK_OUT, 0); + } + + return Data; +} + +/** + Receive an ACK signal from I2C Bus. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + +**/ +BOOLEAN +I2cWaitAck ( + EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + // + // Wait for ACK signal + // + I2cPinWrite (PciIo, I2CDAT_OUT, 1); + I2cPinWrite (PciIo, I2CCLK_OUT, 1); + I2cDelay (); + if (I2cPinRead (PciIo, I2CDAT_IN) == 0) { + I2cPinWrite (PciIo, I2CDAT_OUT, 1); + return TRUE; + } else { + return FALSE; + } +} + +/** + Send an ACK signal onto I2C Bus. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + +**/ +VOID +I2cSendAck ( + EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + I2cPinWrite (PciIo, I2CCLK_OUT, 1); + I2cPinWrite (PciIo, I2CDAT_OUT, 1); + I2cPinWrite (PciIo, I2CDAT_OUT, 0); + I2cPinWrite (PciIo, I2CCLK_OUT, 0); +} + +/** + Start a I2C transfer on I2C Bus. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + +**/ +VOID +I2cStart ( + EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + // + // Init CLK and DAT pins + // + I2cPinWrite (PciIo, I2CCLK_OUT, 1); + I2cPinWrite (PciIo, I2CDAT_OUT, 1); + // + // Start a I2C transfer, set SDA low from high, when SCL is high + // + I2cPinWrite (PciIo, I2CDAT_OUT, 0); + I2cPinWrite (PciIo, I2CCLK_OUT, 0); +} + +/** + Stop a I2C transfer on I2C Bus. + + @param PciIo The pointer to PCI_IO_PROTOCOL. + +**/ +VOID +I2cStop ( + EFI_PCI_IO_PROTOCOL *PciIo + ) +{ + // + // Stop a I2C transfer, set SDA high from low, when SCL is high + // + I2cPinWrite (PciIo, I2CDAT_OUT, 0); + I2cPinWrite (PciIo, I2CCLK_OUT, 1); + I2cPinWrite (PciIo, I2CDAT_OUT, 1); +} + +/** + Read one byte data on I2C Bus. + + Read one byte data from the slave device connectet to I2C Bus. + If Data is NULL, then ASSERT(). + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param DeviceAddress Slave device's address. + @param RegisterAddress The register address on slave device. + @param Data The pointer to returned data if EFI_SUCCESS returned. + + @retval EFI_DEVICE_ERROR + @retval EFI_SUCCESS + +**/ +EFI_STATUS +EFIAPI +I2cReadByte ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 DeviceAddress, + UINT8 RegisterAddress, + UINT8 *Data + ) +{ + ASSERT (Data != NULL); + + // + // Start I2C transfer + // + I2cStart (PciIo); + + // + // Send slave address with enabling write flag + // + I2cSendByte (PciIo, (UINT8) (DeviceAddress & 0xfe)); + + // + // Wait for ACK signal + // + if (I2cWaitAck (PciIo) == FALSE) { + return EFI_DEVICE_ERROR; + } + + // + // Send register address + // + I2cSendByte (PciIo, RegisterAddress); + + // + // Wait for ACK signal + // + if (I2cWaitAck (PciIo) == FALSE) { + return EFI_DEVICE_ERROR; + } + + // + // Send slave address with enabling read flag + // + I2cSendByte (PciIo, (UINT8) (DeviceAddress | 0x01)); + + // + // Wait for ACK signal + // + if (I2cWaitAck (PciIo) == FALSE) { + return EFI_DEVICE_ERROR; + } + + // + // Read byte data from I2C Bus + // + *Data = I2cReceiveByte (PciIo); + + // + // Send ACK signal onto I2C Bus + // + I2cSendAck (PciIo); + + // + // Stop a I2C transfer + // + I2cStop (PciIo); + + return EFI_SUCCESS; +} + +/** + Write one byte data onto I2C Bus. + + Write one byte data to the slave device connectet to I2C Bus. + If Data is NULL, then ASSERT(). + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param DeviceAddress Slave device's address. + @param RegisterAddress The register address on slave device. + @param Data The pointer to write data. + + @retval EFI_DEVICE_ERROR + @retval EFI_SUCCESS + +**/ +EFI_STATUS +EFIAPI +I2cWriteByte ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 DeviceAddress, + UINT8 RegisterAddress, + UINT8 *Data + ) +{ + ASSERT (Data != NULL); + + I2cStart (PciIo); + // + // Send slave address with enabling write flag + // + I2cSendByte (PciIo, (UINT8) (DeviceAddress & 0xfe)); + + // + // Wait for ACK signal + // + if (I2cWaitAck (PciIo) == FALSE) { + return EFI_DEVICE_ERROR; + } + + // + // Send register address + // + I2cSendByte (PciIo, RegisterAddress); + + // + // Wait for ACK signal + // + if (I2cWaitAck (PciIo) == FALSE) { + return EFI_DEVICE_ERROR; + } + + // + // Send byte data onto I2C Bus + // + I2cSendByte (PciIo, *Data); + + // + // Wait for ACK signal + // + if (I2cWaitAck (PciIo) == FALSE) { + return EFI_DEVICE_ERROR; + } + + // + // Stop a I2C transfer + // + I2cStop (PciIo); + + return EFI_SUCCESS; +} + + + diff --git a/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430I2c.h b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430I2c.h new file mode 100644 index 0000000000..505575cc99 --- /dev/null +++ b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430I2c.h @@ -0,0 +1,62 @@ +/** @file + I2c Bus byte read/write functions. + + Copyright (c) 2008 - 2009, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CIRRUS_LOGIC_I2C_H_ +#define _CIRRUS_LOGIC_I2C_H_ + +#include + +/** + Read one byte data on I2C Bus. + + Read one byte data from the slave device connectet to I2C Bus. + If Data is NULL, then ASSERT(). + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param DeviceAddress Slave device's address. + @param RegisterAddress The register address on slave device. + @param Data The pointer to returned data if EFI_SUCCESS returned. + + @retval EFI_DEVICE_ERROR + @retval EFI_SUCCESS + +**/ +EFI_STATUS +EFIAPI +I2cReadByte ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 DeviceAddress, + UINT8 RegisterAddress, + UINT8 *Data + ); + +/** + Write one byte data onto I2C Bus. + + Write one byte data to the slave device connectet to I2C Bus. + If Data is NULL, then ASSERT(). + + @param PciIo The pointer to PCI_IO_PROTOCOL. + @param DeviceAddress Slave device's address. + @param RegisterAddress The register address on slave device. + @param Data The pointer to write data. + + @retval EFI_DEVICE_ERROR + @retval EFI_SUCCESS + +**/ +EFI_STATUS +EFIAPI +I2cWriteByte ( + EFI_PCI_IO_PROTOCOL *PciIo, + UINT8 DeviceAddress, + UINT8 RegisterAddress, + UINT8 *Data + ); + +#endif diff --git a/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430UgaDraw.c b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430UgaDraw.c new file mode 100644 index 0000000000..bdcbd3450c --- /dev/null +++ b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430UgaDraw.c @@ -0,0 +1,412 @@ +/** @file + This file produces the graphics abstration of UGA Draw. It is called by + CirrusLogic5430.c file which deals with the EFI 1.1 driver model. + This file just does graphics. + + Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CirrusLogic5430.h" + +// +// UGA Draw Protocol Member Functions +// +EFI_STATUS +EFIAPI +CirrusLogic5430UgaDrawGetMode ( + IN EFI_UGA_DRAW_PROTOCOL *This, + OUT UINT32 *HorizontalResolution, + OUT UINT32 *VerticalResolution, + OUT UINT32 *ColorDepth, + OUT UINT32 *RefreshRate + ) +{ + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private; + + Private = CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_UGA_DRAW_THIS (This); + + if (Private->HardwareNeedsStarting) { + return EFI_NOT_STARTED; + } + + if ((HorizontalResolution == NULL) || + (VerticalResolution == NULL) || + (ColorDepth == NULL) || + (RefreshRate == NULL)) { + return EFI_INVALID_PARAMETER; + } + + *HorizontalResolution = Private->ModeData[Private->CurrentMode].HorizontalResolution; + *VerticalResolution = Private->ModeData[Private->CurrentMode].VerticalResolution; + *ColorDepth = Private->ModeData[Private->CurrentMode].ColorDepth; + *RefreshRate = Private->ModeData[Private->CurrentMode].RefreshRate; + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +CirrusLogic5430UgaDrawSetMode ( + IN EFI_UGA_DRAW_PROTOCOL *This, + IN UINT32 HorizontalResolution, + IN UINT32 VerticalResolution, + IN UINT32 ColorDepth, + IN UINT32 RefreshRate + ) +{ + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private; + UINTN Index; + + Private = CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_UGA_DRAW_THIS (This); + + for (Index = 0; Index < Private->MaxMode; Index++) { + + if (HorizontalResolution != Private->ModeData[Index].HorizontalResolution) { + continue; + } + + if (VerticalResolution != Private->ModeData[Index].VerticalResolution) { + continue; + } + + if (ColorDepth != Private->ModeData[Index].ColorDepth) { + continue; + } + + if (RefreshRate != Private->ModeData[Index].RefreshRate) { + continue; + } + + if (Private->LineBuffer) { + gBS->FreePool (Private->LineBuffer); + } + + Private->LineBuffer = NULL; + Private->LineBuffer = AllocatePool (HorizontalResolution); + if (Private->LineBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + InitializeGraphicsMode (Private, &CirrusLogic5430VideoModes[Private->ModeData[Index].ModeNumber]); + + Private->CurrentMode = Index; + + Private->HardwareNeedsStarting = FALSE; + + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + +EFI_STATUS +EFIAPI +CirrusLogic5430UgaDrawBlt ( + IN EFI_UGA_DRAW_PROTOCOL *This, + IN EFI_UGA_PIXEL *BltBuffer, OPTIONAL + IN EFI_UGA_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta + ) +{ + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private; + EFI_TPL OriginalTPL; + UINTN DstY; + UINTN SrcY; + EFI_UGA_PIXEL *Blt; + UINTN X; + UINT8 Pixel; + UINT32 WidePixel; + UINTN ScreenWidth; + UINTN Offset; + UINTN SourceOffset; + + Private = CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_UGA_DRAW_THIS (This); + + if ((UINT32)BltOperation >= EfiUgaBltMax) { + return EFI_INVALID_PARAMETER; + } + + if (Width == 0 || Height == 0) { + return EFI_INVALID_PARAMETER; + } + + // + // If Delta is zero, then the entire BltBuffer is being used, so Delta + // is the number of bytes in each row of BltBuffer. Since BltBuffer is Width pixels size, + // the number of bytes in each row can be computed. + // + if (Delta == 0) { + Delta = Width * sizeof (EFI_UGA_PIXEL); + } + + // + // We need to fill the Virtual Screen buffer with the blt data. + // The virtual screen is upside down, as the first row is the bootom row of + // the image. + // + + // + // Make sure the SourceX, SourceY, DestinationX, DestinationY, Width, and Height parameters + // are valid for the operation and the current screen geometry. + // + if (BltOperation == EfiUgaVideoToBltBuffer) { + // + // Video to BltBuffer: Source is Video, destination is BltBuffer + // + if (SourceY + Height > Private->ModeData[Private->CurrentMode].VerticalResolution) { + return EFI_INVALID_PARAMETER; + } + + if (SourceX + Width > Private->ModeData[Private->CurrentMode].HorizontalResolution) { + return EFI_INVALID_PARAMETER; + } + } else { + // + // BltBuffer to Video: Source is BltBuffer, destination is Video + // + if (DestinationY + Height > Private->ModeData[Private->CurrentMode].VerticalResolution) { + return EFI_INVALID_PARAMETER; + } + + if (DestinationX + Width > Private->ModeData[Private->CurrentMode].HorizontalResolution) { + return EFI_INVALID_PARAMETER; + } + } + // + // We have to raise to TPL Notify, so we make an atomic write the frame buffer. + // We would not want a timer based event (Cursor, ...) to come in while we are + // doing this operation. + // + OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY); + + switch (BltOperation) { + case EfiUgaVideoToBltBuffer: + // + // Video to BltBuffer: Source is Video, destination is BltBuffer + // + for (SrcY = SourceY, DstY = DestinationY; DstY < (Height + DestinationY); SrcY++, DstY++) { + + Offset = (SrcY * Private->ModeData[Private->CurrentMode].HorizontalResolution) + SourceX; + if (((Offset & 0x03) == 0) && ((Width & 0x03) == 0)) { + Private->PciIo->Mem.Read ( + Private->PciIo, + EfiPciIoWidthUint32, + 0, + Offset, + Width >> 2, + Private->LineBuffer + ); + } else { + Private->PciIo->Mem.Read ( + Private->PciIo, + EfiPciIoWidthUint8, + 0, + Offset, + Width, + Private->LineBuffer + ); + } + + for (X = 0; X < Width; X++) { + Blt = (EFI_UGA_PIXEL *) ((UINT8 *) BltBuffer + (DstY * Delta) + (DestinationX + X) * sizeof (EFI_UGA_PIXEL)); + + Blt->Red = (UINT8) (Private->LineBuffer[X] & 0xe0); + Blt->Green = (UINT8) ((Private->LineBuffer[X] & 0x1c) << 3); + Blt->Blue = (UINT8) ((Private->LineBuffer[X] & 0x03) << 6); + } + } + break; + + case EfiUgaVideoToVideo: + // + // Perform hardware acceleration for Video to Video operations + // + ScreenWidth = Private->ModeData[Private->CurrentMode].HorizontalResolution; + SourceOffset = (SourceY * Private->ModeData[Private->CurrentMode].HorizontalResolution) + (SourceX); + Offset = (DestinationY * Private->ModeData[Private->CurrentMode].HorizontalResolution) + (DestinationX); + + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0000); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0010); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0012); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0014); + + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0001); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0011); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0013); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0015); + + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) (((Width << 8) & 0xff00) | 0x20)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((Width & 0xff00) | 0x21)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) (((Height << 8) & 0xff00) | 0x22)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((Height & 0xff00) | 0x23)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) (((ScreenWidth << 8) & 0xff00) | 0x24)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((ScreenWidth & 0xff00) | 0x25)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) (((ScreenWidth << 8) & 0xff00) | 0x26)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((ScreenWidth & 0xff00) | 0x27)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((Offset) << 8) & 0xff00) | 0x28)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((Offset) >> 0) & 0xff00) | 0x29)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((Offset) >> 8) & 0xff00) | 0x2a)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((SourceOffset) << 8) & 0xff00) | 0x2c)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((SourceOffset) >> 0) & 0xff00) | 0x2d)); + outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((SourceOffset) >> 8) & 0xff00) | 0x2e)); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x002f); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0030); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0d32); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0033); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0034); + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0035); + + outw (Private, GRAPH_ADDRESS_REGISTER, 0x0231); + + outb (Private, GRAPH_ADDRESS_REGISTER, 0x31); + while ((inb (Private, GRAPH_DATA_REGISTER) & 0x01) == 0x01) + ; + break; + + case EfiUgaVideoFill: + Blt = BltBuffer; + Pixel = (UINT8) ((Blt->Red & 0xe0) | ((Blt->Green >> 3) & 0x1c) | ((Blt->Blue >> 6) & 0x03)); + WidePixel = (Pixel << 8) | Pixel; + WidePixel = (WidePixel << 16) | WidePixel; + + if (DestinationX == 0 && Width == Private->ModeData[Private->CurrentMode].HorizontalResolution) { + Offset = DestinationY * Private->ModeData[Private->CurrentMode].HorizontalResolution; + if (((Offset & 0x03) == 0) && (((Width * Height) & 0x03) == 0)) { + Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthFillUint32, + 0, + Offset, + (Width * Height) >> 2, + &WidePixel + ); + } else { + Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthFillUint8, + 0, + Offset, + Width * Height, + &Pixel + ); + } + } else { + for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) { + Offset = (DstY * Private->ModeData[Private->CurrentMode].HorizontalResolution) + DestinationX; + if (((Offset & 0x03) == 0) && ((Width & 0x03) == 0)) { + Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthFillUint32, + 0, + Offset, + Width >> 2, + &WidePixel + ); + } else { + Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthFillUint8, + 0, + Offset, + Width, + &Pixel + ); + } + } + } + break; + + case EfiUgaBltBufferToVideo: + for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) { + + for (X = 0; X < Width; X++) { + Blt = (EFI_UGA_PIXEL *) ((UINT8 *) BltBuffer + (SrcY * Delta) + (SourceX + X) * sizeof (EFI_UGA_PIXEL)); + Private->LineBuffer[X] = (UINT8) ((Blt->Red & 0xe0) | ((Blt->Green >> 3) & 0x1c) | ((Blt->Blue >> 6) & 0x03)); + } + + Offset = (DstY * Private->ModeData[Private->CurrentMode].HorizontalResolution) + DestinationX; + + if (((Offset & 0x03) == 0) && ((Width & 0x03) == 0)) { + Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthUint32, + 0, + Offset, + Width >> 2, + Private->LineBuffer + ); + } else { + Private->PciIo->Mem.Write ( + Private->PciIo, + EfiPciIoWidthUint8, + 0, + Offset, + Width, + Private->LineBuffer + ); + } + } + break; + + default: + break; + } + + gBS->RestoreTPL (OriginalTPL); + + return EFI_SUCCESS; +} + +// +// Construction and Destruction functions +// +EFI_STATUS +CirrusLogic5430UgaDrawConstructor ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private + ) +{ + EFI_UGA_DRAW_PROTOCOL *UgaDraw; + + // + // Fill in Private->UgaDraw protocol + // + UgaDraw = &Private->UgaDraw; + + UgaDraw->GetMode = CirrusLogic5430UgaDrawGetMode; + UgaDraw->SetMode = CirrusLogic5430UgaDrawSetMode; + UgaDraw->Blt = CirrusLogic5430UgaDrawBlt; + + // + // Initialize the private data + // + Private->CurrentMode = 0; + Private->HardwareNeedsStarting = TRUE; + Private->LineBuffer = NULL; + + // + // Initialize the hardware + // + UgaDraw->SetMode ( + UgaDraw, + Private->ModeData[Private->CurrentMode].HorizontalResolution, + Private->ModeData[Private->CurrentMode].VerticalResolution, + Private->ModeData[Private->CurrentMode].ColorDepth, + Private->ModeData[Private->CurrentMode].RefreshRate + ); + DrawLogo ( + Private, + Private->ModeData[Private->CurrentMode].HorizontalResolution, + Private->ModeData[Private->CurrentMode].VerticalResolution + ); + + return EFI_SUCCESS; +} + diff --git a/Drivers/OptionRomPkg/CirrusLogic5430Dxe/ComponentName.c b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/ComponentName.c new file mode 100644 index 0000000000..3792937bed --- /dev/null +++ b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/ComponentName.c @@ -0,0 +1,203 @@ +/** @file + Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CirrusLogic5430.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gCirrusLogic5430ComponentName = { + CirrusLogic5430ComponentNameGetDriverName, + CirrusLogic5430ComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gCirrusLogic5430ComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) CirrusLogic5430ComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) CirrusLogic5430ComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mCirrusLogic5430DriverNameTable[] = { + { "eng;en", L"Cirrus Logic 5430 Driver" }, + { NULL , NULL } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mCirrusLogic5430ControllerNameTable[] = { + { "eng;en", L"Cirrus Logic 5430 PCI Adapter" }, + { NULL , NULL } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +CirrusLogic5430ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mCirrusLogic5430DriverNameTable, + DriverName, + (BOOLEAN)(This == &gCirrusLogic5430ComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +CirrusLogic5430ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + + // + // This is a device driver, so ChildHandle must be NULL. + // + if (ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver is currently managing ControllHandle + // + Status = EfiTestManagedDevice ( + ControllerHandle, + gCirrusLogic5430DriverBinding.DriverBindingHandle, + &gEfiPciIoProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the Cirrus Logic 5430's Device structure + // + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mCirrusLogic5430ControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gCirrusLogic5430ComponentName) + ); +} diff --git a/Drivers/OptionRomPkg/CirrusLogic5430Dxe/DriverSupportedEfiVersion.c b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/DriverSupportedEfiVersion.c new file mode 100644 index 0000000000..2d8412bf53 --- /dev/null +++ b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/DriverSupportedEfiVersion.c @@ -0,0 +1,14 @@ +/** @file + Copyright (c) 2007, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + + Module Name: DriverSupportEfiVersion.c + +**/ +#include "CirrusLogic5430.h" + +EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL gCirrusLogic5430DriverSupportedEfiVersion = { + sizeof (EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL), // Size of Protocol structure. + 0 // Version number to be filled at start up. +}; + diff --git a/Drivers/OptionRomPkg/CirrusLogic5430Dxe/Edid.c b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/Edid.c new file mode 100644 index 0000000000..5f288d219e --- /dev/null +++ b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/Edid.c @@ -0,0 +1,525 @@ +/** @file + Read EDID information and parse EDID information. + + Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CirrusLogic5430.h" +#include "CirrusLogic5430I2c.h" + +// +// EDID block +// +typedef struct { + UINT8 Header[8]; //EDID header "00 FF FF FF FF FF FF 00" + UINT16 ManufactureName; //EISA 3-character ID + UINT16 ProductCode; //Vendor assigned code + UINT32 SerialNumber; //32-bit serial number + UINT8 WeekOfManufacture; //Week number + UINT8 YearOfManufacture; //Year + UINT8 EdidVersion; //EDID Structure Version + UINT8 EdidRevision; //EDID Structure Revision + UINT8 VideoInputDefinition; + UINT8 MaxHorizontalImageSize; //cm + UINT8 MaxVerticalImageSize; //cm + UINT8 DisplayTransferCharacteristic; + UINT8 FeatureSupport; + UINT8 RedGreenLowBits; //Rx1 Rx0 Ry1 Ry0 Gx1 Gx0 Gy1Gy0 + UINT8 BlueWhiteLowBits; //Bx1 Bx0 By1 By0 Wx1 Wx0 Wy1 Wy0 + UINT8 RedX; //Red-x Bits 9 - 2 + UINT8 RedY; //Red-y Bits 9 - 2 + UINT8 GreenX; //Green-x Bits 9 - 2 + UINT8 GreenY; //Green-y Bits 9 - 2 + UINT8 BlueX; //Blue-x Bits 9 - 2 + UINT8 BlueY; //Blue-y Bits 9 - 2 + UINT8 WhiteX; //White-x Bits 9 - 2 + UINT8 WhiteY; //White-x Bits 9 - 2 + UINT8 EstablishedTimings[3]; + UINT8 StandardTimingIdentification[16]; + UINT8 DetailedTimingDescriptions[72]; + UINT8 ExtensionFlag; //Number of (optional) 128-byte EDID extension blocks to follow + UINT8 Checksum; +} EDID_BLOCK; + +#define EDID_BLOCK_SIZE 128 +#define VBE_EDID_ESTABLISHED_TIMING_MAX_NUMBER 17 + +typedef struct { + UINT16 HorizontalResolution; + UINT16 VerticalResolution; + UINT16 RefreshRate; +} EDID_TIMING; + +typedef struct { + UINT32 ValidNumber; + UINT32 Key[VBE_EDID_ESTABLISHED_TIMING_MAX_NUMBER]; +} VALID_EDID_TIMING; + +// +// Standard timing defined by VESA EDID +// +EDID_TIMING mVbeEstablishedEdidTiming[] = { + // + // Established Timing I + // + {800, 600, 60}, + {800, 600, 56}, + {640, 480, 75}, + {640, 480, 72}, + {640, 480, 67}, + {640, 480, 60}, + {720, 400, 88}, + {720, 400, 70}, + // + // Established Timing II + // + {1280, 1024, 75}, + {1024, 768, 75}, + {1024, 768, 70}, + {1024, 768, 60}, + {1024, 768, 87}, + {832, 624, 75}, + {800, 600, 75}, + {800, 600, 72}, + // + // Established Timing III + // + {1152, 870, 75} +}; + +/** + Read EDID information from I2C Bus on CirrusLogic. + + @param Private Pointer to CIRRUS_LOGIC_5430_PRIVATE_DATA. + @param EdidDataBlock Pointer to EDID data block. + @param EdidSize Returned EDID block size. + + @retval EFI_UNSUPPORTED + @retval EFI_SUCCESS + +**/ +EFI_STATUS +ReadEdidData ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private, + UINT8 **EdidDataBlock, + UINTN *EdidSize + ) +{ + UINTN Index; + UINT8 EdidData[EDID_BLOCK_SIZE * 2]; + UINT8 *ValidEdid; + UINT64 Signature; + + for (Index = 0; Index < EDID_BLOCK_SIZE * 2; Index ++) { + I2cReadByte (Private->PciIo, 0xa0, (UINT8)Index, &EdidData[Index]); + } + + // + // Search for the EDID signature + // + ValidEdid = &EdidData[0]; + Signature = 0x00ffffffffffff00ull; + for (Index = 0; Index < EDID_BLOCK_SIZE * 2; Index ++, ValidEdid ++) { + if (CompareMem (ValidEdid, &Signature, 8) == 0) { + break; + } + } + + if (Index == 256) { + // + // No EDID signature found + // + return EFI_UNSUPPORTED; + } + + *EdidDataBlock = AllocateCopyPool ( + EDID_BLOCK_SIZE, + ValidEdid + ); + if (*EdidDataBlock == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Currently only support EDID 1.x + // + *EdidSize = EDID_BLOCK_SIZE; + + return EFI_SUCCESS; +} + +/** + Generate a search key for a specified timing data. + + @param EdidTiming Pointer to EDID timing + + @return The 32 bit unique key for search. + +**/ +UINT32 +CalculateEdidKey ( + EDID_TIMING *EdidTiming + ) +{ + UINT32 Key; + + // + // Be sure no conflicts for all standard timing defined by VESA. + // + Key = (EdidTiming->HorizontalResolution * 2) + EdidTiming->VerticalResolution; + return Key; +} + +/** + Search a specified Timing in all the valid EDID timings. + + @param ValidEdidTiming All valid EDID timing information. + @param EdidTiming The Timing to search for. + + @retval TRUE Found. + @retval FALSE Not found. + +**/ +BOOLEAN +SearchEdidTiming ( + VALID_EDID_TIMING *ValidEdidTiming, + EDID_TIMING *EdidTiming + ) +{ + UINT32 Index; + UINT32 Key; + + Key = CalculateEdidKey (EdidTiming); + + for (Index = 0; Index < ValidEdidTiming->ValidNumber; Index ++) { + if (Key == ValidEdidTiming->Key[Index]) { + return TRUE; + } + } + + return FALSE; +} + +/** + Parse the Established Timing and Standard Timing in EDID data block. + + @param EdidBuffer Pointer to EDID data block + @param ValidEdidTiming Valid EDID timing information + + @retval TRUE The EDID data is valid. + @retval FALSE The EDID data is invalid. + +**/ +BOOLEAN +ParseEdidData ( + UINT8 *EdidBuffer, + VALID_EDID_TIMING *ValidEdidTiming + ) +{ + UINT8 CheckSum; + UINT32 Index; + UINT32 ValidNumber; + UINT32 TimingBits; + UINT8 *BufferIndex; + UINT16 HorizontalResolution; + UINT16 VerticalResolution; + UINT8 AspectRatio; + UINT8 RefreshRate; + EDID_TIMING TempTiming; + EDID_BLOCK *EdidDataBlock; + + EdidDataBlock = (EDID_BLOCK *) EdidBuffer; + + // + // Check the checksum of EDID data + // + CheckSum = 0; + for (Index = 0; Index < EDID_BLOCK_SIZE; Index ++) { + CheckSum = (UINT8) (CheckSum + EdidBuffer[Index]); + } + if (CheckSum != 0) { + return FALSE; + } + + ValidNumber = 0; + SetMem (ValidEdidTiming, sizeof (VALID_EDID_TIMING), 0); + + if ((EdidDataBlock->EstablishedTimings[0] != 0) || + (EdidDataBlock->EstablishedTimings[1] != 0) || + (EdidDataBlock->EstablishedTimings[2] != 0) + ) { + // + // Established timing data + // + TimingBits = EdidDataBlock->EstablishedTimings[0] | + (EdidDataBlock->EstablishedTimings[1] << 8) | + ((EdidDataBlock->EstablishedTimings[2] & 0x80) << 9) ; + for (Index = 0; Index < VBE_EDID_ESTABLISHED_TIMING_MAX_NUMBER; Index ++) { + if (TimingBits & 0x1) { + ValidEdidTiming->Key[ValidNumber] = CalculateEdidKey (&mVbeEstablishedEdidTiming[Index]); + ValidNumber ++; + } + TimingBits = TimingBits >> 1; + } + } else { + // + // If no Established timing data, read the standard timing data + // + BufferIndex = &EdidDataBlock->StandardTimingIdentification[0]; + for (Index = 0; Index < 8; Index ++) { + if ((BufferIndex[0] != 0x1) && (BufferIndex[1] != 0x1)){ + // + // A valid Standard Timing + // + HorizontalResolution = (UINT16) (BufferIndex[0] * 8 + 248); + AspectRatio = (UINT8) (BufferIndex[1] >> 6); + switch (AspectRatio) { + case 0: + VerticalResolution = (UINT16) (HorizontalResolution / 16 * 10); + break; + case 1: + VerticalResolution = (UINT16) (HorizontalResolution / 4 * 3); + break; + case 2: + VerticalResolution = (UINT16) (HorizontalResolution / 5 * 4); + break; + case 3: + VerticalResolution = (UINT16) (HorizontalResolution / 16 * 9); + break; + default: + VerticalResolution = (UINT16) (HorizontalResolution / 4 * 3); + break; + } + RefreshRate = (UINT8) ((BufferIndex[1] & 0x1f) + 60); + TempTiming.HorizontalResolution = HorizontalResolution; + TempTiming.VerticalResolution = VerticalResolution; + TempTiming.RefreshRate = RefreshRate; + ValidEdidTiming->Key[ValidNumber] = CalculateEdidKey (&TempTiming); + ValidNumber ++; + } + BufferIndex += 2; + } + } + + ValidEdidTiming->ValidNumber = ValidNumber; + return TRUE; +} + +/** + Construct the valid video modes for CirrusLogic5430. + +**/ +EFI_STATUS +CirrusLogic5430VideoModeSetup ( + CIRRUS_LOGIC_5430_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINT32 Index; + BOOLEAN EdidFound; + EFI_EDID_OVERRIDE_PROTOCOL *EdidOverride; + UINT32 EdidAttributes; + BOOLEAN EdidOverrideFound; + UINTN EdidOverrideDataSize; + UINT8 *EdidOverrideDataBlock; + UINTN EdidDiscoveredDataSize; + UINT8 *EdidDiscoveredDataBlock; + UINTN EdidActiveDataSize; + UINT8 *EdidActiveDataBlock; + VALID_EDID_TIMING ValidEdidTiming; + UINT32 ValidModeCount; + CIRRUS_LOGIC_5430_MODE_DATA *ModeData; + BOOLEAN TimingMatch; + CIRRUS_LOGIC_5430_VIDEO_MODES *VideoMode; + EDID_TIMING TempTiming; + + // + // setup EDID information + // + Private->EdidDiscovered.Edid = NULL; + Private->EdidDiscovered.SizeOfEdid = 0; + Private->EdidActive.Edid = NULL; + Private->EdidActive.SizeOfEdid = 0; + + EdidFound = FALSE; + EdidOverrideFound = FALSE; + EdidAttributes = 0xff; + EdidOverrideDataSize = 0; + EdidOverrideDataBlock = NULL; + EdidActiveDataSize = 0; + EdidActiveDataBlock = NULL; + EdidDiscoveredDataBlock = NULL; + + // + // Find EDID Override protocol firstly, this protocol is installed by platform if needed. + // + Status = gBS->LocateProtocol ( + &gEfiEdidOverrideProtocolGuid, + NULL, + (VOID **) &EdidOverride + ); + if (!EFI_ERROR (Status)) { + // + // Allocate double size of VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE to avoid overflow + // + EdidOverrideDataBlock = AllocatePool (EDID_BLOCK_SIZE * 2); + if (NULL == EdidOverrideDataBlock) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + Status = EdidOverride->GetEdid ( + EdidOverride, + Private->Handle, + &EdidAttributes, + &EdidOverrideDataSize, + (UINT8 **) &EdidOverrideDataBlock + ); + if (!EFI_ERROR (Status) && + EdidAttributes == 0 && + EdidOverrideDataSize != 0) { + // + // Succeeded to get EDID Override Data + // + EdidOverrideFound = TRUE; + } + } + + if (EdidOverrideFound != TRUE || EdidAttributes == EFI_EDID_OVERRIDE_DONT_OVERRIDE) { + // + // If EDID Override data doesn't exist or EFI_EDID_OVERRIDE_DONT_OVERRIDE returned, + // read EDID information through I2C Bus + // + if (ReadEdidData (Private, &EdidDiscoveredDataBlock, &EdidDiscoveredDataSize) == EFI_SUCCESS) { + Private->EdidDiscovered.SizeOfEdid = (UINT32) EdidDiscoveredDataSize; + Private->EdidDiscovered.Edid = (UINT8 *) AllocateCopyPool ( + EdidDiscoveredDataSize, + EdidDiscoveredDataBlock + ); + + if (NULL == Private->EdidDiscovered.Edid) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + EdidActiveDataSize = Private->EdidDiscovered.SizeOfEdid; + EdidActiveDataBlock = Private->EdidDiscovered.Edid; + + EdidFound = TRUE; + } + } + + if (EdidFound != TRUE && EdidOverrideFound == TRUE) { + EdidActiveDataSize = EdidOverrideDataSize; + EdidActiveDataBlock = EdidOverrideDataBlock; + EdidFound = TRUE; + } + + if (EdidFound == TRUE) { + // + // Parse EDID data structure to retrieve modes supported by monitor + // + if (ParseEdidData ((UINT8 *) EdidActiveDataBlock, &ValidEdidTiming) == TRUE) { + // + // Copy EDID Override Data to EDID Active Data + // + Private->EdidActive.SizeOfEdid = (UINT32) EdidActiveDataSize; + Private->EdidActive.Edid = (UINT8 *) AllocateCopyPool ( + EdidActiveDataSize, + EdidActiveDataBlock + ); + if (NULL == Private->EdidActive.Edid) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + } + } else { + Private->EdidActive.SizeOfEdid = 0; + Private->EdidActive.Edid = NULL; + EdidFound = FALSE; + } + + if (EdidFound) { + // + // Initialize the private mode data with the supported modes. + // + ValidModeCount = 0; + ModeData = &Private->ModeData[0]; + VideoMode = &CirrusLogic5430VideoModes[0]; + for (Index = 0; Index < CIRRUS_LOGIC_5430_MODE_COUNT; Index++) { + + TimingMatch = TRUE; + + // + // Check whether match with CirrusLogic5430 video mode + // + TempTiming.HorizontalResolution = (UINT16) VideoMode->Width; + TempTiming.VerticalResolution = (UINT16) VideoMode->Height; + TempTiming.RefreshRate = (UINT16) VideoMode->RefreshRate; + if (SearchEdidTiming (&ValidEdidTiming, &TempTiming) != TRUE) { + TimingMatch = FALSE; + } + + // + // Not export Mode 0x0 as GOP mode, this is not defined in spec. + // + if ((VideoMode->Width == 0) || (VideoMode->Height == 0)) { + TimingMatch = FALSE; + } + + if (TimingMatch) { + ModeData->ModeNumber = Index; + ModeData->HorizontalResolution = VideoMode->Width; + ModeData->VerticalResolution = VideoMode->Height; + ModeData->ColorDepth = VideoMode->ColorDepth; + ModeData->RefreshRate = VideoMode->RefreshRate; + + ModeData ++; + ValidModeCount ++; + } + + VideoMode ++; + } + + Private->MaxMode = ValidModeCount; + + } else { + // + // If EDID information wasn't found + // + ModeData = &Private->ModeData[0]; + VideoMode = &CirrusLogic5430VideoModes[0]; + for (Index = 0; Index < CIRRUS_LOGIC_5430_MODE_COUNT; Index ++) { + ModeData->ModeNumber = Index; + ModeData->HorizontalResolution = VideoMode->Width; + ModeData->VerticalResolution = VideoMode->Height; + ModeData->ColorDepth = VideoMode->ColorDepth; + ModeData->RefreshRate = VideoMode->RefreshRate; + + ModeData ++ ; + VideoMode ++; + } + Private->MaxMode = CIRRUS_LOGIC_5430_MODE_COUNT; + } + + if (EdidOverrideDataBlock != NULL) { + FreePool (EdidOverrideDataBlock); + } + + return EFI_SUCCESS; + +Done: + if (EdidOverrideDataBlock != NULL) { + FreePool (EdidOverrideDataBlock); + } + if (Private->EdidDiscovered.Edid != NULL) { + FreePool (Private->EdidDiscovered.Edid); + } + if (Private->EdidDiscovered.Edid != NULL) { + FreePool (Private->EdidActive.Edid); + } + + return EFI_DEVICE_ERROR; +} diff --git a/Drivers/OptionRomPkg/Include/Library/BltLib.h b/Drivers/OptionRomPkg/Include/Library/BltLib.h new file mode 100644 index 0000000000..48990a296d --- /dev/null +++ b/Drivers/OptionRomPkg/Include/Library/BltLib.h @@ -0,0 +1,253 @@ +/** @file + Library for performing video blt operations + + Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __BLT_LIB__ +#define __BLT_LIB__ + +#include + + +/** + Configure the BltLib for a frame-buffer + + @param[in] FrameBuffer Pointer to the start of the frame buffer + @param[in] FrameBufferInfo Describes the frame buffer characteristics + + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - Blt operation success + +**/ +EFI_STATUS +EFIAPI +BltLibConfigure ( + IN VOID *FrameBuffer, + IN EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *FrameBufferInfo + ); + + +/** + Performs a UEFI Graphics Output Protocol Blt operation. + + @param[in,out] BltBuffer - The data to transfer to screen + @param[in] BltOperation - The operation to perform + @param[in] SourceX - The X coordinate of the source for BltOperation + @param[in] SourceY - The Y coordinate of the source for BltOperation + @param[in] DestinationX - The X coordinate of the destination for BltOperation + @param[in] DestinationY - The Y coordinate of the destination for BltOperation + @param[in] Width - The width of a rectangle in the blt rectangle in pixels + @param[in] Height - The height of a rectangle in the blt rectangle in pixels + @param[in] Delta - Not used for EfiBltVideoFill and EfiBltVideoToVideo operation. + If a Delta of 0 is used, the entire BltBuffer will be operated on. + If a subrectangle of the BltBuffer is used, then Delta represents + the number of bytes in a row of the BltBuffer. + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - Blt operation success + +**/ +EFI_STATUS +EFIAPI +BltLibGopBlt ( + IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL + IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta + ); + + +/** + Performs a UEFI Graphics Output Protocol Blt Video Fill. + + @param[in] Color Color to fill the region with + @param[in] DestinationX X location to start fill operation + @param[in] DestinationY Y location to start fill operation + @param[in] Width Width (in pixels) to fill + @param[in] Height Height to fill + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - Blt operation success + +**/ +EFI_STATUS +EFIAPI +BltLibVideoFill ( + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Color, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height + ); + + +/** + Performs a UEFI Graphics Output Protocol Blt Video to Buffer operation. + + @param[out] BltBuffer Output buffer for pixel color data + @param[in] SourceX X location within video + @param[in] SourceY Y location within video + @param[in] Width Width (in pixels) + @param[in] Height Height + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - Blt operation success + +**/ +EFI_STATUS +EFIAPI +BltLibVideoToBltBuffer ( + OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN Width, + IN UINTN Height + ); + + +/** + Performs a UEFI Graphics Output Protocol Blt Video to Buffer operation + with extended parameters. + + @param[out] BltBuffer Output buffer for pixel color data + @param[in] SourceX X location within video + @param[in] SourceY Y location within video + @param[in] DestinationX X location within BltBuffer + @param[in] DestinationY Y location within BltBuffer + @param[in] Width Width (in pixels) + @param[in] Height Height + @param[in] Delta Number of bytes in a row of BltBuffer + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - Blt operation success + +**/ +EFI_STATUS +EFIAPI +BltLibVideoToBltBufferEx ( + OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta + ); + + +/** + Performs a UEFI Graphics Output Protocol Blt Buffer to Video operation. + + @param[in] BltBuffer Output buffer for pixel color data + @param[in] DestinationX X location within video + @param[in] DestinationY Y location within video + @param[in] Width Width (in pixels) + @param[in] Height Height + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - Blt operation success + +**/ +EFI_STATUS +EFIAPI +BltLibBufferToVideo ( + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height + ); + + +/** + Performs a UEFI Graphics Output Protocol Blt Buffer to Video operation + with extended parameters. + + @param[in] BltBuffer Output buffer for pixel color data + @param[in] SourceX X location within BltBuffer + @param[in] SourceY Y location within BltBuffer + @param[in] DestinationX X location within video + @param[in] DestinationY Y location within video + @param[in] Width Width (in pixels) + @param[in] Height Height + @param[in] Delta Number of bytes in a row of BltBuffer + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - Blt operation success + +**/ +EFI_STATUS +EFIAPI +BltLibBufferToVideoEx ( + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta + ); + + +/** + Performs a UEFI Graphics Output Protocol Blt Video to Video operation + + @param[in] SourceX X location within video + @param[in] SourceY Y location within video + @param[in] DestinationX X location within video + @param[in] DestinationY Y location within video + @param[in] Width Width (in pixels) + @param[in] Height Height + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - Blt operation success + +**/ +EFI_STATUS +EFIAPI +BltLibVideoToVideo ( + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height + ); + + +/** + Returns the sizes related to the video device + + @param[out] Width Width (in pixels) + @param[out] Height Height (in pixels) + + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - The sizes were returned + +**/ +EFI_STATUS +EFIAPI +BltLibGetSizes ( + OUT UINTN *Width, OPTIONAL + OUT UINTN *Height OPTIONAL + ); + +#endif + diff --git a/Drivers/OptionRomPkg/Library/FrameBufferBltLib/FrameBufferBltLib.c b/Drivers/OptionRomPkg/Library/FrameBufferBltLib/FrameBufferBltLib.c new file mode 100644 index 0000000000..520fac4c63 --- /dev/null +++ b/Drivers/OptionRomPkg/Library/FrameBufferBltLib/FrameBufferBltLib.c @@ -0,0 +1,744 @@ +/** @file + FrameBufferBltLib - Library to perform blt operations on a frame buffer. + + Copyright (c) 2007 - 2011, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiDxe.h" +#include +#include +#include +#include + +#if 0 +#define VDEBUG DEBUG +#else +#define VDEBUG(x) +#endif + +#define MAX_LINE_BUFFER_SIZE (SIZE_4KB * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)) + +UINTN mBltLibColorDepth; +UINTN mBltLibWidthInBytes; +UINTN mBltLibBytesPerPixel; +UINTN mBltLibWidthInPixels; +UINTN mBltLibHeight; +UINT8 mBltLibLineBuffer[MAX_LINE_BUFFER_SIZE]; +UINT8 *mBltLibFrameBuffer; +EFI_GRAPHICS_PIXEL_FORMAT mPixelFormat; +EFI_PIXEL_BITMASK mPixelBitMasks; +INTN mPixelShl[4]; // R-G-B-Rsvd +INTN mPixelShr[4]; // R-G-B-Rsvd + + +VOID +ConfigurePixelBitMaskFormat ( + IN EFI_PIXEL_BITMASK *BitMask + ) +{ + UINTN Loop; + UINT32 *Masks; + UINT32 MergedMasks; + + MergedMasks = 0; + Masks = (UINT32*) BitMask; + for (Loop = 0; Loop < 3; Loop++) { + ASSERT ((Loop == 3) || (Masks[Loop] != 0)); + ASSERT ((MergedMasks & Masks[Loop]) == 0); + mPixelShl[Loop] = HighBitSet32 (Masks[Loop]) - 23 + (Loop * 8); + if (mPixelShl[Loop] < 0) { + mPixelShr[Loop] = -mPixelShl[Loop]; + mPixelShl[Loop] = 0; + } else { + mPixelShr[Loop] = 0; + } + MergedMasks = (UINT32) (MergedMasks | Masks[Loop]); + DEBUG ((EFI_D_INFO, "%d: shl:%d shr:%d mask:%x\n", Loop, mPixelShl[Loop], mPixelShr[Loop], Masks[Loop])); + } + MergedMasks = (UINT32) (MergedMasks | Masks[3]); + + ASSERT (MergedMasks != 0); + mBltLibBytesPerPixel = (UINTN) ((HighBitSet32 (MergedMasks) + 7) / 8); + + DEBUG ((EFI_D_INFO, "Bytes per pixel: %d\n", mBltLibBytesPerPixel)); + + CopyMem (&mPixelBitMasks, BitMask, sizeof (*BitMask)); +} + + +/** + Configure the FrameBufferLib instance + + @param[in] FrameBuffer Pointer to the start of the frame buffer + @param[in] FrameBufferInfo Describes the frame buffer characteristics + + @retval EFI_INVALID_PARAMETER - Invalid parameter + @retval EFI_UNSUPPORTED - The BltLib does not support this configuration + @retval EFI_SUCCESS - Blt operation success + +**/ +EFI_STATUS +EFIAPI +BltLibConfigure ( + IN VOID *FrameBuffer, + IN EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *FrameBufferInfo + ) +{ + STATIC EFI_PIXEL_BITMASK RgbPixelMasks = + { 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }; + STATIC EFI_PIXEL_BITMASK BgrPixelMasks = + { 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 }; + + switch (FrameBufferInfo->PixelFormat) { + case PixelRedGreenBlueReserved8BitPerColor: + ConfigurePixelBitMaskFormat (&RgbPixelMasks); + break; + case PixelBlueGreenRedReserved8BitPerColor: + ConfigurePixelBitMaskFormat (&BgrPixelMasks); + break; + case PixelBitMask: + ConfigurePixelBitMaskFormat (&(FrameBufferInfo->PixelInformation)); + break; + case PixelBltOnly: + ASSERT (FrameBufferInfo->PixelFormat != PixelBltOnly); + return EFI_UNSUPPORTED; + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + mPixelFormat = FrameBufferInfo->PixelFormat; + + mBltLibFrameBuffer = (UINT8*) FrameBuffer; + mBltLibWidthInPixels = (UINTN) FrameBufferInfo->HorizontalResolution; + mBltLibHeight = (UINTN) FrameBufferInfo->VerticalResolution; + mBltLibWidthInBytes = mBltLibWidthInPixels * mBltLibBytesPerPixel; + + ASSERT (mBltLibWidthInBytes < sizeof (mBltLibLineBuffer)); + + return EFI_SUCCESS; +} + + +/** + Performs a UEFI Graphics Output Protocol Blt operation. + + @param[in,out] BltBuffer - The data to transfer to screen + @param[in] BltOperation - The operation to perform + @param[in] SourceX - The X coordinate of the source for BltOperation + @param[in] SourceY - The Y coordinate of the source for BltOperation + @param[in] DestinationX - The X coordinate of the destination for BltOperation + @param[in] DestinationY - The Y coordinate of the destination for BltOperation + @param[in] Width - The width of a rectangle in the blt rectangle in pixels + @param[in] Height - The height of a rectangle in the blt rectangle in pixels + @param[in] Delta - Not used for EfiBltVideoFill and EfiBltVideoToVideo operation. + If a Delta of 0 is used, the entire BltBuffer will be operated on. + If a subrectangle of the BltBuffer is used, then Delta represents + the number of bytes in a row of the BltBuffer. + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - Blt operation success + +**/ +EFI_STATUS +EFIAPI +BltLibGopBlt ( + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL + IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta + ) +{ + switch (BltOperation) { + case EfiBltVideoToBltBuffer: + return BltLibVideoToBltBufferEx ( + BltBuffer, + SourceX, + SourceY, + DestinationX, + DestinationY, + Width, + Height, + Delta + ); + + case EfiBltVideoToVideo: + return BltLibVideoToVideo ( + SourceX, + SourceY, + DestinationX, + DestinationY, + Width, + Height + ); + + case EfiBltVideoFill: + return BltLibVideoFill ( + BltBuffer, + DestinationX, + DestinationY, + Width, + Height + ); + + case EfiBltBufferToVideo: + return BltLibBufferToVideoEx ( + BltBuffer, + SourceX, + SourceY, + DestinationX, + DestinationY, + Width, + Height, + Delta + ); + default: + return EFI_INVALID_PARAMETER; + } +} + + +/** + Performs a UEFI Graphics Output Protocol Blt Video Fill. + + @param[in] Color Color to fill the region with + @param[in] DestinationX X location to start fill operation + @param[in] DestinationY Y location to start fill operation + @param[in] Width Width (in pixels) to fill + @param[in] Height Height to fill + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - The sizes were returned + +**/ +EFI_STATUS +EFIAPI +BltLibVideoFill ( + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Color, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height + ) +{ + UINTN DstY; + VOID *BltMemDst; + UINTN X; + UINT8 Uint8; + UINT32 Uint32; + UINT64 WideFill; + BOOLEAN UseWideFill; + BOOLEAN LineBufferReady; + UINTN Offset; + UINTN WidthInBytes; + UINTN SizeInBytes; + + // + // BltBuffer to Video: Source is BltBuffer, destination is Video + // + if (DestinationY + Height > mBltLibHeight) { + DEBUG ((EFI_D_INFO, "VideoFill: Past screen (Y)\n")); + return EFI_INVALID_PARAMETER; + } + + if (DestinationX + Width > mBltLibWidthInPixels) { + DEBUG ((EFI_D_INFO, "VideoFill: Past screen (X)\n")); + return EFI_INVALID_PARAMETER; + } + + if (Width == 0 || Height == 0) { + DEBUG ((EFI_D_INFO, "VideoFill: Width or Height is 0\n")); + return EFI_INVALID_PARAMETER; + } + + WidthInBytes = Width * mBltLibBytesPerPixel; + + Uint32 = *(UINT32*) Color; + WideFill = + (UINT32) ( + (((Uint32 << mPixelShl[0]) >> mPixelShr[0]) & mPixelBitMasks.RedMask) | + (((Uint32 << mPixelShl[1]) >> mPixelShr[1]) & mPixelBitMasks.GreenMask) | + (((Uint32 << mPixelShl[2]) >> mPixelShr[2]) & mPixelBitMasks.BlueMask) + ); + VDEBUG ((EFI_D_INFO, "VideoFill: color=0x%x, wide-fill=0x%x\n", Uint32, WideFill)); + + // + // If the size of the pixel data evenly divides the sizeof + // WideFill, then a wide fill operation can be used + // + UseWideFill = TRUE; + if ((sizeof (WideFill) % mBltLibBytesPerPixel) == 0) { + for (X = mBltLibBytesPerPixel; X < sizeof (WideFill); X++) { + ((UINT8*)&WideFill)[X] = ((UINT8*)&WideFill)[X % mBltLibBytesPerPixel]; + } + } else { + // + // If all the bytes in the pixel are the same value, then use + // a wide fill operation. + // + for ( + X = 1, Uint8 = ((UINT8*)&WideFill)[0]; + X < mBltLibBytesPerPixel; + X++) { + if (Uint8 != ((UINT8*)&WideFill)[X]) { + UseWideFill = FALSE; + break; + } + } + if (UseWideFill) { + SetMem ((VOID*) &WideFill, sizeof (WideFill), Uint8); + } + } + + if (UseWideFill && (DestinationX == 0) && (Width == mBltLibWidthInPixels)) { + VDEBUG ((EFI_D_INFO, "VideoFill (wide, one-shot)\n")); + Offset = DestinationY * mBltLibWidthInPixels; + Offset = mBltLibBytesPerPixel * Offset; + BltMemDst = (VOID*) (mBltLibFrameBuffer + Offset); + SizeInBytes = WidthInBytes * Height; + if (SizeInBytes >= 8) { + SetMem32 (BltMemDst, SizeInBytes & ~3, (UINT32) WideFill); + SizeInBytes = SizeInBytes & 3; + } + if (SizeInBytes > 0) { + SetMem (BltMemDst, SizeInBytes, (UINT8)(UINTN) WideFill); + } + } else { + LineBufferReady = FALSE; + for (DstY = DestinationY; DstY < (Height + DestinationY); DstY++) { + Offset = (DstY * mBltLibWidthInPixels) + DestinationX; + Offset = mBltLibBytesPerPixel * Offset; + BltMemDst = (VOID*) (mBltLibFrameBuffer + Offset); + + if (UseWideFill && (((UINTN) BltMemDst & 7) == 0)) { + VDEBUG ((EFI_D_INFO, "VideoFill (wide)\n")); + SizeInBytes = WidthInBytes; + if (SizeInBytes >= 8) { + SetMem64 (BltMemDst, SizeInBytes & ~7, WideFill); + SizeInBytes = SizeInBytes & 7; + } + if (SizeInBytes > 0) { + CopyMem (BltMemDst, (VOID*) &WideFill, SizeInBytes); + } + } else { + VDEBUG ((EFI_D_INFO, "VideoFill (not wide)\n")); + if (!LineBufferReady) { + CopyMem (mBltLibLineBuffer, &WideFill, mBltLibBytesPerPixel); + for (X = 1; X < Width; ) { + CopyMem( + (mBltLibLineBuffer + (X * mBltLibBytesPerPixel)), + mBltLibLineBuffer, + MIN (X, Width - X) * mBltLibBytesPerPixel + ); + X = X + MIN (X, Width - X); + } + LineBufferReady = TRUE; + } + CopyMem (BltMemDst, mBltLibLineBuffer, WidthInBytes); + } + } + } + + return EFI_SUCCESS; +} + + +/** + Performs a UEFI Graphics Output Protocol Blt Video to Buffer operation. + + @param[out] BltBuffer Output buffer for pixel color data + @param[in] SourceX X location within video + @param[in] SourceY Y location within video + @param[in] Width Width (in pixels) + @param[in] Height Height + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - The sizes were returned + +**/ +EFI_STATUS +EFIAPI +BltLibVideoToBltBuffer ( + OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN Width, + IN UINTN Height + ) +{ + return BltLibVideoToBltBufferEx ( + BltBuffer, + SourceX, + SourceY, + 0, + 0, + Width, + Height, + 0 + ); +} + + +/** + Performs a UEFI Graphics Output Protocol Blt Video to Buffer operation + with extended parameters. + + @param[out] BltBuffer Output buffer for pixel color data + @param[in] SourceX X location within video + @param[in] SourceY Y location within video + @param[in] DestinationX X location within BltBuffer + @param[in] DestinationY Y location within BltBuffer + @param[in] Width Width (in pixels) + @param[in] Height Height + @param[in] Delta Number of bytes in a row of BltBuffer + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - The sizes were returned + +**/ +EFI_STATUS +EFIAPI +BltLibVideoToBltBufferEx ( + OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta + ) +{ + UINTN DstY; + UINTN SrcY; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt; + VOID *BltMemSrc; + VOID *BltMemDst; + UINTN X; + UINT32 Uint32; + UINTN Offset; + UINTN WidthInBytes; + + // + // Video to BltBuffer: Source is Video, destination is BltBuffer + // + if (SourceY + Height > mBltLibHeight) { + return EFI_INVALID_PARAMETER; + } + + if (SourceX + Width > mBltLibWidthInPixels) { + return EFI_INVALID_PARAMETER; + } + + if (Width == 0 || Height == 0) { + return EFI_INVALID_PARAMETER; + } + + // + // If Delta is zero, then the entire BltBuffer is being used, so Delta + // is the number of bytes in each row of BltBuffer. Since BltBuffer is Width pixels size, + // the number of bytes in each row can be computed. + // + if (Delta == 0) { + Delta = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL); + } + + WidthInBytes = Width * mBltLibBytesPerPixel; + + // + // Video to BltBuffer: Source is Video, destination is BltBuffer + // + for (SrcY = SourceY, DstY = DestinationY; DstY < (Height + DestinationY); SrcY++, DstY++) { + + Offset = (SrcY * mBltLibWidthInPixels) + SourceX; + Offset = mBltLibBytesPerPixel * Offset; + BltMemSrc = (VOID *) (mBltLibFrameBuffer + Offset); + + if (mPixelFormat == PixelBlueGreenRedReserved8BitPerColor) { + BltMemDst = + (VOID *) ( + (UINT8 *) BltBuffer + + (DstY * Delta) + + (DestinationX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)) + ); + } else { + BltMemDst = (VOID *) mBltLibLineBuffer; + } + + CopyMem (BltMemDst, BltMemSrc, WidthInBytes); + + if (mPixelFormat != PixelBlueGreenRedReserved8BitPerColor) { + for (X = 0; X < Width; X++) { + Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) ((UINT8 *) BltBuffer + (DstY * Delta) + (DestinationX + X) * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); + Uint32 = *(UINT32*) (mBltLibLineBuffer + (X * mBltLibBytesPerPixel)); + *(UINT32*) Blt = + (UINT32) ( + (((Uint32 & mPixelBitMasks.RedMask) >> mPixelShl[0]) << mPixelShr[0]) | + (((Uint32 & mPixelBitMasks.GreenMask) >> mPixelShl[1]) << mPixelShr[1]) | + (((Uint32 & mPixelBitMasks.BlueMask) >> mPixelShl[2]) << mPixelShr[2]) + ); + } + } + } + + return EFI_SUCCESS; +} + + +/** + Performs a UEFI Graphics Output Protocol Blt Buffer to Video operation. + + @param[in] BltBuffer Output buffer for pixel color data + @param[in] DestinationX X location within video + @param[in] DestinationY Y location within video + @param[in] Width Width (in pixels) + @param[in] Height Height + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - The sizes were returned + +**/ +EFI_STATUS +EFIAPI +BltLibBufferToVideo ( + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height + ) +{ + return BltLibBufferToVideoEx ( + BltBuffer, + 0, + 0, + DestinationX, + DestinationY, + Width, + Height, + 0 + ); +} + + +/** + Performs a UEFI Graphics Output Protocol Blt Buffer to Video operation + with extended parameters. + + @param[in] BltBuffer Output buffer for pixel color data + @param[in] SourceX X location within BltBuffer + @param[in] SourceY Y location within BltBuffer + @param[in] DestinationX X location within video + @param[in] DestinationY Y location within video + @param[in] Width Width (in pixels) + @param[in] Height Height + @param[in] Delta Number of bytes in a row of BltBuffer + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - The sizes were returned + +**/ +EFI_STATUS +EFIAPI +BltLibBufferToVideoEx ( + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta + ) +{ + UINTN DstY; + UINTN SrcY; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt; + VOID *BltMemSrc; + VOID *BltMemDst; + UINTN X; + UINT32 Uint32; + UINTN Offset; + UINTN WidthInBytes; + + // + // BltBuffer to Video: Source is BltBuffer, destination is Video + // + if (DestinationY + Height > mBltLibHeight) { + return EFI_INVALID_PARAMETER; + } + + if (DestinationX + Width > mBltLibWidthInPixels) { + return EFI_INVALID_PARAMETER; + } + + if (Width == 0 || Height == 0) { + return EFI_INVALID_PARAMETER; + } + + // + // If Delta is zero, then the entire BltBuffer is being used, so Delta + // is the number of bytes in each row of BltBuffer. Since BltBuffer is Width pixels size, + // the number of bytes in each row can be computed. + // + if (Delta == 0) { + Delta = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL); + } + + WidthInBytes = Width * mBltLibBytesPerPixel; + + for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) { + + Offset = (DstY * mBltLibWidthInPixels) + DestinationX; + Offset = mBltLibBytesPerPixel * Offset; + BltMemDst = (VOID*) (mBltLibFrameBuffer + Offset); + + if (mPixelFormat == PixelBlueGreenRedReserved8BitPerColor) { + BltMemSrc = (VOID *) ((UINT8 *) BltBuffer + (SrcY * Delta)); + } else { + for (X = 0; X < Width; X++) { + Blt = + (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) ( + (UINT8 *) BltBuffer + + (SrcY * Delta) + + ((SourceX + X) * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)) + ); + Uint32 = *(UINT32*) Blt; + *(UINT32*) (mBltLibLineBuffer + (X * mBltLibBytesPerPixel)) = + (UINT32) ( + (((Uint32 << mPixelShl[0]) >> mPixelShr[0]) & mPixelBitMasks.RedMask) | + (((Uint32 << mPixelShl[1]) >> mPixelShr[1]) & mPixelBitMasks.GreenMask) | + (((Uint32 << mPixelShl[2]) >> mPixelShr[2]) & mPixelBitMasks.BlueMask) + ); + } + BltMemSrc = (VOID *) mBltLibLineBuffer; + } + + CopyMem (BltMemDst, BltMemSrc, WidthInBytes); + } + + return EFI_SUCCESS; +} + + +/** + Performs a UEFI Graphics Output Protocol Blt Video to Video operation + + @param[in] SourceX X location within video + @param[in] SourceY Y location within video + @param[in] DestinationX X location within video + @param[in] DestinationY Y location within video + @param[in] Width Width (in pixels) + @param[in] Height Height + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - The sizes were returned + +**/ +EFI_STATUS +EFIAPI +BltLibVideoToVideo ( + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height + ) +{ + VOID *BltMemSrc; + VOID *BltMemDst; + UINTN Offset; + UINTN WidthInBytes; + INTN LineStride; + + // + // Video to Video: Source is Video, destination is Video + // + if (SourceY + Height > mBltLibHeight) { + return EFI_INVALID_PARAMETER; + } + + if (SourceX + Width > mBltLibWidthInPixels) { + return EFI_INVALID_PARAMETER; + } + + if (DestinationY + Height > mBltLibHeight) { + return EFI_INVALID_PARAMETER; + } + + if (DestinationX + Width > mBltLibWidthInPixels) { + return EFI_INVALID_PARAMETER; + } + + if (Width == 0 || Height == 0) { + return EFI_INVALID_PARAMETER; + } + + WidthInBytes = Width * mBltLibBytesPerPixel; + + Offset = (SourceY * mBltLibWidthInPixels) + SourceX; + Offset = mBltLibBytesPerPixel * Offset; + BltMemSrc = (VOID *) (mBltLibFrameBuffer + Offset); + + Offset = (DestinationY * mBltLibWidthInPixels) + DestinationX; + Offset = mBltLibBytesPerPixel * Offset; + BltMemDst = (VOID *) (mBltLibFrameBuffer + Offset); + + LineStride = mBltLibWidthInBytes; + if ((UINTN) BltMemDst > (UINTN) BltMemSrc) { + LineStride = -LineStride; + } + + while (Height > 0) { + CopyMem (BltMemDst, BltMemSrc, WidthInBytes); + + BltMemSrc = (VOID*) ((UINT8*) BltMemSrc + LineStride); + BltMemDst = (VOID*) ((UINT8*) BltMemDst + LineStride); + Height--; + } + + return EFI_SUCCESS; +} + + +/** + Returns the sizes related to the video device + + @param[out] Width Width (in pixels) + @param[out] Height Height (in pixels) + + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - The sizes were returned + +**/ +EFI_STATUS +EFIAPI +BltLibGetSizes ( + OUT UINTN *Width, OPTIONAL + OUT UINTN *Height OPTIONAL + ) +{ + if (Width != NULL) { + *Width = mBltLibWidthInPixels; + } + if (Height != NULL) { + *Height = mBltLibHeight; + } + + return EFI_SUCCESS; +} + diff --git a/Drivers/OptionRomPkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf b/Drivers/OptionRomPkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf new file mode 100644 index 0000000000..7af8a1baa0 --- /dev/null +++ b/Drivers/OptionRomPkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf @@ -0,0 +1,29 @@ +## @file +# FrameBufferBltLib - Library to perform blt operations on a frame buffer. +# +# Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = FrameBufferBltLib + FILE_GUID = 2a40f516-c852-4baa-b7a8-0e9ea090d659 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = BltLib + +[Sources.common] + FrameBufferBltLib.c + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + +[Packages] + MdePkg/MdePkg.dec + OptionRomPkg/OptionRomPkg.dec + diff --git a/Drivers/OptionRomPkg/Library/GopBltLib/GopBltLib.c b/Drivers/OptionRomPkg/Library/GopBltLib/GopBltLib.c new file mode 100644 index 0000000000..f28affd26b --- /dev/null +++ b/Drivers/OptionRomPkg/Library/GopBltLib/GopBltLib.c @@ -0,0 +1,449 @@ +/** @file + GopBltLib - Library to perform blt using the UEFI Graphics Output Protocol. + + Copyright (c) 2007 - 2011, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiDxe.h" + +#include + +#include +#include +#include +#include +#include +#include + +EFI_GRAPHICS_OUTPUT_PROTOCOL *mGop = NULL; + + +/** + Configure the FrameBufferLib instance + + @param[in] FrameBuffer Pointer to the start of the frame buffer + @param[in] FrameBufferInfo Describes the frame buffer characteristics + + @retval EFI_INVALID_PARAMETER - Invalid parameter + @retval EFI_UNSUPPORTED - The BltLib does not support this configuration + @retval EFI_SUCCESS - Blt operation success + +**/ +EFI_STATUS +EFIAPI +BltLibConfigure ( + IN VOID *FrameBuffer, + IN EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *FrameBufferInfo + ) +{ + EFI_STATUS Status; + EFI_HANDLE *HandleBuffer; + UINTN HandleCount; + UINTN Index; + EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop; + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiGraphicsOutputProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + if (!EFI_ERROR (Status)) { + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiGraphicsOutputProtocolGuid, + (VOID*) &Gop + ); + if (!EFI_ERROR (Status) && + (FrameBuffer == (VOID*)(UINTN) Gop->Mode->FrameBufferBase)) { + mGop = Gop; + FreePool (HandleBuffer); + return EFI_SUCCESS; + } + } + + FreePool (HandleBuffer); + } + + return EFI_UNSUPPORTED; +} + + +/** + Performs a UEFI Graphics Output Protocol Blt operation. + + @param[in,out] BltBuffer - The data to transfer to screen + @param[in] BltOperation - The operation to perform + @param[in] SourceX - The X coordinate of the source for BltOperation + @param[in] SourceY - The Y coordinate of the source for BltOperation + @param[in] DestinationX - The X coordinate of the destination for BltOperation + @param[in] DestinationY - The Y coordinate of the destination for BltOperation + @param[in] Width - The width of a rectangle in the blt rectangle in pixels + @param[in] Height - The height of a rectangle in the blt rectangle in pixels + @param[in] Delta - Not used for EfiBltVideoFill and EfiBltVideoToVideo operation. + If a Delta of 0 is used, the entire BltBuffer will be operated on. + If a subrectangle of the BltBuffer is used, then Delta represents + the number of bytes in a row of the BltBuffer. + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - Blt operation success + +**/ +EFI_STATUS +InternalGopBltCommon ( + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL + IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta + ) +{ + if (mGop == NULL) { + return EFI_DEVICE_ERROR; + } + + return mGop->Blt ( + mGop, + BltBuffer, + BltOperation, + SourceX, + SourceY, + DestinationX, + DestinationY, + Width, + Height, + Delta + ); +} + + +/** + Performs a UEFI Graphics Output Protocol Blt operation. + + @param[in,out] BltBuffer - The data to transfer to screen + @param[in] BltOperation - The operation to perform + @param[in] SourceX - The X coordinate of the source for BltOperation + @param[in] SourceY - The Y coordinate of the source for BltOperation + @param[in] DestinationX - The X coordinate of the destination for BltOperation + @param[in] DestinationY - The Y coordinate of the destination for BltOperation + @param[in] Width - The width of a rectangle in the blt rectangle in pixels + @param[in] Height - The height of a rectangle in the blt rectangle in pixels + @param[in] Delta - Not used for EfiBltVideoFill and EfiBltVideoToVideo operation. + If a Delta of 0 is used, the entire BltBuffer will be operated on. + If a subrectangle of the BltBuffer is used, then Delta represents + the number of bytes in a row of the BltBuffer. + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - Blt operation success + +**/ +EFI_STATUS +EFIAPI +BltLibGopBlt ( + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL + IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta + ) +{ + return InternalGopBltCommon ( + BltBuffer, + BltOperation, + SourceX, + SourceY, + DestinationX, + DestinationY, + Width, + Height, + Delta + ); +} + + +/** + Performs a UEFI Graphics Output Protocol Blt Video Fill. + + @param[in] Color Color to fill the region with + @param[in] DestinationX X location to start fill operation + @param[in] DestinationY Y location to start fill operation + @param[in] Width Width (in pixels) to fill + @param[in] Height Height to fill + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - The sizes were returned + +**/ +EFI_STATUS +EFIAPI +BltLibVideoFill ( + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Color, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height + ) +{ + return InternalGopBltCommon ( + Color, + EfiBltVideoFill, + 0, + 0, + DestinationX, + DestinationY, + Width, + Height, + 0 + ); +} + + +/** + Performs a UEFI Graphics Output Protocol Blt Video to Buffer operation. + + @param[out] BltBuffer Output buffer for pixel color data + @param[in] SourceX X location within video + @param[in] SourceY Y location within video + @param[in] Width Width (in pixels) + @param[in] Height Height + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - The sizes were returned + +**/ +EFI_STATUS +EFIAPI +BltLibVideoToBltBuffer ( + OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN Width, + IN UINTN Height + ) +{ + return InternalGopBltCommon ( + BltBuffer, + EfiBltVideoToBltBuffer, + SourceX, + SourceY, + 0, + 0, + Width, + Height, + 0 + ); +} + + +/** + Performs a UEFI Graphics Output Protocol Blt Video to Buffer operation + with extended parameters. + + @param[out] BltBuffer Output buffer for pixel color data + @param[in] SourceX X location within video + @param[in] SourceY Y location within video + @param[in] DestinationX X location within BltBuffer + @param[in] DestinationY Y location within BltBuffer + @param[in] Width Width (in pixels) + @param[in] Height Height + @param[in] Delta Number of bytes in a row of BltBuffer + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - The sizes were returned + +**/ +EFI_STATUS +EFIAPI +BltLibVideoToBltBufferEx ( + OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta + ) +{ + return InternalGopBltCommon ( + BltBuffer, + EfiBltVideoToBltBuffer, + SourceX, + SourceY, + DestinationX, + DestinationY, + Width, + Height, + Delta + ); +} + + +/** + Performs a UEFI Graphics Output Protocol Blt Buffer to Video operation. + + @param[in] BltBuffer Output buffer for pixel color data + @param[in] DestinationX X location within video + @param[in] DestinationY Y location within video + @param[in] Width Width (in pixels) + @param[in] Height Height + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - The sizes were returned + +**/ +EFI_STATUS +EFIAPI +BltLibBufferToVideo ( + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height + ) +{ + return InternalGopBltCommon ( + BltBuffer, + EfiBltBufferToVideo, + 0, + 0, + DestinationX, + DestinationY, + Width, + Height, + 0 + ); +} + + +/** + Performs a UEFI Graphics Output Protocol Blt Buffer to Video operation + with extended parameters. + + @param[in] BltBuffer Output buffer for pixel color data + @param[in] SourceX X location within BltBuffer + @param[in] SourceY Y location within BltBuffer + @param[in] DestinationX X location within video + @param[in] DestinationY Y location within video + @param[in] Width Width (in pixels) + @param[in] Height Height + @param[in] Delta Number of bytes in a row of BltBuffer + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - The sizes were returned + +**/ +EFI_STATUS +EFIAPI +BltLibBufferToVideoEx ( + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta + ) +{ + return InternalGopBltCommon ( + BltBuffer, + EfiBltBufferToVideo, + SourceX, + SourceY, + DestinationX, + DestinationY, + Width, + Height, + Delta + ); +} + + +/** + Performs a UEFI Graphics Output Protocol Blt Video to Video operation + + @param[in] SourceX X location within video + @param[in] SourceY Y location within video + @param[in] DestinationX X location within video + @param[in] DestinationY Y location within video + @param[in] Width Width (in pixels) + @param[in] Height Height + + @retval EFI_DEVICE_ERROR - A hardware error occured + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - The sizes were returned + +**/ +EFI_STATUS +EFIAPI +BltLibVideoToVideo ( + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height + ) +{ + return InternalGopBltCommon ( + NULL, + EfiBltVideoToVideo, + SourceX, + SourceY, + DestinationX, + DestinationY, + Width, + Height, + 0 + ); +} + +/** + Returns the sizes related to the video device + + @param[out] Width Width (in pixels) + @param[out] Height Height (in pixels) + + @retval EFI_INVALID_PARAMETER - Invalid parameter passed in + @retval EFI_SUCCESS - The sizes were returned + +**/ +EFI_STATUS +EFIAPI +BltLibGetSizes ( + OUT UINTN *Width, OPTIONAL + OUT UINTN *Height OPTIONAL + ) +{ + ASSERT (mGop != NULL); + + if (Width != NULL) { + *Width = mGop->Mode->Info->HorizontalResolution; + } + if (Height != NULL) { + *Height = mGop->Mode->Info->VerticalResolution; + } + + return EFI_SUCCESS; +} + diff --git a/Drivers/OptionRomPkg/Library/GopBltLib/GopBltLib.inf b/Drivers/OptionRomPkg/Library/GopBltLib/GopBltLib.inf new file mode 100644 index 0000000000..c5559133f9 --- /dev/null +++ b/Drivers/OptionRomPkg/Library/GopBltLib/GopBltLib.inf @@ -0,0 +1,31 @@ +## @file +# GopBltLib - Library to perform blt using the UEFI Graphics Output Protocol. +# +# Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = GopBltLib + FILE_GUID = b75b91f0-a0b4-42fe-ba62-849027999b39 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = BltLib + +[Sources.common] + GopBltLib.c + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + MemoryAllocationLib + UefiBootServicesTableLib + +[Packages] + MdePkg/MdePkg.dec + OptionRomPkg/OptionRomPkg.dec + diff --git a/Drivers/OptionRomPkg/OptionRomPkg.dec b/Drivers/OptionRomPkg/OptionRomPkg.dec new file mode 100644 index 0000000000..6881f3648e --- /dev/null +++ b/Drivers/OptionRomPkg/OptionRomPkg.dec @@ -0,0 +1,41 @@ +## @file +# Option Rom Package Reference Implementations. +# +# This package is designed to interoperate with the EDK II open source project +# at http://www.tianocore.org, and this package is required to build PCI compliant +# Option ROM image for all CPU architectures, including EBC target. +# A single driver can support mixes of EFI 1.1, UEFI 2.0 and UEFI 2.1. +# +# Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + DEC_SPECIFICATION = 0x00010005 + PACKAGE_NAME = OptionRomPkg + PACKAGE_GUID = AA3865E8-7F30-4f59-8696-99F560101852 + PACKAGE_VERSION = 0.1 + +[Includes] + Include + +[LibraryClasses] + ## @libraryclass Provides an interface for performing UEFI Graphics + ## Output Protocol Video blt operations + ## + BltLib|Include/Library/BltLib.h + +[Guids] + gOptionRomPkgTokenSpaceGuid = { 0x1e43298f, 0x3478, 0x41a7, { 0xb5, 0x77, 0x86, 0x6, 0x46, 0x35, 0xc7, 0x28 } } + +[PcdsFeatureFlag] + gOptionRomPkgTokenSpaceGuid.PcdSupportScsiPassThru|TRUE|BOOLEAN|0x00010001 + gOptionRomPkgTokenSpaceGuid.PcdSupportExtScsiPassThru|TRUE|BOOLEAN|0x00010002 + gOptionRomPkgTokenSpaceGuid.PcdSupportGop|TRUE|BOOLEAN|0x00010004 + gOptionRomPkgTokenSpaceGuid.PcdSupportUga|TRUE|BOOLEAN|0x00010005 + +[PcdsFixedAtBuild, PcdsPatchableInModule] + gOptionRomPkgTokenSpaceGuid.PcdDriverSupportedEfiVersion|0x0002000a|UINT32|0x00010003 + diff --git a/Drivers/OptionRomPkg/OptionRomPkg.dsc b/Drivers/OptionRomPkg/OptionRomPkg.dsc new file mode 100644 index 0000000000..bea64b585e --- /dev/null +++ b/Drivers/OptionRomPkg/OptionRomPkg.dsc @@ -0,0 +1,113 @@ +## @file +# Option Rom Package build validation file for All Architectures. +# +# This package is designed to interoperate with the EDK II open source project +# at http://www.tianocore.org, and this package is required to build PCI compliant +# Option ROM image for all CPU architectures, including EBC target. +# A single driver can support mixes of EFI 1.1, UEFI 2.0 and UEFI 2.1. +# +# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+# Copyright (c) 2016, Linaro Ltd. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +################################################################################ +# +# Defines Section - statements that will be processed to create a Makefile. +# +################################################################################ +[Defines] + PLATFORM_NAME = OptionRomPkg + PLATFORM_GUID = C7B25F37-B1F4-4c46-99CB-3EA7DCF5FCDC + PLATFORM_VERSION = 0.1 + DSC_SPECIFICATION = 0x00010005 + OUTPUT_DIRECTORY = Build/OptionRomPkg + SUPPORTED_ARCHITECTURES = IA32|X64|EBC|ARM|AARCH64 + BUILD_TARGETS = DEBUG|RELEASE + SKUID_IDENTIFIER = DEFAULT + +################################################################################ +# +# SKU Identification section - list of all SKU IDs supported by this +# Platform. +# +################################################################################ +[SkuIds] + 0|DEFAULT # The entry: 0|DEFAULT is reserved and always required. + +[LibraryClasses] + DebugLib|MdePkg/Library/UefiDebugLibStdErr/UefiDebugLibStdErr.inf + DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf + BaseLib|MdePkg/Library/BaseLib/BaseLib.inf + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + BltLib|OptionRomPkg/Library/GopBltLib/GopBltLib.inf + PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf + TimerLib|MdePkg/Library/BaseTimerLibNullTemplate/BaseTimerLibNullTemplate.inf + UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf + UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf + UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf + UefiLib|MdePkg/Library/UefiLib/UefiLib.inf + PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf + MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf + DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf + UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf + UefiRuntimeLib|MdePkg/Library/UefiRuntimeLib/UefiRuntimeLib.inf + +[LibraryClasses.AARCH64, LibraryClasses.ARM] + NULL|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf + +[LibraryClasses.ARM] + NULL|MdePkg/Library/BaseStackCheckLib/BaseStackCheckLib.inf + +################################################################################ +# +# Pcd Section - list of all EDK II PCD Entries defined by this Platform +# +################################################################################ +[PcdsFeatureFlag] + gOptionRomPkgTokenSpaceGuid.PcdSupportScsiPassThru|TRUE + gOptionRomPkgTokenSpaceGuid.PcdSupportExtScsiPassThru|TRUE + +[PcdsFixedAtBuild] + gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x27 + gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000042 + gEfiMdePkgTokenSpaceGuid.PcdDebugClearMemoryValue|0x0 + gEfiMdePkgTokenSpaceGuid.PcdMaximumUnicodeStringLength|0x0 + gEfiMdePkgTokenSpaceGuid.PcdMaximumAsciiStringLength|0x0 + gEfiMdePkgTokenSpaceGuid.PcdMaximumLinkedListLength|0x0 + gOptionRomPkgTokenSpaceGuid.PcdDriverSupportedEfiVersion|0x0002000a # EFI_2_10_SYSTEM_TABLE_REVISION + +################################################################################################### +# +# Components Section - list of the modules and components that will be processed by compilation +# tools and the EDK II tools to generate PE32/PE32+/Coff image files. +# +# Note: The EDK II DSC file is not used to specify how compiled binary images get placed +# into firmware volume images. This section is just a list of modules to compile from +# source into UEFI-compliant binaries. +# It is the FDF file that contains information on combining binary files into firmware +# volume images, whose concept is beyond UEFI and is described in PI specification. +# Binary modules do not need to be listed in this section, as they should be +# specified in the FDF file. For example: Shell binary (Shell_Full.efi), FAT binary (Fat.efi), +# Logo (Logo.bmp), and etc. +# There may also be modules listed in this section that are not required in the FDF file, +# When a module listed here is excluded from FDF file, then UEFI-compliant binary will be +# generated for it, but the binary will not be put into any firmware volume. +# +################################################################################################### + +[Components] + OptionRomPkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf + OptionRomPkg/Library/GopBltLib/GopBltLib.inf + + OptionRomPkg/AtapiPassThruDxe/AtapiPassThruDxe.inf + OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430Dxe.inf + OptionRomPkg/UndiRuntimeDxe/UndiRuntimeDxe.inf + OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDxe.inf + OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.inf + OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772b.inf + +[Components.IA32, Components.X64] + OptionRomPkg/Application/BltLibSample/BltLibSample.inf diff --git a/Drivers/OptionRomPkg/ReadMe.txt b/Drivers/OptionRomPkg/ReadMe.txt new file mode 100644 index 0000000000..99f97896da --- /dev/null +++ b/Drivers/OptionRomPkg/ReadMe.txt @@ -0,0 +1,17 @@ +AtapiPassThru: + For now, AtapiPassThru driver in this package is to test Scsi Bus support: + ScsiBus driver should support both/either ScsiPassThru and ExtScsiPassThru + installed on a controller handle. + + AtapiPassThru driver in this package can selectively produce ScsiPassThru + and/or ExtScsiPassThru protocol based on feature flags of PcdSupportScsiPassThru + and PcdSupportExtScsiPassThru. + +CirrusLogic5430: + Sample implementation of UGA Draw or Graphics Output Protocol for the Cirrus + Logic 5430 family of PCI video card. It provides reference code for the GOP/UGA, + Component Name (2), EFI driver supported Verison protocol. + +Build Validation: +ICC IA32 X64 IPF + diff --git a/Drivers/OptionRomPkg/UndiRuntimeDxe/ComponentName.c b/Drivers/OptionRomPkg/UndiRuntimeDxe/ComponentName.c new file mode 100644 index 0000000000..b310143271 --- /dev/null +++ b/Drivers/OptionRomPkg/UndiRuntimeDxe/ComponentName.c @@ -0,0 +1,359 @@ +/** @file + UEFI Component Name(2) protocol implementation for EFI UNDI32 driver. + +Copyright (c) 2012, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "Undi32.h" + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UndiComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UndiComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gUndiComponentName = { + UndiComponentNameGetDriverName, + UndiComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUndiComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) UndiComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) UndiComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUndiDriverNameTable[] = { + { + "eng;en", + L"UNDI32 Driver" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUndiControllerNameTable[] = { + { + "eng;en", + L"UNDI32 Controller" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UndiComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mUndiDriverNameTable, + DriverName, + (BOOLEAN)(This == &gUndiComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + Currently not implemented. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UndiComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *Nii; + + if (ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver is currently managing ControllHandle + // + Status = EfiTestManagedDevice ( + ControllerHandle, + gUndiDriverBinding.DriverBindingHandle, + &gEfiPciIoProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Retrieve an instance of a produced protocol from ControllerHandle + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + (VOID **)&Nii, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mUndiControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gUndiComponentName) + ); +} diff --git a/Drivers/OptionRomPkg/UndiRuntimeDxe/Decode.c b/Drivers/OptionRomPkg/UndiRuntimeDxe/Decode.c new file mode 100644 index 0000000000..b7a2d16bc6 --- /dev/null +++ b/Drivers/OptionRomPkg/UndiRuntimeDxe/Decode.c @@ -0,0 +1,1516 @@ +/** @file + Provides the basic UNID functions. + +Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Undi32.h" + +// +// Global variables defined in this file +// +UNDI_CALL_TABLE api_table[PXE_OPCODE_LAST_VALID+1] = { \ + {PXE_CPBSIZE_NOT_USED,PXE_DBSIZE_NOT_USED,0, (UINT16)(ANY_STATE),UNDI_GetState },\ + {(UINT16)(DONT_CHECK),PXE_DBSIZE_NOT_USED,0,(UINT16)(ANY_STATE),UNDI_Start },\ + {PXE_CPBSIZE_NOT_USED,PXE_DBSIZE_NOT_USED,0,MUST_BE_STARTED,UNDI_Stop },\ + {PXE_CPBSIZE_NOT_USED,sizeof(PXE_DB_GET_INIT_INFO),0,MUST_BE_STARTED, UNDI_GetInitInfo },\ + {PXE_CPBSIZE_NOT_USED,sizeof(PXE_DB_GET_CONFIG_INFO),0,MUST_BE_STARTED, UNDI_GetConfigInfo },\ + {sizeof(PXE_CPB_INITIALIZE),(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK),MUST_BE_STARTED,UNDI_Initialize },\ + {PXE_CPBSIZE_NOT_USED,PXE_DBSIZE_NOT_USED,(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED,UNDI_Reset },\ + {PXE_CPBSIZE_NOT_USED,PXE_DBSIZE_NOT_USED,0, MUST_BE_INITIALIZED,UNDI_Shutdown },\ + {PXE_CPBSIZE_NOT_USED,PXE_DBSIZE_NOT_USED,(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED,UNDI_Interrupt },\ + {(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_RecFilter },\ + {(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_StnAddr },\ + {PXE_CPBSIZE_NOT_USED, (UINT16)(DONT_CHECK), (UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_Statistics },\ + {sizeof(PXE_CPB_MCAST_IP_TO_MAC),sizeof(PXE_DB_MCAST_IP_TO_MAC), (UINT16)(DONT_CHECK),MUST_BE_INITIALIZED, UNDI_ip2mac },\ + {(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_NVData },\ + {PXE_CPBSIZE_NOT_USED,(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_Status },\ + {(UINT16)(DONT_CHECK),PXE_DBSIZE_NOT_USED,(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_FillHeader },\ + {(UINT16)(DONT_CHECK),PXE_DBSIZE_NOT_USED,(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_Transmit },\ + {sizeof(PXE_CPB_RECEIVE),sizeof(PXE_DB_RECEIVE),0,MUST_BE_INITIALIZED, UNDI_Receive } \ +}; + +// +// end of global variables +// + + +/** + This routine determines the operational state of the UNDI. It updates the state flags in the + Command Descriptor Block based on information derived from the AdapterInfo instance data. + To ensure the command has completed successfully, CdbPtr->StatCode will contain the result of + the command execution. + The CdbPtr->StatFlags will contain a STOPPED, STARTED, or INITIALIZED state once the command + has successfully completed. + Keep in mind the AdapterInfo->State is the active state of the adapter (based on software + interrogation), and the CdbPtr->StateFlags is the passed back information that is reflected + to the caller of the UNDI API. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_GetState ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + CdbPtr->StatFlags = (PXE_STATFLAGS) (CdbPtr->StatFlags | AdapterInfo->State); + return ; +} + + +/** + This routine is used to change the operational state of the UNDI from stopped to started. + It will do this as long as the adapter's state is PXE_STATFLAGS_GET_STATE_STOPPED, otherwise + the CdbPtr->StatFlags will reflect a command failure, and the CdbPtr->StatCode will reflect the + UNDI as having already been started. + This routine is modified to reflect the undi 1.1 specification changes. The + changes in the spec are mainly in the callback routines, the new spec adds + 3 more callbacks and a unique id. + Since this UNDI supports both old and new undi specifications, + The NIC's data structure is filled in with the callback routines (depending + on the version) pointed to in the caller's CpbPtr. This seeds the Delay, + Virt2Phys, Block, and Mem_IO for old and new versions and Map_Mem, UnMap_Mem + and Sync_Mem routines and a unique id variable for the new version. + This is the function which an external entity (SNP, O/S, etc) would call + to provide it's I/O abstraction to the UNDI. + It's final action is to change the AdapterInfo->State to PXE_STATFLAGS_GET_STATE_STARTED. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_Start ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + PXE_CPB_START_30 *CpbPtr; + PXE_CPB_START_31 *CpbPtr_31; + + // + // check if it is already started. + // + if (AdapterInfo->State != PXE_STATFLAGS_GET_STATE_STOPPED) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_ALREADY_STARTED; + return ; + } + + if (CdbPtr->CPBsize != sizeof(PXE_CPB_START_30) && + CdbPtr->CPBsize != sizeof(PXE_CPB_START_31)) { + + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + CpbPtr = (PXE_CPB_START_30 *) (UINTN) (CdbPtr->CPBaddr); + CpbPtr_31 = (PXE_CPB_START_31 *) (UINTN) (CdbPtr->CPBaddr); + + if (AdapterInfo->VersionFlag == 0x30) { + AdapterInfo->Delay_30 = (bsptr_30) (UINTN) CpbPtr->Delay; + AdapterInfo->Virt2Phys_30 = (virtphys_30) (UINTN) CpbPtr->Virt2Phys; + AdapterInfo->Block_30 = (block_30) (UINTN) CpbPtr->Block; + // + // patch for old buggy 3.0 code: + // In EFI1.0 undi used to provide the full (absolute) I/O address to the + // i/o calls and SNP used to provide a callback that used GlobalIoFncs and + // everything worked fine! In EFI 1.1, UNDI is not using the full + // i/o or memory address to access the device, The base values for the i/o + // and memory address is abstracted by the device specific PciIoFncs and + // UNDI only uses the offset values. Since UNDI3.0 cannot provide any + // identification to SNP, SNP cannot use nic specific PciIoFncs callback! + // + // To fix this and make undi3.0 work with SNP in EFI1.1 we + // use a TmpMemIo function that is defined in init.c + // This breaks the runtime driver feature of undi, but what to do + // if we have to provide the 3.0 compatibility (including the 3.0 bugs) + // + // This TmpMemIo function also takes a UniqueId parameter + // (as in undi3.1 design) and so initialize the UniqueId as well here + // Note: AdapterInfo->Mem_Io_30 is just filled for consistency with other + // parameters but never used, we only use Mem_Io field in the In/Out routines + // inside e100b.c. + // + AdapterInfo->Mem_Io_30 = (mem_io_30) (UINTN) CpbPtr->Mem_IO; + AdapterInfo->Mem_Io = (mem_io) (UINTN) TmpMemIo; + AdapterInfo->Unique_ID = (UINT64) (UINTN) AdapterInfo; + + } else { + AdapterInfo->Delay = (bsptr) (UINTN) CpbPtr_31->Delay; + AdapterInfo->Virt2Phys = (virtphys) (UINTN) CpbPtr_31->Virt2Phys; + AdapterInfo->Block = (block) (UINTN) CpbPtr_31->Block; + AdapterInfo->Mem_Io = (mem_io) (UINTN) CpbPtr_31->Mem_IO; + + AdapterInfo->Map_Mem = (map_mem) (UINTN) CpbPtr_31->Map_Mem; + AdapterInfo->UnMap_Mem = (unmap_mem) (UINTN) CpbPtr_31->UnMap_Mem; + AdapterInfo->Sync_Mem = (sync_mem) (UINTN) CpbPtr_31->Sync_Mem; + AdapterInfo->Unique_ID = CpbPtr_31->Unique_ID; + } + + AdapterInfo->State = PXE_STATFLAGS_GET_STATE_STARTED; + + return ; +} + + +/** + This routine is used to change the operational state of the UNDI from started to stopped. + It will not do this if the adapter's state is PXE_STATFLAGS_GET_STATE_INITIALIZED, otherwise + the CdbPtr->StatFlags will reflect a command failure, and the CdbPtr->StatCode will reflect the + UNDI as having already not been shut down. + The NIC's data structure will have the Delay, Virt2Phys, and Block, pointers zero'd out.. + It's final action is to change the AdapterInfo->State to PXE_STATFLAGS_GET_STATE_STOPPED. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_Stop ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + if (AdapterInfo->State == PXE_STATFLAGS_GET_STATE_INITIALIZED) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_NOT_SHUTDOWN; + return ; + } + + AdapterInfo->Delay_30 = 0; + AdapterInfo->Virt2Phys_30 = 0; + AdapterInfo->Block_30 = 0; + + AdapterInfo->Delay = 0; + AdapterInfo->Virt2Phys = 0; + AdapterInfo->Block = 0; + + AdapterInfo->Map_Mem = 0; + AdapterInfo->UnMap_Mem = 0; + AdapterInfo->Sync_Mem = 0; + + AdapterInfo->State = PXE_STATFLAGS_GET_STATE_STOPPED; + + return ; +} + + +/** + This routine is used to retrieve the initialization information that is needed by drivers and + applications to initialize the UNDI. This will fill in data in the Data Block structure that is + pointed to by the caller's CdbPtr->DBaddr. The fields filled in are as follows: + MemoryRequired, FrameDataLen, LinkSpeeds[0-3], NvCount, NvWidth, MediaHeaderLen, HWaddrLen, + MCastFilterCnt, TxBufCnt, TxBufSize, RxBufCnt, RxBufSize, IFtype, Duplex, and LoopBack. + In addition, the CdbPtr->StatFlags ORs in that this NIC supports cable detection. (APRIORI knowledge) + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_GetInitInfo ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + PXE_DB_GET_INIT_INFO *DbPtr; + + DbPtr = (PXE_DB_GET_INIT_INFO *) (UINTN) (CdbPtr->DBaddr); + + DbPtr->MemoryRequired = MEMORY_NEEDED; + DbPtr->FrameDataLen = PXE_MAX_TXRX_UNIT_ETHER; + DbPtr->LinkSpeeds[0] = 10; + DbPtr->LinkSpeeds[1] = 100; + DbPtr->LinkSpeeds[2] = DbPtr->LinkSpeeds[3] = 0; + DbPtr->NvCount = MAX_EEPROM_LEN; + DbPtr->NvWidth = 4; + DbPtr->MediaHeaderLen = PXE_MAC_HEADER_LEN_ETHER; + DbPtr->HWaddrLen = PXE_HWADDR_LEN_ETHER; + DbPtr->MCastFilterCnt = MAX_MCAST_ADDRESS_CNT; + + DbPtr->TxBufCnt = TX_BUFFER_COUNT; + DbPtr->TxBufSize = (UINT16) sizeof (TxCB); + DbPtr->RxBufCnt = RX_BUFFER_COUNT; + DbPtr->RxBufSize = (UINT16) sizeof (RxFD); + + DbPtr->IFtype = PXE_IFTYPE_ETHERNET; + DbPtr->SupportedDuplexModes = PXE_DUPLEX_ENABLE_FULL_SUPPORTED | + PXE_DUPLEX_FORCE_FULL_SUPPORTED; + DbPtr->SupportedLoopBackModes = PXE_LOOPBACK_INTERNAL_SUPPORTED | + PXE_LOOPBACK_EXTERNAL_SUPPORTED; + + CdbPtr->StatFlags |= (PXE_STATFLAGS_CABLE_DETECT_SUPPORTED | + PXE_STATFLAGS_GET_STATUS_NO_MEDIA_SUPPORTED); + return ; +} + + +/** + This routine is used to retrieve the configuration information about the NIC being controlled by + this driver. This will fill in data in the Data Block structure that is pointed to by the caller's CdbPtr->DBaddr. + The fields filled in are as follows: + DbPtr->pci.BusType, DbPtr->pci.Bus, DbPtr->pci.Device, and DbPtr->pci. + In addition, the DbPtr->pci.Config.Dword[0-63] grabs a copy of this NIC's PCI configuration space. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_GetConfigInfo ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + UINT16 Index; + PXE_DB_GET_CONFIG_INFO *DbPtr; + + DbPtr = (PXE_DB_GET_CONFIG_INFO *) (UINTN) (CdbPtr->DBaddr); + + DbPtr->pci.BusType = PXE_BUSTYPE_PCI; + DbPtr->pci.Bus = AdapterInfo->Bus; + DbPtr->pci.Device = AdapterInfo->Device; + DbPtr->pci.Function = AdapterInfo->Function; + + for (Index = 0; Index < MAX_PCI_CONFIG_LEN; Index++) { + DbPtr->pci.Config.Dword[Index] = AdapterInfo->Config[Index]; + } + + return ; +} + + +/** + This routine resets the network adapter and initializes the UNDI using the parameters supplied in + the CPB. This command must be issued before the network adapter can be setup to transmit and + receive packets. + Once the memory requirements of the UNDI are obtained by using the GetInitInfo command, a block + of non-swappable memory may need to be allocated. The address of this memory must be passed to + UNDI during the Initialize in the CPB. This memory is used primarily for transmit and receive buffers. + The fields CableDetect, LinkSpeed, Duplex, LoopBack, MemoryPtr, and MemoryLength are set with information + that was passed in the CPB and the NIC is initialized. + If the NIC initialization fails, the CdbPtr->StatFlags are updated with PXE_STATFLAGS_COMMAND_FAILED + Otherwise, AdapterInfo->State is updated with PXE_STATFLAGS_GET_STATE_INITIALIZED showing the state of + the UNDI is now initialized. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_Initialize ( + IN PXE_CDB *CdbPtr, + NIC_DATA_INSTANCE *AdapterInfo + ) +{ + PXE_CPB_INITIALIZE *CpbPtr; + + if ((CdbPtr->OpFlags != PXE_OPFLAGS_INITIALIZE_DETECT_CABLE) && + (CdbPtr->OpFlags != PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE)) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + // + // check if it is already initialized + // + if (AdapterInfo->State == PXE_STATFLAGS_GET_STATE_INITIALIZED) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_ALREADY_INITIALIZED; + return ; + } + + CpbPtr = (PXE_CPB_INITIALIZE *) (UINTN) CdbPtr->CPBaddr; + + if (CpbPtr->MemoryLength < (UINT32) MEMORY_NEEDED) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CPB; + return ; + } + + // + // default behaviour is to detect the cable, if the 3rd param is 1, + // do not do that + // + AdapterInfo->CableDetect = (UINT8) ((CdbPtr->OpFlags == (UINT16) PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE) ? (UINT8) 0 : (UINT8) 1); + AdapterInfo->LinkSpeedReq = (UINT16) CpbPtr->LinkSpeed; + AdapterInfo->DuplexReq = CpbPtr->DuplexMode; + AdapterInfo->LoopBack = CpbPtr->LoopBackMode; + AdapterInfo->MemoryPtr = CpbPtr->MemoryAddr; + AdapterInfo->MemoryLength = CpbPtr->MemoryLength; + + CdbPtr->StatCode = (PXE_STATCODE) E100bInit (AdapterInfo); + + if (CdbPtr->StatCode != PXE_STATCODE_SUCCESS) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + } else { + AdapterInfo->State = PXE_STATFLAGS_GET_STATE_INITIALIZED; + } + + return ; +} + + +/** + This routine resets the network adapter and initializes the UNDI using the parameters supplied in + the CPB. The transmit and receive queues are emptied and any pending interrupts are cleared. + If the NIC reset fails, the CdbPtr->StatFlags are updated with PXE_STATFLAGS_COMMAND_FAILED + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_Reset ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + if (CdbPtr->OpFlags != PXE_OPFLAGS_NOT_USED && + CdbPtr->OpFlags != PXE_OPFLAGS_RESET_DISABLE_INTERRUPTS && + CdbPtr->OpFlags != PXE_OPFLAGS_RESET_DISABLE_FILTERS ) { + + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + CdbPtr->StatCode = (UINT16) E100bReset (AdapterInfo, CdbPtr->OpFlags); + + if (CdbPtr->StatCode != PXE_STATCODE_SUCCESS) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + } +} + + +/** + This routine resets the network adapter and leaves it in a safe state for another driver to + initialize. Any pending transmits or receives are lost. Receive filters and external + interrupt enables are disabled. Once the UNDI has been shutdown, it can then be stopped + or initialized again. + If the NIC reset fails, the CdbPtr->StatFlags are updated with PXE_STATFLAGS_COMMAND_FAILED + Otherwise, AdapterInfo->State is updated with PXE_STATFLAGS_GET_STATE_STARTED showing the state of + the NIC as being started. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_Shutdown ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + // + // do the shutdown stuff here + // + CdbPtr->StatCode = (UINT16) E100bShutdown (AdapterInfo); + + if (CdbPtr->StatCode != PXE_STATCODE_SUCCESS) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + } else { + AdapterInfo->State = PXE_STATFLAGS_GET_STATE_STARTED; + } + + return ; +} + + +/** + This routine can be used to read and/or change the current external interrupt enable + settings. Disabling an external interrupt enable prevents and external (hardware) + interrupt from being signaled by the network device. Internally the interrupt events + can still be polled by using the UNDI_GetState command. + The resulting information on the interrupt state will be passed back in the CdbPtr->StatFlags. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_Interrupt ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + UINT8 IntMask; + + IntMask = (UINT8)(UINTN)(CdbPtr->OpFlags & (PXE_OPFLAGS_INTERRUPT_RECEIVE | + PXE_OPFLAGS_INTERRUPT_TRANSMIT | + PXE_OPFLAGS_INTERRUPT_COMMAND | + PXE_OPFLAGS_INTERRUPT_SOFTWARE)); + + switch (CdbPtr->OpFlags & PXE_OPFLAGS_INTERRUPT_OPMASK) { + case PXE_OPFLAGS_INTERRUPT_READ: + break; + + case PXE_OPFLAGS_INTERRUPT_ENABLE: + if (IntMask == 0) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + AdapterInfo->int_mask = IntMask; + E100bSetInterruptState (AdapterInfo); + break; + + case PXE_OPFLAGS_INTERRUPT_DISABLE: + if (IntMask != 0) { + AdapterInfo->int_mask = (UINT16) (AdapterInfo->int_mask & ~(IntMask)); + E100bSetInterruptState (AdapterInfo); + break; + } + + // + // else fall thru. + // + default: + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + if ((AdapterInfo->int_mask & PXE_OPFLAGS_INTERRUPT_RECEIVE) != 0) { + CdbPtr->StatFlags |= PXE_STATFLAGS_INTERRUPT_RECEIVE; + + } + + if ((AdapterInfo->int_mask & PXE_OPFLAGS_INTERRUPT_TRANSMIT) != 0) { + CdbPtr->StatFlags |= PXE_STATFLAGS_INTERRUPT_TRANSMIT; + + } + + if ((AdapterInfo->int_mask & PXE_OPFLAGS_INTERRUPT_COMMAND) != 0) { + CdbPtr->StatFlags |= PXE_STATFLAGS_INTERRUPT_COMMAND; + + } + + return ; +} + + +/** + This routine is used to read and change receive filters and, if supported, read + and change multicast MAC address filter list. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_RecFilter ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + UINT16 NewFilter; + UINT16 OpFlags; + PXE_DB_RECEIVE_FILTERS *DbPtr; + UINT8 *MacAddr; + UINTN MacCount; + UINT16 Index; + UINT16 copy_len; + UINT8 *ptr1; + UINT8 *ptr2; + BOOLEAN InvalidMacAddr; + + OpFlags = CdbPtr->OpFlags; + NewFilter = (UINT16) (OpFlags & 0x1F); + + switch (OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_OPMASK) { + case PXE_OPFLAGS_RECEIVE_FILTER_READ: + + // + // not expecting a cpb, not expecting any filter bits + // + if ((NewFilter != 0) || (CdbPtr->CPBsize != 0)) { + goto BadCdb; + + } + + if ((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST) == 0) { + goto JustRead; + + } + + NewFilter = (UINT16) (NewFilter | AdapterInfo->Rx_Filter); + // + // all other flags are ignored except mcast_reset + // + break; + + case PXE_OPFLAGS_RECEIVE_FILTER_ENABLE: + // + // there should be atleast one other filter bit set. + // + if (NewFilter == 0) { + // + // nothing to enable + // + goto BadCdb; + } + + if (CdbPtr->CPBsize != 0) { + // + // this must be a multicast address list! + // don't accept the list unless selective_mcast is set + // don't accept confusing mcast settings with this + // + if (((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) == 0) || + ((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST) != 0) || + ((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST) != 0) || + ((CdbPtr->CPBsize % sizeof (PXE_MAC_ADDR)) != 0) ) { + goto BadCdb; + } + + MacAddr = (UINT8 *) ((UINTN) (CdbPtr->CPBaddr)); + MacCount = CdbPtr->CPBsize / sizeof (PXE_MAC_ADDR); + + // + // The format of Ethernet multicast address for IPv6 is defined in RFC2464, + // for IPv4 is defined in RFC1112. Check whether the address is valid. + // + InvalidMacAddr = FALSE; + + for (; MacCount-- != 0; MacAddr += sizeof (PXE_MAC_ADDR)) { + if (MacAddr[0] == 0x01) { + // + // This multicast MAC address is mapped from IPv4 address. + // + if (MacAddr[1] != 0x00 || MacAddr[2] != 0x5E || (MacAddr[3] & 0x80) != 0) { + InvalidMacAddr = TRUE; + } + } else if (MacAddr[0] == 0x33) { + // + // This multicast MAC address is mapped from IPv6 address. + // + if (MacAddr[1] != 0x33) { + InvalidMacAddr = TRUE; + } + } else { + InvalidMacAddr = TRUE; + } + + if (InvalidMacAddr) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CPB; + return ; + } + } + } + + // + // check selective mcast case enable case + // + if ((OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) != 0) { + if (((OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST) != 0) || + ((OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST) != 0) ) { + goto BadCdb; + + } + // + // if no cpb, make sure we have an old list + // + if ((CdbPtr->CPBsize == 0) && (AdapterInfo->mcast_list.list_len == 0)) { + goto BadCdb; + } + } + // + // if you want to enable anything, you got to have unicast + // and you have what you already enabled! + // + NewFilter = (UINT16) (NewFilter | (PXE_OPFLAGS_RECEIVE_FILTER_UNICAST | AdapterInfo->Rx_Filter)); + + break; + + case PXE_OPFLAGS_RECEIVE_FILTER_DISABLE: + + // + // mcast list not expected, i.e. no cpb here! + // + if (CdbPtr->CPBsize != PXE_CPBSIZE_NOT_USED) { + goto BadCdb; + } + + NewFilter = (UINT16) ((~(CdbPtr->OpFlags & 0x1F)) & AdapterInfo->Rx_Filter); + + break; + + default: + goto BadCdb; + } + + if ((OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST) != 0) { + AdapterInfo->mcast_list.list_len = 0; + NewFilter &= (~PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST); + } + + E100bSetfilter (AdapterInfo, NewFilter, CdbPtr->CPBaddr, CdbPtr->CPBsize); + +JustRead: + // + // give the current mcast list + // + if ((CdbPtr->DBsize != 0) && (AdapterInfo->mcast_list.list_len != 0)) { + // + // copy the mc list to db + // + + DbPtr = (PXE_DB_RECEIVE_FILTERS *) (UINTN) CdbPtr->DBaddr; + ptr1 = (UINT8 *) (&DbPtr->MCastList[0]); + + // + // DbPtr->mc_count = AdapterInfo->mcast_list.list_len; + // + copy_len = (UINT16) (AdapterInfo->mcast_list.list_len * PXE_MAC_LENGTH); + + if (copy_len > CdbPtr->DBsize) { + copy_len = CdbPtr->DBsize; + + } + + ptr2 = (UINT8 *) (&AdapterInfo->mcast_list.mc_list[0]); + for (Index = 0; Index < copy_len; Index++) { + ptr1[Index] = ptr2[Index]; + } + } + // + // give the stat flags here + // + if (AdapterInfo->Receive_Started) { + CdbPtr->StatFlags = (PXE_STATFLAGS) (CdbPtr->StatFlags | AdapterInfo->Rx_Filter); + + } + + return ; + +BadCdb: + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; +} + + +/** + This routine is used to get the current station and broadcast MAC addresses, and to change the + current station MAC address. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_StnAddr ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + PXE_CPB_STATION_ADDRESS *CpbPtr; + PXE_DB_STATION_ADDRESS *DbPtr; + UINT16 Index; + + if (CdbPtr->OpFlags == PXE_OPFLAGS_STATION_ADDRESS_RESET) { + // + // configure the permanent address. + // change the AdapterInfo->CurrentNodeAddress field. + // + if (CompareMem ( + &AdapterInfo->CurrentNodeAddress[0], + &AdapterInfo->PermNodeAddress[0], + PXE_MAC_LENGTH + ) != 0) { + for (Index = 0; Index < PXE_MAC_LENGTH; Index++) { + AdapterInfo->CurrentNodeAddress[Index] = AdapterInfo->PermNodeAddress[Index]; + } + + E100bSetupIAAddr (AdapterInfo); + } + } + + if (CdbPtr->CPBaddr != (UINT64) 0) { + CpbPtr = (PXE_CPB_STATION_ADDRESS *) (UINTN) (CdbPtr->CPBaddr); + // + // configure the new address + // + for (Index = 0; Index < PXE_MAC_LENGTH; Index++) { + AdapterInfo->CurrentNodeAddress[Index] = CpbPtr->StationAddr[Index]; + } + + E100bSetupIAAddr (AdapterInfo); + } + + if (CdbPtr->DBaddr != (UINT64) 0) { + DbPtr = (PXE_DB_STATION_ADDRESS *) (UINTN) (CdbPtr->DBaddr); + // + // fill it with the new values + // + for (Index = 0; Index < PXE_MAC_LENGTH; Index++) { + DbPtr->StationAddr[Index] = AdapterInfo->CurrentNodeAddress[Index]; + DbPtr->BroadcastAddr[Index] = AdapterInfo->BroadcastNodeAddress[Index]; + DbPtr->PermanentAddr[Index] = AdapterInfo->PermNodeAddress[Index]; + } + } + + return ; +} + + +/** + This routine is used to read and clear the NIC traffic statistics. This command is supported only + if the !PXE structure's Implementation flags say so. + Results will be parsed out in the following manner: + CdbPtr->DBaddr.Data[0] R Total Frames (Including frames with errors and dropped frames) + CdbPtr->DBaddr.Data[1] R Good Frames (All frames copied into receive buffer) + CdbPtr->DBaddr.Data[2] R Undersize Frames (Frames below minimum length for media <64 for ethernet) + CdbPtr->DBaddr.Data[4] R Dropped Frames (Frames that were dropped because receive buffers were full) + CdbPtr->DBaddr.Data[8] R CRC Error Frames (Frames with alignment or CRC errors) + CdbPtr->DBaddr.Data[A] T Total Frames (Including frames with errors and dropped frames) + CdbPtr->DBaddr.Data[B] T Good Frames (All frames copied into transmit buffer) + CdbPtr->DBaddr.Data[C] T Undersize Frames (Frames below minimum length for media <64 for ethernet) + CdbPtr->DBaddr.Data[E] T Dropped Frames (Frames that were dropped because of collisions) + CdbPtr->DBaddr.Data[14] T Total Collision Frames (Total collisions on this subnet) + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_Statistics ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + if ((CdbPtr->OpFlags &~(PXE_OPFLAGS_STATISTICS_RESET)) != 0) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + if ((CdbPtr->OpFlags & PXE_OPFLAGS_STATISTICS_RESET) != 0) { + // + // Reset the statistics + // + CdbPtr->StatCode = (UINT16) E100bStatistics (AdapterInfo, 0, 0); + } else { + CdbPtr->StatCode = (UINT16) E100bStatistics (AdapterInfo, CdbPtr->DBaddr, CdbPtr->DBsize); + } + + return ; +} + + +/** + This routine is used to translate a multicast IP address to a multicast MAC address. + This results in a MAC address composed of 25 bits of fixed data with the upper 23 bits of the IP + address being appended to it. Results passed back in the equivalent of CdbPtr->DBaddr->MAC[0-5]. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_ip2mac ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + PXE_CPB_MCAST_IP_TO_MAC *CpbPtr; + PXE_DB_MCAST_IP_TO_MAC *DbPtr; + UINT8 *TmpPtr; + + CpbPtr = (PXE_CPB_MCAST_IP_TO_MAC *) (UINTN) CdbPtr->CPBaddr; + DbPtr = (PXE_DB_MCAST_IP_TO_MAC *) (UINTN) CdbPtr->DBaddr; + + if ((CdbPtr->OpFlags & PXE_OPFLAGS_MCAST_IPV6_TO_MAC) != 0) { + // + // for now this is not supported + // + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_UNSUPPORTED; + return ; + } + + TmpPtr = (UINT8 *) (&CpbPtr->IP.IPv4); + // + // check if the ip given is a mcast IP + // + if ((TmpPtr[0] & 0xF0) != 0xE0) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CPB; + } + // + // take the last 23 bits in IP. + // be very careful. accessing word on a non-word boundary will hang motherboard codenamed Big Sur + // casting the mac array (in the middle) to a UINT32 pointer and accessing + // the UINT32 content hung the system... + // + DbPtr->MAC[0] = 0x01; + DbPtr->MAC[1] = 0x00; + DbPtr->MAC[2] = 0x5e; + DbPtr->MAC[3] = (UINT8) (TmpPtr[1] & 0x7f); + DbPtr->MAC[4] = (UINT8) TmpPtr[2]; + DbPtr->MAC[5] = (UINT8) TmpPtr[3]; + + return ; +} + + +/** + This routine is used to read and write non-volatile storage on the NIC (if supported). The NVRAM + could be EEPROM, FLASH, or battery backed RAM. + This is an optional function according to the UNDI specification (or will be......) + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_NVData ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + PXE_DB_NVDATA *DbPtr; + UINT16 Index; + + if ((CdbPtr->OpFlags == PXE_OPFLAGS_NVDATA_READ) != 0) { + + if ((CdbPtr->DBsize == PXE_DBSIZE_NOT_USED) != 0) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + DbPtr = (PXE_DB_NVDATA *) (UINTN) CdbPtr->DBaddr; + + for (Index = 0; Index < MAX_PCI_CONFIG_LEN; Index++) { + DbPtr->Data.Dword[Index] = AdapterInfo->NVData[Index]; + + } + + } else { + // + // no write for now + // + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_UNSUPPORTED; + } + + return ; +} + + +/** + This routine returns the current interrupt status and/or the transmitted buffer addresses. + If the current interrupt status is returned, pending interrupts will be acknowledged by this + command. Transmitted buffer addresses that are written to the DB are removed from the transmit + buffer queue. + Normally, this command would be polled with interrupts disabled. + The transmit buffers are returned in CdbPtr->DBaddr->TxBufer[0 - NumEntries]. + The interrupt status is returned in CdbPtr->StatFlags. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_Status ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + PXE_DB_GET_STATUS *DbPtr; + PXE_DB_GET_STATUS TmpGetStatus; + UINT16 Index; + UINT16 Status; + UINT16 NumEntries; + RxFD *RxPtr; + + // + // Fill in temporary GetStatus storage. + // + RxPtr = &AdapterInfo->rx_ring[AdapterInfo->cur_rx_ind]; + + if ((RxPtr->cb_header.status & RX_COMPLETE) != 0) { + TmpGetStatus.RxFrameLen = RxPtr->ActualCount & 0x3fff; + } else { + TmpGetStatus.RxFrameLen = 0; + } + + TmpGetStatus.reserved = 0; + + // + // Fill in size of next available receive packet and + // reserved field in caller's DB storage. + // + DbPtr = (PXE_DB_GET_STATUS *) (UINTN) CdbPtr->DBaddr; + + if (CdbPtr->DBsize > 0 && CdbPtr->DBsize < sizeof (UINT32) * 2) { + CopyMem (DbPtr, &TmpGetStatus, CdbPtr->DBsize); + } else { + CopyMem (DbPtr, &TmpGetStatus, sizeof (UINT32) * 2); + } + + // + // + // + if ((CdbPtr->OpFlags & PXE_OPFLAGS_GET_TRANSMITTED_BUFFERS) != 0) { + // + // DBsize of zero is invalid if Tx buffers are requested. + // + if (CdbPtr->DBsize == 0) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + // + // remember this b4 we overwrite + // + NumEntries = (UINT16) (CdbPtr->DBsize - sizeof (UINT64)); + + // + // We already filled in 2 UINT32s. + // + CdbPtr->DBsize = (UINT16) (sizeof (UINT32) * 2); + + // + // will claim any hanging free CBs + // + CheckCBList (AdapterInfo); + + if (AdapterInfo->xmit_done_head == AdapterInfo->xmit_done_tail) { + CdbPtr->StatFlags |= PXE_STATFLAGS_GET_STATUS_TXBUF_QUEUE_EMPTY; + } else { + for (Index = 0; ((Index < MAX_XMIT_BUFFERS) && (NumEntries >= sizeof (UINT64))); Index++, NumEntries -= sizeof (UINT64)) { + if (AdapterInfo->xmit_done_head != AdapterInfo->xmit_done_tail) { + DbPtr->TxBuffer[Index] = AdapterInfo->xmit_done[AdapterInfo->xmit_done_head]; + AdapterInfo->xmit_done_head = next (AdapterInfo->xmit_done_head); + CdbPtr->DBsize += sizeof (UINT64); + } else { + break; + } + } + } + + if (AdapterInfo->xmit_done_head != AdapterInfo->xmit_done_tail) { + CdbPtr->StatFlags |= PXE_STATFLAGS_DB_WRITE_TRUNCATED; + + } + // + // check for a receive buffer and give it's size in db + // + } + // + // + // + if ((CdbPtr->OpFlags & PXE_OPFLAGS_GET_INTERRUPT_STATUS) != 0) { + + Status = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBStatus); + AdapterInfo->Int_Status = (UINT16) (AdapterInfo->Int_Status | Status); + + // + // acknoledge the interrupts + // + OutWord (AdapterInfo, (UINT16) (Status & 0xfc00), (UINT32) (AdapterInfo->ioaddr + SCBStatus)); + + // + // report all the outstanding interrupts + // + Status = AdapterInfo->Int_Status; + if ((Status & SCB_STATUS_FR) != 0) { + CdbPtr->StatFlags |= PXE_STATFLAGS_GET_STATUS_RECEIVE; + } + + if ((Status & SCB_STATUS_SWI) != 0) { + CdbPtr->StatFlags |= PXE_STATFLAGS_GET_STATUS_SOFTWARE; + } + } + + // + // Return current media status + // + if ((CdbPtr->OpFlags & PXE_OPFLAGS_GET_MEDIA_STATUS) != 0) { + AdapterInfo->PhyAddress = 0xFF; + AdapterInfo->CableDetect = 1; + + if (!PhyDetect (AdapterInfo)) { + CdbPtr->StatFlags |= PXE_STATFLAGS_GET_STATUS_NO_MEDIA; + } + } + + return ; +} + + +/** + This routine is used to fill media header(s) in transmit packet(s). + Copies the MAC address into the media header whether it is dealing + with fragmented or non-fragmented packets. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_FillHeader ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + PXE_CPB_FILL_HEADER *Cpb; + PXE_CPB_FILL_HEADER_FRAGMENTED *Cpbf; + EtherHeader *MacHeader; + UINTN Index; + + if (CdbPtr->CPBsize == PXE_CPBSIZE_NOT_USED) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + if ((CdbPtr->OpFlags & PXE_OPFLAGS_FILL_HEADER_FRAGMENTED) != 0) { + Cpbf = (PXE_CPB_FILL_HEADER_FRAGMENTED *) (UINTN) CdbPtr->CPBaddr; + + // + // assume 1st fragment is big enough for the mac header + // + if ((Cpbf->FragCnt == 0) || (Cpbf->FragDesc[0].FragLen < PXE_MAC_HEADER_LEN_ETHER)) { + // + // no buffers given + // + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + MacHeader = (EtherHeader *) (UINTN) Cpbf->FragDesc[0].FragAddr; + // + // we don't swap the protocol bytes + // + MacHeader->type = Cpbf->Protocol; + + for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) { + MacHeader->dest_addr[Index] = Cpbf->DestAddr[Index]; + MacHeader->src_addr[Index] = Cpbf->SrcAddr[Index]; + } + } else { + Cpb = (PXE_CPB_FILL_HEADER *) (UINTN) CdbPtr->CPBaddr; + + MacHeader = (EtherHeader *) (UINTN) Cpb->MediaHeader; + // + // we don't swap the protocol bytes + // + MacHeader->type = Cpb->Protocol; + + for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) { + MacHeader->dest_addr[Index] = Cpb->DestAddr[Index]; + MacHeader->src_addr[Index] = Cpb->SrcAddr[Index]; + } + } + + return ; +} + + +/** + This routine is used to place a packet into the transmit queue. The data buffers given to + this command are to be considered locked and the application or network driver loses + ownership of these buffers and must not free or relocate them until the ownership returns. + When the packets are transmitted, a transmit complete interrupt is generated (if interrupts + are disabled, the transmit interrupt status is still set and can be checked using the UNDI_Status + command. + Some implementations and adapters support transmitting multiple packets with one transmit + command. If this feature is supported, the transmit CPBs can be linked in one transmit + command. + All UNDIs support fragmented frames, now all network devices or protocols do. If a fragmented + frame CPB is given to UNDI and the network device does not support fragmented frames + (see !PXE.Implementation flag), the UNDI will have to copy the fragments into a local buffer + before transmitting. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_Transmit ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + + if (CdbPtr->CPBsize == PXE_CPBSIZE_NOT_USED) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + CdbPtr->StatCode = (PXE_STATCODE) E100bTransmit (AdapterInfo, CdbPtr->CPBaddr, CdbPtr->OpFlags); + + if (CdbPtr->StatCode != PXE_STATCODE_SUCCESS) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + } + + return ; +} + + +/** + When the network adapter has received a frame, this command is used to copy the frame + into the driver/application storage location. Once a frame has been copied, it is + removed from the receive queue. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_Receive ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + + // + // check if RU has started... + // + if (!AdapterInfo->Receive_Started) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_NOT_INITIALIZED; + return ; + } + + + CdbPtr->StatCode = (UINT16) E100bReceive (AdapterInfo, CdbPtr->CPBaddr, CdbPtr->DBaddr); + if (CdbPtr->StatCode != PXE_STATCODE_SUCCESS) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + + } + + return ; +} + + + +/** + This is the main SW UNDI API entry using the newer nii protocol. + The parameter passed in is a 64 bit flat model virtual + address of the cdb. We then jump into the common routine for both old and + new nii protocol entries. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +// TODO: cdb - add argument and description to function comment +VOID +EFIAPI +UNDI_APIEntry_new ( + IN UINT64 cdb + ) +{ + PXE_CDB *CdbPtr; + NIC_DATA_INSTANCE *AdapterInfo; + + if (cdb == (UINT64) 0) { + return ; + + } + + CdbPtr = (PXE_CDB *) (UINTN) cdb; + + if (CdbPtr->IFnum >= (pxe_31->IFcnt | pxe_31->IFcntExt << 8) ) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + AdapterInfo = &(UNDI32DeviceList[CdbPtr->IFnum]->NicInfo); + // + // entering from older entry point + // + AdapterInfo->VersionFlag = 0x31; + UNDI_APIEntry_Common (cdb); +} + + +/** + This is the common routine for both old and new entry point procedures. + The parameter passed in is a 64 bit flat model virtual + address of the cdb. We then jump into the service routine pointed to by the + Api_Table[OpCode]. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +// TODO: cdb - add argument and description to function comment +VOID +UNDI_APIEntry_Common ( + IN UINT64 cdb + ) +{ + PXE_CDB *CdbPtr; + NIC_DATA_INSTANCE *AdapterInfo; + UNDI_CALL_TABLE *tab_ptr; + + CdbPtr = (PXE_CDB *) (UINTN) cdb; + + // + // check the OPCODE range + // + if ((CdbPtr->OpCode > PXE_OPCODE_LAST_VALID) || + (CdbPtr->StatCode != PXE_STATCODE_INITIALIZE) || + (CdbPtr->StatFlags != PXE_STATFLAGS_INITIALIZE) || + (CdbPtr->IFnum >= (pxe_31->IFcnt | pxe_31->IFcntExt << 8))) { + goto badcdb; + + } + + if (CdbPtr->CPBsize == PXE_CPBSIZE_NOT_USED) { + if (CdbPtr->CPBaddr != PXE_CPBADDR_NOT_USED) { + goto badcdb; + } + } else if (CdbPtr->CPBaddr == PXE_CPBADDR_NOT_USED) { + goto badcdb; + } + + if (CdbPtr->DBsize == PXE_DBSIZE_NOT_USED) { + if (CdbPtr->DBaddr != PXE_DBADDR_NOT_USED) { + goto badcdb; + } + } else if (CdbPtr->DBaddr == PXE_DBADDR_NOT_USED) { + goto badcdb; + } + + // + // check if cpbsize and dbsize are as needed + // check if opflags are as expected + // + tab_ptr = &api_table[CdbPtr->OpCode]; + + if (tab_ptr->cpbsize != (UINT16) (DONT_CHECK) && tab_ptr->cpbsize != CdbPtr->CPBsize) { + goto badcdb; + } + + if (tab_ptr->dbsize != (UINT16) (DONT_CHECK) && tab_ptr->dbsize != CdbPtr->DBsize) { + goto badcdb; + } + + if (tab_ptr->opflags != (UINT16) (DONT_CHECK) && tab_ptr->opflags != CdbPtr->OpFlags) { + goto badcdb; + + } + + AdapterInfo = &(UNDI32DeviceList[CdbPtr->IFnum]->NicInfo); + + // + // check if UNDI_State is valid for this call + // + if (tab_ptr->state != (UINT16) (-1)) { + // + // should atleast be started + // + if (AdapterInfo->State == PXE_STATFLAGS_GET_STATE_STOPPED) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_NOT_STARTED; + return ; + } + // + // check if it should be initialized + // + if (tab_ptr->state == 2) { + if (AdapterInfo->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) { + CdbPtr->StatCode = PXE_STATCODE_NOT_INITIALIZED; + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + return ; + } + } + } + // + // set the return variable for success case here + // + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE; + CdbPtr->StatCode = PXE_STATCODE_SUCCESS; + + tab_ptr->api_ptr (CdbPtr, AdapterInfo); + return ; + // + // %% AVL - check for command linking + // +badcdb: + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; +} + + +/** + When called with a null NicPtr, this routine decrements the number of NICs + this UNDI is supporting and removes the NIC_DATA_POINTER from the array. + Otherwise, it increments the number of NICs this UNDI is supported and + updates the pxe.Fudge to ensure a proper check sum results. + + @param NicPtr Pointer to the NIC data structure. + + @return None + +**/ +VOID +PxeUpdate ( + IN NIC_DATA_INSTANCE *NicPtr, + IN PXE_SW_UNDI *PxePtr + ) +{ + UINT16 NicNum; + NicNum = (PxePtr->IFcnt | PxePtr->IFcntExt << 8); + + if (NicPtr == NULL) { + if (NicNum > 0) { + // + // number of NICs this undi supports + // + NicNum --; + } + goto done; + } + + // + // number of NICs this undi supports + // + NicNum++; + +done: + PxePtr->IFcnt = (UINT8)(NicNum & 0xFF); + PxePtr->IFcntExt = (UINT8) ((NicNum & 0xFF00) >> 8); + PxePtr->Fudge = (UINT8) (PxePtr->Fudge - CalculateSum8 ((VOID *) PxePtr, PxePtr->Len)); + return ; +} + + +/** + Initialize the !PXE structure + + @param PxePtr Pointer to SW_UNDI data structure. + + @retval EFI_SUCCESS This driver is added to Controller. + @retval other This driver does not support this device. + +**/ +VOID +PxeStructInit ( + IN PXE_SW_UNDI *PxePtr + ) +{ + // + // Initialize the !PXE structure + // + PxePtr->Signature = PXE_ROMID_SIGNATURE; + PxePtr->Len = (UINT8) sizeof (PXE_SW_UNDI); + // + // cksum + // + PxePtr->Fudge = 0; + // + // number of NICs this undi supports + // + PxePtr->IFcnt = 0; + PxePtr->IFcntExt = 0; + PxePtr->Rev = PXE_ROMID_REV; + PxePtr->MajorVer = PXE_ROMID_MAJORVER; + PxePtr->MinorVer = PXE_ROMID_MINORVER; + PxePtr->reserved1 = 0; + + PxePtr->Implementation = PXE_ROMID_IMP_SW_VIRT_ADDR | + PXE_ROMID_IMP_FRAG_SUPPORTED | + PXE_ROMID_IMP_CMD_LINK_SUPPORTED | + PXE_ROMID_IMP_NVDATA_READ_ONLY | + PXE_ROMID_IMP_STATION_ADDR_SETTABLE | + PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED | + PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED | + PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED | + PXE_ROMID_IMP_FILTERED_MULTICAST_RX_SUPPORTED | + PXE_ROMID_IMP_SOFTWARE_INT_SUPPORTED | + PXE_ROMID_IMP_PACKET_RX_INT_SUPPORTED; + + PxePtr->EntryPoint = (UINT64) (UINTN) UNDI_APIEntry_new; + PxePtr->MinorVer = PXE_ROMID_MINORVER_31; + + PxePtr->reserved2[0] = 0; + PxePtr->reserved2[1] = 0; + PxePtr->reserved2[2] = 0; + PxePtr->BusCnt = 1; + PxePtr->BusType[0] = PXE_BUSTYPE_PCI; + + PxePtr->Fudge = (UINT8) (PxePtr->Fudge - CalculateSum8 ((VOID *) PxePtr, PxePtr->Len)); +} + diff --git a/Drivers/OptionRomPkg/UndiRuntimeDxe/E100b.c b/Drivers/OptionRomPkg/UndiRuntimeDxe/E100b.c new file mode 100644 index 0000000000..199f5430ad --- /dev/null +++ b/Drivers/OptionRomPkg/UndiRuntimeDxe/E100b.c @@ -0,0 +1,3541 @@ +/** @file + Provides basic function upon network adapter card. + +Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Undi32.h" + +UINT8 basic_config_cmd[22] = { + 22, 0x08, + 0, 0, + 0, (UINT8)0x80, + 0x32, 0x03, + 1, 0, + 0x2E, 0, + 0x60, 0, + (UINT8)0xf2, 0x48, + 0, 0x40, + (UINT8)0xf2, (UINT8)0x80, // 0x40=Force full-duplex + 0x3f, 0x05, +}; + +// +// How to wait for the command unit to accept a command. +// Typically this takes 0 ticks. +// +#define wait_for_cmd_done(cmd_ioaddr) \ +{ \ + INT16 wait_count = 2000; \ + while ((InByte (AdapterInfo, cmd_ioaddr) != 0) && --wait_count >= 0) \ + DelayIt (AdapterInfo, 10); \ + if (wait_count == 0) \ + DelayIt (AdapterInfo, 50); \ +} + + +/** + This function calls the MemIo callback to read a byte from the device's + address space + Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine) + which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have + to make undi3.0 a special case + + @param Port Which port to read from. + + @retval Results The data read from the port. + +**/ +// TODO: AdapterInfo - add argument and description to function comment +UINT8 +InByte ( + IN NIC_DATA_INSTANCE *AdapterInfo, + IN UINT32 Port + ) +{ + UINT8 Results; + + (*AdapterInfo->Mem_Io) ( + AdapterInfo->Unique_ID, + PXE_MEM_READ, + 1, + (UINT64)Port, + (UINT64) (UINTN) &Results + ); + return Results; +} + + +/** + This function calls the MemIo callback to read a word from the device's + address space + Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine) + which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have + to make undi3.0 a special case + + @param Port Which port to read from. + + @retval Results The data read from the port. + +**/ +// TODO: AdapterInfo - add argument and description to function comment +UINT16 +InWord ( + IN NIC_DATA_INSTANCE *AdapterInfo, + IN UINT32 Port + ) +{ + UINT16 Results; + + (*AdapterInfo->Mem_Io) ( + AdapterInfo->Unique_ID, + PXE_MEM_READ, + 2, + (UINT64)Port, + (UINT64)(UINTN)&Results + ); + return Results; +} + + +/** + This function calls the MemIo callback to read a dword from the device's + address space + Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine) + which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have + to make undi3.0 a special case + + @param Port Which port to read from. + + @retval Results The data read from the port. + +**/ +// TODO: AdapterInfo - add argument and description to function comment +UINT32 +InLong ( + IN NIC_DATA_INSTANCE *AdapterInfo, + IN UINT32 Port + ) +{ + UINT32 Results; + + (*AdapterInfo->Mem_Io) ( + AdapterInfo->Unique_ID, + PXE_MEM_READ, + 4, + (UINT64)Port, + (UINT64)(UINTN)&Results + ); + return Results; +} + + +/** + This function calls the MemIo callback to write a byte from the device's + address space + Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine) + which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have + to make undi3.0 a special case + + @param Data Data to write to Port. + @param Port Which port to write to. + + @return none + +**/ +// TODO: AdapterInfo - add argument and description to function comment +VOID +OutByte ( + IN NIC_DATA_INSTANCE *AdapterInfo, + IN UINT8 Data, + IN UINT32 Port + ) +{ + UINT8 Val; + + Val = Data; + (*AdapterInfo->Mem_Io) ( + AdapterInfo->Unique_ID, + PXE_MEM_WRITE, + 1, + (UINT64)Port, + (UINT64)(UINTN)(UINTN)&Val + ); + return ; +} + + +/** + This function calls the MemIo callback to write a word from the device's + address space + Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine) + which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have + to make undi3.0 a special case + + @param Data Data to write to Port. + @param Port Which port to write to. + + @return none + +**/ +// TODO: AdapterInfo - add argument and description to function comment +VOID +OutWord ( + IN NIC_DATA_INSTANCE *AdapterInfo, + IN UINT16 Data, + IN UINT32 Port + ) +{ + UINT16 Val; + + Val = Data; + (*AdapterInfo->Mem_Io) ( + AdapterInfo->Unique_ID, + PXE_MEM_WRITE, + 2, + (UINT64)Port, + (UINT64)(UINTN)&Val + ); + return ; +} + + +/** + This function calls the MemIo callback to write a dword from the device's + address space + Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine) + which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have + to make undi3.0 a special case + + @param Data Data to write to Port. + @param Port Which port to write to. + + @return none + +**/ +// TODO: AdapterInfo - add argument and description to function comment +VOID +OutLong ( + IN NIC_DATA_INSTANCE *AdapterInfo, + IN UINT32 Data, + IN UINT32 Port + ) +{ + UINT32 Val; + + Val = Data; + (*AdapterInfo->Mem_Io) ( + AdapterInfo->Unique_ID, + PXE_MEM_WRITE, + 4, + (UINT64)Port, + (UINT64)(UINTN)&Val + ); + return ; +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + @param MemAddr TODO: add argument description + @param Size TODO: add argument description + @param Direction TODO: add argument description + @param MappedAddr TODO: add argument description + + @return TODO: add return values + +**/ +UINTN +MapIt ( + IN NIC_DATA_INSTANCE *AdapterInfo, + IN UINT64 MemAddr, + IN UINT32 Size, + IN UINT32 Direction, + OUT UINT64 MappedAddr + ) +{ + UINT64 *PhyAddr; + + PhyAddr = (UINT64 *) (UINTN) MappedAddr; + // + // mapping is different for theold and new NII protocols + // + if (AdapterInfo->VersionFlag == 0x30) { + if (AdapterInfo->Virt2Phys_30 == (VOID *) NULL) { + *PhyAddr = (UINT64) AdapterInfo->MemoryPtr; + } else { + (*AdapterInfo->Virt2Phys_30) (MemAddr, (UINT64) (UINTN) PhyAddr); + } + + if (*PhyAddr > FOUR_GIGABYTE) { + return PXE_STATCODE_INVALID_PARAMETER; + } + } else { + if (AdapterInfo->Map_Mem == (VOID *) NULL) { + // + // this UNDI cannot handle addresses beyond 4 GB without a map routine + // + if (MemAddr > FOUR_GIGABYTE) { + return PXE_STATCODE_INVALID_PARAMETER; + } else { + *PhyAddr = MemAddr; + } + } else { + (*AdapterInfo->Map_Mem) ( + AdapterInfo->Unique_ID, + MemAddr, + Size, + Direction, + MappedAddr + ); + } + } + + return PXE_STATCODE_SUCCESS; +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + @param MemAddr TODO: add argument description + @param Size TODO: add argument description + @param Direction TODO: add argument description + @param MappedAddr TODO: add argument description + + @return TODO: add return values + +**/ +VOID +UnMapIt ( + IN NIC_DATA_INSTANCE *AdapterInfo, + IN UINT64 MemAddr, + IN UINT32 Size, + IN UINT32 Direction, + IN UINT64 MappedAddr + ) +{ + if (AdapterInfo->VersionFlag > 0x30) { + // + // no mapping service + // + if (AdapterInfo->UnMap_Mem != (VOID *) NULL) { + (*AdapterInfo->UnMap_Mem) ( + AdapterInfo->Unique_ID, + MemAddr, + Size, + Direction, + MappedAddr + ); + + } + } + + return ; +} + + +/** + + @param AdapterInfo Pointer to the NIC data structure + information which the UNDI driver is + layering on.. + + +**/ +// TODO: MicroSeconds - add argument and description to function comment +VOID +DelayIt ( + IN NIC_DATA_INSTANCE *AdapterInfo, + UINT16 MicroSeconds + ) +{ + if (AdapterInfo->VersionFlag == 0x30) { + (*AdapterInfo->Delay_30) (MicroSeconds); + } else { + (*AdapterInfo->Delay) (AdapterInfo->Unique_ID, MicroSeconds); + } +} + + +/** + + @param AdapterInfo Pointer to the NIC data structure + information which the UNDI driver is + layering on.. + + +**/ +// TODO: flag - add argument and description to function comment +VOID +BlockIt ( + IN NIC_DATA_INSTANCE *AdapterInfo, + UINT32 flag + ) +{ + if (AdapterInfo->VersionFlag == 0x30) { + (*AdapterInfo->Block_30) (flag); + } else { + (*AdapterInfo->Block) (AdapterInfo->Unique_ID, flag); + } +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + + @return TODO: add return values + +**/ +UINT8 +Load_Base_Regs ( + NIC_DATA_INSTANCE *AdapterInfo + ) +{ + // + // we will use the linear (flat) memory model and fill our base registers + // with 0's so that the entire physical address is our offset + // + // + // we reset the statistics totals here because this is where we are loading stats addr + // + AdapterInfo->RxTotals = 0; + AdapterInfo->TxTotals = 0; + + // + // Load the statistics block address. + // + wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd); + OutLong (AdapterInfo, (UINT32) AdapterInfo->stat_phy_addr, AdapterInfo->ioaddr + SCBPointer); + OutByte (AdapterInfo, CU_STATSADDR, AdapterInfo->ioaddr + SCBCmd); + AdapterInfo->statistics->done_marker = 0; + + wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd); + OutLong (AdapterInfo, 0, AdapterInfo->ioaddr + SCBPointer); + OutByte (AdapterInfo, RX_ADDR_LOAD, AdapterInfo->ioaddr + SCBCmd); + + wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd); + OutLong (AdapterInfo, 0, AdapterInfo->ioaddr + SCBPointer); + OutByte (AdapterInfo, CU_CMD_BASE, AdapterInfo->ioaddr + SCBCmd); + + return 0; +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + @param cmd_ptr TODO: add argument description + + @return TODO: add return values + +**/ +UINT8 +IssueCB ( + NIC_DATA_INSTANCE *AdapterInfo, + TxCB *cmd_ptr + ) +{ + UINT16 status; + + wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd); + + // + // read the CU status, if it is idle, write the address of cb_ptr + // in the scbpointer and issue a cu_start, + // if it is suspended, remove the suspend bit in the previous command + // block and issue a resume + // + // Ensure that the CU Active Status bit is not on from previous CBs. + // + status = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBStatus); + + // + // Skip acknowledging the interrupt if it is not already set + // + + // + // ack only the cna the integer + // + if ((status & SCB_STATUS_CNA) != 0) { + OutWord (AdapterInfo, SCB_STATUS_CNA, AdapterInfo->ioaddr + SCBStatus); + + } + + if ((status & SCB_STATUS_CU_MASK) == SCB_STATUS_CU_IDLE) { + // + // give a cu_start + // + OutLong (AdapterInfo, cmd_ptr->PhysTCBAddress, AdapterInfo->ioaddr + SCBPointer); + OutByte (AdapterInfo, CU_START, AdapterInfo->ioaddr + SCBCmd); + } else { + // + // either active or suspended, give a resume + // + + cmd_ptr->PrevTCBVirtualLinkPtr->cb_header.command &= ~(CmdSuspend | CmdIntr); + OutByte (AdapterInfo, CU_RESUME, AdapterInfo->ioaddr + SCBCmd); + } + + return 0; +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + + @return TODO: add return values + +**/ +UINT8 +Configure ( + NIC_DATA_INSTANCE *AdapterInfo + ) +{ + // + // all command blocks are of TxCB format + // + TxCB *cmd_ptr; + UINT8 *data_ptr; + volatile INT16 Index; + UINT8 my_filter; + + cmd_ptr = GetFreeCB (AdapterInfo); + ASSERT (cmd_ptr != NULL); + data_ptr = (UINT8 *) cmd_ptr + sizeof (struct CB_Header); + + // + // start the config data right after the command header + // + for (Index = 0; Index < sizeof (basic_config_cmd); Index++) { + data_ptr[Index] = basic_config_cmd[Index]; + } + + my_filter = (UINT8) ((AdapterInfo->Rx_Filter & PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS) ? 1 : 0); + my_filter = (UINT8) (my_filter | ((AdapterInfo->Rx_Filter & PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST) ? 0 : 2)); + + data_ptr[15] = (UINT8) (data_ptr[15] | my_filter); + data_ptr[19] = (UINT8) (AdapterInfo->Duplex ? 0xC0 : 0x80); + data_ptr[21] = (UINT8) ((AdapterInfo->Rx_Filter & PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST) ? 0x0D : 0x05); + + // + // check if we have to use the AUI port instead + // + if ((AdapterInfo->PhyRecord[0] & 0x8000) != 0) { + data_ptr[15] |= 0x80; + data_ptr[8] = 0; + } + + BlockIt (AdapterInfo, TRUE); + cmd_ptr->cb_header.command = CmdSuspend | CmdConfigure; + + IssueCB (AdapterInfo, cmd_ptr); + wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd); + + BlockIt (AdapterInfo, FALSE); + + CommandWaitForCompletion (cmd_ptr, AdapterInfo); + + // + // restore the cb values for tx + // + cmd_ptr->PhysTBDArrayAddres = cmd_ptr->PhysArrayAddr; + cmd_ptr->ByteCount = cmd_ptr->Threshold = cmd_ptr->TBDCount = 0; + // + // fields beyond the immediatedata are assumed to be safe + // add the CB to the free list again + // + SetFreeCB (AdapterInfo, cmd_ptr); + return 0; +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + + @return TODO: add return values + +**/ +UINT8 +E100bSetupIAAddr ( + NIC_DATA_INSTANCE *AdapterInfo + ) +{ + // + // all command blocks are of TxCB format + // + TxCB *cmd_ptr; + UINT16 *data_ptr; + UINT16 *eaddrs; + + eaddrs = (UINT16 *) AdapterInfo->CurrentNodeAddress; + + cmd_ptr = GetFreeCB (AdapterInfo); + ASSERT (cmd_ptr != NULL); + data_ptr = (UINT16 *) ((UINT8 *) cmd_ptr +sizeof (struct CB_Header)); + + // + // AVOID a bug (?!) here by marking the command already completed. + // + cmd_ptr->cb_header.command = (CmdSuspend | CmdIASetup); + cmd_ptr->cb_header.status = 0; + data_ptr[0] = eaddrs[0]; + data_ptr[1] = eaddrs[1]; + data_ptr[2] = eaddrs[2]; + + BlockIt (AdapterInfo, TRUE); + IssueCB (AdapterInfo, cmd_ptr); + wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd); + BlockIt (AdapterInfo, FALSE); + + CommandWaitForCompletion (cmd_ptr, AdapterInfo); + + // + // restore the cb values for tx + // + cmd_ptr->PhysTBDArrayAddres = cmd_ptr->PhysArrayAddr; + cmd_ptr->ByteCount = cmd_ptr->Threshold = cmd_ptr->TBDCount = 0; + // + // fields beyond the immediatedata are assumed to be safe + // add the CB to the free list again + // + SetFreeCB (AdapterInfo, cmd_ptr); + return 0; +} + + +/** + Instructs the NIC to stop receiving packets. + + @param AdapterInfo Pointer to the NIC data structure + information which the UNDI driver is + layering on.. + + +**/ +VOID +StopRU ( + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + if (AdapterInfo->Receive_Started) { + + // + // Todo: verify that we must wait for previous command completion. + // + wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd); + + // + // Disable interrupts, and stop the chip's Rx process. + // + OutWord (AdapterInfo, INT_MASK, AdapterInfo->ioaddr + SCBCmd); + OutWord (AdapterInfo, INT_MASK | RX_ABORT, AdapterInfo->ioaddr + SCBCmd); + + AdapterInfo->Receive_Started = FALSE; + } + + return ; +} + + +/** + Instructs the NIC to start receiving packets. + + @param AdapterInfo Pointer to the NIC data structure + information which the UNDI driver is + layering on.. + + @retval 0 Successful + @retval -1 Already Started + +**/ +INT8 +StartRU ( + NIC_DATA_INSTANCE *AdapterInfo + ) +{ + + if (AdapterInfo->Receive_Started) { + // + // already started + // + return -1; + } + + AdapterInfo->cur_rx_ind = 0; + AdapterInfo->Int_Status = 0; + + wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd); + + OutLong (AdapterInfo, (UINT32) AdapterInfo->rx_phy_addr, AdapterInfo->ioaddr + SCBPointer); + OutByte (AdapterInfo, RX_START, AdapterInfo->ioaddr + SCBCmd); + + wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd); + + AdapterInfo->Receive_Started = TRUE; + return 0; +} + + +/** + Configures the chip. This routine expects the NIC_DATA_INSTANCE structure to be filled in. + + @param AdapterInfo Pointer to the NIC data structure + information which the UNDI driver is + layering on.. + + @retval 0 Successful + @retval PXE_STATCODE_NOT_ENOUGH_MEMORY Insufficient length of locked memory + @retval other Failure initializing chip + +**/ +UINTN +E100bInit ( + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + PCI_CONFIG_HEADER *CfgHdr; + UINTN stat; + UINTN rx_size; + UINTN tx_size; + + if (AdapterInfo->MemoryLength < MEMORY_NEEDED) { + return PXE_STATCODE_NOT_ENOUGH_MEMORY; + } + + stat = MapIt ( + AdapterInfo, + AdapterInfo->MemoryPtr, + AdapterInfo->MemoryLength, + TO_AND_FROM_DEVICE, + (UINT64)(UINTN) &AdapterInfo->Mapped_MemoryPtr + ); + + if (stat != 0) { + return stat; + } + + CfgHdr = (PCI_CONFIG_HEADER *) &(AdapterInfo->Config[0]); + + // + // fill in the ioaddr, int... from the config space + // + AdapterInfo->int_num = CfgHdr->int_line; + + // + // we don't need to validate integer number, what if they don't want to assign one? + // if (AdapterInfo->int_num == 0 || AdapterInfo->int_num == 0xff) + // return PXE_STATCODE_DEVICE_FAILURE; + // + AdapterInfo->ioaddr = 0; + AdapterInfo->VendorID = CfgHdr->VendorID; + AdapterInfo->DeviceID = CfgHdr->DeviceID; + AdapterInfo->RevID = CfgHdr->RevID; + AdapterInfo->SubVendorID = CfgHdr->SubVendorID; + AdapterInfo->SubSystemID = CfgHdr->SubSystemID; + AdapterInfo->flash_addr = 0; + + // + // Read the station address EEPROM before doing the reset. + // Perhaps this should even be done before accepting the device, + // then we wouldn't have a device name with which to report the error. + // + if (E100bReadEepromAndStationAddress (AdapterInfo) != 0) { + return PXE_STATCODE_DEVICE_FAILURE; + + } + // + // ## calculate the buffer #s depending on memory given + // ## calculate the rx and tx ring pointers + // + + AdapterInfo->TxBufCnt = TX_BUFFER_COUNT; + AdapterInfo->RxBufCnt = RX_BUFFER_COUNT; + rx_size = (AdapterInfo->RxBufCnt * sizeof (RxFD)); + tx_size = (AdapterInfo->TxBufCnt * sizeof (TxCB)); + AdapterInfo->rx_ring = (RxFD *) (UINTN) (AdapterInfo->MemoryPtr); + AdapterInfo->tx_ring = (TxCB *) (UINTN) (AdapterInfo->MemoryPtr + rx_size); + AdapterInfo->statistics = (struct speedo_stats *) (UINTN) (AdapterInfo->MemoryPtr + rx_size + tx_size); + + AdapterInfo->rx_phy_addr = AdapterInfo->Mapped_MemoryPtr; + AdapterInfo->tx_phy_addr = AdapterInfo->Mapped_MemoryPtr + rx_size; + AdapterInfo->stat_phy_addr = AdapterInfo->tx_phy_addr + tx_size; + + // + // auto detect. + // + AdapterInfo->PhyAddress = 0xFF; + AdapterInfo->Rx_Filter = PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST; + AdapterInfo->Receive_Started = FALSE; + AdapterInfo->mcast_list.list_len = 0; + return InitializeChip (AdapterInfo); +} + + +/** + Sets the interrupt state for the NIC. + + @param AdapterInfo Pointer to the NIC data structure + information which the UNDI driver is + layering on.. + + @retval 0 Successful + +**/ +UINT8 +E100bSetInterruptState ( + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + // + // don't set receive interrupt if receiver is disabled... + // + UINT16 cmd_word; + + if ((AdapterInfo->int_mask & PXE_OPFLAGS_INTERRUPT_RECEIVE) != 0) { + cmd_word = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBCmd); + cmd_word &= ~INT_MASK; + OutWord (AdapterInfo, cmd_word, AdapterInfo->ioaddr + SCBCmd); + } else { + // + // disable ints, should not be given for SW Int. + // + OutWord (AdapterInfo, INT_MASK, AdapterInfo->ioaddr + SCBCmd); + } + + if ((AdapterInfo->int_mask & PXE_OPFLAGS_INTERRUPT_SOFTWARE) != 0) { + // + // reset the bit in our mask, it is only one time!! + // + AdapterInfo->int_mask &= ~(PXE_OPFLAGS_INTERRUPT_SOFTWARE); + cmd_word = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBCmd); + cmd_word |= DRVR_INT; + OutWord (AdapterInfo, cmd_word, AdapterInfo->ioaddr + SCBCmd); + } + + return 0; +} +// +// we are not going to disable broadcast for the WOL's sake! +// + +/** + Instructs the NIC to start receiving packets. + + @param AdapterInfo Pointer to the NIC data structure + information which the UNDI driver is + layering on.. new_filter + - cpb - + cpbsize - + + @retval 0 Successful + @retval -1 Already Started + +**/ +UINTN +E100bSetfilter ( + NIC_DATA_INSTANCE *AdapterInfo, + UINT16 new_filter, + UINT64 cpb, + UINT32 cpbsize + ) +{ + PXE_CPB_RECEIVE_FILTERS *mc_list = (PXE_CPB_RECEIVE_FILTERS *) (UINTN)cpb; + UINT16 cfg_flt; + UINT16 old_filter; + UINT16 Index; + UINT16 Index2; + UINT16 mc_count; + TxCB *cmd_ptr; + struct MC_CB_STRUCT *data_ptr; + UINT16 mc_byte_cnt; + + old_filter = AdapterInfo->Rx_Filter; + + // + // only these bits need a change in the configuration + // actually change in bcast requires configure but we ignore that change + // + cfg_flt = PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS | + PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST; + + if ((old_filter & cfg_flt) != (new_filter & cfg_flt)) { + XmitWaitForCompletion (AdapterInfo); + + if (AdapterInfo->Receive_Started) { + StopRU (AdapterInfo); + } + + AdapterInfo->Rx_Filter = (UINT8) (new_filter | PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST); + Configure (AdapterInfo); + } + + // + // check if mcast setting changed + // + if ( ((new_filter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) != + (old_filter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) ) || + (mc_list != NULL) ) { + + + if (mc_list != NULL) { + mc_count = AdapterInfo->mcast_list.list_len = (UINT16) (cpbsize / PXE_MAC_LENGTH); + + for (Index = 0; (Index < mc_count && Index < MAX_MCAST_ADDRESS_CNT); Index++) { + for (Index2 = 0; Index2 < PXE_MAC_LENGTH; Index2++) { + AdapterInfo->mcast_list.mc_list[Index][Index2] = mc_list->MCastList[Index][Index2]; + } + } + } + + // + // are we setting the list or resetting?? + // + if ((new_filter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) != 0) { + // + // we are setting a new list! + // + mc_count = AdapterInfo->mcast_list.list_len; + // + // count should be the actual # of bytes in the list + // so multiply this with 6 + // + mc_byte_cnt = (UINT16) ((mc_count << 2) + (mc_count << 1)); + AdapterInfo->Rx_Filter |= PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST; + } else { + // + // disabling the list in the NIC. + // + mc_byte_cnt = mc_count = 0; + AdapterInfo->Rx_Filter &= (~PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST); + } + + // + // before issuing any new command! + // + XmitWaitForCompletion (AdapterInfo); + + if (AdapterInfo->Receive_Started) { + StopRU (AdapterInfo); + + } + + cmd_ptr = GetFreeCB (AdapterInfo); + if (cmd_ptr == NULL) { + return PXE_STATCODE_QUEUE_FULL; + } + // + // fill the command structure and issue + // + data_ptr = (struct MC_CB_STRUCT *) (&cmd_ptr->PhysTBDArrayAddres); + // + // first 2 bytes are the count; + // + data_ptr->count = mc_byte_cnt; + for (Index = 0; Index < mc_count; Index++) { + for (Index2 = 0; Index2 < PXE_HWADDR_LEN_ETHER; Index2++) { + data_ptr->m_list[Index][Index2] = AdapterInfo->mcast_list.mc_list[Index][Index2]; + } + } + + cmd_ptr->cb_header.command = CmdSuspend | CmdMulticastList; + cmd_ptr->cb_header.status = 0; + + BlockIt (AdapterInfo, TRUE); + IssueCB (AdapterInfo, cmd_ptr); + wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd); + + BlockIt (AdapterInfo, FALSE); + + CommandWaitForCompletion (cmd_ptr, AdapterInfo); + + cmd_ptr->PhysTBDArrayAddres = cmd_ptr->PhysArrayAddr; + cmd_ptr->ByteCount = cmd_ptr->Threshold = cmd_ptr->TBDCount = 0; + // + // fields beyond the immediatedata are assumed to be safe + // add the CB to the free list again + // + SetFreeCB (AdapterInfo, cmd_ptr); + } + + if (new_filter != 0) { + // + // enable unicast and start the RU + // + AdapterInfo->Rx_Filter = (UINT8) (AdapterInfo->Rx_Filter | (new_filter | PXE_OPFLAGS_RECEIVE_FILTER_UNICAST)); + StartRU (AdapterInfo); + } else { + // + // may be disabling everything! + // + if (AdapterInfo->Receive_Started) { + StopRU (AdapterInfo); + } + + AdapterInfo->Rx_Filter |= (~PXE_OPFLAGS_RECEIVE_FILTER_UNICAST); + } + + return 0; +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + @param cpb TODO: add argument description + @param opflags TODO: add argument description + + @return TODO: add return values + +**/ +UINTN +E100bTransmit ( + NIC_DATA_INSTANCE *AdapterInfo, + UINT64 cpb, + UINT16 opflags + ) +{ + PXE_CPB_TRANSMIT_FRAGMENTS *tx_ptr_f; + PXE_CPB_TRANSMIT *tx_ptr_1; + TxCB *tcb_ptr; + UINT64 Tmp_ptr; + UINTN stat; + INT32 Index; + UINT16 wait_sec; + + tx_ptr_1 = (PXE_CPB_TRANSMIT *) (UINTN) cpb; + tx_ptr_f = (PXE_CPB_TRANSMIT_FRAGMENTS *) (UINTN) cpb; + Tmp_ptr = 0; + + // + // stop reentrancy here + // + if (AdapterInfo->in_transmit) { + return PXE_STATCODE_BUSY; + + } + + AdapterInfo->in_transmit = TRUE; + + // + // Prevent interrupts from changing the Tx ring from underneath us. + // + // Calculate the Tx descriptor entry. + // + if ((tcb_ptr = GetFreeCB (AdapterInfo)) == NULL) { + AdapterInfo->in_transmit = FALSE; + return PXE_STATCODE_QUEUE_FULL; + } + + AdapterInfo->TxTotals++; + + tcb_ptr->cb_header.command = (CmdSuspend | CmdTx | CmdTxFlex); + tcb_ptr->cb_header.status = 0; + + // + // no immediate data, set EOF in the ByteCount + // + tcb_ptr->ByteCount = 0x8000; + + // + // The data region is always in one buffer descriptor, Tx FIFO + // threshold of 256. + // 82557 multiplies the threashold value by 8, so give 256/8 + // + tcb_ptr->Threshold = 32; + if ((opflags & PXE_OPFLAGS_TRANSMIT_FRAGMENTED) != 0) { + + if (tx_ptr_f->FragCnt > MAX_XMIT_FRAGMENTS) { + SetFreeCB (AdapterInfo, tcb_ptr); + AdapterInfo->in_transmit = FALSE; + return PXE_STATCODE_INVALID_PARAMETER; + } + + tcb_ptr->TBDCount = (UINT8) tx_ptr_f->FragCnt; + + for (Index = 0; Index < tx_ptr_f->FragCnt; Index++) { + stat = MapIt ( + AdapterInfo, + tx_ptr_f->FragDesc[Index].FragAddr, + tx_ptr_f->FragDesc[Index].FragLen, + TO_DEVICE, + (UINT64)(UINTN) &Tmp_ptr + ); + if (stat != 0) { + SetFreeCB (AdapterInfo, tcb_ptr); + AdapterInfo->in_transmit = FALSE; + return PXE_STATCODE_INVALID_PARAMETER; + } + + tcb_ptr->TBDArray[Index].phys_buf_addr = (UINT32) Tmp_ptr; + tcb_ptr->TBDArray[Index].buf_len = tx_ptr_f->FragDesc[Index].FragLen; + } + + tcb_ptr->free_data_ptr = tx_ptr_f->FragDesc[0].FragAddr; + + } else { + // + // non fragmented case + // + tcb_ptr->TBDCount = 1; + stat = MapIt ( + AdapterInfo, + tx_ptr_1->FrameAddr, + tx_ptr_1->DataLen + tx_ptr_1->MediaheaderLen, + TO_DEVICE, + (UINT64)(UINTN) &Tmp_ptr + ); + if (stat != 0) { + SetFreeCB (AdapterInfo, tcb_ptr); + AdapterInfo->in_transmit = FALSE; + return PXE_STATCODE_INVALID_PARAMETER; + } + + tcb_ptr->TBDArray[0].phys_buf_addr = (UINT32) (Tmp_ptr); + tcb_ptr->TBDArray[0].buf_len = tx_ptr_1->DataLen + tx_ptr_1->MediaheaderLen; + tcb_ptr->free_data_ptr = tx_ptr_1->FrameAddr; + } + + // + // must wait for previous command completion only if it was a non-transmit + // + BlockIt (AdapterInfo, TRUE); + IssueCB (AdapterInfo, tcb_ptr); + BlockIt (AdapterInfo, FALSE); + + // + // see if we need to wait for completion here + // + if ((opflags & PXE_OPFLAGS_TRANSMIT_BLOCK) != 0) { + // + // don't wait for more than 1 second!!! + // + wait_sec = 1000; + while (tcb_ptr->cb_header.status == 0) { + DelayIt (AdapterInfo, 10); + wait_sec--; + if (wait_sec == 0) { + break; + } + } + // + // we need to un-map any mapped buffers here + // + if ((opflags & PXE_OPFLAGS_TRANSMIT_FRAGMENTED) != 0) { + + for (Index = 0; Index < tx_ptr_f->FragCnt; Index++) { + Tmp_ptr = tcb_ptr->TBDArray[Index].phys_buf_addr; + UnMapIt ( + AdapterInfo, + tx_ptr_f->FragDesc[Index].FragAddr, + tx_ptr_f->FragDesc[Index].FragLen, + TO_DEVICE, + (UINT64) Tmp_ptr + ); + } + } else { + Tmp_ptr = tcb_ptr->TBDArray[0].phys_buf_addr; + UnMapIt ( + AdapterInfo, + tx_ptr_1->FrameAddr, + tx_ptr_1->DataLen + tx_ptr_1->MediaheaderLen, + TO_DEVICE, + (UINT64) Tmp_ptr + ); + } + + if (tcb_ptr->cb_header.status == 0) { + SetFreeCB (AdapterInfo, tcb_ptr); + AdapterInfo->in_transmit = FALSE; + return PXE_STATCODE_DEVICE_FAILURE; + } + + SetFreeCB (AdapterInfo, tcb_ptr); + } + // + // CB will be set free later in get_status (or when we run out of xmit buffers + // + AdapterInfo->in_transmit = FALSE; + + return 0; +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + @param cpb TODO: add argument description + @param db TODO: add argument description + + @return TODO: add return values + +**/ +UINTN +E100bReceive ( + NIC_DATA_INSTANCE *AdapterInfo, + UINT64 cpb, + UINT64 db + ) +{ + PXE_CPB_RECEIVE *rx_cpbptr; + PXE_DB_RECEIVE *rx_dbptr; + RxFD *rx_ptr; + INT32 status; + INT32 Index; + UINT16 pkt_len; + UINT16 ret_code; + PXE_FRAME_TYPE pkt_type; + UINT16 Tmp_len; + EtherHeader *hdr_ptr; + ret_code = PXE_STATCODE_NO_DATA; + pkt_type = PXE_FRAME_TYPE_NONE; + status = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBStatus); + AdapterInfo->Int_Status = (UINT16) (AdapterInfo->Int_Status | status); + // + // acknoledge the interrupts + // + OutWord (AdapterInfo, (UINT16) (status & 0xfc00), (UINT32) (AdapterInfo->ioaddr + SCBStatus)); + + // + // include the prev ints as well + // + status = AdapterInfo->Int_Status; + rx_cpbptr = (PXE_CPB_RECEIVE *) (UINTN) cpb; + rx_dbptr = (PXE_DB_RECEIVE *) (UINTN) db; + + rx_ptr = &AdapterInfo->rx_ring[AdapterInfo->cur_rx_ind]; + + // + // be in a loop just in case (we may drop a pkt) + // + while ((status = rx_ptr->cb_header.status) & RX_COMPLETE) { + + AdapterInfo->RxTotals++; + // + // If we own the next entry, it's a new packet. Send it up. + // + if (rx_ptr->forwarded) { + goto FreeRFD; + + } + + // + // discard bad frames + // + + // + // crc, align, dma overrun, too short, receive error (v22 no coll) + // + if ((status & 0x0D90) != 0) { + goto FreeRFD; + + } + + // + // make sure the status is OK + // + if ((status & 0x02000) == 0) { + goto FreeRFD; + } + + pkt_len = (UINT16) (rx_ptr->ActualCount & 0x3fff); + + if (pkt_len != 0) { + + Tmp_len = pkt_len; + if (pkt_len > rx_cpbptr->BufferLen) { + Tmp_len = (UINT16) rx_cpbptr->BufferLen; + } + + CopyMem ((INT8 *) (UINTN) rx_cpbptr->BufferAddr, (INT8 *) &rx_ptr->RFDBuffer, Tmp_len); + + hdr_ptr = (EtherHeader *) &rx_ptr->RFDBuffer; + // + // fill the CDB and break the loop + // + + // + // includes header + // + rx_dbptr->FrameLen = pkt_len; + rx_dbptr->MediaHeaderLen = PXE_MAC_HEADER_LEN_ETHER; + + for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) { + if (hdr_ptr->dest_addr[Index] != AdapterInfo->CurrentNodeAddress[Index]) { + break; + } + } + + if (Index >= PXE_HWADDR_LEN_ETHER) { + pkt_type = PXE_FRAME_TYPE_UNICAST; + } else { + for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) { + if (hdr_ptr->dest_addr[Index] != AdapterInfo->BroadcastNodeAddress[Index]) { + break; + } + } + + if (Index >= PXE_HWADDR_LEN_ETHER) { + pkt_type = PXE_FRAME_TYPE_BROADCAST; + } else { + if ((hdr_ptr->dest_addr[0] & 1) == 1) { + // + // mcast + // + + pkt_type = PXE_FRAME_TYPE_FILTERED_MULTICAST; + } else { + pkt_type = PXE_FRAME_TYPE_PROMISCUOUS; + } + } + } + + rx_dbptr->Type = pkt_type; + rx_dbptr->Protocol = hdr_ptr->type; + + for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) { + rx_dbptr->SrcAddr[Index] = hdr_ptr->src_addr[Index]; + rx_dbptr->DestAddr[Index] = hdr_ptr->dest_addr[Index]; + } + + rx_ptr->forwarded = TRUE; + // + // success + // + ret_code = 0; + Recycle_RFD (AdapterInfo, AdapterInfo->cur_rx_ind); + AdapterInfo->cur_rx_ind++; + if (AdapterInfo->cur_rx_ind == AdapterInfo->RxBufCnt) { + AdapterInfo->cur_rx_ind = 0; + } + break; + } + +FreeRFD: + Recycle_RFD (AdapterInfo, AdapterInfo->cur_rx_ind); + AdapterInfo->cur_rx_ind++; + if (AdapterInfo->cur_rx_ind == AdapterInfo->RxBufCnt) { + AdapterInfo->cur_rx_ind = 0; + } + + rx_ptr = &AdapterInfo->rx_ring[AdapterInfo->cur_rx_ind]; + } + + if (pkt_type == PXE_FRAME_TYPE_NONE) { + AdapterInfo->Int_Status &= (~SCB_STATUS_FR); + } + + status = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBStatus); + if ((status & SCB_RUS_NO_RESOURCES) != 0) { + // + // start the receive unit here! + // leave all the filled frames, + // + SetupReceiveQueues (AdapterInfo); + OutLong (AdapterInfo, (UINT32) AdapterInfo->rx_phy_addr, AdapterInfo->ioaddr + SCBPointer); + OutWord (AdapterInfo, RX_START, AdapterInfo->ioaddr + SCBCmd); + AdapterInfo->cur_rx_ind = 0; + } + + return ret_code; +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + + @return TODO: add return values + +**/ +INT16 +E100bReadEepromAndStationAddress ( + NIC_DATA_INSTANCE *AdapterInfo + ) +{ + INT32 Index; + INT32 Index2; + UINT16 sum; + UINT16 eeprom_len; + UINT8 addr_len; + UINT16 *eedata; + + eedata = (UINT16 *) (&AdapterInfo->NVData[0]); + + sum = 0; + addr_len = E100bGetEepromAddrLen (AdapterInfo); + + // + // in words + // + AdapterInfo->NVData_Len = eeprom_len = (UINT16) (1 << addr_len); + for (Index2 = 0, Index = 0; ((Index2 < PXE_MAC_LENGTH - 1) && (Index < eeprom_len)); Index++) { + UINT16 value; + value = E100bReadEeprom (AdapterInfo, Index, addr_len); + eedata[Index] = value; + sum = (UINT16) (sum + value); + if (Index < 3) { + AdapterInfo->PermNodeAddress[Index2++] = (UINT8) value; + AdapterInfo->PermNodeAddress[Index2++] = (UINT8) (value >> 8); + } + } + + if (sum != 0xBABA) { + return -1; + } + + for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) { + AdapterInfo->CurrentNodeAddress[Index] = AdapterInfo->PermNodeAddress[Index]; + } + + for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) { + AdapterInfo->BroadcastNodeAddress[Index] = 0xff; + } + + for (Index = PXE_HWADDR_LEN_ETHER; Index < PXE_MAC_LENGTH; Index++) { + AdapterInfo->CurrentNodeAddress[Index] = 0; + AdapterInfo->PermNodeAddress[Index] = 0; + AdapterInfo->BroadcastNodeAddress[Index] = 0; + } + + return 0; +} + +// +// CBList is a circular linked list +// 1) When all are free, Tail->next == Head and FreeCount == # allocated +// 2) When none are free, Tail == Head and FreeCount == 0 +// 3) when one is free, Tail == Head and Freecount == 1 +// 4) First non-Free frame is always at Tail->next +// + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + + @return TODO: add return values + +**/ +UINT8 +SetupCBlink ( + NIC_DATA_INSTANCE *AdapterInfo + ) +{ + TxCB *head_ptr; + TxCB *tail_ptr; + TxCB *cur_ptr; + INT32 Index; + UINTN array_off; + + cur_ptr = &(AdapterInfo->tx_ring[0]); + array_off = (UINTN) (&cur_ptr->TBDArray) - (UINTN) cur_ptr; + for (Index = 0; Index < AdapterInfo->TxBufCnt; Index++) { + cur_ptr[Index].cb_header.status = 0; + cur_ptr[Index].cb_header.command = 0; + + cur_ptr[Index].PhysTCBAddress = + (UINT32) AdapterInfo->tx_phy_addr + (Index * sizeof (TxCB)); + + cur_ptr[Index].PhysArrayAddr = (UINT32)(cur_ptr[Index].PhysTCBAddress + array_off); + cur_ptr[Index].PhysTBDArrayAddres = (UINT32)(cur_ptr[Index].PhysTCBAddress + array_off); + + cur_ptr->free_data_ptr = (UINT64) 0; + + if (Index < AdapterInfo->TxBufCnt - 1) { + cur_ptr[Index].cb_header.link = cur_ptr[Index].PhysTCBAddress + sizeof (TxCB); + cur_ptr[Index].NextTCBVirtualLinkPtr = &cur_ptr[Index + 1]; + cur_ptr[Index + 1].PrevTCBVirtualLinkPtr = &cur_ptr[Index]; + } + } + + head_ptr = &cur_ptr[0]; + tail_ptr = &cur_ptr[AdapterInfo->TxBufCnt - 1]; + tail_ptr->cb_header.link = head_ptr->PhysTCBAddress; + tail_ptr->NextTCBVirtualLinkPtr = head_ptr; + head_ptr->PrevTCBVirtualLinkPtr = tail_ptr; + + AdapterInfo->FreeCBCount = AdapterInfo->TxBufCnt; + AdapterInfo->FreeTxHeadPtr = head_ptr; + // + // set tail of the free list, next to this would be either in use + // or the head itself + // + AdapterInfo->FreeTxTailPtr = tail_ptr; + + AdapterInfo->xmit_done_head = AdapterInfo->xmit_done_tail = 0; + + return 0; +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + + @return TODO: add return values + +**/ +TxCB * +GetFreeCB ( + NIC_DATA_INSTANCE *AdapterInfo + ) +{ + TxCB *free_cb_ptr; + + // + // claim any hanging free CBs + // + if (AdapterInfo->FreeCBCount <= 1) { + CheckCBList (AdapterInfo); + } + + // + // don't use up the last CB problem if the previous CB that the CU used + // becomes the last CB we submit because of the SUSPEND bit we set. + // the CU thinks it was never cleared. + // + + if (AdapterInfo->FreeCBCount <= 1) { + return NULL; + } + + BlockIt (AdapterInfo, TRUE); + free_cb_ptr = AdapterInfo->FreeTxHeadPtr; + AdapterInfo->FreeTxHeadPtr = free_cb_ptr->NextTCBVirtualLinkPtr; + --AdapterInfo->FreeCBCount; + BlockIt (AdapterInfo, FALSE); + return free_cb_ptr; +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + @param cb_ptr TODO: add argument description + + @return TODO: add return values + +**/ +VOID +SetFreeCB ( + IN NIC_DATA_INSTANCE *AdapterInfo, + IN TxCB *cb_ptr + ) +{ + // + // here we assume cb are returned in the order they are taken out + // and we link the newly freed cb at the tail of free cb list + // + cb_ptr->cb_header.status = 0; + cb_ptr->free_data_ptr = (UINT64) 0; + + AdapterInfo->FreeTxTailPtr = cb_ptr; + ++AdapterInfo->FreeCBCount; + return ; +} + + +/** + TODO: Add function description + + @param ind TODO: add argument description + + @return TODO: add return values + +**/ +UINT16 +next ( + IN UINT16 ind + ) +{ + UINT16 Tmp; + + Tmp = (UINT16) (ind + 1); + if (Tmp >= (TX_BUFFER_COUNT << 1)) { + Tmp = 0; + } + + return Tmp; +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + + @return TODO: add return values + +**/ +UINT16 +CheckCBList ( + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + TxCB *Tmp_ptr; + UINT16 cnt; + + cnt = 0; + while (1) { + Tmp_ptr = AdapterInfo->FreeTxTailPtr->NextTCBVirtualLinkPtr; + if ((Tmp_ptr->cb_header.status & CMD_STATUS_MASK) != 0) { + // + // check if Q is full + // + if (next (AdapterInfo->xmit_done_tail) != AdapterInfo->xmit_done_head) { + ASSERT (AdapterInfo->xmit_done_tail < TX_BUFFER_COUNT << 1); + AdapterInfo->xmit_done[AdapterInfo->xmit_done_tail] = Tmp_ptr->free_data_ptr; + + UnMapIt ( + AdapterInfo, + Tmp_ptr->free_data_ptr, + Tmp_ptr->TBDArray[0].buf_len, + TO_DEVICE, + (UINT64) Tmp_ptr->TBDArray[0].phys_buf_addr + ); + + AdapterInfo->xmit_done_tail = next (AdapterInfo->xmit_done_tail); + } + + SetFreeCB (AdapterInfo, Tmp_ptr); + } else { + break; + } + } + + return cnt; +} +// +// Description : Initialize the RFD list list by linking each element together +// in a circular list. The simplified memory model is used. +// All data is in the RFD. The RFDs are linked together and the +// last one points back to the first one. When the current RFD +// is processed (frame received), its EL bit is set and the EL +// bit in the previous RXFD is cleared. +// Allocation done during INIT, this is making linked list. +// + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + + @return TODO: add return values + +**/ +UINT8 +SetupReceiveQueues ( + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + RxFD *rx_ptr; + RxFD *tail_ptr; + UINT16 Index; + + AdapterInfo->cur_rx_ind = 0; + rx_ptr = (&AdapterInfo->rx_ring[0]); + + for (Index = 0; Index < AdapterInfo->RxBufCnt; Index++) { + rx_ptr[Index].cb_header.status = 0; + rx_ptr[Index].cb_header.command = 0; + rx_ptr[Index].RFDSize = RX_BUFFER_SIZE; + rx_ptr[Index].ActualCount = 0; + // + // RBDs not used, simple memory model + // + rx_ptr[Index].rx_buf_addr = (UINT32) (-1); + + // + // RBDs not used, simple memory model + // + rx_ptr[Index].forwarded = FALSE; + + // + // don't use Tmp_ptr if it is beyond the last one + // + if (Index < AdapterInfo->RxBufCnt - 1) { + rx_ptr[Index].cb_header.link = (UINT32) AdapterInfo->rx_phy_addr + ((Index + 1) * sizeof (RxFD)); + } + } + + tail_ptr = (&AdapterInfo->rx_ring[AdapterInfo->RxBufCnt - 1]); + tail_ptr->cb_header.link = (UINT32) AdapterInfo->rx_phy_addr; + + // + // set the EL bit + // + tail_ptr->cb_header.command = 0xC000; + AdapterInfo->RFDTailPtr = tail_ptr; + return 0; +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + @param rx_index TODO: add argument description + + @return TODO: add return values + +**/ +VOID +Recycle_RFD ( + IN NIC_DATA_INSTANCE *AdapterInfo, + IN UINT16 rx_index + ) +{ + RxFD *rx_ptr; + RxFD *tail_ptr; + // + // change the EL bit and change the AdapterInfo->RxTailPtr + // rx_ptr is assumed to be the head of the Q + // AdapterInfo->rx_forwarded[rx_index] = FALSE; + // + rx_ptr = &AdapterInfo->rx_ring[rx_index]; + tail_ptr = AdapterInfo->RFDTailPtr; + // + // set el_bit and suspend bit + // + rx_ptr->cb_header.command = 0xc000; + rx_ptr->cb_header.status = 0; + rx_ptr->ActualCount = 0; + rx_ptr->forwarded = FALSE; + AdapterInfo->RFDTailPtr = rx_ptr; + // + // resetting the el_bit. + // + tail_ptr->cb_header.command = 0; + // + // check the receive unit, fix if there is any problem + // + return ; +} +// +// Serial EEPROM section. +// +// EEPROM_Ctrl bits. +// +#define EE_SHIFT_CLK 0x01 /* EEPROM shift clock. */ +#define EE_CS 0x02 /* EEPROM chip select. */ +#define EE_DI 0x04 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x01 +#define EE_WRITE_1 0x05 +#define EE_DO 0x08 /* EEPROM chip data out. */ +#define EE_ENB (0x4800 | EE_CS) + +// +// Delay between EEPROM clock transitions. +// This will actually work with no delay on 33Mhz PCI. +// +#define eeprom_delay(nanosec) DelayIt (AdapterInfo, nanosec); + +// +// The EEPROM commands include the alway-set leading bit. +// +#define EE_WRITE_CMD 5 // 101b +#define EE_READ_CMD 6 // 110b +#define EE_ERASE_CMD (7 << 6) + +VOID +shift_bits_out ( + IN NIC_DATA_INSTANCE *AdapterInfo, + IN UINT16 val, + IN UINT8 num_bits + ) +/*++ + +Routine Description: + + TODO: Add function description + +Arguments: + + AdapterInfo - TODO: add argument description + val - TODO: add argument description + num_bits - TODO: add argument description + +Returns: + + TODO: add return values + +--*/ +{ + INT32 Index; + UINT8 Tmp; + UINT32 EEAddr; + + EEAddr = AdapterInfo->ioaddr + SCBeeprom; + + for (Index = num_bits; Index >= 0; Index--) { + INT16 dataval; + + // + // will be 0 or 4 + // + dataval = (INT16) ((val & (1 << Index)) ? EE_DI : 0); + + // + // mask off the data_in bit + // + Tmp = (UINT8) (InByte (AdapterInfo, EEAddr) &~EE_DI); + Tmp = (UINT8) (Tmp | dataval); + OutByte (AdapterInfo, Tmp, EEAddr); + eeprom_delay (100); + // + // raise the eeprom clock + // + OutByte (AdapterInfo, (UINT8) (Tmp | EE_SHIFT_CLK), EEAddr); + eeprom_delay (150); + // + // lower the eeprom clock + // + OutByte (AdapterInfo, (UINT8) (Tmp &~EE_SHIFT_CLK), EEAddr); + eeprom_delay (150); + } +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + + @return TODO: add return values + +**/ +UINT16 +shift_bits_in ( + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + UINT8 Tmp; + INT32 Index; + UINT16 retval; + UINT32 EEAddr; + + EEAddr = AdapterInfo->ioaddr + SCBeeprom; + + retval = 0; + for (Index = 15; Index >= 0; Index--) { + // + // raise the clock + // + + // + // mask off the data_in bit + // + Tmp = InByte (AdapterInfo, EEAddr); + OutByte (AdapterInfo, (UINT8) (Tmp | EE_SHIFT_CLK), EEAddr); + eeprom_delay (100); + Tmp = InByte (AdapterInfo, EEAddr); + retval = (UINT16) ((retval << 1) | ((Tmp & EE_DO) ? 1 : 0)); + // + // lower the clock + // + OutByte (AdapterInfo, (UINT8) (Tmp &~EE_SHIFT_CLK), EEAddr); + eeprom_delay (100); + } + + return retval; +} + + +/** + This routine sets the EEPROM lockout bit to gain exclusive access to the + eeprom. the access bit is the most significant bit in the General Control + Register 2 in the SCB space. + + @param AdapterInfo Pointer to the NIC data structure + information which the UNDI driver is + layering on.. + + @retval TRUE if it got the access + @retval FALSE if it fails to get the exclusive access + +**/ +BOOLEAN +E100bSetEepromLockOut ( + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + UINTN wait; + UINT8 tmp; + + if ((AdapterInfo->DeviceID == D102_DEVICE_ID) || + (AdapterInfo->RevID >= D102_REVID)) { + + wait = 500; + + while (wait--) { + + tmp = InByte (AdapterInfo, AdapterInfo->ioaddr + SCBGenCtrl2); + tmp |= GCR2_EEPROM_ACCESS_SEMAPHORE; + OutByte (AdapterInfo, tmp, AdapterInfo->ioaddr + SCBGenCtrl2); + + DelayIt (AdapterInfo, 50); + tmp = InByte (AdapterInfo, AdapterInfo->ioaddr + SCBGenCtrl2); + + if (tmp & GCR2_EEPROM_ACCESS_SEMAPHORE) { + return TRUE; + } + } + + return FALSE; + } + + return TRUE; +} + + +/** + This routine Resets the EEPROM lockout bit to giveup access to the + eeprom. the access bit is the most significant bit in the General Control + Register 2 in the SCB space. + + @param AdapterInfo Pointer to the NIC data structure + information which the UNDI driver is + layering on.. + + @return None + +**/ +VOID +E100bReSetEepromLockOut ( + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + UINT8 tmp; + + if ((AdapterInfo->DeviceID == D102_DEVICE_ID) || + (AdapterInfo->RevID >= D102_REVID)) { + + tmp = InByte (AdapterInfo, AdapterInfo->ioaddr + SCBGenCtrl2); + tmp &= ~(GCR2_EEPROM_ACCESS_SEMAPHORE); + OutByte (AdapterInfo, tmp, AdapterInfo->ioaddr + SCBGenCtrl2); + + DelayIt (AdapterInfo, 50); + } +} + + +/** + Using the NIC data structure information, read the EEPROM to get a Word of data for the MAC address. + + @param AdapterInfo Pointer to the NIC data structure + information which the UNDI driver is + layering on.. + @param Location Word offset into the MAC address to read. + @param AddrLen Number of bits of address length. + + @retval RetVal The word read from the EEPROM. + +**/ +UINT16 +E100bReadEeprom ( + IN NIC_DATA_INSTANCE *AdapterInfo, + IN INT32 Location, + IN UINT8 AddrLen + ) +{ + UINT16 RetVal; + UINT8 Tmp; + + UINT32 EEAddr; + UINT16 ReadCmd; + + EEAddr = AdapterInfo->ioaddr + SCBeeprom; + ReadCmd = (UINT16) (Location | (EE_READ_CMD << AddrLen)); + + RetVal = 0; + + // + // get exclusive access to the eeprom first! + // + E100bSetEepromLockOut (AdapterInfo); + + // + // eeprom control reg bits: x,x,x,x,DO,DI,CS,SK + // to write the opcode+data value out one bit at a time in DI starting at msb + // and then out a 1 to sk, wait, out 0 to SK and wait + // repeat this for all the bits to be written + // + + // + // 11110010b + // + Tmp = (UINT8) (InByte (AdapterInfo, EEAddr) & 0xF2); + OutByte (AdapterInfo, (UINT8) (Tmp | EE_CS), EEAddr); + + // + // 3 for the read opcode 110b + // + shift_bits_out (AdapterInfo, ReadCmd, (UINT8) (3 + AddrLen)); + + // + // read the eeprom word one bit at a time + // + RetVal = shift_bits_in (AdapterInfo); + + // + // Terminate the EEPROM access and leave eeprom in a clean state. + // + Tmp = InByte (AdapterInfo, EEAddr); + Tmp &= ~(EE_CS | EE_DI); + OutByte (AdapterInfo, Tmp, EEAddr); + + // + // raise the clock and lower the eeprom shift clock + // + OutByte (AdapterInfo, (UINT8) (Tmp | EE_SHIFT_CLK), EEAddr); + eeprom_delay (100); + + OutByte (AdapterInfo, (UINT8) (Tmp &~EE_SHIFT_CLK), EEAddr); + eeprom_delay (100); + + // + // giveup access to the eeprom + // + E100bReSetEepromLockOut (AdapterInfo); + + return RetVal; +} + + +/** + Using the NIC data structure information, read the EEPROM to determine how many bits of address length + this EEPROM is in Words. + + @param AdapterInfo Pointer to the NIC data structure + information which the UNDI driver is + layering on.. + + @retval RetVal The word read from the EEPROM. + +**/ +UINT8 +E100bGetEepromAddrLen ( + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + UINT8 Tmp; + UINT8 AddrLen; + UINT32 EEAddr; + // + // assume 64word eeprom (so,6 bits of address_length) + // + UINT16 ReadCmd; + + EEAddr = AdapterInfo->ioaddr + SCBeeprom; + ReadCmd = (EE_READ_CMD << 6); + + // + // get exclusive access to the eeprom first! + // + E100bSetEepromLockOut (AdapterInfo); + + // + // address we are trying to read is 0 + // eeprom control reg bits: x,x,x,x,DO,,DI,,CS,SK + // to write the opcode+data value out one bit at a time in DI starting at msb + // and then out a 1 to sk, wait, out 0 to SK and wait + // repeat this for all the bits to be written + // + Tmp = (UINT8) (InByte (AdapterInfo, EEAddr) & 0xF2); + + // + // enable eeprom access + // + OutByte (AdapterInfo, (UINT8) (Tmp | EE_CS), EEAddr); + + // + // 3 for opcode, 6 for the default address len + // + shift_bits_out (AdapterInfo, ReadCmd, (UINT8) (3 + 6)); + + // + // (in case of a 64 word eeprom). + // read the "dummy zero" from EE_DO to say that the address we wrote + // (six 0s) is accepted, write more zeros (until 8) to get a "dummy zero" + // + + // + // assume the smallest + // + AddrLen = 6; + Tmp = InByte (AdapterInfo, EEAddr); + while ((AddrLen < 8) && ((Tmp & EE_DO) != 0)) { + OutByte (AdapterInfo, (UINT8) (Tmp &~EE_DI), EEAddr); + eeprom_delay (100); + + // + // raise the eeprom clock + // + OutByte (AdapterInfo, (UINT8) (Tmp | EE_SHIFT_CLK), EEAddr); + eeprom_delay (150); + + // + // lower the eeprom clock + // + OutByte (AdapterInfo, (UINT8) (Tmp &~EE_SHIFT_CLK), EEAddr); + eeprom_delay (150); + Tmp = InByte (AdapterInfo, EEAddr); + AddrLen++; + } + + // + // read the eeprom word, even though we don't need this + // + shift_bits_in (AdapterInfo); + + // + // Terminate the EEPROM access. + // + Tmp = InByte (AdapterInfo, EEAddr); + Tmp &= ~(EE_CS | EE_DI); + OutByte (AdapterInfo, Tmp, EEAddr); + + // + // raise the clock and lower the eeprom shift clock + // + OutByte (AdapterInfo, (UINT8) (Tmp | EE_SHIFT_CLK), EEAddr); + eeprom_delay (100); + + OutByte (AdapterInfo, (UINT8) (Tmp &~EE_SHIFT_CLK), EEAddr); + eeprom_delay (100); + + // + // giveup access to the eeprom! + // + E100bReSetEepromLockOut (AdapterInfo); + + return AddrLen; +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + @param DBaddr TODO: add argument description + @param DBsize TODO: add argument description + + @return TODO: add return values + +**/ +UINTN +E100bStatistics ( + NIC_DATA_INSTANCE *AdapterInfo, + UINT64 DBaddr, + UINT16 DBsize + ) +{ + PXE_DB_STATISTICS db; + // + // wait upto one second (each wait is 100 micro s) + // + UINT32 Wait; + Wait = 10000; + wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd); + + // + // Clear statistics done marker. + // + AdapterInfo->statistics->done_marker = 0; + + // + // Issue statistics dump (or dump w/ reset) command. + // + OutByte ( + AdapterInfo, + (UINT8) (DBsize ? CU_SHOWSTATS : CU_DUMPSTATS), + (UINT32) (AdapterInfo->ioaddr + SCBCmd) + ); + + // + // Wait for command to complete. + // + // zero the db here just to chew up a little more time. + // + + ZeroMem ((VOID *) &db, sizeof db); + + while (Wait != 0) { + // + // Wait a bit before checking. + // + + DelayIt (AdapterInfo, 100); + + // + // Look for done marker at end of statistics. + // + + switch (AdapterInfo->statistics->done_marker) { + case 0xA005: + case 0xA007: + break; + + default: + Wait--; + continue; + } + + // + // if we did not "continue" from the above switch, we are done, + // + break; + } + + // + // If this is a reset, we are out of here! + // + if (DBsize == 0) { + return PXE_STATCODE_SUCCESS; + } + + // + // Convert NIC statistics counter format to EFI/UNDI + // specification statistics counter format. + // + + // + // 54 3210 fedc ba98 7654 3210 + // db.Supported = 01 0000 0100 1101 0001 0111; + // + db.Supported = 0x104D17; + + // + // Statistics from the NIC + // + + db.Data[0x01] = AdapterInfo->statistics->rx_good_frames; + + db.Data[0x02] = AdapterInfo->statistics->rx_runt_errs; + + db.Data[0x08] = AdapterInfo->statistics->rx_crc_errs + + AdapterInfo->statistics->rx_align_errs; + + db.Data[0x04] = db.Data[0x02] + + db.Data[0x08] + + AdapterInfo->statistics->rx_resource_errs + + AdapterInfo->statistics->rx_overrun_errs; + + db.Data[0x00] = db.Data[0x01] + db.Data[0x04]; + + db.Data[0x0B] = AdapterInfo->statistics->tx_good_frames; + + db.Data[0x0E] = AdapterInfo->statistics->tx_coll16_errs + + AdapterInfo->statistics->tx_late_colls + + AdapterInfo->statistics->tx_underruns + + AdapterInfo->statistics->tx_one_colls + + AdapterInfo->statistics->tx_multi_colls; + + db.Data[0x14] = AdapterInfo->statistics->tx_total_colls; + + db.Data[0x0A] = db.Data[0x0B] + + db.Data[0x0E] + + AdapterInfo->statistics->tx_lost_carrier; + + if (DBsize > sizeof db) { + DBsize = (UINT16) sizeof (db); + } + + CopyMem ((VOID *) (UINTN) DBaddr, (VOID *) &db, (UINTN) DBsize); + + return PXE_STATCODE_SUCCESS; +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + @param OpFlags TODO: add argument description + + @return TODO: add return values + +**/ +UINTN +E100bReset ( + IN NIC_DATA_INSTANCE *AdapterInfo, + IN INT32 OpFlags + ) +{ + + UINT16 save_filter; + // + // disable the interrupts + // + OutWord (AdapterInfo, INT_MASK, AdapterInfo->ioaddr + SCBCmd); + + // + // wait for the tx queue to complete + // + CheckCBList (AdapterInfo); + + XmitWaitForCompletion (AdapterInfo); + + if (AdapterInfo->Receive_Started) { + StopRU (AdapterInfo); + } + + InitializeChip (AdapterInfo); + + // + // check the opflags and restart receive filters + // + if ((OpFlags & PXE_OPFLAGS_RESET_DISABLE_FILTERS) == 0) { + + save_filter = AdapterInfo->Rx_Filter; + // + // if we give the filter same as Rx_Filter, + // this routine will not set mcast list (it thinks there is no change) + // to force it, we will reset that flag in the Rx_Filter + // + AdapterInfo->Rx_Filter &= (~PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST); + E100bSetfilter (AdapterInfo, save_filter, (UINT64) 0, (UINT32) 0); + } + + if ((OpFlags & PXE_OPFLAGS_RESET_DISABLE_INTERRUPTS) != 0) { + // + // disable the interrupts + // + AdapterInfo->int_mask = 0; + } + // + // else leave the interrupt in the pre-set state!!! + // + E100bSetInterruptState (AdapterInfo); + + return 0; +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + + @return TODO: add return values + +**/ +UINTN +E100bShutdown ( + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + // + // disable the interrupts + // + OutWord (AdapterInfo, INT_MASK, AdapterInfo->ioaddr + SCBCmd); + + // + // stop the receive unit + // + if (AdapterInfo->Receive_Started) { + StopRU (AdapterInfo); + } + + // + // wait for the tx queue to complete + // + CheckCBList (AdapterInfo); + if (AdapterInfo->FreeCBCount != AdapterInfo->TxBufCnt) { + wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd); + } + + // + // we do not want to reset the phy, it takes a long time to renegotiate the + // link after that (3-4 seconds) + // + InitializeChip (AdapterInfo); + SelectiveReset (AdapterInfo); + return 0; +} + + +/** + This routine will write a value to the specified MII register + of an external MDI compliant device (e.g. PHY 100). The command will + execute in polled mode. + + @param AdapterInfo pointer to the structure that contains + the NIC's context. + @param RegAddress The MII register that we are writing to + @param PhyAddress The MDI address of the Phy component. + @param DataValue The value that we are writing to the MII + register. + + @return nothing + +**/ +VOID +MdiWrite ( + IN NIC_DATA_INSTANCE *AdapterInfo, + IN UINT8 RegAddress, + IN UINT8 PhyAddress, + IN UINT16 DataValue + ) +{ + UINT32 WriteCommand; + + WriteCommand = ((UINT32) DataValue) | + ((UINT32)(RegAddress << 16)) | + ((UINT32)(PhyAddress << 21)) | + ((UINT32)(MDI_WRITE << 26)); + + // + // Issue the write command to the MDI control register. + // + OutLong (AdapterInfo, WriteCommand, AdapterInfo->ioaddr + SCBCtrlMDI); + + // + // wait 20usec before checking status + // + DelayIt (AdapterInfo, 20); + + // + // poll for the mdi write to complete + while ((InLong (AdapterInfo, AdapterInfo->ioaddr + SCBCtrlMDI) & + MDI_PHY_READY) == 0){ + DelayIt (AdapterInfo, 20); + } +} + + +/** + This routine will read a value from the specified MII register + of an external MDI compliant device (e.g. PHY 100), and return + it to the calling routine. The command will execute in polled mode. + + @param AdapterInfo pointer to the structure that contains + the NIC's context. + @param RegAddress The MII register that we are reading from + @param PhyAddress The MDI address of the Phy component. + @param DataValue pointer to the value that we read from + the MII register. + + +**/ +VOID +MdiRead ( + IN NIC_DATA_INSTANCE *AdapterInfo, + IN UINT8 RegAddress, + IN UINT8 PhyAddress, + IN OUT UINT16 *DataValue + ) +{ + UINT32 ReadCommand; + + ReadCommand = ((UINT32) (RegAddress << 16)) | + ((UINT32) (PhyAddress << 21)) | + ((UINT32) (MDI_READ << 26)); + + // + // Issue the read command to the MDI control register. + // + OutLong (AdapterInfo, ReadCommand, AdapterInfo->ioaddr + SCBCtrlMDI); + + // + // wait 20usec before checking status + // + DelayIt (AdapterInfo, 20); + + // + // poll for the mdi read to complete + // + while ((InLong (AdapterInfo, AdapterInfo->ioaddr + SCBCtrlMDI) & + MDI_PHY_READY) == 0) { + DelayIt (AdapterInfo, 20); + + } + + *DataValue = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBCtrlMDI); +} + + +/** + This routine will reset the PHY that the adapter is currently + configured to use. + + @param AdapterInfo pointer to the structure that contains + the NIC's context. + + +**/ +VOID +PhyReset ( + NIC_DATA_INSTANCE *AdapterInfo + ) +{ + UINT16 MdiControlReg; + + MdiControlReg = (MDI_CR_AUTO_SELECT | + MDI_CR_RESTART_AUTO_NEG | + MDI_CR_RESET); + + // + // Write the MDI control register with our new Phy configuration + // + MdiWrite ( + AdapterInfo, + MDI_CONTROL_REG, + AdapterInfo->PhyAddress, + MdiControlReg + ); + + return ; +} + + +/** + This routine will detect what phy we are using, set the line + speed, FDX or HDX, and configure the phy if necessary. + The following combinations are supported: + - TX or T4 PHY alone at PHY address 1 + - T4 or TX PHY at address 1 and MII PHY at address 0 + - 82503 alone (10Base-T mode, no full duplex support) + - 82503 and MII PHY (TX or T4) at address 0 + The sequence / priority of detection is as follows: + - PHY 1 with cable termination + - PHY 0 with cable termination + - PHY 1 (if found) without cable termination + - 503 interface + Additionally auto-negotiation capable (NWAY) and parallel + detection PHYs are supported. The flow-chart is described in + the 82557 software writer's manual. + NOTE: 1. All PHY MDI registers are read in polled mode. + 2. The routines assume that the 82557 has been RESET and we have + obtained the virtual memory address of the CSR. + 3. PhyDetect will not RESET the PHY. + 4. If FORCEFDX is set, SPEED should also be set. The driver will + check the values for inconsistency with the detected PHY + technology. + 5. PHY 1 (the PHY on the adapter) may have an address in the range + 1 through 31 inclusive. The driver will accept addresses in + this range. + 6. Driver ignores FORCEFDX and SPEED overrides if a 503 interface + is detected. + + @param AdapterInfo pointer to the structure that contains + the NIC's context. + + @retval TRUE If a Phy was detected, and configured + correctly. + @retval FALSE If a valid phy could not be detected and + configured. + +**/ +BOOLEAN +PhyDetect ( + NIC_DATA_INSTANCE *AdapterInfo + ) +{ + UINT16 *eedata; + UINT16 MdiControlReg; + UINT16 MdiStatusReg; + BOOLEAN FoundPhy1; + UINT8 ReNegotiateTime; + + eedata = (UINT16 *) (&AdapterInfo->NVData[0]); + + FoundPhy1 = FALSE; + ReNegotiateTime = 35; + // + // EEPROM word [6] contains the Primary PHY record in which the least 3 bits + // indicate the PHY address + // and word [7] contains the secondary PHY record + // + AdapterInfo->PhyRecord[0] = eedata[6]; + AdapterInfo->PhyRecord[1] = eedata[7]; + AdapterInfo->PhyAddress = (UINT8) (AdapterInfo->PhyRecord[0] & 7); + + // + // Check for a phy address over-ride of 32 which indicates force use of 82503 + // not detecting the link in this case + // + if (AdapterInfo->PhyAddress == 32) { + // + // 503 interface over-ride + // Record the current speed and duplex. We will be in half duplex + // mode unless the user used the force full duplex over-ride. + // + AdapterInfo->LinkSpeed = 10; + return (TRUE); + } + + // + // If the Phy Address is between 1-31 then we must first look for phy 1, + // at that address. + // + if ((AdapterInfo->PhyAddress > 0) && (AdapterInfo->PhyAddress < 32)) { + + // + // Read the MDI control and status registers at phy 1 + // and check if we found a valid phy + // + MdiRead ( + AdapterInfo, + MDI_CONTROL_REG, + AdapterInfo->PhyAddress, + &MdiControlReg + ); + + MdiRead ( + AdapterInfo, + MDI_STATUS_REG, + AdapterInfo->PhyAddress, + &MdiStatusReg + ); + + if (!((MdiControlReg == 0xffff) || + ((MdiStatusReg == 0) && (MdiControlReg == 0)))) { + + // + // we have a valid phy1 + // Read the status register again because of sticky bits + // + FoundPhy1 = TRUE; + MdiRead ( + AdapterInfo, + MDI_STATUS_REG, + AdapterInfo->PhyAddress, + &MdiStatusReg + ); + + // + // If there is a valid link then use this Phy. + // + if (MdiStatusReg & MDI_SR_LINK_STATUS) { + return (SetupPhy(AdapterInfo)); + } + } + } + + // + // Next try to detect a PHY at address 0x00 because there was no Phy 1, + // or Phy 1 didn't have link, or we had a phy 0 over-ride + // + + // + // Read the MDI control and status registers at phy 0 + // + MdiRead (AdapterInfo, MDI_CONTROL_REG, 0, &MdiControlReg); + MdiRead (AdapterInfo, MDI_STATUS_REG, 0, &MdiStatusReg); + + // + // check if we found a valid phy 0 + // + if (((MdiControlReg == 0xffff) || + ((MdiStatusReg == 0) && (MdiControlReg == 0)))) { + + // + // we don't have a valid phy at address 0 + // if phy address was forced to 0, then error out because we + // didn't find a phy at that address + // + if (AdapterInfo->PhyAddress == 0x0000) { + return (FALSE); + } else { + // + // at this point phy1 does not have link and there is no phy 0 at all + // if we are forced to detect the cable, error out here! + // + if (AdapterInfo->CableDetect != 0) { + return FALSE; + + } + + if (FoundPhy1) { + // + // no phy 0, but there is a phy 1 (no link I guess), so use phy 1 + // + return SetupPhy (AdapterInfo); + } else { + // + // didn't find phy 0 or phy 1, so assume a 503 interface + // + AdapterInfo->PhyAddress = 32; + + // + // Record the current speed and duplex. We'll be in half duplex + // mode unless the user used the force full duplex over-ride. + // + AdapterInfo->LinkSpeed = 10; + return (TRUE); + } + } + } else { + // + // We have a valid phy at address 0. If phy 0 has a link then we use + // phy 0. If Phy 0 doesn't have a link then we use Phy 1 (no link) + // if phy 1 is present, or phy 0 if phy 1 is not present + // If phy 1 was present, then we must isolate phy 1 before we enable + // phy 0 to see if Phy 0 has a link. + // + if (FoundPhy1) { + // + // isolate phy 1 + // + MdiWrite ( + AdapterInfo, + MDI_CONTROL_REG, + AdapterInfo->PhyAddress, + MDI_CR_ISOLATE + ); + + // + // wait 100 microseconds for the phy to isolate. + // + DelayIt (AdapterInfo, 100); + } + + // + // Since this Phy is at address 0, we must enable it. So clear + // the isolate bit, and set the auto-speed select bit + // + MdiWrite ( + AdapterInfo, + MDI_CONTROL_REG, + 0, + MDI_CR_AUTO_SELECT + ); + + // + // wait 100 microseconds for the phy to be enabled. + // + DelayIt (AdapterInfo, 100); + + // + // restart the auto-negotion process + // + MdiWrite ( + AdapterInfo, + MDI_CONTROL_REG, + 0, + MDI_CR_RESTART_AUTO_NEG | MDI_CR_AUTO_SELECT + ); + + // + // wait no more than 3.5 seconds for auto-negotiation to complete + // + while (ReNegotiateTime) { + // + // Read the status register twice because of sticky bits + // + MdiRead (AdapterInfo, MDI_STATUS_REG, 0, &MdiStatusReg); + MdiRead (AdapterInfo, MDI_STATUS_REG, 0, &MdiStatusReg); + + if (MdiStatusReg & MDI_SR_AUTO_NEG_COMPLETE) { + break; + } + + DelayIt (AdapterInfo, 100); + ReNegotiateTime--; + } + + // + // Read the status register again because of sticky bits + // + MdiRead (AdapterInfo, MDI_STATUS_REG, 0, &MdiStatusReg); + + // + // If the link was not set + // + if ((MdiStatusReg & MDI_SR_LINK_STATUS) == 0) { + // + // PHY1 does not have a link and phy 0 does not have a link + // do not proceed if we need to detect the link! + // + if (AdapterInfo->CableDetect != 0) { + return FALSE; + } + + // + // the link wasn't set, so use phy 1 if phy 1 was present + // + if (FoundPhy1) { + // + // isolate phy 0 + // + MdiWrite (AdapterInfo, MDI_CONTROL_REG, 0, MDI_CR_ISOLATE); + + // + // wait 100 microseconds for the phy to isolate. + // + DelayIt (AdapterInfo, 100); + + // + // Now re-enable PHY 1 + // + MdiWrite ( + AdapterInfo, + MDI_CONTROL_REG, + AdapterInfo->PhyAddress, + MDI_CR_AUTO_SELECT + ); + + // + // wait 100 microseconds for the phy to be enabled + // + DelayIt (AdapterInfo, 100); + + // + // restart the auto-negotion process + // + MdiWrite ( + AdapterInfo, + MDI_CONTROL_REG, + AdapterInfo->PhyAddress, + MDI_CR_RESTART_AUTO_NEG | MDI_CR_AUTO_SELECT + ); + + // + // Don't wait for it to complete (we didn't have link earlier) + // + return (SetupPhy (AdapterInfo)); + } + } + + // + // Definitely using Phy 0 + // + AdapterInfo->PhyAddress = 0; + return (SetupPhy(AdapterInfo)); + } +} + + +/** + This routine will setup phy 1 or phy 0 so that it is configured + to match a speed and duplex over-ride option. If speed or + duplex mode is not explicitly specified in the registry, the + driver will skip the speed and duplex over-ride code, and + assume the adapter is automatically setting the line speed, and + the duplex mode. At the end of this routine, any truly Phy + specific code will be executed (each Phy has its own quirks, + and some require that certain special bits are set). + NOTE: The driver assumes that SPEED and FORCEFDX are specified at the + same time. If FORCEDPX is set without speed being set, the driver + will encouter a fatal error and log a message into the event viewer. + + @param AdapterInfo pointer to the structure that contains + the NIC's context. + + @retval TRUE If the phy could be configured correctly + @retval FALSE If the phy couldn't be configured + correctly, because an unsupported + over-ride option was used + +**/ +BOOLEAN +SetupPhy ( + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + UINT16 MdiControlReg; + UINT16 MdiStatusReg; + UINT16 MdiIdLowReg; + UINT16 MdiIdHighReg; + UINT16 MdiMiscReg; + UINT32 PhyId; + BOOLEAN ForcePhySetting; + + ForcePhySetting = FALSE; + + // + // If we are NOT forcing a setting for line speed or full duplex, then + // we won't force a link setting, and we'll jump down to the phy + // specific code. + // + if (((AdapterInfo->LinkSpeedReq) || (AdapterInfo->DuplexReq))) { + // + // Find out what kind of technology this Phy is capable of. + // + MdiRead ( + AdapterInfo, + MDI_STATUS_REG, + AdapterInfo->PhyAddress, + &MdiStatusReg + ); + + // + // Read the MDI control register at our phy + // + MdiRead ( + AdapterInfo, + MDI_CONTROL_REG, + AdapterInfo->PhyAddress, + &MdiControlReg + ); + + // + // Now check the validity of our forced option. If the force option is + // valid, then force the setting. If the force option is not valid, + // we'll set a flag indicating that we should error out. + // + + // + // If speed is forced to 10mb + // + if (AdapterInfo->LinkSpeedReq == 10) { + // + // If half duplex is forced + // + if ((AdapterInfo->DuplexReq & PXE_FORCE_HALF_DUPLEX) != 0) { + if (MdiStatusReg & MDI_SR_10T_HALF_DPX) { + + MdiControlReg &= ~(MDI_CR_10_100 | MDI_CR_AUTO_SELECT | MDI_CR_FULL_HALF); + ForcePhySetting = TRUE; + } + } else if ((AdapterInfo->DuplexReq & PXE_FORCE_FULL_DUPLEX) != 0) { + + // + // If full duplex is forced + // + if (MdiStatusReg & MDI_SR_10T_FULL_DPX) { + + MdiControlReg &= ~(MDI_CR_10_100 | MDI_CR_AUTO_SELECT); + MdiControlReg |= MDI_CR_FULL_HALF; + ForcePhySetting = TRUE; + } + } else { + // + // If auto duplex (we actually set phy to 1/2) + // + if (MdiStatusReg & (MDI_SR_10T_FULL_DPX | MDI_SR_10T_HALF_DPX)) { + + MdiControlReg &= ~(MDI_CR_10_100 | MDI_CR_AUTO_SELECT | MDI_CR_FULL_HALF); + ForcePhySetting = TRUE; + } + } + } + + // + // If speed is forced to 100mb + // + else if (AdapterInfo->LinkSpeedReq == 100) { + // + // If half duplex is forced + // + if ((AdapterInfo->DuplexReq & PXE_FORCE_HALF_DUPLEX) != 0) { + if (MdiStatusReg & (MDI_SR_TX_HALF_DPX | MDI_SR_T4_CAPABLE)) { + + MdiControlReg &= ~(MDI_CR_AUTO_SELECT | MDI_CR_FULL_HALF); + MdiControlReg |= MDI_CR_10_100; + ForcePhySetting = TRUE; + } + } else if ((AdapterInfo->DuplexReq & PXE_FORCE_FULL_DUPLEX) != 0) { + // + // If full duplex is forced + // + if (MdiStatusReg & MDI_SR_TX_FULL_DPX) { + MdiControlReg &= ~MDI_CR_AUTO_SELECT; + MdiControlReg |= (MDI_CR_10_100 | MDI_CR_FULL_HALF); + ForcePhySetting = TRUE; + } + } else { + // + // If auto duplex (we set phy to 1/2) + // + if (MdiStatusReg & (MDI_SR_TX_HALF_DPX | MDI_SR_T4_CAPABLE)) { + + MdiControlReg &= ~(MDI_CR_AUTO_SELECT | MDI_CR_FULL_HALF); + MdiControlReg |= MDI_CR_10_100; + ForcePhySetting = TRUE; + } + } + } + + if (!ForcePhySetting) { + return (FALSE); + } + + // + // Write the MDI control register with our new Phy configuration + // + MdiWrite ( + AdapterInfo, + MDI_CONTROL_REG, + AdapterInfo->PhyAddress, + MdiControlReg + ); + + // + // wait 100 milliseconds for auto-negotiation to complete + // + DelayIt (AdapterInfo, 100); + } + + // + // Find out specifically what Phy this is. We do this because for certain + // phys there are specific bits that must be set so that the phy and the + // 82557 work together properly. + // + + MdiRead ( + AdapterInfo, + PHY_ID_REG_1, + AdapterInfo->PhyAddress, + &MdiIdLowReg + ); + MdiRead ( + AdapterInfo, + PHY_ID_REG_2, + AdapterInfo->PhyAddress, + &MdiIdHighReg + ); + + PhyId = ((UINT32) MdiIdLowReg | ((UINT32) MdiIdHighReg << 16)); + + // + // And out the revsion field of the Phy ID so that we'll be able to detect + // future revs of the same Phy. + // + PhyId &= PHY_MODEL_REV_ID_MASK; + + // + // Handle the National TX + // + if (PhyId == PHY_NSC_TX) { + + MdiRead ( + AdapterInfo, + NSC_CONG_CONTROL_REG, + AdapterInfo->PhyAddress, + &MdiMiscReg + ); + + MdiMiscReg |= (NSC_TX_CONG_TXREADY | NSC_TX_CONG_F_CONNECT); + + MdiWrite ( + AdapterInfo, + NSC_CONG_CONTROL_REG, + AdapterInfo->PhyAddress, + MdiMiscReg + ); + } + + FindPhySpeedAndDpx (AdapterInfo, PhyId); + + // + // We put a hardware fix on to our adapters to work-around the PHY_100 errata + // described below. The following code is only compiled in, if we wanted + // to attempt a software workaround to the PHY_100 A/B step problem. + // + + return (TRUE); +} + + +/** + This routine will figure out what line speed and duplex mode + the PHY is currently using. + + @param AdapterInfo pointer to the structure that contains + the NIC's context. + @param PhyId The ID of the PHY in question. + + @return NOTHING + +**/ +VOID +FindPhySpeedAndDpx ( + IN NIC_DATA_INSTANCE *AdapterInfo, + IN UINT32 PhyId + ) +{ + UINT16 MdiStatusReg; + UINT16 MdiMiscReg; + UINT16 MdiOwnAdReg; + UINT16 MdiLinkPartnerAdReg; + + // + // If there was a speed and/or duplex override, then set our current + // value accordingly + // + AdapterInfo->LinkSpeed = AdapterInfo->LinkSpeedReq; + AdapterInfo->Duplex = (UINT8) ((AdapterInfo->DuplexReq & PXE_FORCE_FULL_DUPLEX) ? + FULL_DUPLEX : HALF_DUPLEX); + + // + // If speed and duplex were forced, then we know our current settings, so + // we'll just return. Otherwise, we'll need to figure out what NWAY set + // us to. + // + if (AdapterInfo->LinkSpeed && AdapterInfo->Duplex) { + return ; + + } + // + // If we didn't have a valid link, then we'll assume that our current + // speed is 10mb half-duplex. + // + + // + // Read the status register twice because of sticky bits + // + MdiRead ( + AdapterInfo, + MDI_STATUS_REG, + AdapterInfo->PhyAddress, + &MdiStatusReg + ); + MdiRead ( + AdapterInfo, + MDI_STATUS_REG, + AdapterInfo->PhyAddress, + &MdiStatusReg + ); + + // + // If there wasn't a valid link then use default speed & duplex + // + if (!(MdiStatusReg & MDI_SR_LINK_STATUS)) { + + AdapterInfo->LinkSpeed = 10; + AdapterInfo->Duplex = HALF_DUPLEX; + return ; + } + + // + // If this is an Intel PHY (a T4 PHY_100 or a TX PHY_TX), then read bits + // 1 and 0 of extended register 0, to get the current speed and duplex + // settings. + // + if ((PhyId == PHY_100_A) || (PhyId == PHY_100_C) || (PhyId == PHY_TX_ID)) { + // + // Read extended register 0 + // + MdiRead ( + AdapterInfo, + EXTENDED_REG_0, + AdapterInfo->PhyAddress, + &MdiMiscReg + ); + + // + // Get current speed setting + // + if (MdiMiscReg & PHY_100_ER0_SPEED_INDIC) { + AdapterInfo->LinkSpeed = 100; + } else { + AdapterInfo->LinkSpeed = 10; + } + + // + // Get current duplex setting -- if bit is set then FDX is enabled + // + if (MdiMiscReg & PHY_100_ER0_FDX_INDIC) { + AdapterInfo->Duplex = FULL_DUPLEX; + } else { + AdapterInfo->Duplex = HALF_DUPLEX; + } + + return ; + } + // + // Read our link partner's advertisement register + // + MdiRead ( + AdapterInfo, + AUTO_NEG_LINK_PARTNER_REG, + AdapterInfo->PhyAddress, + &MdiLinkPartnerAdReg + ); + + // + // See if Auto-Negotiation was complete (bit 5, reg 1) + // + MdiRead ( + AdapterInfo, + MDI_STATUS_REG, + AdapterInfo->PhyAddress, + &MdiStatusReg + ); + + // + // If a True NWAY connection was made, then we can detect speed/duplex by + // ANDing our adapter's advertised abilities with our link partner's + // advertised ablilities, and then assuming that the highest common + // denominator was chosed by NWAY. + // + if ((MdiLinkPartnerAdReg & NWAY_LP_ABILITY) && + (MdiStatusReg & MDI_SR_AUTO_NEG_COMPLETE)) { + + // + // Read our advertisement register + // + MdiRead ( + AdapterInfo, + AUTO_NEG_ADVERTISE_REG, + AdapterInfo->PhyAddress, + &MdiOwnAdReg + ); + + // + // AND the two advertisement registers together, and get rid of any + // extraneous bits. + // + MdiOwnAdReg = (UINT16) (MdiOwnAdReg & (MdiLinkPartnerAdReg & NWAY_LP_ABILITY)); + + // + // Get speed setting + // + if (MdiOwnAdReg & (NWAY_AD_TX_HALF_DPX | NWAY_AD_TX_FULL_DPX | NWAY_AD_T4_CAPABLE)) { + AdapterInfo->LinkSpeed = 100; + } else { + AdapterInfo->LinkSpeed = 10; + } + + // + // Get duplex setting -- use priority resolution algorithm + // + if (MdiOwnAdReg & (NWAY_AD_T4_CAPABLE)) { + AdapterInfo->Duplex = HALF_DUPLEX; + return ; + } else if (MdiOwnAdReg & (NWAY_AD_TX_FULL_DPX)) { + AdapterInfo->Duplex = FULL_DUPLEX; + return ; + } else if (MdiOwnAdReg & (NWAY_AD_TX_HALF_DPX)) { + AdapterInfo->Duplex = HALF_DUPLEX; + return ; + } else if (MdiOwnAdReg & (NWAY_AD_10T_FULL_DPX)) { + AdapterInfo->Duplex = FULL_DUPLEX; + return ; + } else { + AdapterInfo->Duplex = HALF_DUPLEX; + return ; + } + } + + // + // If we are connected to a dumb (non-NWAY) repeater or hub, and the line + // speed was determined automatically by parallel detection, then we have + // no way of knowing exactly what speed the PHY is set to unless that PHY + // has a propietary register which indicates speed in this situation. The + // NSC TX PHY does have such a register. Also, since NWAY didn't establish + // the connection, the duplex setting should HALF duplex. + // + AdapterInfo->Duplex = HALF_DUPLEX; + + if (PhyId == PHY_NSC_TX) { + // + // Read register 25 to get the SPEED_10 bit + // + MdiRead ( + AdapterInfo, + NSC_SPEED_IND_REG, + AdapterInfo->PhyAddress, + &MdiMiscReg + ); + + // + // If bit 6 was set then we're at 10mb + // + if (MdiMiscReg & NSC_TX_SPD_INDC_SPEED) { + AdapterInfo->LinkSpeed = 10; + } else { + AdapterInfo->LinkSpeed = 100; + } + } + + // + // If we don't know what line speed we are set at, then we'll default to + // 10mbs + // + else { + AdapterInfo->LinkSpeed = 10; + } +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + + @return TODO: add return values + +**/ +VOID +XmitWaitForCompletion ( + NIC_DATA_INSTANCE *AdapterInfo + ) +{ + TxCB *TxPtr; + + if (AdapterInfo->FreeCBCount == AdapterInfo->TxBufCnt) { + return ; + } + + // + // used xmit cb list starts right after the free tail (ends before the + // free head ptr) + // + TxPtr = AdapterInfo->FreeTxTailPtr->NextTCBVirtualLinkPtr; + while (TxPtr != AdapterInfo->FreeTxHeadPtr) { + CommandWaitForCompletion (TxPtr, AdapterInfo); + SetFreeCB (AdapterInfo, TxPtr); + TxPtr = TxPtr->NextTCBVirtualLinkPtr; + } +} + + +/** + TODO: Add function description + + @param cmd_ptr TODO: add argument description + @param AdapterInfo TODO: add argument description + + @return TODO: add return values + +**/ +INT8 +CommandWaitForCompletion ( + TxCB *cmd_ptr, + NIC_DATA_INSTANCE *AdapterInfo + ) +{ + INT16 wait; + wait = 5000; + while ((cmd_ptr->cb_header.status == 0) && (--wait > 0)) { + DelayIt (AdapterInfo, 10); + } + + if (cmd_ptr->cb_header.status == 0) { + return -1; + } + + return 0; +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + + @return TODO: add return values + +**/ +INT8 +SoftwareReset ( + NIC_DATA_INSTANCE *AdapterInfo + ) +{ + UINT8 tco_stat; + UINT16 wait; + + tco_stat = 0; + + // + // Reset the chip: stop Tx and Rx processes and clear counters. + // This takes less than 10usec and will easily finish before the next + // action. + // + + OutLong (AdapterInfo, PORT_RESET, AdapterInfo->ioaddr + SCBPort); + // + // wait for 5 milli seconds here! + // + DelayIt (AdapterInfo, 5000); + // + // TCO Errata work around for 559s only + // ----------------------------------------------------------------------------------- + // TCO Workaround Code + // haifa workaround + // ----------------------------------------------------------------------------------- + // 1. Issue SW-RST ^^^ (already done above) + // 2. Issue a redundant Set CU Base CMD immediately + // Do not set the General Pointer before the Set CU Base cycle + // Do not check the SCB CMD before the Set CU Base cycle + // 3. Wait for the SCB-CMD to be cleared + // this indicates the transition to post-driver + // 4. Poll the TCO-Req bit in the PMDR to be cleared + // this indicates the tco activity has stopped for real + // 5. Proceed with the nominal Driver Init: + // Actual Set CU & RU Base ... + // + // Check for ICH2 device ID. If this is an ICH2, + // do the TCO workaround code. + // + if (AdapterInfo->VendorID == D102_DEVICE_ID || + AdapterInfo->VendorID == ICH3_DEVICE_ID_1 || + AdapterInfo->VendorID == ICH3_DEVICE_ID_2 || + AdapterInfo->VendorID == ICH3_DEVICE_ID_3 || + AdapterInfo->VendorID == ICH3_DEVICE_ID_4 || + AdapterInfo->VendorID == ICH3_DEVICE_ID_5 || + AdapterInfo->VendorID == ICH3_DEVICE_ID_6 || + AdapterInfo->VendorID == ICH3_DEVICE_ID_7 || + AdapterInfo->VendorID == ICH3_DEVICE_ID_8 || + AdapterInfo->RevID >= 8) { // do the TCO fix + // + // donot load the scb pointer but just give load_cu cmd. + // + OutByte (AdapterInfo, CU_CMD_BASE, AdapterInfo->ioaddr + SCBCmd); + // + // wait for command to be accepted. + // + wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd); + // + // read PMDR register and check bit 1 in it to see if TCO is active + // + + // + // wait for 5 milli seconds + // + wait = 5000; + while (wait) { + tco_stat = InByte (AdapterInfo, AdapterInfo->ioaddr + 0x1b); + if ((tco_stat & 2) == 0) { + // + // is the activity bit clear?? + // + break; + } + + wait--; + DelayIt (AdapterInfo, 1); + } + + if ((tco_stat & 2) != 0) { + // + // not zero?? + // + return -1; + } + } + + return 0; +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + + @return TODO: add return values + +**/ +UINT8 +SelectiveReset ( + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + UINT16 wait; + UINT32 stat; + + wait = 10; + stat = 0; + OutLong (AdapterInfo, POR_SELECTIVE_RESET, AdapterInfo->ioaddr + SCBPort); + // + // wait for this to complete + // + + // + // wait for 2 milli seconds here! + // + DelayIt (AdapterInfo, 2000); + while (wait > 0) { + wait--; + stat = InLong (AdapterInfo, AdapterInfo->ioaddr + SCBPort); + if (stat == 0) { + break; + } + + // + // wait for 1 milli second + // + DelayIt (AdapterInfo, 1000); + } + + if (stat != 0) { + return PXE_STATCODE_DEVICE_FAILURE; + } + + return 0; +} + + +/** + TODO: Add function description + + @param AdapterInfo TODO: add argument description + + @return TODO: add return values + +**/ +UINT16 +InitializeChip ( + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + UINT16 ret_val; + if (SoftwareReset (AdapterInfo) != 0) { + return PXE_STATCODE_DEVICE_FAILURE; + } + + // + // disable interrupts + // + OutWord (AdapterInfo, INT_MASK, AdapterInfo->ioaddr + SCBCmd); + + // + // Load the base registers with 0s (we will give the complete address as + // offset later when we issue any command + // + if ((ret_val = Load_Base_Regs (AdapterInfo)) != 0) { + return ret_val; + } + + if ((ret_val = SetupCBlink (AdapterInfo)) != 0) { + return ret_val; + } + + if ((ret_val = SetupReceiveQueues (AdapterInfo)) != 0) { + return ret_val; + } + + // + // detect the PHY only if we need to detect the cable as requested by the + // initialize parameters + // + AdapterInfo->PhyAddress = 0xFF; + + if (AdapterInfo->CableDetect != 0) { + if (!PhyDetect (AdapterInfo)) { + return PXE_STATCODE_DEVICE_FAILURE; + } + } + + if ((ret_val = E100bSetupIAAddr (AdapterInfo)) != 0) { + return ret_val; + } + + if ((ret_val = Configure (AdapterInfo)) != 0) { + return ret_val; + } + + return 0; +} diff --git a/Drivers/OptionRomPkg/UndiRuntimeDxe/E100b.h b/Drivers/OptionRomPkg/UndiRuntimeDxe/E100b.h new file mode 100644 index 0000000000..18ff7aa94d --- /dev/null +++ b/Drivers/OptionRomPkg/UndiRuntimeDxe/E100b.h @@ -0,0 +1,665 @@ +/** @file + Definitions for network adapter card. + +Copyright (c) 2006 - 2007, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _E100B_H_ +#define _E100B_H_ + +// pci config offsets: + +#define RX_BUFFER_COUNT 32 +#define TX_BUFFER_COUNT 32 + +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_DEVICE_ID_INTEL_82557 0x1229 +#define D100_VENDOR_ID 0x8086 +#define D100_DEVICE_ID 0x1229 +#define D102_DEVICE_ID 0x2449 + +#define ICH3_DEVICE_ID_1 0x1031 +#define ICH3_DEVICE_ID_2 0x1032 +#define ICH3_DEVICE_ID_3 0x1033 +#define ICH3_DEVICE_ID_4 0x1034 +#define ICH3_DEVICE_ID_5 0x1035 +#define ICH3_DEVICE_ID_6 0x1036 +#define ICH3_DEVICE_ID_7 0x1037 +#define ICH3_DEVICE_ID_8 0x1038 + +#define SPEEDO_DEVICE_ID 0x1227 +#define SPLASH1_DEVICE_ID 0x1226 + + +// bit fields for the command +#define PCI_COMMAND_MASTER 0x04 // bit 2 +#define PCI_COMMAND_IO 0x01 // bit 0 +#define PCI_COMMAND 0x04 +#define PCI_LATENCY_TIMER 0x0D + +#define ETHER_MAC_ADDR_LEN 6 +#ifdef AVL_XXX +#define ETHER_HEADER_LEN 14 +// media interface type +// #define INTERFACE_TYPE " + +// Hardware type values +#define HW_ETHER_TYPE 1 +#define HW_EXPERIMENTAL_ETHER_TYPE 2 +#define HW_IEEE_TYPE 6 +#define HW_ARCNET_TYPE 7 + +#endif // AVL_XXX + +#define MAX_ETHERNET_PKT_SIZE 1514 // including eth header +#define RX_BUFFER_SIZE 1536 // including crc and padding +#define TX_BUFFER_SIZE 64 +#define ETH_MTU 1500 // does not include ethernet header length + +#define SPEEDO3_TOTAL_SIZE 0x20 + +#pragma pack(1) + +typedef struct eth { + UINT8 dest_addr[PXE_HWADDR_LEN_ETHER]; + UINT8 src_addr[PXE_HWADDR_LEN_ETHER]; + UINT16 type; +} EtherHeader; + +#pragma pack(1) +typedef struct CONFIG_HEADER { + UINT16 VendorID; + UINT16 DeviceID; + UINT16 Command; + UINT16 Status; + UINT16 RevID; + UINT16 ClassID; + UINT8 CacheLineSize; + UINT8 LatencyTimer; + UINT8 HeaderType; // must be zero to impose this structure... + UINT8 BIST; // built-in self test + UINT32 BaseAddressReg_0; // memory mapped address + UINT32 BaseAddressReg_1; //io mapped address, Base IO address + UINT32 BaseAddressReg_2; // option rom address + UINT32 BaseAddressReg_3; + UINT32 BaseAddressReg_4; + UINT32 BaseAddressReg_5; + UINT32 CardBusCISPtr; + UINT16 SubVendorID; + UINT16 SubSystemID; + UINT32 ExpansionROMBaseAddr; + UINT8 CapabilitiesPtr; + UINT8 reserved1; + UINT16 Reserved2; + UINT32 Reserved3; + UINT8 int_line; + UINT8 int_pin; + UINT8 Min_gnt; + UINT8 Max_lat; +} PCI_CONFIG_HEADER; +#pragma pack() + +//------------------------------------------------------------------------- +// Offsets to the various registers. +// All accesses need not be longword aligned. +//------------------------------------------------------------------------- +enum speedo_offsets { + SCBStatus = 0, SCBCmd = 2, // Rx/Command Unit command and status. + SCBPointer = 4, // General purpose pointer. + SCBPort = 8, // Misc. commands and operands. + SCBflash = 12, SCBeeprom = 14, // EEPROM and flash memory control. + SCBCtrlMDI = 16, // MDI interface control. + SCBEarlyRx = 20, // Early receive byte count. + SCBEarlyRxInt = 24, SCBFlowCtrlReg = 25, SCBPmdr = 27, + // offsets for general control registers (GCRs) + SCBGenCtrl = 28, SCBGenStatus = 29, SCBGenCtrl2 = 30, SCBRsvd = 31 +}; + +#define GCR2_EEPROM_ACCESS_SEMAPHORE 0x80 // bit offset into the gcr2 + +//------------------------------------------------------------------------- +// Action commands - Commands that can be put in a command list entry. +//------------------------------------------------------------------------- +enum commands { + CmdNOp = 0, CmdIASetup = 1, CmdConfigure = 2, CmdMulticastList = 3, + CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7, + CmdSuspend = 0x4000, /* Suspend after completion. */ + CmdIntr = 0x2000, /* Interrupt after completion. */ + CmdTxFlex = 0x0008 /* Use "Flexible mode" for CmdTx command. */ +}; + +//------------------------------------------------------------------------- +// port commands +//------------------------------------------------------------------------- +#define PORT_RESET 0 +#define PORT_SELF_TEST 1 +#define POR_SELECTIVE_RESET 2 +#define PORT_DUMP_POINTER 2 + +//------------------------------------------------------------------------- +// SCB Command Word bit definitions +//------------------------------------------------------------------------- +//- CUC fields +#define CU_START 0x0010 +#define CU_RESUME 0x0020 +#define CU_STATSADDR 0x0040 +#define CU_SHOWSTATS 0x0050 /* Dump statistics counters. */ +#define CU_CMD_BASE 0x0060 /* Base address to add to add CU commands. */ +#define CU_DUMPSTATS 0x0070 /* Dump then reset stats counters. */ + +//- RUC fields +#define RX_START 0x0001 +#define RX_RESUME 0x0002 +#define RX_ABORT 0x0004 +#define RX_ADDR_LOAD 0x0006 /* load ru_base_reg */ +#define RX_RESUMENR 0x0007 + +// Interrupt fields (assuming byte addressing) +#define INT_MASK 0x0100 +#define DRVR_INT 0x0200 /* Driver generated interrupt. */ + +//- CB Status Word +#define CMD_STATUS_COMPLETE 0x8000 +#define RX_STATUS_COMPLETE 0x8000 +#define CMD_STATUS_MASK 0xF000 + +//------------------------------------------------------------------------- +//- SCB Status bits: +// Interrupts are ACKed by writing to the upper 6 interrupt bits +//------------------------------------------------------------------------- +#define SCB_STATUS_MASK 0xFC00 // bits 2-7 - STATUS/ACK Mask +#define SCB_STATUS_CX_TNO 0x8000 // BIT_15 - CX or TNO Interrupt +#define SCB_STATUS_FR 0x4000 // BIT_14 - FR Interrupt +#define SCB_STATUS_CNA 0x2000 // BIT_13 - CNA Interrupt +#define SCB_STATUS_RNR 0x1000 // BIT_12 - RNR Interrupt +#define SCB_STATUS_MDI 0x0800 // BIT_11 - MDI R/W Done Interrupt +#define SCB_STATUS_SWI 0x0400 // BIT_10 - SWI Interrupt + +// CU STATUS: bits 6 & 7 +#define SCB_STATUS_CU_MASK 0x00C0 // bits 6 & 7 +#define SCB_STATUS_CU_IDLE 0x0000 // 00 +#define SCB_STATUS_CU_SUSPEND 0x0040 // 01 +#define SCB_STATUS_CU_ACTIVE 0x0080 // 10 + +// RU STATUS: bits 2-5 +#define SCB_RUS_IDLE 0x0000 +#define SCB_RUS_SUSPENDED 0x0004 // bit 2 +#define SCB_RUS_NO_RESOURCES 0x0008 // bit 3 +#define SCB_RUS_READY 0x0010 // bit 4 + +//------------------------------------------------------------------------- +// Bit Mask definitions +//------------------------------------------------------------------------- +#define BIT_0 0x0001 +#define BIT_1 0x0002 +#define BIT_2 0x0004 +#define BIT_3 0x0008 +#define BIT_4 0x0010 +#define BIT_5 0x0020 +#define BIT_6 0x0040 +#define BIT_7 0x0080 +#define BIT_8 0x0100 +#define BIT_9 0x0200 +#define BIT_10 0x0400 +#define BIT_11 0x0800 +#define BIT_12 0x1000 +#define BIT_13 0x2000 +#define BIT_14 0x4000 +#define BIT_15 0x8000 +#define BIT_24 0x01000000 +#define BIT_28 0x10000000 + + +//------------------------------------------------------------------------- +// MDI Control register bit definitions +//------------------------------------------------------------------------- +#define MDI_DATA_MASK BIT_0_15 // MDI Data port +#define MDI_REG_ADDR BIT_16_20 // which MDI register to read/write +#define MDI_PHY_ADDR BIT_21_25 // which PHY to read/write +#define MDI_PHY_OPCODE BIT_26_27 // which PHY to read/write +#define MDI_PHY_READY BIT_28 // PHY is ready for another MDI cycle +#define MDI_PHY_INT_ENABLE BIT_29 // Assert INT at MDI cycle completion + +#define BIT_0_2 0x0007 +#define BIT_0_3 0x000F +#define BIT_0_4 0x001F +#define BIT_0_5 0x003F +#define BIT_0_6 0x007F +#define BIT_0_7 0x00FF +#define BIT_0_8 0x01FF +#define BIT_0_13 0x3FFF +#define BIT_0_15 0xFFFF +#define BIT_1_2 0x0006 +#define BIT_1_3 0x000E +#define BIT_2_5 0x003C +#define BIT_3_4 0x0018 +#define BIT_4_5 0x0030 +#define BIT_4_6 0x0070 +#define BIT_4_7 0x00F0 +#define BIT_5_7 0x00E0 +#define BIT_5_9 0x03E0 +#define BIT_5_12 0x1FE0 +#define BIT_5_15 0xFFE0 +#define BIT_6_7 0x00c0 +#define BIT_7_11 0x0F80 +#define BIT_8_10 0x0700 +#define BIT_9_13 0x3E00 +#define BIT_12_15 0xF000 + +#define BIT_16_20 0x001F0000 +#define BIT_21_25 0x03E00000 +#define BIT_26_27 0x0C000000 + +//------------------------------------------------------------------------- +// MDI Control register opcode definitions +//------------------------------------------------------------------------- +#define MDI_WRITE 1 // Phy Write +#define MDI_READ 2 // Phy read + +//------------------------------------------------------------------------- +// PHY 100 MDI Register/Bit Definitions +//------------------------------------------------------------------------- +// MDI register set +#define MDI_CONTROL_REG 0x00 // MDI control register +#define MDI_STATUS_REG 0x01 // MDI Status regiser +#define PHY_ID_REG_1 0x02 // Phy indentification reg (word 1) +#define PHY_ID_REG_2 0x03 // Phy indentification reg (word 2) +#define AUTO_NEG_ADVERTISE_REG 0x04 // Auto-negotiation advertisement +#define AUTO_NEG_LINK_PARTNER_REG 0x05 // Auto-negotiation link partner ability +#define AUTO_NEG_EXPANSION_REG 0x06 // Auto-negotiation expansion +#define AUTO_NEG_NEXT_PAGE_REG 0x07 // Auto-negotiation next page transmit +#define EXTENDED_REG_0 0x10 // Extended reg 0 (Phy 100 modes) +#define EXTENDED_REG_1 0x14 // Extended reg 1 (Phy 100 error indications) +#define NSC_CONG_CONTROL_REG 0x17 // National (TX) congestion control +#define NSC_SPEED_IND_REG 0x19 // National (TX) speed indication + +// MDI Control register bit definitions +#define MDI_CR_COLL_TEST_ENABLE BIT_7 // Collision test enable +#define MDI_CR_FULL_HALF BIT_8 // FDX =1, half duplex =0 +#define MDI_CR_RESTART_AUTO_NEG BIT_9 // Restart auto negotiation +#define MDI_CR_ISOLATE BIT_10 // Isolate PHY from MII +#define MDI_CR_POWER_DOWN BIT_11 // Power down +#define MDI_CR_AUTO_SELECT BIT_12 // Auto speed select enable +#define MDI_CR_10_100 BIT_13 // 0 = 10Mbs, 1 = 100Mbs +#define MDI_CR_LOOPBACK BIT_14 // 0 = normal, 1 = loopback +#define MDI_CR_RESET BIT_15 // 0 = normal, 1 = PHY reset + +// MDI Status register bit definitions +#define MDI_SR_EXT_REG_CAPABLE BIT_0 // Extended register capabilities +#define MDI_SR_JABBER_DETECT BIT_1 // Jabber detected +#define MDI_SR_LINK_STATUS BIT_2 // Link Status -- 1 = link +#define MDI_SR_AUTO_SELECT_CAPABLE BIT_3 // Auto speed select capable +#define MDI_SR_REMOTE_FAULT_DETECT BIT_4 // Remote fault detect +#define MDI_SR_AUTO_NEG_COMPLETE BIT_5 // Auto negotiation complete +#define MDI_SR_10T_HALF_DPX BIT_11 // 10BaseT Half Duplex capable +#define MDI_SR_10T_FULL_DPX BIT_12 // 10BaseT full duplex capable +#define MDI_SR_TX_HALF_DPX BIT_13 // TX Half Duplex capable +#define MDI_SR_TX_FULL_DPX BIT_14 // TX full duplex capable +#define MDI_SR_T4_CAPABLE BIT_15 // T4 capable + +// Auto-Negotiation advertisement register bit definitions +#define NWAY_AD_SELCTOR_FIELD BIT_0_4 // identifies supported protocol +#define NWAY_AD_ABILITY BIT_5_12 // technologies that are supported +#define NWAY_AD_10T_HALF_DPX BIT_5 // 10BaseT Half Duplex capable +#define NWAY_AD_10T_FULL_DPX BIT_6 // 10BaseT full duplex capable +#define NWAY_AD_TX_HALF_DPX BIT_7 // TX Half Duplex capable +#define NWAY_AD_TX_FULL_DPX BIT_8 // TX full duplex capable +#define NWAY_AD_T4_CAPABLE BIT_9 // T4 capable +#define NWAY_AD_REMOTE_FAULT BIT_13 // indicates local remote fault +#define NWAY_AD_RESERVED BIT_14 // reserved +#define NWAY_AD_NEXT_PAGE BIT_15 // Next page (not supported) + +// Auto-Negotiation link partner ability register bit definitions +#define NWAY_LP_SELCTOR_FIELD BIT_0_4 // identifies supported protocol +#define NWAY_LP_ABILITY BIT_5_9 // technologies that are supported +#define NWAY_LP_REMOTE_FAULT BIT_13 // indicates partner remote fault +#define NWAY_LP_ACKNOWLEDGE BIT_14 // acknowledge +#define NWAY_LP_NEXT_PAGE BIT_15 // Next page (not supported) + +// Auto-Negotiation expansion register bit definitions +#define NWAY_EX_LP_NWAY BIT_0 // link partner is NWAY +#define NWAY_EX_PAGE_RECEIVED BIT_1 // link code word received +#define NWAY_EX_NEXT_PAGE_ABLE BIT_2 // local is next page able +#define NWAY_EX_LP_NEXT_PAGE_ABLE BIT_3 // partner is next page able +#define NWAY_EX_PARALLEL_DET_FLT BIT_4 // parallel detection fault +#define NWAY_EX_RESERVED BIT_5_15 // reserved + + +// PHY 100 Extended Register 0 bit definitions +#define PHY_100_ER0_FDX_INDIC BIT_0 // 1 = FDX, 0 = half duplex +#define PHY_100_ER0_SPEED_INDIC BIT_1 // 1 = 100mbs, 0= 10mbs +#define PHY_100_ER0_WAKE_UP BIT_2 // Wake up DAC +#define PHY_100_ER0_RESERVED BIT_3_4 // Reserved +#define PHY_100_ER0_REV_CNTRL BIT_5_7 // Revsion control (A step = 000) +#define PHY_100_ER0_FORCE_FAIL BIT_8 // Force Fail is enabled +#define PHY_100_ER0_TEST BIT_9_13 // Revsion control (A step = 000) +#define PHY_100_ER0_LINKDIS BIT_14 // Link integrity test is disabled +#define PHY_100_ER0_JABDIS BIT_15 // Jabber function is disabled + + +// PHY 100 Extended Register 1 bit definitions +#define PHY_100_ER1_RESERVED BIT_0_8 // Reserved +#define PHY_100_ER1_CH2_DET_ERR BIT_9 // Channel 2 EOF detection error +#define PHY_100_ER1_MANCH_CODE_ERR BIT_10 // Manchester code error +#define PHY_100_ER1_EOP_ERR BIT_11 // EOP error +#define PHY_100_ER1_BAD_CODE_ERR BIT_12 // bad code error +#define PHY_100_ER1_INV_CODE_ERR BIT_13 // invalid code error +#define PHY_100_ER1_DC_BAL_ERR BIT_14 // DC balance error +#define PHY_100_ER1_PAIR_SKEW_ERR BIT_15 // Pair skew error + +// National Semiconductor TX phy congestion control register bit definitions +#define NSC_TX_CONG_TXREADY BIT_10 // Makes TxReady an input +#define NSC_TX_CONG_ENABLE BIT_8 // Enables congestion control +#define NSC_TX_CONG_F_CONNECT BIT_5 // Enables congestion control + +// National Semiconductor TX phy speed indication register bit definitions +#define NSC_TX_SPD_INDC_SPEED BIT_6 // 0 = 100mb, 1=10mb + +//------------------------------------------------------------------------- +// Phy related constants +//------------------------------------------------------------------------- +#define PHY_503 0 +#define PHY_100_A 0x000003E0 +#define PHY_100_C 0x035002A8 +#define PHY_TX_ID 0x015002A8 +#define PHY_NSC_TX 0x5c002000 +#define PHY_OTHER 0xFFFF + +#define PHY_MODEL_REV_ID_MASK 0xFFF0FFFF +#define PARALLEL_DETECT 0 +#define N_WAY 1 + +#define RENEGOTIATE_TIME 35 // (3.5 Seconds) + +#define CONNECTOR_AUTO 0 +#define CONNECTOR_TPE 1 +#define CONNECTOR_MII 2 + +//------------------------------------------------------------------------- + +/* The Speedo3 Rx and Tx frame/buffer descriptors. */ +#pragma pack(1) +struct CB_Header { /* A generic descriptor. */ + UINT16 status; /* Offset 0. */ + UINT16 command; /* Offset 2. */ + UINT32 link; /* struct descriptor * */ +}; + +/* transmit command block structure */ +#pragma pack(1) +typedef struct s_TxCB { + struct CB_Header cb_header; + UINT32 PhysTBDArrayAddres; /* address of an array that contains + physical TBD pointers */ + UINT16 ByteCount; /* immediate data count = 0 always */ + UINT8 Threshold; + UINT8 TBDCount; + UINT8 ImmediateData[TX_BUFFER_SIZE]; + /* following fields are not seen by the 82557 */ + struct TBD { + UINT32 phys_buf_addr; + UINT32 buf_len; + } TBDArray[MAX_XMIT_FRAGMENTS]; + UINT32 PhysArrayAddr; /* in case the one in the header is lost */ + UINT32 PhysTCBAddress; /* for this TCB */ + struct s_TxCB *NextTCBVirtualLinkPtr; + struct s_TxCB *PrevTCBVirtualLinkPtr; + UINT64 free_data_ptr; // to be given to the upper layer when this xmit completes1 +}TxCB; + +/* The Speedo3 Rx and Tx buffer descriptors. */ +#pragma pack(1) +typedef struct s_RxFD { /* Receive frame descriptor. */ + struct CB_Header cb_header; + UINT32 rx_buf_addr; /* VOID * */ + UINT16 ActualCount; + UINT16 RFDSize; + UINT8 RFDBuffer[RX_BUFFER_SIZE]; + UINT8 forwarded; + UINT8 junk[3]; +}RxFD; + +/* Elements of the RxFD.status word. */ +#define RX_COMPLETE 0x8000 +#define RX_FRAME_OK 0x2000 + +/* Elements of the dump_statistics block. This block must be lword aligned. */ +#pragma pack(1) +struct speedo_stats { + UINT32 tx_good_frames; + UINT32 tx_coll16_errs; + UINT32 tx_late_colls; + UINT32 tx_underruns; + UINT32 tx_lost_carrier; + UINT32 tx_deferred; + UINT32 tx_one_colls; + UINT32 tx_multi_colls; + UINT32 tx_total_colls; + UINT32 rx_good_frames; + UINT32 rx_crc_errs; + UINT32 rx_align_errs; + UINT32 rx_resource_errs; + UINT32 rx_overrun_errs; + UINT32 rx_colls_errs; + UINT32 rx_runt_errs; + UINT32 done_marker; +}; +#pragma pack() + + +struct Krn_Mem{ + RxFD rx_ring[RX_BUFFER_COUNT]; + TxCB tx_ring[TX_BUFFER_COUNT]; + struct speedo_stats statistics; +}; +#define MEMORY_NEEDED sizeof(struct Krn_Mem) + +/* The parameters for a CmdConfigure operation. + There are so many options that it would be difficult to document each bit. + We mostly use the default or recommended settings. +*/ + +/* + *-------------------------------------------------------------------------- + * Configuration CB Parameter Bit Definitions + *-------------------------------------------------------------------------- + */ +// - Byte 0 (Default Value = 16h) +#define CFIG_BYTE_COUNT 0x16 // 22 Configuration Bytes + +//- Byte 1 (Default Value = 88h) +#define CFIG_TXRX_FIFO_LIMIT 0x88 + +//- Byte 2 (Default Value = 0) +#define CFIG_ADAPTIVE_IFS 0 + +//- Byte 3 (Default Value = 0, ALWAYS. This byte is RESERVED) +#define CFIG_RESERVED 0 + +//- Byte 4 (Default Value = 0. Default implies that Rx DMA cannot be +//- preempted). +#define CFIG_RXDMA_BYTE_COUNT 0 + +//- Byte 5 (Default Value = 80h. Default implies that Tx DMA cannot be +//- preempted. However, setting these counters is enabled.) +#define CFIG_DMBC_ENABLE 0x80 + +//- Byte 6 (Default Value = 33h. Late SCB enabled, No TNO interrupts, +//- CNA interrupts and do not save bad frames.) +#define CFIG_LATE_SCB 1 // BIT 0 +#define CFIG_TNO_INTERRUPT 0x4 // BIT 2 +#define CFIG_CI_INTERRUPT 0x8 // BIT 3 +#define CFIG_SAVE_BAD_FRAMES 0x80 // BIT_7 + +//- Byte 7 (Default Value = 7h. Discard short frames automatically and +//- attempt upto 3 retries on transmit.) +#define CFIG_DISCARD_SHORTRX 0x00001 +#define CFIG_URUN_RETRY BIT_1 OR BIT_2 + +//- Byte 8 (Default Value = 1. Enable MII mode.) +#define CFIG_503_MII BIT_0 + +//- Byte 9 (Default Value = 0, ALWAYS) + +//- Byte 10 (Default Value = 2Eh) +#define CFIG_NSAI BIT_3 +#define CFIG_PREAMBLE_LENGTH BIT_5 ;- Bit 5-4 = 1-0 +#define CFIG_NO_LOOPBACK 0 +#define CFIG_INTERNAL_LOOPBACK BIT_6 +#define CFIG_EXT_LOOPBACK BIT_7 +#define CFIG_EXT_PIN_LOOPBACK BIT_6 OR BIT_7 + +//- Byte 11 (Default Value = 0) +#define CFIG_LINEAR_PRIORITY 0 + +//- Byte 12 (Default Value = 60h) +#define CFIG_LPRIORITY_MODE 0 +#define CFIG_IFS 6 ;- 6 * 16 = 96 + +//- Byte 13 (Default Value = 0, ALWAYS) + +//- Byte 14 (Default Value = 0F2h, ALWAYS) + +//- Byte 15 (Default Value = E8h) +#define CFIG_PROMISCUOUS_MODE BIT_0 +#define CFIG_BROADCAST_DISABLE BIT_1 +#define CFIG_CRS_CDT BIT_7 + +//- Byte 16 (Default Value = 0, ALWAYS) + +//- Byte 17 (Default Value = 40h, ALWAYS) + +//- Byte 18 (Default Value = F2h) +#define CFIG_STRIPPING BIT_0 +#define CFIG_PADDING BIT_1 +#define CFIG_RX_CRC_TRANSFER BIT_2 + +//- Byte 19 (Default Value = 80h) +#define CFIG_FORCE_FDX BIT_6 +#define CFIG_FDX_PIN_ENABLE BIT_7 + +//- Byte 20 (Default Value = 3Fh) +#define CFIG_MULTI_IA BIT_6 + +//- Byte 21 (Default Value = 05) +#define CFIG_MC_ALL BIT_3 + +/*-----------------------------------------------------------------------*/ +#define D102_REVID 0x0b + +#define HALF_DUPLEX 1 +#define FULL_DUPLEX 2 + +typedef struct s_data_instance { + + UINT16 State; // stopped, started or initialized + UINT16 Bus; + UINT8 Device; + UINT8 Function; + UINT16 VendorID; + UINT16 DeviceID; + UINT16 RevID; + UINT16 SubVendorID; + UINT16 SubSystemID; + + UINT8 PermNodeAddress[PXE_MAC_LENGTH]; + UINT8 CurrentNodeAddress[PXE_MAC_LENGTH]; + UINT8 BroadcastNodeAddress[PXE_MAC_LENGTH]; + UINT32 Config[MAX_PCI_CONFIG_LEN]; + UINT32 NVData[MAX_EEPROM_LEN]; + + UINT32 ioaddr; + UINT32 flash_addr; + + UINT16 LinkSpeed; // actual link speed setting + UINT16 LinkSpeedReq; // requested (forced) link speed + UINT8 DuplexReq; // requested duplex + UINT8 Duplex; // Duplex set + UINT8 CableDetect; // 1 to detect and 0 not to detect the cable + UINT8 LoopBack; + + UINT16 TxBufCnt; + UINT16 TxBufSize; + UINT16 RxBufCnt; + UINT16 RxBufSize; + UINT32 RxTotals; + UINT32 TxTotals; + + UINT16 int_mask; + UINT16 Int_Status; + UINT16 PhyRecord[2]; // primary and secondary PHY record registers from eeprom + UINT8 PhyAddress; + UINT8 int_num; + UINT16 NVData_Len; + UINT32 MemoryLength; + + RxFD *rx_ring; // array of rx buffers + TxCB *tx_ring; // array of tx buffers + struct speedo_stats *statistics; + TxCB *FreeTxHeadPtr; + TxCB *FreeTxTailPtr; + RxFD *RFDTailPtr; + + UINT64 rx_phy_addr; // physical addresses + UINT64 tx_phy_addr; + UINT64 stat_phy_addr; + UINT64 MemoryPtr; + UINT64 Mapped_MemoryPtr; + + UINT64 xmit_done[TX_BUFFER_COUNT << 1]; // circular buffer + UINT16 xmit_done_head; // index into the xmit_done array + UINT16 xmit_done_tail; // where are we filling now (index into xmit_done) + UINT16 cur_rx_ind; // current RX Q head index + UINT16 FreeCBCount; + + BOOLEAN in_interrupt; + BOOLEAN in_transmit; + BOOLEAN Receive_Started; + UINT8 Rx_Filter; + UINT8 VersionFlag; // UNDI30 or UNDI31?? + UINT8 rsvd[3]; + + struct mc{ + UINT16 reserved [3]; // padding for this structure to make it 8 byte aligned + UINT16 list_len; + UINT8 mc_list[MAX_MCAST_ADDRESS_CNT][PXE_MAC_LENGTH]; // 8*32 is the size + } mcast_list; + + UINT64 Unique_ID; + + EFI_PCI_IO_PROTOCOL *Io_Function; + // + // Original PCI attributes + // + UINT64 OriginalPciAttributes; + + VOID (*Delay_30)(UINTN); // call back routine + VOID (*Virt2Phys_30)(UINT64 virtual_addr, UINT64 physical_ptr); // call back routine + VOID (*Block_30)(UINT32 enable); // call back routine + VOID (*Mem_Io_30)(UINT8 read_write, UINT8 len, UINT64 port, UINT64 buf_addr); + VOID (*Delay)(UINT64, UINTN); // call back routine + VOID (*Virt2Phys)(UINT64 unq_id, UINT64 virtual_addr, UINT64 physical_ptr); // call back routine + VOID (*Block)(UINT64 unq_id, UINT32 enable); // call back routine + VOID (*Mem_Io)(UINT64 unq_id, UINT8 read_write, UINT8 len, UINT64 port, + UINT64 buf_addr); + VOID (*Map_Mem)(UINT64 unq_id, UINT64 virtual_addr, UINT32 size, + UINT32 Direction, UINT64 mapped_addr); + VOID (*UnMap_Mem)(UINT64 unq_id, UINT64 virtual_addr, UINT32 size, + UINT32 Direction, UINT64 mapped_addr); + VOID (*Sync_Mem)(UINT64 unq_id, UINT64 virtual_addr, + UINT32 size, UINT32 Direction, UINT64 mapped_addr); +} NIC_DATA_INSTANCE; + +#pragma pack(1) +struct MC_CB_STRUCT{ + UINT16 count; + UINT8 m_list[MAX_MCAST_ADDRESS_CNT][ETHER_MAC_ADDR_LEN]; +}; +#pragma pack() + +#define FOUR_GIGABYTE (UINT64)0x100000000ULL + +#endif + diff --git a/Drivers/OptionRomPkg/UndiRuntimeDxe/Init.c b/Drivers/OptionRomPkg/UndiRuntimeDxe/Init.c new file mode 100644 index 0000000000..2625a6cc5c --- /dev/null +++ b/Drivers/OptionRomPkg/UndiRuntimeDxe/Init.c @@ -0,0 +1,1051 @@ +/** @file + Initialization functions for EFI UNDI32 driver. + +Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Undi32.h" +// +// Global Variables +// + +PXE_SW_UNDI *pxe_31 = NULL; // 3.1 entry +UNDI32_DEV *UNDI32DeviceList[MAX_NIC_INTERFACES]; +UNDI_CONFIG_TABLE *UndiDataPointer = NULL; + +// +// UNDI Class Driver Global Variables +// +EFI_DRIVER_BINDING_PROTOCOL gUndiDriverBinding = { + UndiDriverSupported, + UndiDriverStart, + UndiDriverStop, + 0xa, + NULL, + NULL +}; + + +/** + When address mapping changes to virtual this should make the appropriate + address conversions. + + (Standard Event handler) + + @return None + +**/ +VOID +EFIAPI +UndiNotifyVirtual ( + EFI_EVENT Event, + VOID *Context + ) +{ + UINT16 Index; + VOID *Pxe31Pointer; + + if (pxe_31 != NULL) { + Pxe31Pointer = (VOID *) pxe_31; + + EfiConvertPointer ( + EFI_OPTIONAL_PTR, + (VOID **) &Pxe31Pointer + ); + + // + // UNDI32DeviceList is an array of pointers + // + for (Index = 0; Index < (pxe_31->IFcnt | pxe_31->IFcntExt << 8); Index++) { + UNDI32DeviceList[Index]->NIIProtocol_31.Id = (UINT64) (UINTN) Pxe31Pointer; + EfiConvertPointer ( + EFI_OPTIONAL_PTR, + (VOID **) &(UNDI32DeviceList[Index]) + ); + } + + EfiConvertPointer ( + EFI_OPTIONAL_PTR, + (VOID **) &(pxe_31->EntryPoint) + ); + pxe_31 = Pxe31Pointer; + } + + for (Index = 0; Index <= PXE_OPCODE_LAST_VALID; Index++) { + EfiConvertPointer ( + EFI_OPTIONAL_PTR, + (VOID **) &api_table[Index].api_ptr + ); + } +} + + +/** + When EFI is shuting down the boot services, we need to install a + configuration table for UNDI to work at runtime! + + (Standard Event handler) + + @return None + +**/ +VOID +EFIAPI +UndiNotifyReadyToBoot ( + EFI_EVENT Event, + VOID *Context + ) +{ + InstallConfigTable (); +} + + +/** + Test to see if this driver supports ControllerHandle. Any ControllerHandle + than contains a DevicePath, PciIo protocol, Class code of 2, Vendor ID of 0x8086, + and DeviceId of (D100_DEVICE_ID || D102_DEVICE_ID || ICH3_DEVICE_ID_1 || + ICH3_DEVICE_ID_2 || ICH3_DEVICE_ID_3 || ICH3_DEVICE_ID_4 || ICH3_DEVICE_ID_5 || + ICH3_DEVICE_ID_6 || ICH3_DEVICE_ID_7 || ICH3_DEVICE_ID_8) can be supported. + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param RemainingDevicePath Not used. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +UndiDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE00 Pci; + + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + NULL, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + 0, + sizeof (PCI_CONFIG_HEADER), + &Pci + ); + + if (!EFI_ERROR (Status)) { + Status = EFI_UNSUPPORTED; + + if (Pci.Hdr.ClassCode[2] == 0x02 && Pci.Hdr.VendorId == PCI_VENDOR_ID_INTEL) { + switch (Pci.Hdr.DeviceId) { + case D100_DEVICE_ID: + case D102_DEVICE_ID: + case ICH3_DEVICE_ID_1: + case ICH3_DEVICE_ID_2: + case ICH3_DEVICE_ID_3: + case ICH3_DEVICE_ID_4: + case ICH3_DEVICE_ID_5: + case ICH3_DEVICE_ID_6: + case ICH3_DEVICE_ID_7: + case ICH3_DEVICE_ID_8: + case 0x1039: + case 0x103A: + case 0x103B: + case 0x103C: + case 0x103D: + case 0x103E: + case 0x1050: + case 0x1051: + case 0x1052: + case 0x1053: + case 0x1054: + case 0x1055: + case 0x1056: + case 0x1057: + case 0x1059: + case 0x1064: + Status = EFI_SUCCESS; + } + } + } + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + + +/** + Start this driver on Controller by opening PciIo and DevicePath protocol. + Initialize PXE structures, create a copy of the Controller Device Path with the + NIC's MAC address appended to it, install the NetworkInterfaceIdentifier protocol + on the newly created Device Path. + + @param This Protocol instance pointer. + @param Controller Handle of device to work with. + @param RemainingDevicePath Not used, always produce all possible children. + + @retval EFI_SUCCESS This driver is added to Controller. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +UndiDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *UndiDevicePath; + PCI_CONFIG_HEADER *CfgHdr; + UNDI32_DEV *UNDI32Device; + UINT16 NewCommand; + UINT8 *TmpPxePointer; + EFI_PCI_IO_PROTOCOL *PciIoFncs; + UINTN Len; + UINT64 Supports; + BOOLEAN PciAttributesSaved; + + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIoFncs, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &UndiDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; + } + + PciAttributesSaved = FALSE; + + Status = gBS->AllocatePool ( + EfiRuntimeServicesData, + sizeof (UNDI32_DEV), + (VOID **) &UNDI32Device + ); + + if (EFI_ERROR (Status)) { + goto UndiError; + } + + ZeroMem ((CHAR8 *) UNDI32Device, sizeof (UNDI32_DEV)); + + // + // Get original PCI attributes + // + Status = PciIoFncs->Attributes ( + PciIoFncs, + EfiPciIoAttributeOperationGet, + 0, + &UNDI32Device->NicInfo.OriginalPciAttributes + ); + + if (EFI_ERROR (Status)) { + goto UndiErrorDeleteDevice; + } + PciAttributesSaved = TRUE; + + // + // allocate and initialize both (old and new) the !pxe structures here, + // there should only be one copy of each of these structure for any number + // of NICs this undi supports. Also, these structures need to be on a + // paragraph boundary as per the spec. so, while allocating space for these, + // make sure that there is space for 2 !pxe structures (old and new) and a + // 32 bytes padding for alignment adjustment (in case) + // + TmpPxePointer = NULL; + if (pxe_31 == NULL) { + Status = gBS->AllocatePool ( + EfiRuntimeServicesData, + (sizeof (PXE_SW_UNDI) + sizeof (PXE_SW_UNDI) + 32), + (VOID **) &TmpPxePointer + ); + + if (EFI_ERROR (Status)) { + goto UndiErrorDeleteDevice; + } + + ZeroMem ( + TmpPxePointer, + sizeof (PXE_SW_UNDI) + sizeof (PXE_SW_UNDI) + 32 + ); + // + // check for paragraph alignment here, assuming that the pointer is + // already 8 byte aligned. + // + if (((UINTN) TmpPxePointer & 0x0F) != 0) { + pxe_31 = (PXE_SW_UNDI *) ((UINTN) (TmpPxePointer + 8)); + } else { + pxe_31 = (PXE_SW_UNDI *) TmpPxePointer; + } + + PxeStructInit (pxe_31); + } + + UNDI32Device->NIIProtocol_31.Id = (UINT64) (UINTN) (pxe_31); + + Status = PciIoFncs->Attributes ( + PciIoFncs, + EfiPciIoAttributeOperationSupported, + 0, + &Supports + ); + if (!EFI_ERROR (Status)) { + Supports &= EFI_PCI_DEVICE_ENABLE; + Status = PciIoFncs->Attributes ( + PciIoFncs, + EfiPciIoAttributeOperationEnable, + Supports, + NULL + ); + } + // + // Read all the registers from device's PCI Configuration space + // + Status = PciIoFncs->Pci.Read ( + PciIoFncs, + EfiPciIoWidthUint32, + 0, + MAX_PCI_CONFIG_LEN, + &UNDI32Device->NicInfo.Config + ); + + CfgHdr = (PCI_CONFIG_HEADER *) &(UNDI32Device->NicInfo.Config[0]); + + // + // make sure that this device is a PCI bus master + // + + NewCommand = (UINT16) (CfgHdr->Command | PCI_COMMAND_MASTER | PCI_COMMAND_IO); + if (CfgHdr->Command != NewCommand) { + PciIoFncs->Pci.Write ( + PciIoFncs, + EfiPciIoWidthUint16, + PCI_COMMAND, + 1, + &NewCommand + ); + CfgHdr->Command = NewCommand; + } + + // + // make sure that the latency timer is at least 32 + // + if (CfgHdr->LatencyTimer < 32) { + CfgHdr->LatencyTimer = 32; + PciIoFncs->Pci.Write ( + PciIoFncs, + EfiPciIoWidthUint8, + PCI_LATENCY_TIMER, + 1, + &CfgHdr->LatencyTimer + ); + } + // + // the IfNum index for the current interface will be the total number + // of interfaces initialized so far + // + UNDI32Device->NIIProtocol_31.IfNum = pxe_31->IFcnt | pxe_31->IFcntExt << 8; + + PxeUpdate (&UNDI32Device->NicInfo, pxe_31); + + UNDI32Device->NicInfo.Io_Function = PciIoFncs; + UNDI32DeviceList[UNDI32Device->NIIProtocol_31.IfNum] = UNDI32Device; + UNDI32Device->Undi32BaseDevPath = UndiDevicePath; + + Status = AppendMac2DevPath ( + &UNDI32Device->Undi32DevPath, + UNDI32Device->Undi32BaseDevPath, + &UNDI32Device->NicInfo + ); + + if (Status != 0) { + goto UndiErrorDeletePxe; + } + + UNDI32Device->Signature = UNDI_DEV_SIGNATURE; + + UNDI32Device->NIIProtocol_31.Revision = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION_31; + UNDI32Device->NIIProtocol_31.Type = EfiNetworkInterfaceUndi; + UNDI32Device->NIIProtocol_31.MajorVer = PXE_ROMID_MAJORVER; + UNDI32Device->NIIProtocol_31.MinorVer = PXE_ROMID_MINORVER_31; + UNDI32Device->NIIProtocol_31.ImageSize = 0; + UNDI32Device->NIIProtocol_31.ImageAddr = 0; + UNDI32Device->NIIProtocol_31.Ipv6Supported = TRUE; + + UNDI32Device->NIIProtocol_31.StringId[0] = 'U'; + UNDI32Device->NIIProtocol_31.StringId[1] = 'N'; + UNDI32Device->NIIProtocol_31.StringId[2] = 'D'; + UNDI32Device->NIIProtocol_31.StringId[3] = 'I'; + + UNDI32Device->DeviceHandle = NULL; + + UNDI32Device->Aip.GetInformation = UndiAipGetInfo; + UNDI32Device->Aip.SetInformation = UndiAipSetInfo; + UNDI32Device->Aip.GetSupportedTypes = UndiAipGetSupportedTypes; + + // + // install both the 3.0 and 3.1 NII protocols. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &UNDI32Device->DeviceHandle, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + &UNDI32Device->NIIProtocol_31, + &gEfiDevicePathProtocolGuid, + UNDI32Device->Undi32DevPath, + &gEfiAdapterInformationProtocolGuid, + &UNDI32Device->Aip, + NULL + ); + + if (EFI_ERROR (Status)) { + goto UndiErrorDeleteDevicePath; + } + + // + // if the table exists, free it and alloc again, or alloc it directly + // + if (UndiDataPointer != NULL) { + Status = gBS->FreePool(UndiDataPointer); + } + if (EFI_ERROR (Status)) { + goto UndiErrorDeleteDevicePath; + } + + Len = ((pxe_31->IFcnt|pxe_31->IFcntExt << 8)* sizeof (UndiDataPointer->NII_entry)) + sizeof (UndiDataPointer); + Status = gBS->AllocatePool (EfiRuntimeServicesData, Len, (VOID **) &UndiDataPointer); + + if (EFI_ERROR (Status)) { + goto UndiErrorAllocDataPointer; + } + + // + // Open For Child Device + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIoFncs, + This->DriverBindingHandle, + UNDI32Device->DeviceHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + return EFI_SUCCESS; +UndiErrorAllocDataPointer: + gBS->UninstallMultipleProtocolInterfaces ( + &UNDI32Device->DeviceHandle, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + &UNDI32Device->NIIProtocol_31, + &gEfiDevicePathProtocolGuid, + UNDI32Device->Undi32DevPath, + &gEfiAdapterInformationProtocolGuid, + &UNDI32Device->Aip, + NULL + ); + +UndiErrorDeleteDevicePath: + UNDI32DeviceList[UNDI32Device->NIIProtocol_31.IfNum] = NULL; + gBS->FreePool (UNDI32Device->Undi32DevPath); + +UndiErrorDeletePxe: + PxeUpdate (NULL, pxe_31); + if (TmpPxePointer != NULL) { + gBS->FreePool (TmpPxePointer); + + } + +UndiErrorDeleteDevice: + if (PciAttributesSaved) { + // + // Restore original PCI attributes + // + PciIoFncs->Attributes ( + PciIoFncs, + EfiPciIoAttributeOperationSet, + UNDI32Device->NicInfo.OriginalPciAttributes, + NULL + ); + } + + gBS->FreePool (UNDI32Device); + +UndiError: + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + + +/** + Stop this driver on Controller by removing NetworkInterfaceIdentifier protocol and + closing the DevicePath and PciIo protocols on Controller. + + @param This Protocol instance pointer. + @param Controller Handle of device to stop driver on. + @param NumberOfChildren How many children need to be stopped. + @param ChildHandleBuffer Not used. + + @retval EFI_SUCCESS This driver is removed Controller. + @retval other This driver was not removed from this device. + +**/ +// TODO: EFI_DEVICE_ERROR - add return value to function comment +EFI_STATUS +EFIAPI +UndiDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + BOOLEAN AllChildrenStopped; + UINTN Index; + UNDI32_DEV *UNDI32Device; + EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *NIIProtocol; + + // + // Complete all outstanding transactions to Controller. + // Don't allow any new transaction to Controller to be started. + // + if (NumberOfChildren == 0) { + + // + // Close the bus driver + // + Status = gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + Status = gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; + } + + AllChildrenStopped = TRUE; + + for (Index = 0; Index < NumberOfChildren; Index++) { + + Status = gBS->OpenProtocol ( + ChildHandleBuffer[Index], + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + (VOID **) &NIIProtocol, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + + UNDI32Device = UNDI_DEV_FROM_THIS (NIIProtocol); + + Status = gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + ChildHandleBuffer[Index] + ); + if (!EFI_ERROR (Status)) { + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandleBuffer[Index], + &gEfiDevicePathProtocolGuid, + UNDI32Device->Undi32DevPath, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + &UNDI32Device->NIIProtocol_31, + NULL + ); + if (!EFI_ERROR (Status)) { + // + // Restore original PCI attributes + // + Status = UNDI32Device->NicInfo.Io_Function->Attributes ( + UNDI32Device->NicInfo.Io_Function, + EfiPciIoAttributeOperationSet, + UNDI32Device->NicInfo.OriginalPciAttributes, + NULL + ); + + ASSERT_EFI_ERROR (Status); + + gBS->FreePool (UNDI32Device->Undi32DevPath); + gBS->FreePool (UNDI32Device); + + } + } + } + + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + } + } + + if (!AllChildrenStopped) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; + +} + + +/** + Use the EFI boot services to produce a pause. This is also the routine which + gets replaced during RunTime by the O/S in the NIC_DATA_INSTANCE so it can + do it's own pause. + + @param UnqId Runtime O/S routine might use this, this temp + routine does not use it + @param MicroSeconds Determines the length of pause. + + @return none + +**/ +VOID +TmpDelay ( + IN UINT64 UnqId, + IN UINTN MicroSeconds + ) +{ + gBS->Stall ((UINT32) MicroSeconds); +} + + +/** + Use the PCI IO abstraction to issue memory or I/O reads and writes. This is also the routine which + gets replaced during RunTime by the O/S in the NIC_DATA_INSTANCE so it can do it's own I/O abstractions. + + @param UnqId Runtime O/S routine may use this field, this temp + routine does not. + @param ReadWrite Determine if it is an I/O or Memory Read/Write + Operation. + @param Len Determines the width of the data operation. + @param Port What port to Read/Write from. + @param BuffAddr Address to read to or write from. + + @return none + +**/ +VOID +TmpMemIo ( + IN UINT64 UnqId, + IN UINT8 ReadWrite, + IN UINT8 Len, + IN UINT64 Port, + IN UINT64 BuffAddr + ) +{ + EFI_PCI_IO_PROTOCOL_WIDTH Width; + NIC_DATA_INSTANCE *AdapterInfo; + + Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 0; + AdapterInfo = (NIC_DATA_INSTANCE *) (UINTN) UnqId; + switch (Len) { + case 2: + Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 1; + break; + + case 4: + Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 2; + break; + + case 8: + Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 3; + break; + } + + switch (ReadWrite) { + case PXE_IO_READ: + AdapterInfo->Io_Function->Io.Read ( + AdapterInfo->Io_Function, + Width, + 1, + Port, + 1, + (VOID *) (UINTN) (BuffAddr) + ); + break; + + case PXE_IO_WRITE: + AdapterInfo->Io_Function->Io.Write ( + AdapterInfo->Io_Function, + Width, + 1, + Port, + 1, + (VOID *) (UINTN) (BuffAddr) + ); + break; + + case PXE_MEM_READ: + AdapterInfo->Io_Function->Mem.Read ( + AdapterInfo->Io_Function, + Width, + 0, + Port, + 1, + (VOID *) (UINTN) (BuffAddr) + ); + break; + + case PXE_MEM_WRITE: + AdapterInfo->Io_Function->Mem.Write ( + AdapterInfo->Io_Function, + Width, + 0, + Port, + 1, + (VOID *) (UINTN) (BuffAddr) + ); + break; + } + + return ; +} + + +/** + Using the NIC data structure information, read the EEPROM to get the MAC address and then allocate space + for a new devicepath (**DevPtr) which will contain the original device path the NIC was found on (*BaseDevPtr) + and an added MAC node. + + @param DevPtr Pointer which will point to the newly created device + path with the MAC node attached. + @param BaseDevPtr Pointer to the device path which the UNDI device + driver is latching on to. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @retval EFI_SUCCESS A MAC address was successfully appended to the Base + Device Path. + @retval other Not enough resources available to create new Device + Path node. + +**/ +EFI_STATUS +AppendMac2DevPath ( + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPtr, + IN EFI_DEVICE_PATH_PROTOCOL *BaseDevPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + EFI_MAC_ADDRESS MACAddress; + PCI_CONFIG_HEADER *CfgHdr; + INT32 Val; + INT32 Index; + INT32 Index2; + UINT8 AddrLen; + MAC_ADDR_DEVICE_PATH MacAddrNode; + EFI_DEVICE_PATH_PROTOCOL *EndNode; + UINT8 *DevicePtr; + UINT16 TotalPathLen; + UINT16 BasePathLen; + EFI_STATUS Status; + + // + // set the environment ready (similar to UNDI_Start call) so that we can + // execute the other UNDI_ calls to get the mac address + // we are using undi 3.1 style + // + AdapterInfo->Delay = TmpDelay; + AdapterInfo->Virt2Phys = (VOID *) 0; + AdapterInfo->Block = (VOID *) 0; + AdapterInfo->Map_Mem = (VOID *) 0; + AdapterInfo->UnMap_Mem = (VOID *) 0; + AdapterInfo->Sync_Mem = (VOID *) 0; + AdapterInfo->Mem_Io = TmpMemIo; + // + // these tmp call-backs follow 3.1 undi style + // i.e. they have the unique_id parameter. + // + AdapterInfo->VersionFlag = 0x31; + AdapterInfo->Unique_ID = (UINT64) (UINTN) AdapterInfo; + + // + // undi init portion + // + CfgHdr = (PCI_CONFIG_HEADER *) &(AdapterInfo->Config[0]); + AdapterInfo->ioaddr = 0; + AdapterInfo->RevID = CfgHdr->RevID; + + AddrLen = E100bGetEepromAddrLen (AdapterInfo); + + for (Index = 0, Index2 = 0; Index < 3; Index++) { + Val = E100bReadEeprom (AdapterInfo, Index, AddrLen); + MACAddress.Addr[Index2++] = (UINT8) Val; + MACAddress.Addr[Index2++] = (UINT8) (Val >> 8); + } + + SetMem (MACAddress.Addr + Index2, sizeof (EFI_MAC_ADDRESS) - Index2, 0); + //for (; Index2 < sizeof (EFI_MAC_ADDRESS); Index2++) { + // MACAddress.Addr[Index2] = 0; + //} + // + // stop undi + // + AdapterInfo->Delay = (VOID *) 0; + AdapterInfo->Mem_Io = (VOID *) 0; + + // + // fill the mac address node first + // + ZeroMem ((CHAR8 *) &MacAddrNode, sizeof MacAddrNode); + CopyMem ( + (CHAR8 *) &MacAddrNode.MacAddress, + (CHAR8 *) &MACAddress, + sizeof (EFI_MAC_ADDRESS) + ); + + MacAddrNode.Header.Type = MESSAGING_DEVICE_PATH; + MacAddrNode.Header.SubType = MSG_MAC_ADDR_DP; + MacAddrNode.Header.Length[0] = (UINT8) sizeof (MacAddrNode); + MacAddrNode.Header.Length[1] = 0; + + // + // find the size of the base dev path. + // + EndNode = BaseDevPtr; + + while (!IsDevicePathEnd (EndNode)) { + EndNode = NextDevicePathNode (EndNode); + } + + BasePathLen = (UINT16) ((UINTN) (EndNode) - (UINTN) (BaseDevPtr)); + + // + // create space for full dev path + // + TotalPathLen = (UINT16) (BasePathLen + sizeof (MacAddrNode) + sizeof (EFI_DEVICE_PATH_PROTOCOL)); + + Status = gBS->AllocatePool ( + EfiRuntimeServicesData, + TotalPathLen, + (VOID **) &DevicePtr + ); + + if (Status != EFI_SUCCESS) { + return Status; + } + // + // copy the base path, mac addr and end_dev_path nodes + // + *DevPtr = (EFI_DEVICE_PATH_PROTOCOL *) DevicePtr; + CopyMem (DevicePtr, (CHAR8 *) BaseDevPtr, BasePathLen); + DevicePtr += BasePathLen; + CopyMem (DevicePtr, (CHAR8 *) &MacAddrNode, sizeof (MacAddrNode)); + DevicePtr += sizeof (MacAddrNode); + CopyMem (DevicePtr, (CHAR8 *) EndNode, sizeof (EFI_DEVICE_PATH_PROTOCOL)); + + return EFI_SUCCESS; +} + + +/** + Install a GUID/Pointer pair into the system's configuration table. + + none + + @retval EFI_SUCCESS Install a GUID/Pointer pair into the system's + configuration table. + @retval other Did not successfully install the GUID/Pointer pair + into the configuration table. + +**/ +// TODO: VOID - add argument and description to function comment +EFI_STATUS +InstallConfigTable ( + IN VOID + ) +{ + EFI_STATUS Status; + EFI_CONFIGURATION_TABLE *CfgPtr; + UNDI_CONFIG_TABLE *TmpData; + UINT16 Index; + UNDI_CONFIG_TABLE *UndiData; + + if (pxe_31 == NULL) { + return EFI_SUCCESS; + } + + if(UndiDataPointer == NULL) { + return EFI_SUCCESS; + } + + UndiData = (UNDI_CONFIG_TABLE *)UndiDataPointer; + + UndiData->NumberOfInterfaces = (pxe_31->IFcnt | pxe_31->IFcntExt << 8); + UndiData->nextlink = NULL; + + for (Index = 0; Index < (pxe_31->IFcnt | pxe_31->IFcntExt << 8); Index++) { + UndiData->NII_entry[Index].NII_InterfacePointer = &UNDI32DeviceList[Index]->NIIProtocol_31; + UndiData->NII_entry[Index].DevicePathPointer = UNDI32DeviceList[Index]->Undi32DevPath; + } + + // + // see if there is an entry in the config table already + // + CfgPtr = gST->ConfigurationTable; + + for (Index = 0; Index < gST->NumberOfTableEntries; Index++) { + Status = CompareGuid ( + &CfgPtr->VendorGuid, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31 + ); + if (Status != EFI_SUCCESS) { + break; + } + + CfgPtr++; + } + + if (Index < gST->NumberOfTableEntries) { + TmpData = (UNDI_CONFIG_TABLE *) CfgPtr->VendorTable; + + // + // go to the last link + // + while (TmpData->nextlink != NULL) { + TmpData = TmpData->nextlink; + } + + TmpData->nextlink = UndiData; + + // + // 1st one in chain + // + UndiData = (UNDI_CONFIG_TABLE *) CfgPtr->VendorTable; + } + + // + // create an entry in the configuration table for our GUID + // + Status = gBS->InstallConfigurationTable ( + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + UndiData + ); + return Status; +} + +/** + +**/ +EFI_STATUS +EFIAPI +InitializeUndi( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_EVENT Event; + EFI_STATUS Status; + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gUndiDriverBinding, + ImageHandle, + &gUndiComponentName, + &gUndiComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + UndiNotifyReadyToBoot, + NULL, + &gEfiEventReadyToBootGuid, + &Event + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + UndiNotifyVirtual, + NULL, + &gEfiEventVirtualAddressChangeGuid, + &Event + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/Drivers/OptionRomPkg/UndiRuntimeDxe/Undi32.h b/Drivers/OptionRomPkg/UndiRuntimeDxe/Undi32.h new file mode 100644 index 0000000000..31c55a8e11 --- /dev/null +++ b/Drivers/OptionRomPkg/UndiRuntimeDxe/Undi32.h @@ -0,0 +1,439 @@ +/** @file + EFI internal structures for the EFI UNDI driver. + +Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _UNDI_32_H_ +#define _UNDI_32_H_ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#include "E100b.h" + +extern EFI_DRIVER_BINDING_PROTOCOL gUndiDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gUndiComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gUndiComponentName2; + +#define MAX_NIC_INTERFACES 16 + +#define EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION_31 0x00010001 +#define PXE_ROMID_MINORVER_31 0x10 +#define PXE_STATFLAGS_DB_WRITE_TRUNCATED 0x2000 + +// +// UNDI_CALL_TABLE.state can have the following values +// +#define DONT_CHECK -1 +#define ANY_STATE -1 +#define MUST_BE_STARTED 1 +#define MUST_BE_INITIALIZED 2 + +#define UNDI_DEV_SIGNATURE SIGNATURE_32('u','n','d','i') +#define UNDI_DEV_FROM_THIS(a) CR(a, UNDI32_DEV, NIIProtocol_31, UNDI_DEV_SIGNATURE) +#define UNDI_DEV_FROM_NIC(a) CR(a, UNDI32_DEV, NicInfo, UNDI_DEV_SIGNATURE) +#define UNDI_DEV_FROM_AIP(a) CR(a, UNDI32_DEV, Aip, UNDI_DEV_SIGNATURE) + +typedef struct { + UINTN Signature; + EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL NIIProtocol_31; + EFI_ADAPTER_INFORMATION_PROTOCOL Aip; + EFI_HANDLE DeviceHandle; + EFI_DEVICE_PATH_PROTOCOL *Undi32BaseDevPath; + EFI_DEVICE_PATH_PROTOCOL *Undi32DevPath; + NIC_DATA_INSTANCE NicInfo; +} UNDI32_DEV; + +typedef struct { + UINT16 cpbsize; + UINT16 dbsize; + UINT16 opflags; + UINT16 state; + VOID (*api_ptr)(); +} UNDI_CALL_TABLE; + +typedef VOID (*ptr)(VOID); +typedef VOID (*bsptr_30)(UINTN); +typedef VOID (*virtphys_30)(UINT64, UINT64); +typedef VOID (*block_30)(UINT32); +typedef VOID (*mem_io_30)(UINT8, UINT8, UINT64, UINT64); + +typedef VOID (*bsptr)(UINT64, UINTN); +typedef VOID (*virtphys)(UINT64, UINT64, UINT64); +typedef VOID (*block)(UINT64, UINT32); +typedef VOID (*mem_io)(UINT64, UINT8, UINT8, UINT64, UINT64); + +typedef VOID (*map_mem)(UINT64, UINT64, UINT32, UINT32, UINT64); +typedef VOID (*unmap_mem)(UINT64, UINT64, UINT32, UINT32, UINT64); +typedef VOID (*sync_mem)(UINT64, UINT64, UINT32, UINT32, UINT64); + +extern UNDI_CALL_TABLE api_table[]; +extern PXE_SW_UNDI *pxe_31; // !pxe structure for 3.1 drivers +extern UNDI32_DEV *UNDI32DeviceList[MAX_NIC_INTERFACES]; + +// +// functions defined in e100b.c +// +UINT8 InByte (NIC_DATA_INSTANCE *AdapterInfo, UINT32 Port); +UINT16 InWord (NIC_DATA_INSTANCE *AdapterInfo, UINT32 Port); +UINT32 InLong (NIC_DATA_INSTANCE *AdapterInfo, UINT32 Port); +VOID OutByte (NIC_DATA_INSTANCE *AdapterInfo, UINT8 Data, UINT32 Port); +VOID OutWord (NIC_DATA_INSTANCE *AdapterInfo, UINT16 Data, UINT32 Port); +VOID OutLong (NIC_DATA_INSTANCE *AdapterInfo, UINT32 Data, UINT32 Port); + +UINTN E100bInit (NIC_DATA_INSTANCE *AdapterInfo); +UINTN E100bReset (NIC_DATA_INSTANCE *AdapterInfo, INT32 OpFlags); +UINTN E100bShutdown (NIC_DATA_INSTANCE *AdapterInfo); +UINTN E100bTransmit (NIC_DATA_INSTANCE *AdapterInfo, UINT64 cpb, UINT16 opflags); +UINTN E100bReceive (NIC_DATA_INSTANCE *AdapterInfo, UINT64 cpb, UINT64 db); +UINTN E100bSetfilter (NIC_DATA_INSTANCE *AdapterInfo, UINT16 New_filter, + UINT64 cpb, UINT32 cpbsize); +UINTN E100bStatistics(NIC_DATA_INSTANCE *AdapterInfo, UINT64 db, UINT16 dbsize); +UINT8 E100bSetupIAAddr (NIC_DATA_INSTANCE *AdapterInfo); +UINT8 E100bSetInterruptState (NIC_DATA_INSTANCE *AdapterInfo); + +UINT8 E100bGetEepromAddrLen (NIC_DATA_INSTANCE *AdapterInfo); +UINT16 E100bReadEeprom (NIC_DATA_INSTANCE *AdapterInfo, INT32 Location, UINT8 address_len); +INT16 E100bReadEepromAndStationAddress (NIC_DATA_INSTANCE *AdapterInfo); + +UINT16 next(UINT16); +UINT8 SetupCBlink (NIC_DATA_INSTANCE *AdapterInfo); +VOID SetFreeCB (NIC_DATA_INSTANCE *AdapterInfo,TxCB *); +TxCB *GetFreeCB (NIC_DATA_INSTANCE *AdapterInfo); +UINT16 CheckCBList (NIC_DATA_INSTANCE *AdapterInfo); + +UINT8 SelectiveReset (NIC_DATA_INSTANCE *AdapterInfo); +UINT16 InitializeChip (NIC_DATA_INSTANCE *AdapterInfo); +UINT8 SetupReceiveQueues (NIC_DATA_INSTANCE *AdapterInfo); +VOID Recycle_RFD (NIC_DATA_INSTANCE *AdapterInfo, UINT16); +VOID XmitWaitForCompletion (NIC_DATA_INSTANCE *AdapterInfo); +INT8 CommandWaitForCompletion (TxCB *cmd_ptr, NIC_DATA_INSTANCE *AdapterInfo); + +BOOLEAN PhyDetect (NIC_DATA_INSTANCE *AdapterInfo); +VOID PhyReset (NIC_DATA_INSTANCE *AdapterInfo); +VOID +MdiWrite ( + IN NIC_DATA_INSTANCE *AdapterInfo, + IN UINT8 RegAddress, + IN UINT8 PhyAddress, + IN UINT16 DataValue + ); + +VOID +MdiRead( + IN NIC_DATA_INSTANCE *AdapterInfo, + IN UINT8 RegAddress, + IN UINT8 PhyAddress, + IN OUT UINT16 *DataValue + ); + +BOOLEAN SetupPhy (NIC_DATA_INSTANCE *AdapterInfo); +VOID FindPhySpeedAndDpx (NIC_DATA_INSTANCE *AdapterInfo, UINT32 PhyId); + + + +// +// functions defined in init.c +// +EFI_STATUS +InstallConfigTable ( + IN VOID + ); + +EFI_STATUS +EFIAPI +InitializeUNDIDriver ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +VOID +UNDI_notify_virtual ( + EFI_EVENT event, + VOID *context + ); + +EFI_STATUS +EFIAPI +UndiDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +EFI_STATUS +EFIAPI +UndiDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +EFI_STATUS +EFIAPI +UndiDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +EFI_STATUS +AppendMac2DevPath ( + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPtr, + IN EFI_DEVICE_PATH_PROTOCOL *BaseDevPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +TmpDelay ( + IN UINT64 UnqId, + IN UINTN MicroSeconds + ); + +VOID +TmpMemIo ( + IN UINT64 UnqId, + IN UINT8 ReadWrite, + IN UINT8 Len, + IN UINT64 Port, + IN UINT64 BufAddr + ); + +// +// functions defined in decode.c +// +VOID +UNDI_GetState ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_Start ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_Stop ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_GetInitInfo ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_GetConfigInfo ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_Initialize ( + IN PXE_CDB *CdbPtr, + NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_Reset ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_Shutdown ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_Interrupt ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_RecFilter ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_StnAddr ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_Statistics ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_ip2mac ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_NVData ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_Status ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_FillHeader ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_Transmit ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_Receive ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID EFIAPI UNDI_APIEntry_new(UINT64); +VOID UNDI_APIEntry_Common(UINT64); + +PXE_IPV4 convert_mcip(PXE_MAC_ADDR *); +INT32 validate_mcip (PXE_MAC_ADDR *MCastAddr); + +VOID PxeStructInit (PXE_SW_UNDI *PxePtr); +VOID PxeUpdate (NIC_DATA_INSTANCE *NicPtr, PXE_SW_UNDI *PxePtr); + +// +// functions defined in UndiAipImpl.c +// + +/** + Returns the current state information for the adapter. + + This function returns information of type InformationType from the adapter. + If an adapter does not support the requested informational type, then + EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_ADAPTER_INFORMATION_PROTOCOL instance. + @param[in] InformationType A pointer to an EFI_GUID that defines the contents of InformationBlock. + @param[out] InforamtionBlock The service returns a pointer to the buffer with the InformationBlock + structure which contains details about the data specific to InformationType. + @param[out] InforamtionBlockSize The driver returns the size of the InformationBlock in bytes. + + @retval EFI_SUCCESS The InformationType information was retrieved. + @retval EFI_UNSUPPORTED The InformationType is not known. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_INVALID_PARAMETER InformationBlock is NULL. + @retval EFI_INVALID_PARAMETER InformationBlockSize is NULL. + +**/ +EFI_STATUS +EFIAPI +UndiAipGetInfo ( + IN EFI_ADAPTER_INFORMATION_PROTOCOL *This, + IN EFI_GUID *InformationType, + OUT VOID **InformationBlock, + OUT UINTN *InformationBlockSize + ); + +/** + Sets state information for an adapter. + + This function sends information of type InformationType for an adapter. + If an adapter does not support the requested information type, then EFI_UNSUPPORTED + is returned. + + @param[in] This A pointer to the EFI_ADAPTER_INFORMATION_PROTOCOL instance. + @param[in] InformationType A pointer to an EFI_GUID that defines the contents of InformationBlock. + @param[in] InforamtionBlock A pointer to the InformationBlock structure which contains details + about the data specific to InformationType. + @param[in] InforamtionBlockSize The size of the InformationBlock in bytes. + + @retval EFI_SUCCESS The information was received and interpreted successfully. + @retval EFI_UNSUPPORTED The InformationType is not known. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_INVALID_PARAMETER InformationBlock is NULL. + @retval EFI_WRITE_PROTECTED The InformationType cannot be modified using EFI_ADAPTER_INFO_SET_INFO(). + +**/ +EFI_STATUS +EFIAPI +UndiAipSetInfo ( + IN EFI_ADAPTER_INFORMATION_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN VOID *InformationBlock, + IN UINTN InformationBlockSize + ); + +/** + Get a list of supported information types for this instance of the protocol. + + This function returns a list of InformationType GUIDs that are supported on an + adapter with this instance of EFI_ADAPTER_INFORMATION_PROTOCOL. The list is returned + in InfoTypesBuffer, and the number of GUID pointers in InfoTypesBuffer is returned in + InfoTypesBufferCount. + + @param[in] This A pointer to the EFI_ADAPTER_INFORMATION_PROTOCOL instance. + @param[out] InfoTypesBuffer A pointer to the list of InformationType GUID pointers that are supported + by This. + @param[out] InfoTypesBufferCount A pointer to the number of GUID pointers present in InfoTypesBuffer. + + @retval EFI_SUCCESS The list of information type GUIDs that are supported on this adapter was + returned in InfoTypesBuffer. The number of information type GUIDs was + returned in InfoTypesBufferCount. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_INVALID_PARAMETER InfoTypesBuffer is NULL. + @retval EFI_INVALID_PARAMETER InfoTypesBufferCount is NULL. + @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the results. + +**/ +EFI_STATUS +EFIAPI +UndiAipGetSupportedTypes ( + IN EFI_ADAPTER_INFORMATION_PROTOCOL *This, + OUT EFI_GUID **InfoTypesBuffer, + OUT UINTN *InfoTypesBufferCount + ); + +#endif diff --git a/Drivers/OptionRomPkg/UndiRuntimeDxe/UndiAipImpl.c b/Drivers/OptionRomPkg/UndiRuntimeDxe/UndiAipImpl.c new file mode 100644 index 0000000000..21151a076f --- /dev/null +++ b/Drivers/OptionRomPkg/UndiRuntimeDxe/UndiAipImpl.c @@ -0,0 +1,145 @@ +/** @file + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + + +#include "Undi32.h" + + +UINTN mSupportedInfoTypesCount = 1; +EFI_GUID mSupportedInfoTypes[] = { + EFI_ADAPTER_INFO_UNDI_IPV6_SUPPORT_GUID +}; + +/** + Returns the current state information for the adapter. + + This function returns information of type InformationType from the adapter. + If an adapter does not support the requested informational type, then + EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_ADAPTER_INFORMATION_PROTOCOL instance. + @param[in] InformationType A pointer to an EFI_GUID that defines the contents of InformationBlock. + @param[out] InforamtionBlock The service returns a pointer to the buffer with the InformationBlock + structure which contains details about the data specific to InformationType. + @param[out] InforamtionBlockSize The driver returns the size of the InformationBlock in bytes. + + @retval EFI_SUCCESS The InformationType information was retrieved. + @retval EFI_UNSUPPORTED The InformationType is not known. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_INVALID_PARAMETER InformationBlock is NULL. + @retval EFI_INVALID_PARAMETER InformationBlockSize is NULL. + +**/ +EFI_STATUS +EFIAPI +UndiAipGetInfo ( + IN EFI_ADAPTER_INFORMATION_PROTOCOL *This, + IN EFI_GUID *InformationType, + OUT VOID **InformationBlock, + OUT UINTN *InformationBlockSize + ) +{ + UNDI32_DEV *UNDI32Device; + EFI_ADAPTER_INFO_UNDI_IPV6_SUPPORT *UndiIpv6Support; + + if (This == NULL || InformationBlock == NULL || InformationBlockSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (!CompareGuid (InformationType, &gEfiAdapterInfoUndiIpv6SupportGuid)) { + return EFI_UNSUPPORTED; + } + + UNDI32Device = UNDI_DEV_FROM_AIP (This); + *InformationBlockSize = sizeof (EFI_ADAPTER_INFO_UNDI_IPV6_SUPPORT); + *InformationBlock = AllocateZeroPool (*InformationBlockSize); + if (*InformationBlock == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + UndiIpv6Support = (EFI_ADAPTER_INFO_UNDI_IPV6_SUPPORT *) (*InformationBlock); + UndiIpv6Support->Ipv6Support = UNDI32Device->NIIProtocol_31.Ipv6Supported; + + return EFI_SUCCESS; +} + +/** + Sets state information for an adapter. + + This function sends information of type InformationType for an adapter. + If an adapter does not support the requested information type, then EFI_UNSUPPORTED + is returned. + + @param[in] This A pointer to the EFI_ADAPTER_INFORMATION_PROTOCOL instance. + @param[in] InformationType A pointer to an EFI_GUID that defines the contents of InformationBlock. + @param[in] InforamtionBlock A pointer to the InformationBlock structure which contains details + about the data specific to InformationType. + @param[in] InforamtionBlockSize The size of the InformationBlock in bytes. + + @retval EFI_SUCCESS The information was received and interpreted successfully. + @retval EFI_UNSUPPORTED The InformationType is not known. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_INVALID_PARAMETER InformationBlock is NULL. + @retval EFI_WRITE_PROTECTED The InformationType cannot be modified using EFI_ADAPTER_INFO_SET_INFO(). + +**/ +EFI_STATUS +EFIAPI +UndiAipSetInfo ( + IN EFI_ADAPTER_INFORMATION_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN VOID *InformationBlock, + IN UINTN InformationBlockSize + ) +{ + return EFI_WRITE_PROTECTED; +} + +/** + Get a list of supported information types for this instance of the protocol. + + This function returns a list of InformationType GUIDs that are supported on an + adapter with this instance of EFI_ADAPTER_INFORMATION_PROTOCOL. The list is returned + in InfoTypesBuffer, and the number of GUID pointers in InfoTypesBuffer is returned in + InfoTypesBufferCount. + + @param[in] This A pointer to the EFI_ADAPTER_INFORMATION_PROTOCOL instance. + @param[out] InfoTypesBuffer A pointer to the list of InformationType GUID pointers that are supported + by This. + @param[out] InfoTypesBufferCount A pointer to the number of GUID pointers present in InfoTypesBuffer. + + @retval EFI_SUCCESS The list of information type GUIDs that are supported on this adapter was + returned in InfoTypesBuffer. The number of information type GUIDs was + returned in InfoTypesBufferCount. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_INVALID_PARAMETER InfoTypesBuffer is NULL. + @retval EFI_INVALID_PARAMETER InfoTypesBufferCount is NULL. + @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the results. + +**/ +EFI_STATUS +EFIAPI +UndiAipGetSupportedTypes ( + IN EFI_ADAPTER_INFORMATION_PROTOCOL *This, + OUT EFI_GUID **InfoTypesBuffer, + OUT UINTN *InfoTypesBufferCount + ) +{ + if (This == NULL || InfoTypesBuffer == NULL || InfoTypesBufferCount == NULL) { + return EFI_INVALID_PARAMETER; + } + + *InfoTypesBufferCount = 1; + *InfoTypesBuffer = AllocateCopyPool (sizeof (EFI_GUID), &gEfiAdapterInfoUndiIpv6SupportGuid); + if (InfoTypesBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + return EFI_SUCCESS; +} diff --git a/Drivers/OptionRomPkg/UndiRuntimeDxe/UndiRuntimeDxe.inf b/Drivers/OptionRomPkg/UndiRuntimeDxe/UndiRuntimeDxe.inf new file mode 100644 index 0000000000..96666dc88a --- /dev/null +++ b/Drivers/OptionRomPkg/UndiRuntimeDxe/UndiRuntimeDxe.inf @@ -0,0 +1,72 @@ +## @file +# Component description file for Undi module. +# +# This module provides support for Universal Network Driver Interface. +# Notes: this module is no longer regular maintained/validated. +# +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = UndiRuntimeDxe + FILE_GUID = A1f436EA-A127-4EF8-957C-8048606FF670 + MODULE_TYPE = DXE_RUNTIME_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = InitializeUndi + +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + Undi32.h + E100b.h + E100b.c + Decode.c + Init.c + ComponentName.c + UndiAipImpl.c + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiLib + UefiBootServicesTableLib + BaseMemoryLib + DebugLib + UefiRuntimeLib + UefiDriverEntryPoint + BaseLib + MemoryAllocationLib + +[Protocols] + gEfiNetworkInterfaceIdentifierProtocolGuid_31 + gEfiPciIoProtocolGuid + gEfiDevicePathProtocolGuid + gEfiAdapterInformationProtocolGuid + +[Guids] + gEfiEventExitBootServicesGuid ## PRODUCES ## Event + gEfiEventVirtualAddressChangeGuid ## PRODUCES ## Event + gEfiAdapterInfoUndiIpv6SupportGuid ## PRODUCES + +[Depex] + gEfiBdsArchProtocolGuid AND + gEfiCpuArchProtocolGuid AND + gEfiMetronomeArchProtocolGuid AND + gEfiMonotonicCounterArchProtocolGuid AND + gEfiRealTimeClockArchProtocolGuid AND + gEfiResetArchProtocolGuid AND + gEfiRuntimeArchProtocolGuid AND + gEfiSecurityArchProtocolGuid AND + gEfiTimerArchProtocolGuid AND + gEfiVariableWriteArchProtocolGuid AND + gEfiVariableArchProtocolGuid AND + gEfiWatchdogTimerArchProtocolGuid -- 2.21.0.windows.1