--- /dev/null
+/** @file\r
+* SMSC LAN91x series Network Controller Driver.\r
+*\r
+* Copyright (c) 2013-2017 Linaro.org\r
+*\r
+* Derived from the LAN9118 driver. Original sources\r
+* Copyright (c) 2012-2013, ARM Limited. All rights reserved.\r
+*\r
+* This program and the accompanying materials are licensed and\r
+* made available under the terms and conditions of the BSD License\r
+* which accompanies this distribution. The full text of the license\r
+* may be found at: http://opensource.org/licenses/bsd-license.php\r
+*\r
+* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+*\r
+**/\r
+\r
+#include <Uefi.h>\r
+#include <Uefi/UefiSpec.h>\r
+#include <Base.h>\r
+\r
+// Protocols used by this driver\r
+#include <Protocol/SimpleNetwork.h>\r
+#include <Protocol/ComponentName2.h>\r
+#include <Protocol/PxeBaseCode.h>\r
+#include <Protocol/DevicePath.h>\r
+\r
+// Libraries used by this driver\r
+#include <Library/UefiLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/IoLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Library/NetLib.h>\r
+#include <Library/DevicePathLib.h>\r
+\r
+// Hardware register definitions\r
+#include "Lan91xDxeHw.h"\r
+\r
+// Debugging output options\r
+//#define LAN91X_PRINT_REGISTERS 1\r
+//#define LAN91X_PRINT_PACKET_HEADERS 1\r
+//#define LAN91X_PRINT_RECEIVE_FILTERS 1\r
+\r
+// Chip power-down option -- UNTESTED\r
+//#define LAN91X_POWER_DOWN 1\r
+\r
+/*---------------------------------------------------------------------------------------------------------------------\r
+\r
+ LAN91x Information Structure\r
+\r
+---------------------------------------------------------------------------------------------------------------------*/\r
+typedef struct _LAN91X_DRIVER {\r
+ // Driver signature\r
+ UINT32 Signature;\r
+ EFI_HANDLE ControllerHandle;\r
+\r
+ // EFI SNP protocol instances\r
+ EFI_SIMPLE_NETWORK_PROTOCOL Snp;\r
+ EFI_SIMPLE_NETWORK_MODE SnpMode;\r
+\r
+ // EFI Snp statistics instance\r
+ EFI_NETWORK_STATISTICS Stats;\r
+\r
+ // Transmit Buffer recycle queue\r
+\r
+ LIST_ENTRY TransmitQueueHead;\r
+\r
+ // Register access variables\r
+ UINTN IoBase; // I/O Base Address\r
+ UINT8 Revision; // Chip Revision Number\r
+ INT8 PhyAd; // Phy Address\r
+ UINT8 BankSel; // Currently selected register bank\r
+\r
+} LAN91X_DRIVER;\r
+\r
+#define LAN91X_NO_PHY (-1) // PhyAd value if PHY not detected\r
+\r
+#define LAN91X_SIGNATURE SIGNATURE_32('S', 'M', '9', '1')\r
+#define INSTANCE_FROM_SNP_THIS(a) CR(a, LAN91X_DRIVER, Snp, LAN91X_SIGNATURE)\r
+\r
+#define LAN91X_STALL 2\r
+#define LAN91X_MEMORY_ALLOC_POLLS 100 // Max times to poll for memory allocation\r
+#define LAN91X_PKT_OVERHEAD 6 // Overhead bytes in packet buffer\r
+\r
+// Synchronization TPLs\r
+#define LAN91X_TPL TPL_CALLBACK\r
+\r
+// Most common CRC32 Polynomial for little endian machines\r
+#define CRC_POLYNOMIAL 0xEDB88320\r
+\r
+\r
+typedef struct {\r
+ MAC_ADDR_DEVICE_PATH Lan91x;\r
+ EFI_DEVICE_PATH_PROTOCOL End;\r
+} LAN91X_DEVICE_PATH;\r
+\r
+LAN91X_DEVICE_PATH Lan91xPathTemplate = {\r
+ {\r
+ {\r
+ MESSAGING_DEVICE_PATH, MSG_MAC_ADDR_DP,\r
+ { (UINT8) (sizeof(MAC_ADDR_DEVICE_PATH)), (UINT8) ((sizeof(MAC_ADDR_DEVICE_PATH)) >> 8) }\r
+ },\r
+ { { 0 } },\r
+ 0\r
+ },\r
+ {\r
+ END_DEVICE_PATH_TYPE,\r
+ END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
+ { sizeof(EFI_DEVICE_PATH_PROTOCOL), 0 }\r
+ }\r
+};\r
+\r
+// Chip ID numbers and name strings\r
+#define CHIP_9192 3\r
+#define CHIP_9194 4\r
+#define CHIP_9195 5\r
+#define CHIP_9196 6\r
+#define CHIP_91100 7\r
+#define CHIP_91100FD 8\r
+#define CHIP_91111FD 9\r
+\r
+STATIC CHAR16 CONST * CONST ChipIds[ 16 ] = {\r
+ NULL, NULL, NULL,\r
+ /* 3 */ L"SMC91C90/91C92",\r
+ /* 4 */ L"SMC91C94",\r
+ /* 5 */ L"SMC91C95",\r
+ /* 6 */ L"SMC91C96",\r
+ /* 7 */ L"SMC91C100",\r
+ /* 8 */ L"SMC91C100FD",\r
+ /* 9 */ L"SMC91C11xFD",\r
+ NULL, NULL, NULL,\r
+ NULL, NULL, NULL\r
+};\r
+\r
+/* ------------------ TxBuffer Queue structures ------------------- */\r
+\r
+typedef struct {\r
+ VOID *Buf;\r
+ UINTN Length;\r
+} MSK_SYSTEM_BUF;\r
+\r
+typedef struct {\r
+ UINTN Signature;\r
+ LIST_ENTRY Link;\r
+ MSK_SYSTEM_BUF SystemBuf;\r
+} MSK_LINKED_SYSTEM_BUF;\r
+\r
+#define TX_MBUF_SIGNATURE SIGNATURE_32 ('t','x','m','b')\r
+\r
+/* ------------------ MAC Address Hash Calculations ------------------- */\r
+\r
+/*\r
+** Generate a hash value from a multicast address\r
+**\r
+** This uses the Ethernet standard CRC32 algorithm\r
+**\r
+** INFO USED:\r
+** 1: http://en.wikipedia.org/wiki/Cyclic_redundancy_check\r
+**\r
+** 2: http://www.erg.abdn.ac.uk/~gorry/eg3567/dl-pages/crc.html\r
+**\r
+** 3: http://en.wikipedia.org/wiki/Computation_of_CRC\r
+*/\r
+STATIC\r
+UINT32\r
+MulticastHash (\r
+ IN EFI_MAC_ADDRESS *Mac,\r
+ IN UINT32 AddrLen\r
+ )\r
+{\r
+ UINT32 Iter;\r
+ UINT32 Remainder;\r
+ UINT32 Crc32;\r
+ UINT8 *Addr;\r
+\r
+ // 0xFFFFFFFF is standard seed for Ethernet\r
+ Remainder = 0xFFFFFFFF;\r
+\r
+ // Generate the remainder byte-by-byte (LSB first)\r
+ Addr = &Mac->Addr[0];\r
+ while (AddrLen-- > 0) {\r
+ Remainder ^= *Addr++;\r
+ for (Iter = 0; Iter < 8; ++Iter) {\r
+ // Check if exponent is set\r
+ if ((Remainder & 1) != 0) {\r
+ Remainder = (Remainder >> 1) ^ CRC_POLYNOMIAL;\r
+ } else {\r
+ Remainder = (Remainder >> 1) ^ 0;\r
+ }\r
+ }\r
+ }\r
+\r
+ // Reverse the bits of the remainder\r
+ Crc32 = 0;\r
+ for (Iter = 0; Iter < 32; ++Iter) {\r
+ Crc32 <<= 1;\r
+ Crc32 |= Remainder & 1;\r
+ Remainder >>= 1;\r
+ }\r
+ return Crc32;\r
+}\r
+\r
+\r
+/* ---------------- Banked Register Operations ------------------ */\r
+\r
+// Select the proper I/O bank\r
+STATIC\r
+VOID\r
+SelectIoBank (\r
+ LAN91X_DRIVER *LanDriver,\r
+ UINTN Register\r
+ )\r
+{\r
+ UINT8 Bank;\r
+\r
+ Bank = RegisterToBank (Register);\r
+\r
+ // Select the proper I/O bank\r
+ if (LanDriver->BankSel != Bank) {\r
+ MmioWrite16 (LanDriver->IoBase + LAN91X_BANK_OFFSET, Bank);\r
+ LanDriver->BankSel = Bank;\r
+ }\r
+}\r
+\r
+// Read a 16-bit I/O-space register\r
+STATIC\r
+UINT16\r
+ReadIoReg16 (\r
+ LAN91X_DRIVER *LanDriver,\r
+ UINTN Register\r
+ )\r
+{\r
+ UINT8 Offset;\r
+\r
+ // Select the proper I/O bank\r
+ SelectIoBank (LanDriver, Register);\r
+\r
+ // Read the requested register\r
+ Offset = RegisterToOffset (Register);\r
+ return MmioRead16 (LanDriver->IoBase + Offset);\r
+}\r
+\r
+// Write a 16-bit I/O-space register\r
+STATIC\r
+UINT16\r
+WriteIoReg16 (\r
+ LAN91X_DRIVER *LanDriver,\r
+ UINTN Register,\r
+ UINT16 Value\r
+ )\r
+{\r
+ UINT8 Offset;\r
+\r
+ // Select the proper I/O bank\r
+ SelectIoBank (LanDriver, Register);\r
+\r
+ // Write the requested register\r
+ Offset = RegisterToOffset (Register);\r
+ return MmioWrite16 (LanDriver->IoBase + Offset, Value);\r
+}\r
+\r
+// Read an 8-bit I/O-space register\r
+STATIC\r
+UINT8\r
+ReadIoReg8 (\r
+ LAN91X_DRIVER *LanDriver,\r
+ UINTN Register\r
+ )\r
+{\r
+ UINT8 Offset;\r
+\r
+ // Select the proper I/O bank\r
+ SelectIoBank (LanDriver, Register);\r
+\r
+ // Read the requested register\r
+ Offset = RegisterToOffset (Register);\r
+ return MmioRead8 (LanDriver->IoBase + Offset);\r
+}\r
+\r
+// Write an 8-bit I/O-space register\r
+STATIC\r
+UINT8\r
+WriteIoReg8 (\r
+ LAN91X_DRIVER *LanDriver,\r
+ UINTN Register,\r
+ UINT8 Value\r
+ )\r
+{\r
+ UINT8 Offset;\r
+\r
+ // Select the proper I/O bank\r
+ SelectIoBank (LanDriver, Register);\r
+\r
+ // Write the requested register\r
+ Offset = RegisterToOffset (Register);\r
+ return MmioWrite8 (LanDriver->IoBase + Offset, Value);\r
+}\r
+\r
+\r
+/* ---------------- MII/PHY Access Operations ------------------ */\r
+\r
+#define LAN91X_MDIO_STALL 1\r
+\r
+STATIC\r
+VOID\r
+MdioOutput (\r
+ LAN91X_DRIVER *LanDriver,\r
+ UINTN Bits,\r
+ UINT32 Value\r
+ )\r
+{\r
+ UINT16 MgmtReg;\r
+ UINT32 Mask;\r
+\r
+ MgmtReg = ReadIoReg16 (LanDriver, LAN91X_MGMT);\r
+ MgmtReg &= ~MGMT_MCLK;\r
+ MgmtReg |= MGMT_MDOE;\r
+\r
+ for (Mask = (1 << (Bits - 1)); Mask != 0; Mask >>= 1) {\r
+ if ((Value & Mask) != 0) {\r
+ MgmtReg |= MGMT_MDO;\r
+ } else {\r
+ MgmtReg &= ~MGMT_MDO;\r
+ }\r
+\r
+ WriteIoReg16 (LanDriver, LAN91X_MGMT, MgmtReg);\r
+ gBS->Stall (LAN91X_MDIO_STALL);\r
+ WriteIoReg16 (LanDriver, LAN91X_MGMT, MgmtReg | MGMT_MCLK);\r
+ gBS->Stall (LAN91X_MDIO_STALL);\r
+ }\r
+}\r
+#define PHY_OUTPUT_TIME (2 * LAN91X_MDIO_STALL)\r
+\r
+STATIC\r
+UINT32\r
+MdioInput (\r
+ LAN91X_DRIVER *LanDriver,\r
+ UINTN Bits\r
+ )\r
+{\r
+ UINT16 MgmtReg;\r
+ UINT32 Mask;\r
+ UINT32 Value;\r
+\r
+ MgmtReg = ReadIoReg16 (LanDriver, LAN91X_MGMT);\r
+ MgmtReg &= ~(MGMT_MDOE | MGMT_MCLK | MGMT_MDO);\r
+ WriteIoReg16 (LanDriver, LAN91X_MGMT, MgmtReg);\r
+\r
+ Value = 0;\r
+ for (Mask = (1 << (Bits - 1)); Mask != 0; Mask >>= 1) {\r
+ if ((ReadIoReg16 (LanDriver, LAN91X_MGMT) & MGMT_MDI) != 0) {\r
+ Value |= Mask;\r
+ }\r
+\r
+ WriteIoReg16 (LanDriver, LAN91X_MGMT, MgmtReg);\r
+ gBS->Stall (LAN91X_MDIO_STALL);\r
+ WriteIoReg16 (LanDriver, LAN91X_MGMT, MgmtReg | MGMT_MCLK);\r
+ gBS->Stall (LAN91X_MDIO_STALL);\r
+ }\r
+\r
+ return Value;\r
+}\r
+#define PHY_INPUT_TIME (2 * LAN91X_MDIO_STALL)\r
+\r
+STATIC\r
+VOID\r
+MdioIdle (\r
+ LAN91X_DRIVER *LanDriver\r
+ )\r
+{\r
+ UINT16 MgmtReg;\r
+\r
+ MgmtReg = ReadIoReg16 (LanDriver, LAN91X_MGMT);\r
+ MgmtReg &= ~(MGMT_MDOE | MGMT_MCLK | MGMT_MDO);\r
+ WriteIoReg16 (LanDriver, LAN91X_MGMT, MgmtReg);\r
+}\r
+\r
+// Write to a PHY register\r
+STATIC\r
+VOID\r
+WritePhyReg16 (\r
+ LAN91X_DRIVER *LanDriver,\r
+ UINTN RegAd,\r
+ UINT16 Value\r
+ )\r
+{\r
+ // Bit-bang the MII Serial Frame write operation\r
+ MdioOutput (LanDriver, 32, 0xffffffff); // Send 32 Ones as a preamble\r
+ MdioOutput (LanDriver, 2, 0x01); // Send Start (01)\r
+ MdioOutput (LanDriver, 2, 0x01); // Send Write (01)\r
+ MdioOutput (LanDriver, 5, LanDriver->PhyAd); // Send PHYAD[4:0]\r
+ MdioOutput (LanDriver, 5, RegAd); // Send REGAD[4:0]\r
+ MdioOutput (LanDriver, 2, 0x02); // Send TurnAround (10)\r
+ MdioOutput (LanDriver, 16, Value); // Write 16 data bits\r
+\r
+ // Idle the MDIO bus\r
+ MdioIdle (LanDriver);\r
+}\r
+// Calculate approximate time to write a PHY register in microseconds\r
+#define PHY_WRITE_TIME ((32 + 2 + 2 + 5 + 5 + 2 + 16) * PHY_OUTPUT_TIME)\r
+\r
+// Read from a PHY register\r
+STATIC\r
+UINT16\r
+ReadPhyReg16 (\r
+ LAN91X_DRIVER *LanDriver,\r
+ UINTN RegAd\r
+ )\r
+{\r
+ UINT32 Value;\r
+\r
+ // Bit-bang the MII Serial Frame read operation\r
+ MdioOutput (LanDriver, 32, 0xffffffff); // Send 32 Ones as a preamble\r
+ MdioOutput (LanDriver, 2, 0x01); // Send Start (01)\r
+ MdioOutput (LanDriver, 2, 0x02); // Send Read (10)\r
+ MdioOutput (LanDriver, 5, LanDriver->PhyAd); // Send PHYAD[4:0]\r
+ MdioOutput (LanDriver, 5, RegAd); // Send REGAD[4:0]\r
+\r
+ (VOID) MdioInput (LanDriver, 2); // Discard TurnAround bits\r
+ Value = MdioInput (LanDriver, 16); // Read 16 data bits\r
+\r
+ // Idle the MDIO bus\r
+ MdioIdle (LanDriver);\r
+\r
+ return (Value & 0xffff);\r
+}\r
+// Calculate approximate time to read a PHY register in microseconds\r
+#define PHY_READ_TIME (((32 + 2 + 2 + 5 + 5) * PHY_OUTPUT_TIME) + \\r
+ ((2 + 16) * PHY_INPUT_TIME))\r
+\r
+\r
+/* ---------------- Debug Functions ------------------ */\r
+\r
+#ifdef LAN91X_PRINT_REGISTERS\r
+STATIC\r
+VOID\r
+PrintIoRegisters (\r
+ IN LAN91X_DRIVER *LanDriver\r
+ )\r
+{\r
+ UINTN Bank;\r
+ UINTN Offset;\r
+ UINT16 Value;\r
+\r
+ DEBUG ((DEBUG_ERROR, "\nLAN91x I/O Register Dump:\n"));\r
+\r
+ // Print currrent bank select register\r
+ Value = MmioRead16 (LanDriver->IoBase + LAN91X_BANK_OFFSET);\r
+ DEBUG ((DEBUG_ERROR, " BankSel: %d Bank Register %04x (%d)\n",\r
+ LanDriver->BankSel, Value, Value & 0x0007));\r
+\r
+ // Print all I/O registers\r
+ for (Offset = 0; Offset < 0x0e; Offset += 2) {\r
+ DEBUG ((DEBUG_ERROR, " %02x:", Offset));\r
+ for (Bank = 0; Bank <= 3; ++Bank) {\r
+ DEBUG ((DEBUG_ERROR, " %04x", ReadIoReg16 (LanDriver, MakeRegister (Bank, Offset))));\r
+ }\r
+ DEBUG ((DEBUG_ERROR, "\n"));\r
+ }\r
+}\r
+\r
+STATIC\r
+VOID\r
+PrintPhyRegisters (\r
+ IN LAN91X_DRIVER *LanDriver\r
+ )\r
+{\r
+ UINTN RegNum;\r
+\r
+ DEBUG ((DEBUG_ERROR, "\nLAN91x Phy %d Register Dump:\n", LanDriver->PhyAd));\r
+\r
+ // Print all Phy registers\r
+ for (RegNum = 0; RegNum <= 5; ++RegNum) {\r
+ DEBUG ((DEBUG_ERROR, " %2d: %04x\n",\r
+ RegNum,\r
+ ReadPhyReg16 (LanDriver, RegNum)\r
+ ));\r
+ }\r
+ for (RegNum = 16; RegNum <= 20; ++RegNum) {\r
+ DEBUG ((DEBUG_ERROR, " %2d: %04x\n",\r
+ RegNum,\r
+ ReadPhyReg16 (LanDriver, RegNum)\r
+ ));\r
+ }\r
+}\r
+#endif\r
+\r
+#if LAN91X_PRINT_PACKET_HEADERS\r
+STATIC\r
+VOID\r
+PrintIpDgram (\r
+ IN CONST VOID *DstMac,\r
+ IN CONST VOID *SrcMac,\r
+ IN CONST VOID *Proto,\r
+ IN CONST VOID *IpDgram\r
+ )\r
+{\r
+ CONST UINT8 *Ptr;\r
+ UINT16 SrcPort;\r
+ UINT16 DstPort;\r
+\r
+ Ptr = DstMac;\r
+ DEBUG ((DEBUG_ERROR, " Dst: %02x-%02x-%02x",\r
+ Ptr[0], Ptr[1], Ptr[2]));\r
+ DEBUG ((DEBUG_ERROR, "-%02x-%02x-%02x",\r
+ Ptr[3], Ptr[4], Ptr[5]));\r
+\r
+ Ptr = SrcMac;\r
+ DEBUG ((DEBUG_ERROR, " Src: %02x-%02x-%02x",\r
+ Ptr[0], Ptr[1], Ptr[2]));\r
+ DEBUG ((DEBUG_ERROR, "-%02x-%02x-%02x",\r
+ Ptr[3], Ptr[4], Ptr[5]));\r
+\r
+ Ptr = Proto;\r
+ DEBUG ((DEBUG_ERROR, " Proto: %02x%02x\n",\r
+ Ptr[0], Ptr[1]));\r
+\r
+ Ptr = IpDgram;\r
+ switch (Ptr[9]) {\r
+ case EFI_IP_PROTO_ICMP:\r
+ DEBUG ((DEBUG_ERROR, " ICMP"));\r
+ break;\r
+ case EFI_IP_PROTO_TCP:\r
+ DEBUG ((DEBUG_ERROR, " TCP"));\r
+ break;\r
+ case EFI_IP_PROTO_UDP:\r
+ DEBUG ((DEBUG_ERROR, " UDP"));\r
+ break;\r
+ default:\r
+ DEBUG ((DEBUG_ERROR, " IpProto %d\n", Ptr[9]));\r
+ return;\r
+ }\r
+\r
+ DEBUG ((DEBUG_ERROR, " SrcIp: %d.%d.%d.%d",\r
+ Ptr[12], Ptr[13], Ptr[14], Ptr[15]));\r
+ DEBUG ((DEBUG_ERROR, " DstIp: %d.%d.%d.%d",\r
+ Ptr[16], Ptr[17], Ptr[18], Ptr[19]));\r
+\r
+ SrcPort = (Ptr[20] << 8) | Ptr[21];\r
+ DstPort = (Ptr[22] << 8) | Ptr[23];\r
+ DEBUG ((DEBUG_ERROR, " SrcPort: %d DstPort: %d\n", SrcPort, DstPort));\r
+}\r
+#endif\r
+\r
+\r
+/* ---------------- PHY Management Operations ----------------- */\r
+\r
+STATIC\r
+EFI_STATUS\r
+PhyDetect (\r
+ IN LAN91X_DRIVER *LanDriver\r
+ )\r
+{\r
+ UINT16 PhyId1;\r
+ UINT16 PhyId2;\r
+\r
+ for (LanDriver->PhyAd = 0x1f; LanDriver->PhyAd >= 0 ; --LanDriver->PhyAd) {\r
+ PhyId1 = ReadPhyReg16 (LanDriver, PHY_INDEX_ID1);\r
+ PhyId2 = ReadPhyReg16 (LanDriver, PHY_INDEX_ID2);\r
+\r
+ if ((PhyId1 != 0x0000) && (PhyId1 != 0xffff) &&\r
+ (PhyId2 != 0x0000) && (PhyId2 != 0xffff)) {\r
+ if ((PhyId1 == 0x0016) && ((PhyId2 & 0xfff0) == 0xf840)) {\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: PHY type LAN83C183 (LAN91C111 Internal)\n"));\r
+ } else if ((PhyId1 == 0x0282) && ((PhyId2 & 0xfff0) == 0x1c50)) {\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: PHY type LAN83C180\n"));\r
+ } else {\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: PHY id %04x:%04x\n", PhyId1, PhyId2));\r
+ }\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: PHY detection failed\n"));\r
+ return EFI_NO_MEDIA;\r
+}\r
+\r
+\r
+// Check the Link Status and take appropriate action\r
+STATIC\r
+BOOLEAN\r
+CheckLinkStatus (\r
+ IN LAN91X_DRIVER *LanDriver\r
+ )\r
+{\r
+ UINT16 PhyStatus;\r
+\r
+ // Get the PHY Status\r
+ PhyStatus = ReadPhyReg16 (LanDriver, PHY_INDEX_BASIC_STATUS);\r
+\r
+ return (PhyStatus & PHYSTS_LINK_STS) != 0;\r
+}\r
+\r
+\r
+// Do auto-negotiation\r
+STATIC\r
+EFI_STATUS\r
+PhyAutoNegotiate (\r
+ IN LAN91X_DRIVER *LanDriver\r
+ )\r
+{\r
+ UINTN Retries;\r
+ UINT16 PhyControl;\r
+ UINT16 PhyStatus;\r
+ UINT16 PhyAdvert;\r
+\r
+ // If there isn't a PHY, don't try to reset it\r
+ if (LanDriver->PhyAd == LAN91X_NO_PHY) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ // Next check that auto-negotiation is supported\r
+ PhyStatus = ReadPhyReg16 (LanDriver, PHY_INDEX_BASIC_STATUS);\r
+ if ((PhyStatus & PHYSTS_AUTO_CAP) == 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ // Translate capabilities to advertise\r
+ PhyAdvert = PHYANA_CSMA;\r
+\r
+ if ((PhyStatus & PHYSTS_10BASET_HDPLX) != 0) {\r
+ PhyAdvert |= PHYANA_10BASET;\r
+ }\r
+ if ((PhyStatus & PHYSTS_10BASET_FDPLX) != 0) {\r
+ PhyAdvert |= PHYANA_10BASETFD;\r
+ }\r
+ if ((PhyStatus & PHYSTS_100BASETX_HDPLX) != 0) {\r
+ PhyAdvert |= PHYANA_100BASETX;\r
+ }\r
+ if ((PhyStatus & PHYSTS_100BASETX_FDPLX) != 0) {\r
+ PhyAdvert |= PHYANA_100BASETXFD;\r
+ }\r
+ if ((PhyStatus & PHYSTS_100BASE_T4) != 0) {\r
+ PhyAdvert |= PHYANA_100BASET4;\r
+ }\r
+\r
+ // Set the capabilities to advertise\r
+ WritePhyReg16 (LanDriver, PHY_INDEX_AUTO_NEG_ADVERT, PhyAdvert);\r
+ (VOID) ReadPhyReg16 (LanDriver, PHY_INDEX_AUTO_NEG_ADVERT);\r
+\r
+ // Restart Auto-Negotiation\r
+ PhyControl = ReadPhyReg16 (LanDriver, PHY_INDEX_BASIC_CTRL);\r
+ PhyControl &= ~(PHYCR_SPEED_SEL | PHYCR_DUPLEX_MODE);\r
+ PhyControl |= PHYCR_AUTO_EN | PHYCR_RST_AUTO;\r
+ WritePhyReg16 (LanDriver, PHY_INDEX_BASIC_CTRL, PhyControl);\r
+\r
+ // Wait up to 2 seconds for the process to complete\r
+ Retries = 2000000 / (PHY_READ_TIME + 100);\r
+ while ((ReadPhyReg16 (LanDriver, PHY_INDEX_BASIC_STATUS) & PHYSTS_AUTO_COMP) == 0) {\r
+ if (--Retries == 0) {\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: PHY auto-negotiation timed-out\n"));\r
+ return EFI_TIMEOUT;\r
+ }\r
+ gBS->Stall (100);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+// Perform PHY software reset\r
+STATIC\r
+EFI_STATUS\r
+PhySoftReset (\r
+ IN LAN91X_DRIVER *LanDriver\r
+ )\r
+{\r
+ UINTN Retries;\r
+\r
+ // If there isn't a PHY, don't try to reset it\r
+ if (LanDriver->PhyAd == LAN91X_NO_PHY) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ // Request a PHY reset\r
+ WritePhyReg16 (LanDriver, PHY_INDEX_BASIC_CTRL, PHYCR_RESET);\r
+\r
+ // The internal PHY will reset within 50ms. Allow 100ms.\r
+ Retries = 100000 / (PHY_READ_TIME + 100);\r
+ while (ReadPhyReg16 (LanDriver, PHY_INDEX_BASIC_CTRL) & PHYCR_RESET) {\r
+ if (--Retries == 0) {\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: PHY reset timed-out\n"));\r
+ return EFI_TIMEOUT;\r
+ }\r
+ gBS->Stall (100);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/* ---------------- General Operations ----------------- */\r
+\r
+STATIC\r
+EFI_MAC_ADDRESS\r
+GetCurrentMacAddress (\r
+ IN LAN91X_DRIVER *LanDriver\r
+ )\r
+{\r
+ UINTN RegNum;\r
+ UINT8 *Addr;\r
+ EFI_MAC_ADDRESS MacAddress;\r
+\r
+ SetMem (&MacAddress, sizeof(MacAddress), 0);\r
+\r
+ Addr = &MacAddress.Addr[0];\r
+ for (RegNum = LAN91X_IAR0; RegNum <= LAN91X_IAR5; ++RegNum) {\r
+ *Addr = ReadIoReg8 (LanDriver, RegNum);\r
+ ++Addr;\r
+ }\r
+\r
+ return MacAddress;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+SetCurrentMacAddress (\r
+ IN LAN91X_DRIVER *LanDriver,\r
+ IN EFI_MAC_ADDRESS *MacAddress\r
+ )\r
+{\r
+ UINTN RegNum;\r
+ UINT8 *Addr;\r
+\r
+ Addr = &MacAddress->Addr[0];\r
+ for (RegNum = LAN91X_IAR0; RegNum <= LAN91X_IAR5; ++RegNum) {\r
+ WriteIoReg8 (LanDriver, RegNum, *Addr);\r
+ ++Addr;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+MmuOperation (\r
+ IN LAN91X_DRIVER *LanDriver,\r
+ IN UINTN MmuOp\r
+ )\r
+{\r
+ UINTN Polls;\r
+\r
+ WriteIoReg16 (LanDriver, LAN91X_MMUCR, MmuOp);\r
+ Polls = 100;\r
+ while ((ReadIoReg16 (LanDriver, LAN91X_MMUCR) & MMUCR_BUSY) != 0) {\r
+ if (--Polls == 0) {\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: MMU operation %04x timed-out\n", MmuOp));\r
+ return EFI_TIMEOUT;\r
+ }\r
+ gBS->Stall (LAN91X_STALL);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+// Read bytes from the DATA register\r
+STATIC\r
+EFI_STATUS\r
+ReadIoData (\r
+ IN LAN91X_DRIVER *LanDriver,\r
+ IN VOID *Buffer,\r
+ IN UINTN BufLen\r
+ )\r
+{\r
+ UINT8 *Ptr;\r
+\r
+ Ptr = Buffer;\r
+ for (; BufLen > 0; --BufLen) {\r
+ *Ptr = ReadIoReg8 (LanDriver, LAN91X_DATA0);\r
+ ++Ptr;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+// Write bytes to the DATA register\r
+STATIC\r
+EFI_STATUS\r
+WriteIoData (\r
+ IN LAN91X_DRIVER *LanDriver,\r
+ IN VOID *Buffer,\r
+ IN UINTN BufLen\r
+ )\r
+{\r
+ UINT8 *Ptr;\r
+\r
+ Ptr = Buffer;\r
+ for (; BufLen > 0; --BufLen) {\r
+ WriteIoReg8 (LanDriver, LAN91X_DATA0, *Ptr);\r
+ ++Ptr;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+// Disable the interface\r
+STATIC\r
+EFI_STATUS\r
+ChipDisable (\r
+ IN LAN91X_DRIVER *LanDriver\r
+ )\r
+{\r
+#ifdef LAN91X_POWER_DOWN\r
+ UINT16 Val16;\r
+#endif\r
+\r
+ // Stop Rx and Tx operations\r
+ WriteIoReg16 (LanDriver, LAN91X_RCR, RCR_CLEAR);\r
+ WriteIoReg16 (LanDriver, LAN91X_TCR, TCR_CLEAR);\r
+\r
+#ifdef LAN91X_POWER_DOWN\r
+ // Power-down the chip\r
+ Val16 = ReadIoReg16 (LanDriver, LAN91X_CR);\r
+ Val16 &= ~CR_EPH_POWER_EN;\r
+ WriteIoReg16 (LanDriver, LAN91X_CR, Val16);\r
+#endif\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+// Enable the interface\r
+STATIC\r
+EFI_STATUS\r
+ChipEnable (\r
+ IN LAN91X_DRIVER *LanDriver\r
+ )\r
+{\r
+#ifdef LAN91X_POWER_DOWN\r
+ UINT16 Val16;\r
+\r
+ // Power-up the chip\r
+ Val16 = ReadIoReg16 (LanDriver, LAN91X_CR);\r
+ Val16 |= CR_EPH_POWER_EN;\r
+ WriteIoReg16 (LanDriver, LAN91X_CR, Val16);\r
+ gBS->Stall (LAN91X_STALL);\r
+#endif\r
+\r
+ // Start Rx and Tx operations\r
+ WriteIoReg16 (LanDriver, LAN91X_TCR, TCR_DEFAULT);\r
+ WriteIoReg16 (LanDriver, LAN91X_RCR, RCR_DEFAULT);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+// Perform software reset on the LAN91x\r
+STATIC\r
+EFI_STATUS\r
+SoftReset (\r
+ IN LAN91X_DRIVER *LanDriver\r
+ )\r
+{\r
+ UINT16 Val16;\r
+\r
+ // Issue the reset\r
+ WriteIoReg16 (LanDriver, LAN91X_RCR, RCR_SOFT_RST);\r
+ gBS->Stall (LAN91X_STALL);\r
+ WriteIoReg16 (LanDriver, LAN91X_RCR, RCR_CLEAR);\r
+\r
+ // Set the configuration register\r
+ WriteIoReg16 (LanDriver, LAN91X_CR, CR_DEFAULT);\r
+ gBS->Stall (LAN91X_STALL);\r
+\r
+ // Stop Rx and Tx\r
+ WriteIoReg16 (LanDriver, LAN91X_RCR, RCR_CLEAR);\r
+ WriteIoReg16 (LanDriver, LAN91X_TCR, TCR_CLEAR);\r
+\r
+ // Initialize the Control Register\r
+ Val16 = ReadIoReg16 (LanDriver, LAN91X_CTR);\r
+ Val16 |= CTR_AUTO_REL;\r
+ WriteIoReg16 (LanDriver, LAN91X_CTR, Val16);\r
+\r
+ // Reset the MMU\r
+ MmuOperation (LanDriver, MMUCR_OP_RESET_MMU);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/*\r
+** Probe()\r
+**\r
+** Validate that there is a LAN91x device.\r
+**\r
+*/\r
+STATIC\r
+EFI_STATUS\r
+Probe (\r
+ IN LAN91X_DRIVER *LanDriver\r
+ )\r
+{\r
+ UINT16 Bank;\r
+ UINT16 Val16;\r
+ CHAR16 CONST *ChipId;\r
+ UINTN ResetTime;\r
+\r
+ // First check that the Bank Select register is valid\r
+ Bank = MmioRead16 (LanDriver->IoBase + LAN91X_BANK_OFFSET);\r
+ if ((Bank & 0xff00) != 0x3300) {\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: signature error: expecting 33xx, read %04x\n", Bank));\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ // Try reading the revision register next\r
+ LanDriver->BankSel = 0xff;\r
+ Val16 = ReadIoReg16 (LanDriver, LAN91X_REV);\r
+\r
+ Bank = MmioRead16 (LanDriver->IoBase + LAN91X_BANK_OFFSET);\r
+ if ((Bank & 0xff03) != 0x3303) {\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: signature error: expecting 33x3, read %04x\n", Bank));\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ // Validate the revision register\r
+ if ((Val16 & 0xff00) != 0x3300) {\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: revision error: expecting 33xx, read %04x\n", Val16));\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ ChipId = ChipIds[(Val16 >> 4) & 0x0f];\r
+ if (ChipId == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: unrecognized revision: %04x\n", Val16));\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: detected chip %s rev %d\n", ChipId, Val16 & 0xf));\r
+ LanDriver->Revision = Val16 & 0xff;\r
+\r
+ // Reload from EEPROM to get the hardware MAC address\r
+ WriteIoReg16 (LanDriver, LAN91X_CTR, CTR_RESERVED | CTR_RELOAD);\r
+ ResetTime = 1000;\r
+ while ((ReadIoReg16 (LanDriver, LAN91X_CTR) & CTR_RELOAD) != 0) {\r
+ if (--ResetTime == 0) {\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: reload from EEPROM timed-out\n"));\r
+ WriteIoReg16 (LanDriver, LAN91X_CTR, CTR_RESERVED);\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ gBS->Stall (LAN91X_STALL);\r
+ }\r
+\r
+ // Read and save the Permanent MAC Address\r
+ LanDriver->SnpMode.PermanentAddress = GetCurrentMacAddress (LanDriver);\r
+ LanDriver->SnpMode.CurrentAddress = LanDriver->SnpMode.PermanentAddress;\r
+ DEBUG ((DEBUG_ERROR, //DEBUG_NET | DEBUG_INFO,\r
+ "LAN91x: HW MAC Address: %02x-%02x-%02x-%02x-%02x-%02x\n",\r
+ LanDriver->SnpMode.PermanentAddress.Addr[0],\r
+ LanDriver->SnpMode.PermanentAddress.Addr[1],\r
+ LanDriver->SnpMode.PermanentAddress.Addr[2],\r
+ LanDriver->SnpMode.PermanentAddress.Addr[3],\r
+ LanDriver->SnpMode.PermanentAddress.Addr[4],\r
+ LanDriver->SnpMode.PermanentAddress.Addr[5]\r
+ ));\r
+\r
+ // Reset the device\r
+ SoftReset (LanDriver);\r
+\r
+ // Try to detect a PHY\r
+ if (LanDriver->Revision > (CHIP_91100 << 4)) {\r
+ PhyDetect (LanDriver);\r
+ } else {\r
+ LanDriver->PhyAd = LAN91X_NO_PHY;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+\r
+\r
+/*------------------ Simple Network Driver entry point functions ------------------*/\r
+\r
+// Refer to the Simple Network Protocol section (21.1)\r
+// in the UEFI 2.3.1 Specification for documentation.\r
+\r
+#define ReturnUnlock(s) do { Status = (s); goto exit_unlock; } while(0)\r
+\r
+\r
+/*\r
+** UEFI Start() function\r
+**\r
+*/\r
+EFI_STATUS\r
+EFIAPI\r
+SnpStart (\r
+ IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp\r
+ )\r
+{\r
+ EFI_SIMPLE_NETWORK_MODE *Mode;\r
+ EFI_TPL SavedTpl;\r
+ EFI_STATUS Status;\r
+\r
+ // Check Snp instance\r
+ if (Snp == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Serialize access to data and registers\r
+ SavedTpl = gBS->RaiseTPL (LAN91X_TPL);\r
+ Mode = Snp->Mode;\r
+\r
+ // Check state of the driver\r
+ switch (Mode->State) {\r
+ case EfiSimpleNetworkStopped:\r
+ break;\r
+ case EfiSimpleNetworkStarted:\r
+ case EfiSimpleNetworkInitialized:\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Driver already started\n"));\r
+ ReturnUnlock (EFI_ALREADY_STARTED);\r
+ default:\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",\r
+ (UINTN)Snp->Mode->State));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ }\r
+\r
+\r
+ // Change state\r
+ Mode->State = EfiSimpleNetworkStarted;\r
+ Status = EFI_SUCCESS;\r
+\r
+ // Restore TPL and return\r
+exit_unlock:\r
+ gBS->RestoreTPL (SavedTpl);\r
+ return Status;\r
+}\r
+\r
+/*\r
+** UEFI Stop() function\r
+**\r
+*/\r
+EFI_STATUS\r
+EFIAPI\r
+SnpStop (\r
+ IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp\r
+ )\r
+{\r
+ LAN91X_DRIVER *LanDriver;\r
+ EFI_TPL SavedTpl;\r
+ EFI_STATUS Status;\r
+\r
+ // Check Snp Instance\r
+ if (Snp == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Serialize access to data and registers\r
+ SavedTpl = gBS->RaiseTPL (LAN91X_TPL);\r
+\r
+ // Check state of the driver\r
+ switch (Snp->Mode->State) {\r
+ case EfiSimpleNetworkStarted:\r
+ case EfiSimpleNetworkInitialized:\r
+ break;\r
+ case EfiSimpleNetworkStopped:\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n"));\r
+ ReturnUnlock (EFI_NOT_STARTED);\r
+ default:\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",\r
+ (UINTN)Snp->Mode->State));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ }\r
+\r
+ // Find the LanDriver structure\r
+ LanDriver = INSTANCE_FROM_SNP_THIS(Snp);\r
+\r
+ // Stop the Tx and Rx\r
+ ChipDisable (LanDriver);\r
+\r
+ // Change the state\r
+ Snp->Mode->State = EfiSimpleNetworkStopped;\r
+ Status = EFI_SUCCESS;\r
+\r
+ // Restore TPL and return\r
+exit_unlock:\r
+ gBS->RestoreTPL (SavedTpl);\r
+ return Status;\r
+}\r
+\r
+/*\r
+** UEFI Initialize() function\r
+**\r
+*/\r
+EFI_STATUS\r
+EFIAPI\r
+SnpInitialize (\r
+ IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp,\r
+ IN UINTN RxBufferSize OPTIONAL,\r
+ IN UINTN TxBufferSize OPTIONAL\r
+ )\r
+{\r
+ LAN91X_DRIVER *LanDriver;\r
+ EFI_TPL SavedTpl;\r
+ EFI_STATUS Status;\r
+\r
+ // Check Snp Instance\r
+ if (Snp == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Serialize access to data and registers\r
+ SavedTpl = gBS->RaiseTPL (LAN91X_TPL);\r
+\r
+ // Check that driver was started but not initialised\r
+ switch (Snp->Mode->State) {\r
+ case EfiSimpleNetworkStarted:\r
+ break;\r
+ case EfiSimpleNetworkInitialized:\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Driver already initialized\n"));\r
+ ReturnUnlock (EFI_SUCCESS);\r
+ case EfiSimpleNetworkStopped:\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n"));\r
+ ReturnUnlock (EFI_NOT_STARTED);\r
+ default:\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",\r
+ (UINTN)Snp->Mode->State));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ }\r
+\r
+ // Find the LanDriver structure\r
+ LanDriver = INSTANCE_FROM_SNP_THIS(Snp);\r
+\r
+ // Initiate a software reset\r
+ Status = SoftReset (LanDriver);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Soft reset failed\n"));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ }\r
+\r
+ // Initiate a PHY reset\r
+ if (PhySoftReset (LanDriver) < 0) {\r
+ Snp->Mode->State = EfiSimpleNetworkStopped;\r
+ DEBUG ((DEBUG_WARN, "LAN91x: PHY soft reset timeout\n"));\r
+ ReturnUnlock (EFI_NOT_STARTED);\r
+ }\r
+\r
+ // Do auto-negotiation\r
+ Status = PhyAutoNegotiate (LanDriver);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_WARN, "LAN91x: PHY auto-negotiation failed\n"));\r
+ }\r
+\r
+ // Enable the receiver and transmitter\r
+ ChipEnable (LanDriver);\r
+\r
+ // Now acknowledge all interrupts\r
+ WriteIoReg8 (LanDriver, LAN91X_IST, 0xFF);\r
+\r
+ // Declare the driver as initialized\r
+ Snp->Mode->State = EfiSimpleNetworkInitialized;\r
+ Status = EFI_SUCCESS;\r
+\r
+ // Restore TPL and return\r
+exit_unlock:\r
+ gBS->RestoreTPL (SavedTpl);\r
+ return Status;\r
+}\r
+\r
+/*\r
+** UEFI Reset () function\r
+**\r
+*/\r
+EFI_STATUS\r
+EFIAPI\r
+SnpReset (\r
+ IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp,\r
+ IN BOOLEAN Verification\r
+ )\r
+{\r
+ LAN91X_DRIVER *LanDriver;\r
+ EFI_TPL SavedTpl;\r
+ EFI_STATUS Status;\r
+\r
+ // Check Snp Instance\r
+ if (Snp == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Serialize access to data and registers\r
+ SavedTpl = gBS->RaiseTPL (LAN91X_TPL);\r
+\r
+ // Check that driver was started and initialised\r
+ switch (Snp->Mode->State) {\r
+ case EfiSimpleNetworkInitialized:\r
+ break;\r
+ case EfiSimpleNetworkStarted:\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n"));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ case EfiSimpleNetworkStopped:\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n"));\r
+ ReturnUnlock (EFI_NOT_STARTED);\r
+ default:\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",\r
+ (UINTN)Snp->Mode->State));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ }\r
+\r
+ // Find the LanDriver structure\r
+ LanDriver = INSTANCE_FROM_SNP_THIS(Snp);\r
+\r
+ // Initiate a software reset\r
+ if (EFI_ERROR (SoftReset (LanDriver))) {\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Soft reset failed\n"));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ }\r
+\r
+ // Initiate a PHY reset\r
+ if (EFI_ERROR (PhySoftReset (LanDriver))) {\r
+ DEBUG ((DEBUG_WARN, "LAN91x: PHY soft reset failed\n"));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ }\r
+\r
+ // Enable the receiver and transmitter\r
+ Status = ChipEnable (LanDriver);\r
+\r
+ // Restore TPL and return\r
+exit_unlock:\r
+ gBS->RestoreTPL (SavedTpl);\r
+ return Status;\r
+}\r
+\r
+/*\r
+** UEFI Shutdown () function\r
+**\r
+*/\r
+EFI_STATUS\r
+EFIAPI\r
+SnpShutdown (\r
+ IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp\r
+ )\r
+{\r
+ LAN91X_DRIVER *LanDriver;\r
+ EFI_TPL SavedTpl;\r
+ EFI_STATUS Status;\r
+\r
+ // Check Snp Instance\r
+ if (Snp == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Serialize access to data and registers\r
+ SavedTpl = gBS->RaiseTPL (LAN91X_TPL);\r
+\r
+ // First check that driver has already been initialized\r
+ switch (Snp->Mode->State) {\r
+ case EfiSimpleNetworkInitialized:\r
+ break;\r
+ case EfiSimpleNetworkStarted:\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n"));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ case EfiSimpleNetworkStopped:\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Driver in stopped state\n"));\r
+ ReturnUnlock (EFI_NOT_STARTED);\r
+ default:\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",\r
+ (UINTN)Snp->Mode->State));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ }\r
+\r
+ // Find the LanDriver structure\r
+ LanDriver = INSTANCE_FROM_SNP_THIS(Snp);\r
+\r
+ // Disable the interface\r
+ Status = ChipDisable (LanDriver);\r
+\r
+ // Restore TPL and return\r
+exit_unlock:\r
+ gBS->RestoreTPL (SavedTpl);\r
+ return Status;\r
+}\r
+\r
+\r
+/*\r
+** UEFI ReceiveFilters() function\r
+**\r
+*/\r
+EFI_STATUS\r
+EFIAPI\r
+SnpReceiveFilters (\r
+ IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp,\r
+ IN UINT32 Enable,\r
+ IN UINT32 Disable,\r
+ IN BOOLEAN Reset,\r
+ IN UINTN NumMfilter OPTIONAL,\r
+ IN EFI_MAC_ADDRESS *Mfilter OPTIONAL\r
+ )\r
+{\r
+#define MCAST_HASH_BYTES 8\r
+\r
+ LAN91X_DRIVER *LanDriver;\r
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;\r
+ EFI_TPL SavedTpl;\r
+ EFI_STATUS Status;\r
+ UINTN i;\r
+ UINT32 Crc;\r
+ UINT16 RcvCtrl;\r
+ UINT8 McastHash[MCAST_HASH_BYTES];\r
+\r
+ // Check Snp Instance\r
+ if (Snp == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Serialize access to data and registers\r
+ SavedTpl = gBS->RaiseTPL (LAN91X_TPL);\r
+\r
+ // First check that driver has already been initialized\r
+ switch (Snp->Mode->State) {\r
+ case EfiSimpleNetworkInitialized:\r
+ break;\r
+ case EfiSimpleNetworkStarted:\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n"));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ case EfiSimpleNetworkStopped:\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n"));\r
+ ReturnUnlock (EFI_NOT_STARTED);\r
+ default:\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",\r
+ (UINTN)Snp->Mode->State));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ }\r
+\r
+ // Find the LanDriver structure\r
+ LanDriver = INSTANCE_FROM_SNP_THIS(Snp);\r
+ SnpMode = Snp->Mode;\r
+\r
+#ifdef LAN91X_PRINT_RECEIVE_FILTERS\r
+ DEBUG ((DEBUG_ERROR, "LAN91x:SnpReceiveFilters()\n"));\r
+ DEBUG ((DEBUG_ERROR, " Enable = %08x\n", Enable));\r
+ DEBUG ((DEBUG_ERROR, " Disable = %08x\n", Disable));\r
+ DEBUG ((DEBUG_ERROR, " Reset = %d\n", Reset));\r
+ DEBUG ((DEBUG_ERROR, " NumMfilter = %d\n", NumMfilter));\r
+ for (i = 0; i < NumMfilter; ++i) {\r
+ DEBUG ((DEBUG_ERROR,\r
+ " [%2d] = %02x-%02x-%02x-%02x-%02x-%02x\n",\r
+ i,\r
+ Mfilter[i].Addr[0],\r
+ Mfilter[i].Addr[1],\r
+ Mfilter[i].Addr[2],\r
+ Mfilter[i].Addr[3],\r
+ Mfilter[i].Addr[4],\r
+ Mfilter[i].Addr[5]));\r
+ }\r
+#endif\r
+\r
+ // Update the Multicast Hash registers\r
+ if (Reset) {\r
+ // Clear the hash table\r
+ SetMem (McastHash, MCAST_HASH_BYTES, 0);\r
+ SnpMode->MCastFilterCount = 0;\r
+ } else {\r
+ // Read the current hash table\r
+ for (i = 0; i < MCAST_HASH_BYTES; ++i) {\r
+ McastHash[i] = ReadIoReg8 (LanDriver, LAN91X_MT0 + i);\r
+ }\r
+ // Set the new additions\r
+ for (i = 0; i < NumMfilter; ++i) {\r
+ Crc = MulticastHash (&Mfilter[i], NET_ETHER_ADDR_LEN);\r
+ McastHash[(Crc >> 29) & 0x3] |= 1 << ((Crc >> 26) & 0x3);\r
+ }\r
+ SnpMode->MCastFilterCount = NumMfilter;\r
+ }\r
+ // If the hash registers need updating, write them\r
+ if (Reset || NumMfilter > 0) {\r
+ for (i = 0; i < MCAST_HASH_BYTES; ++i) {\r
+ WriteIoReg8 (LanDriver, LAN91X_MT0 + i, McastHash[i]);\r
+ }\r
+ }\r
+\r
+ RcvCtrl = ReadIoReg16 (LanDriver, LAN91X_RCR);\r
+ if ((Enable & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) != 0) {\r
+ RcvCtrl |= RCR_PRMS;\r
+ SnpMode->ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS;\r
+ }\r
+ if ((Disable & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) != 0) {\r
+ RcvCtrl &= ~RCR_PRMS;\r
+ SnpMode->ReceiveFilterSetting &= ~EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS;\r
+ }\r
+\r
+ if ((Enable & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) != 0) {\r
+ RcvCtrl |= RCR_ALMUL;\r
+ SnpMode->ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;\r
+ }\r
+ if ((Disable & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) != 0) {\r
+ RcvCtrl &= ~RCR_ALMUL;\r
+ SnpMode->ReceiveFilterSetting &= ~EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;\r
+ }\r
+ WriteIoReg16 (LanDriver, LAN91X_RCR, RcvCtrl);\r
+\r
+ Status = SetCurrentMacAddress (LanDriver, &SnpMode->CurrentAddress);\r
+\r
+ // Restore TPL and return\r
+exit_unlock:\r
+ gBS->RestoreTPL (SavedTpl);\r
+ return Status;\r
+}\r
+\r
+/*\r
+** UEFI StationAddress() function\r
+**\r
+*/\r
+EFI_STATUS\r
+EFIAPI\r
+SnpStationAddress (\r
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp,\r
+ IN BOOLEAN Reset,\r
+ IN EFI_MAC_ADDRESS *NewMac\r
+)\r
+{\r
+ LAN91X_DRIVER *LanDriver;\r
+ EFI_TPL SavedTpl;\r
+ EFI_STATUS Status;\r
+\r
+ // Check Snp instance\r
+ if (Snp == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Serialize access to data and registers\r
+ SavedTpl = gBS->RaiseTPL (LAN91X_TPL);\r
+\r
+ // Check that driver was started and initialised\r
+ switch (Snp->Mode->State) {\r
+ case EfiSimpleNetworkInitialized:\r
+ break;\r
+ case EfiSimpleNetworkStarted:\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n"));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ case EfiSimpleNetworkStopped:\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n"));\r
+ ReturnUnlock (EFI_NOT_STARTED);\r
+ default:\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",\r
+ (UINTN)Snp->Mode->State));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ }\r
+\r
+ // Find the LanDriver structure\r
+ LanDriver = INSTANCE_FROM_SNP_THIS(Snp);\r
+\r
+ if (Reset) {\r
+ Snp->Mode->CurrentAddress = Snp->Mode->PermanentAddress;\r
+ } else {\r
+ if (NewMac == NULL) {\r
+ ReturnUnlock (EFI_INVALID_PARAMETER);\r
+ }\r
+ Snp->Mode->CurrentAddress = *NewMac;\r
+ }\r
+\r
+ Status = SetCurrentMacAddress (LanDriver, &Snp->Mode->CurrentAddress);\r
+\r
+ // Restore TPL and return\r
+exit_unlock:\r
+ gBS->RestoreTPL (SavedTpl);\r
+ return Status;\r
+}\r
+\r
+/*\r
+** UEFI Statistics() function\r
+**\r
+*/\r
+EFI_STATUS\r
+EFIAPI\r
+SnpStatistics (\r
+ IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp,\r
+ IN BOOLEAN Reset,\r
+ IN OUT UINTN *StatSize,\r
+ OUT EFI_NETWORK_STATISTICS *Statistics\r
+ )\r
+{\r
+ LAN91X_DRIVER *LanDriver;\r
+ EFI_TPL SavedTpl;\r
+ EFI_STATUS Status;\r
+\r
+ // Check Snp instance\r
+ if (Snp == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Check pointless condition\r
+ if ((!Reset) && (StatSize == NULL) && (Statistics == NULL)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ // Check the parameters\r
+ if ((StatSize == NULL) && (Statistics != NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Serialize access to data and registers\r
+ SavedTpl = gBS->RaiseTPL (LAN91X_TPL);\r
+\r
+ // Check that driver was started and initialised\r
+ switch (Snp->Mode->State) {\r
+ case EfiSimpleNetworkInitialized:\r
+ break;\r
+ case EfiSimpleNetworkStarted:\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n"));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ case EfiSimpleNetworkStopped:\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n"));\r
+ ReturnUnlock (EFI_NOT_STARTED);\r
+ default:\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",\r
+ (UINTN)Snp->Mode->State));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ }\r
+\r
+ // Find the LanDriver structure\r
+ LanDriver = INSTANCE_FROM_SNP_THIS(Snp);\r
+\r
+ // Do a reset if required\r
+ if (Reset) {\r
+ ZeroMem (&LanDriver->Stats, sizeof(EFI_NETWORK_STATISTICS));\r
+ }\r
+\r
+ // Check buffer size\r
+ if (*StatSize < sizeof(EFI_NETWORK_STATISTICS)) {\r
+ *StatSize = sizeof(EFI_NETWORK_STATISTICS);\r
+ ReturnUnlock (EFI_BUFFER_TOO_SMALL);\r
+ goto exit_unlock;\r
+ }\r
+\r
+ // Fill in the statistics\r
+ CopyMem(&Statistics, &LanDriver->Stats, sizeof(EFI_NETWORK_STATISTICS));\r
+ Status = EFI_SUCCESS;\r
+\r
+ // Restore TPL and return\r
+exit_unlock:\r
+ gBS->RestoreTPL (SavedTpl);\r
+ return Status;\r
+}\r
+\r
+/*\r
+** UEFI MCastIPtoMAC() function\r
+**\r
+*/\r
+EFI_STATUS\r
+EFIAPI\r
+SnpMcastIptoMac (\r
+ IN EFI_SIMPLE_NETWORK_PROTOCOL* Snp,\r
+ IN BOOLEAN IsIpv6,\r
+ IN EFI_IP_ADDRESS *Ip,\r
+ OUT EFI_MAC_ADDRESS *McastMac\r
+ )\r
+{\r
+ // Check Snp instance\r
+ if (Snp == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Check parameters\r
+ if ((McastMac == NULL) || (Ip == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Make sure MAC address is empty\r
+ ZeroMem (McastMac, sizeof(EFI_MAC_ADDRESS));\r
+\r
+ // If we need ipv4 address\r
+ if (!IsIpv6) {\r
+ // Most significant 25 bits of a multicast HW address are set\r
+ McastMac->Addr[0] = 0x01;\r
+ McastMac->Addr[1] = 0x00;\r
+ McastMac->Addr[2] = 0x5E;\r
+\r
+ // Lower 23 bits from ipv4 address\r
+ McastMac->Addr[3] = (Ip->v4.Addr[1] & 0x7F); // Clear the ms bit (25th bit of MAC must be 0)\r
+ McastMac->Addr[4] = Ip->v4.Addr[2];\r
+ McastMac->Addr[5] = Ip->v4.Addr[3];\r
+ } else {\r
+ // Most significant 16 bits of multicast v6 HW address are set\r
+ McastMac->Addr[0] = 0x33;\r
+ McastMac->Addr[1] = 0x33;\r
+\r
+ // lower four octets are taken from ipv6 address\r
+ McastMac->Addr[2] = Ip->v6.Addr[8];\r
+ McastMac->Addr[3] = Ip->v6.Addr[9];\r
+ McastMac->Addr[4] = Ip->v6.Addr[10];\r
+ McastMac->Addr[5] = Ip->v6.Addr[11];\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/*\r
+** UEFI NvData() function\r
+**\r
+*/\r
+EFI_STATUS\r
+EFIAPI\r
+SnpNvData (\r
+ IN EFI_SIMPLE_NETWORK_PROTOCOL* pobj,\r
+ IN BOOLEAN read_write,\r
+ IN UINTN offset,\r
+ IN UINTN buff_size,\r
+ IN OUT VOID *data\r
+ )\r
+{\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: Non-volatile storage not supported\n"));\r
+\r
+ return EFI_UNSUPPORTED;\r
+}\r
+\r
+\r
+/*\r
+** UEFI GetStatus () function\r
+**\r
+*/\r
+EFI_STATUS\r
+EFIAPI\r
+SnpGetStatus (\r
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp,\r
+ OUT UINT32 *IrqStat OPTIONAL,\r
+ OUT VOID **TxBuff OPTIONAL\r
+ )\r
+{\r
+ LAN91X_DRIVER *LanDriver;\r
+ EFI_TPL SavedTpl;\r
+ EFI_STATUS Status;\r
+ BOOLEAN MediaPresent;\r
+ UINT8 IstReg;\r
+ MSK_LINKED_SYSTEM_BUF *LinkedTXRecycleBuff;\r
+\r
+ // Check preliminaries\r
+ if (Snp == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Serialize access to data and registers\r
+ SavedTpl = gBS->RaiseTPL (LAN91X_TPL);\r
+\r
+ // Check that driver was started and initialised\r
+ switch (Snp->Mode->State) {\r
+ case EfiSimpleNetworkInitialized:\r
+ break;\r
+ case EfiSimpleNetworkStarted:\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n"));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ case EfiSimpleNetworkStopped:\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n"));\r
+ ReturnUnlock (EFI_NOT_STARTED);\r
+ default:\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",\r
+ (UINTN)Snp->Mode->State));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ }\r
+\r
+ // Find the LanDriver structure\r
+ LanDriver = INSTANCE_FROM_SNP_THIS(Snp);\r
+\r
+ // Arbitrarily set the interrupt status to 0\r
+ if (IrqStat != NULL) {\r
+ *IrqStat = 0;\r
+ IstReg = ReadIoReg8 (LanDriver, LAN91X_IST);\r
+ if ((IstReg & IST_RCV) != 0) {\r
+ *IrqStat |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;\r
+ }\r
+ if ((IstReg & IST_TX) != 0) {\r
+ *IrqStat |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;\r
+ }\r
+ }\r
+\r
+ // Pass back the completed buffer address\r
+ // The transmit buffer status is not read when TxBuf is NULL\r
+ if (TxBuff != NULL) {\r
+ *((UINT8 **) TxBuff) = (UINT8 *) 0;\r
+ if( !IsListEmpty (&LanDriver->TransmitQueueHead))\r
+ {\r
+ LinkedTXRecycleBuff = CR (GetFirstNode (&LanDriver->TransmitQueueHead), MSK_LINKED_SYSTEM_BUF, Link, TX_MBUF_SIGNATURE);\r
+ if(LinkedTXRecycleBuff != NULL) {\r
+ *TxBuff = LinkedTXRecycleBuff->SystemBuf.Buf;\r
+ RemoveEntryList (&LinkedTXRecycleBuff->Link);\r
+ FreePool (LinkedTXRecycleBuff);\r
+ }\r
+ }\r
+ }\r
+\r
+ // Update the media status\r
+ MediaPresent = CheckLinkStatus (LanDriver);\r
+ if (MediaPresent != Snp->Mode->MediaPresent) {\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Link %s\n", MediaPresent ? L"up" : L"down"));\r
+ }\r
+ Snp->Mode->MediaPresent = MediaPresent;\r
+ Status = EFI_SUCCESS;\r
+\r
+ // Restore TPL and return\r
+exit_unlock:\r
+ gBS->RestoreTPL (SavedTpl);\r
+ return Status;\r
+}\r
+\r
+\r
+/*\r
+** UEFI Transmit() function\r
+**\r
+*/\r
+EFI_STATUS\r
+EFIAPI\r
+SnpTransmit (\r
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp,\r
+ IN UINTN HdrSize,\r
+ IN UINTN BufSize,\r
+ IN VOID *BufAddr,\r
+ IN EFI_MAC_ADDRESS *SrcAddr OPTIONAL,\r
+ IN EFI_MAC_ADDRESS *DstAddr OPTIONAL,\r
+ IN UINT16 *Protocol OPTIONAL\r
+ )\r
+{\r
+ LAN91X_DRIVER *LanDriver;\r
+ EFI_TPL SavedTpl;\r
+ EFI_STATUS Status;\r
+ UINT8 *Ptr;\r
+ UINTN Len;\r
+ UINTN MmuPages;\r
+ UINTN Retries;\r
+ UINT16 Proto;\r
+ UINT8 PktNum;\r
+ MSK_LINKED_SYSTEM_BUF *LinkedTXRecycleBuff;\r
+\r
+\r
+ // Check preliminaries\r
+ if ((Snp == NULL) || (BufAddr == NULL)) {\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: SnpTransmit(): NULL Snp (%p) or BufAddr (%p)\n",\r
+ Snp, BufAddr));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Serialize access to data and registers\r
+ SavedTpl = gBS->RaiseTPL (LAN91X_TPL);\r
+\r
+ // Check that driver was started and initialised\r
+ switch (Snp->Mode->State) {\r
+ case EfiSimpleNetworkInitialized:\r
+ break;\r
+ case EfiSimpleNetworkStarted:\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n"));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ case EfiSimpleNetworkStopped:\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n"));\r
+ ReturnUnlock (EFI_NOT_STARTED);\r
+ default:\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",\r
+ (UINTN)Snp->Mode->State));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ }\r
+\r
+ // Find the LanDriver structure\r
+ LanDriver = INSTANCE_FROM_SNP_THIS(Snp);\r
+\r
+ // Ensure header is correct size if non-zero\r
+ if (HdrSize != 0) {\r
+ if (HdrSize != Snp->Mode->MediaHeaderSize) {\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: SnpTransmit(): Invalid HdrSize %d\n", HdrSize));\r
+ ReturnUnlock (EFI_INVALID_PARAMETER);\r
+ }\r
+\r
+ if ((DstAddr == NULL) || (Protocol == NULL)) {\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: SnpTransmit(): NULL DstAddr %p or Protocol %p\n",\r
+ DstAddr, Protocol));\r
+ ReturnUnlock (EFI_INVALID_PARAMETER);\r
+ }\r
+ }\r
+\r
+ // Before transmitting check the link status\r
+ if (!Snp->Mode->MediaPresent) {\r
+ DEBUG ((DEBUG_WARN, "LAN91x: SnpTransmit(): Link not ready\n"));\r
+ ReturnUnlock (EFI_NOT_READY);\r
+ }\r
+\r
+ // Calculate the request size in 256-byte "pages" minus 1\r
+ // The 91C111 ignores this, but some older devices need it.\r
+ MmuPages = ((BufSize & ~1) + LAN91X_PKT_OVERHEAD - 1) >> 8;\r
+ if (MmuPages > 7) {\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Tx buffer too large (%d bytes)\n", BufSize));\r
+ LanDriver->Stats.TxOversizeFrames += 1;\r
+ LanDriver->Stats.TxDroppedFrames += 1;\r
+ ReturnUnlock (EFI_BAD_BUFFER_SIZE);\r
+ }\r
+\r
+ // Request allocation of a transmit buffer\r
+ Status = MmuOperation (LanDriver, MMUCR_OP_TX_ALLOC | MmuPages);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: Tx buffer request failure: %d\n", Status));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ }\r
+\r
+ // Wait for allocation request completion\r
+ Retries = LAN91X_MEMORY_ALLOC_POLLS;\r
+ while ((ReadIoReg8 (LanDriver, LAN91X_IST) & IST_ALLOC) == 0) {\r
+ if (--Retries == 0) {\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: Tx buffer allocation timeout\n"));\r
+ ReturnUnlock (EFI_TIMEOUT);\r
+ }\r
+ }\r
+\r
+ // Check for successful allocation\r
+ PktNum = ReadIoReg8 (LanDriver, LAN91X_ARR);\r
+ if ((PktNum & ARR_FAILED) != 0) {\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: Tx buffer allocation failure: %02x\n", PktNum));\r
+ ReturnUnlock (EFI_NOT_READY);\r
+ }\r
+ PktNum &= ARR_PACKET;\r
+\r
+ // Check for the nature of the frame\r
+ // If no destination address, it's ARP broadcast\r
+ if(DstAddr != NULL)\r
+ {\r
+ if (DstAddr->Addr[0] == 0xFF) {\r
+ LanDriver->Stats.TxBroadcastFrames += 1;\r
+ } else if ((DstAddr->Addr[0] & 0x1) == 1) {\r
+ LanDriver->Stats.TxMulticastFrames += 1;\r
+ } else {\r
+ LanDriver->Stats.TxUnicastFrames += 1;\r
+ }\r
+ } else {\r
+ LanDriver->Stats.TxBroadcastFrames += 1;\r
+ }\r
+\r
+ // Set the Packet Number and Pointer registers\r
+ WriteIoReg8 (LanDriver, LAN91X_PNR, PktNum);\r
+ WriteIoReg16 (LanDriver, LAN91X_PTR, PTR_AUTO_INCR);\r
+\r
+ // Set up mutable buffer information variables\r
+ Ptr = BufAddr;\r
+ Len = BufSize;\r
+\r
+ // Write Status and Byte Count first\r
+ WriteIoReg16 (LanDriver, LAN91X_DATA0, 0);\r
+ WriteIoReg16 (LanDriver, LAN91X_DATA0, (Len + LAN91X_PKT_OVERHEAD) & BCW_COUNT);\r
+\r
+ // This packet may come with a preconfigured Ethernet header.\r
+ // If not, we need to construct one from optional parameters.\r
+ if (HdrSize) {\r
+\r
+ // Write the destination address\r
+ WriteIoData (LanDriver, DstAddr, NET_ETHER_ADDR_LEN);\r
+\r
+ // Write the Source Address\r
+ if (SrcAddr != NULL) {\r
+ WriteIoData (LanDriver, SrcAddr, NET_ETHER_ADDR_LEN);\r
+ } else {\r
+ WriteIoData (LanDriver, &LanDriver->SnpMode.CurrentAddress, NET_ETHER_ADDR_LEN);\r
+ }\r
+\r
+ // Write the Protocol word\r
+ Proto = HTONS (*Protocol);\r
+ WriteIoReg16 (LanDriver, LAN91X_DATA0, Proto);\r
+\r
+ // Adjust the data start and length\r
+ Ptr += sizeof(ETHER_HEAD);\r
+ Len -= sizeof(ETHER_HEAD);\r
+ }\r
+\r
+ // Copy the remainder data buffer, except the odd byte\r
+ WriteIoData (LanDriver, Ptr, Len & ~1);\r
+ Ptr += Len & ~1;\r
+ Len &= 1;\r
+\r
+ // Write the Packet Control Word and odd byte\r
+ WriteIoReg16 (LanDriver, LAN91X_DATA0,\r
+ (Len != 0) ? (PCW_ODD | PCW_CRC | *Ptr) : PCW_CRC);\r
+\r
+ // Release the packet for transmission\r
+ Status = MmuOperation (LanDriver, MMUCR_OP_TX_PUSH);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: Tx buffer release failure: %d\n", Status));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ }\r
+\r
+ // Update the Tx statistics\r
+ LanDriver->Stats.TxTotalBytes += BufSize;\r
+ LanDriver->Stats.TxGoodFrames += 1;\r
+\r
+ // Update the Tx Buffer cache\r
+ LinkedTXRecycleBuff = AllocateZeroPool (sizeof (MSK_LINKED_SYSTEM_BUF));\r
+ if (LinkedTXRecycleBuff == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ LinkedTXRecycleBuff->Signature = TX_MBUF_SIGNATURE;\r
+ //\r
+ // Add the passed Buffer to the transmit queue. Don't copy.\r
+ //\r
+ LinkedTXRecycleBuff->SystemBuf.Buf = BufAddr;\r
+ LinkedTXRecycleBuff->SystemBuf.Length = BufSize;\r
+ InsertTailList (&LanDriver->TransmitQueueHead, &LinkedTXRecycleBuff->Link);\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ // Dump the packet header\r
+#if LAN91X_PRINT_PACKET_HEADERS\r
+ Ptr = BufAddr;\r
+ DEBUG ((DEBUG_ERROR, "LAN91X:SnpTransmit()\n"));\r
+ DEBUG ((DEBUG_ERROR, " HdrSize: %d, SrcAddr: %p, Length: %d, Last byte: %02x\n",\r
+ HdrSize, SrcAddr, BufSize, Ptr[BufSize - 1]));\r
+ PrintIpDgram (\r
+ (HdrSize == 0) ? (EFI_MAC_ADDRESS *)&Ptr[0] : DstAddr,\r
+ (HdrSize == 0) ? (EFI_MAC_ADDRESS *)&Ptr[6] : (SrcAddr != NULL) ? SrcAddr : &LanDriver->SnpMode.CurrentAddress,\r
+ (HdrSize == 0) ? (UINT16 *)&Ptr[12] : &Proto,\r
+ &Ptr[14]\r
+ );\r
+#endif\r
+\r
+ // Restore TPL and return\r
+exit_unlock:\r
+ gBS->RestoreTPL (SavedTpl);\r
+ return Status;\r
+}\r
+\r
+\r
+/*\r
+** UEFI Receive() function\r
+**\r
+*/\r
+EFI_STATUS\r
+EFIAPI\r
+SnpReceive (\r
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp,\r
+ OUT UINTN *HdrSize OPTIONAL,\r
+ IN OUT UINTN *BuffSize,\r
+ OUT VOID *Data,\r
+ OUT EFI_MAC_ADDRESS *SrcAddr OPTIONAL,\r
+ OUT EFI_MAC_ADDRESS *DstAddr OPTIONAL,\r
+ OUT UINT16 *Protocol OPTIONAL\r
+ )\r
+{\r
+ EFI_TPL SavedTpl;\r
+ EFI_STATUS Status;\r
+ LAN91X_DRIVER *LanDriver;\r
+ UINT8 *DataPtr;\r
+ UINT16 PktStatus;\r
+ UINT16 PktLength;\r
+ UINT16 PktControl;\r
+ UINT8 IstReg;\r
+\r
+ // Check preliminaries\r
+ if ((Snp == NULL) || (Data == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Serialize access to data and registers\r
+ SavedTpl = gBS->RaiseTPL (LAN91X_TPL);\r
+\r
+ // Check that driver was started and initialised\r
+ switch (Snp->Mode->State) {\r
+ case EfiSimpleNetworkInitialized:\r
+ break;\r
+ case EfiSimpleNetworkStarted:\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Driver not yet initialized\n"));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ case EfiSimpleNetworkStopped:\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Driver not started\n"));\r
+ ReturnUnlock (EFI_NOT_STARTED);\r
+ default:\r
+ DEBUG ((DEBUG_ERROR, "LAN91x: Driver in an invalid state: %u\n",\r
+ (UINTN)Snp->Mode->State));\r
+ ReturnUnlock (EFI_DEVICE_ERROR);\r
+ }\r
+\r
+ // Find the LanDriver structure\r
+ LanDriver = INSTANCE_FROM_SNP_THIS(Snp);\r
+\r
+ // Check for Rx Overrun\r
+ IstReg = ReadIoReg8 (LanDriver, LAN91X_IST);\r
+ if ((IstReg & IST_RX_OVRN) != 0) {\r
+ LanDriver->Stats.RxTotalFrames += 1;\r
+ LanDriver->Stats.RxDroppedFrames += 1;\r
+ WriteIoReg8 (LanDriver, LAN91X_IST, IST_RX_OVRN);\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Receiver overrun\n"));\r
+ }\r
+\r
+ // Check for Rx data available\r
+ if ((IstReg & IST_RCV) == 0) {\r
+ ReturnUnlock (EFI_NOT_READY);\r
+ }\r
+\r
+ // Configure the PTR register for reading\r
+ WriteIoReg16 (LanDriver, LAN91X_PTR, PTR_RCV | PTR_AUTO_INCR | PTR_READ);\r
+\r
+ // Read the Packet Status and Packet Length words\r
+ PktStatus = ReadIoReg16 (LanDriver, LAN91X_DATA0);\r
+ PktLength = ReadIoReg16 (LanDriver, LAN91X_DATA0) & BCW_COUNT;\r
+\r
+ // Check for valid received packet\r
+ if ((PktStatus == 0) && (PktLength == 0)) {\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Received zero-length packet. IST=%04x\n", IstReg));\r
+ ReturnUnlock (EFI_NOT_READY);\r
+ }\r
+ LanDriver->Stats.RxTotalFrames += 1;\r
+\r
+ // Check if we got a CRC error\r
+ if ((PktStatus & RX_BAD_CRC) != 0) {\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Received frame CRC error\n"));\r
+ LanDriver->Stats.RxCrcErrorFrames += 1;\r
+ LanDriver->Stats.RxDroppedFrames += 1;\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto exit_release;\r
+ }\r
+\r
+ // Check if we got a too-short frame\r
+ if ((PktStatus & RX_TOO_SHORT) != 0) {\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Received frame too short (%d bytes)\n", PktLength));\r
+ LanDriver->Stats.RxUndersizeFrames += 1;\r
+ LanDriver->Stats.RxDroppedFrames += 1;\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto exit_release;\r
+ }\r
+\r
+ // Check if we got a too-long frame\r
+ if ((PktStatus & RX_TOO_LONG) != 0) {\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Received frame too long (%d bytes)\n", PktLength));\r
+ LanDriver->Stats.RxOversizeFrames += 1;\r
+ LanDriver->Stats.RxDroppedFrames += 1;\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto exit_release;\r
+ }\r
+\r
+ // Check if we got an alignment error\r
+ if ((PktStatus & RX_ALGN_ERR) != 0) {\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Received frame alignment error\n"));\r
+ // Don't seem to keep track of these specifically\r
+ LanDriver->Stats.RxDroppedFrames += 1;\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto exit_release;\r
+ }\r
+\r
+ // Classify the received fram\r
+ if ((PktStatus & RX_MULTICAST) != 0) {\r
+ LanDriver->Stats.RxMulticastFrames += 1;\r
+ } else if ((PktStatus & RX_BROADCAST) != 0) {\r
+ LanDriver->Stats.RxBroadcastFrames += 1;\r
+ } else {\r
+ LanDriver->Stats.RxUnicastFrames += 1;\r
+ }\r
+\r
+ // Calculate the received packet data length\r
+ PktLength -= LAN91X_PKT_OVERHEAD;\r
+ if ((PktStatus & RX_ODD_FRAME) != 0) {\r
+ PktLength += 1;\r
+ }\r
+\r
+ // Check buffer size\r
+ if (*BuffSize < PktLength) {\r
+ DEBUG ((DEBUG_WARN, "LAN91x: Receive buffer too small for packet (%d < %d)\n",\r
+ *BuffSize, PktLength));\r
+ *BuffSize = PktLength;\r
+ Status = EFI_BUFFER_TOO_SMALL;\r
+ goto exit_release;\r
+ }\r
+\r
+ // Transfer the data bytes\r
+ DataPtr = Data;\r
+ ReadIoData (LanDriver, DataPtr, PktLength & ~0x0001);\r
+\r
+ // Read the PktControl and Odd Byte from the FIFO\r
+ PktControl = ReadIoReg16 (LanDriver, LAN91X_DATA0);\r
+ if ((PktControl & PCW_ODD) != 0) {\r
+ DataPtr[PktLength - 1] = PktControl & PCW_ODD_BYTE;\r
+ }\r
+\r
+ // Update buffer size\r
+ *BuffSize = PktLength;\r
+\r
+ if (HdrSize != NULL) {\r
+ *HdrSize = LanDriver->SnpMode.MediaHeaderSize;\r
+ }\r
+\r
+ // Extract the destination address\r
+ if (DstAddr != NULL) {\r
+ CopyMem (DstAddr, &DataPtr[0], NET_ETHER_ADDR_LEN);\r
+ }\r
+\r
+ // Get the source address\r
+ if (SrcAddr != NULL) {\r
+ CopyMem (SrcAddr, &DataPtr[6], NET_ETHER_ADDR_LEN);\r
+ }\r
+\r
+ // Get the protocol\r
+ if (Protocol != NULL) {\r
+ *Protocol = NTOHS (*(UINT16*)(&DataPtr[12]));\r
+ }\r
+\r
+ // Update the Rx statistics\r
+ LanDriver->Stats.RxTotalBytes += PktLength;\r
+ LanDriver->Stats.RxGoodFrames += 1;\r
+ Status = EFI_SUCCESS;\r
+\r
+#if LAN91X_PRINT_PACKET_HEADERS\r
+ // Dump the packet header\r
+ DEBUG ((DEBUG_ERROR, "LAN91X:SnpReceive()\n"));\r
+ DEBUG ((DEBUG_ERROR, " HdrSize: %p, SrcAddr: %p, DstAddr: %p, Protocol: %p\n",\r
+ HdrSize, SrcAddr, DstAddr, Protocol));\r
+ DEBUG ((DEBUG_ERROR, " Length: %d, Last byte: %02x\n", PktLength, DataPtr[PktLength - 1]));\r
+ PrintIpDgram (&DataPtr[0], &DataPtr[6], &DataPtr[12], &DataPtr[14]);\r
+#endif\r
+\r
+ // Release the FIFO buffer\r
+exit_release:\r
+ MmuOperation (LanDriver, MMUCR_OP_RX_POP_REL);\r
+\r
+ // Restore TPL and return\r
+exit_unlock:\r
+ gBS->RestoreTPL (SavedTpl);\r
+ return Status;\r
+}\r
+\r
+\r
+/*------------------ Driver Execution Environment main entry point ------------------*/\r
+\r
+/*\r
+** Entry point for the LAN91x driver\r
+**\r
+*/\r
+EFI_STATUS\r
+Lan91xDxeEntry (\r
+ IN EFI_HANDLE Handle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ LAN91X_DRIVER *LanDriver;\r
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;\r
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;\r
+ LAN91X_DEVICE_PATH *Lan91xPath;\r
+\r
+ // The PcdLan91xDxeBaseAddress PCD must be defined\r
+ ASSERT(PcdGet32 (PcdLan91xDxeBaseAddress) != 0);\r
+\r
+ // Allocate Resources\r
+ LanDriver = AllocateZeroPool (sizeof(LAN91X_DRIVER));\r
+ Lan91xPath = AllocateCopyPool (sizeof(LAN91X_DEVICE_PATH), &Lan91xPathTemplate);\r
+\r
+ // Initialize I/O Space access info\r
+ LanDriver->IoBase = PcdGet32 (PcdLan91xDxeBaseAddress);\r
+ LanDriver->PhyAd = LAN91X_NO_PHY;\r
+ LanDriver->BankSel = 0xff;\r
+\r
+ // Initialize pointers\r
+ Snp = &(LanDriver->Snp);\r
+ SnpMode = &(LanDriver->SnpMode);\r
+ Snp->Mode = SnpMode;\r
+\r
+ // Set the signature of the LAN Driver structure\r
+ LanDriver->Signature = LAN91X_SIGNATURE;\r
+\r
+ // Probe the device\r
+ Status = Probe (LanDriver);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "LAN91x:Lan91xDxeEntry(): Probe failed with status %d\n", Status));\r
+ return Status;\r
+ }\r
+\r
+#ifdef LAN91X_PRINT_REGISTERS\r
+ PrintIoRegisters (LanDriver);\r
+ PrintPhyRegisters (LanDriver);\r
+#endif\r
+\r
+ // Initialize transmit queue\r
+ InitializeListHead (&LanDriver->TransmitQueueHead);\r
+\r
+ // Assign fields and func pointers\r
+ Snp->Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;\r
+ Snp->WaitForPacket = NULL;\r
+ Snp->Initialize = SnpInitialize;\r
+ Snp->Start = SnpStart;\r
+ Snp->Stop = SnpStop;\r
+ Snp->Reset = SnpReset;\r
+ Snp->Shutdown = SnpShutdown;\r
+ Snp->ReceiveFilters = SnpReceiveFilters;\r
+ Snp->StationAddress = SnpStationAddress;\r
+ Snp->Statistics = SnpStatistics;\r
+ Snp->MCastIpToMac = SnpMcastIptoMac;\r
+ Snp->NvData = SnpNvData;\r
+ Snp->GetStatus = SnpGetStatus;\r
+ Snp->Transmit = SnpTransmit;\r
+ Snp->Receive = SnpReceive;\r
+\r
+ // Fill in simple network mode structure\r
+ SnpMode->State = EfiSimpleNetworkStopped;\r
+ SnpMode->HwAddressSize = NET_ETHER_ADDR_LEN; // HW address is 6 bytes\r
+ SnpMode->MediaHeaderSize = sizeof(ETHER_HEAD); // Size of an Ethernet header\r
+ SnpMode->MaxPacketSize = EFI_PAGE_SIZE; // Ethernet Frame (with VLAN tag +4 bytes)\r
+\r
+ // Supported receive filters\r
+ SnpMode->ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |\r
+ EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST |\r
+ EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST |\r
+ EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS |\r
+ EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;\r
+\r
+ // Initially-enabled receive filters\r
+ SnpMode->ReceiveFilterSetting = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |\r
+ EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST |\r
+ EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST;\r
+\r
+ // LAN91x has 64bit hash table. We can filter an infinite MACs, but\r
+ // higher-level software must filter out any hash collisions.\r
+ SnpMode->MaxMCastFilterCount = MAX_MCAST_FILTER_CNT;\r
+ SnpMode->MCastFilterCount = 0;\r
+ ZeroMem (&SnpMode->MCastFilter, MAX_MCAST_FILTER_CNT * sizeof(EFI_MAC_ADDRESS));\r
+\r
+ // Set the interface type (1: Ethernet or 6: IEEE 802 Networks)\r
+ SnpMode->IfType = NET_IFTYPE_ETHERNET;\r
+\r
+ // Mac address is changeable\r
+ SnpMode->MacAddressChangeable = TRUE;\r
+\r
+ // We can only transmit one packet at a time\r
+ SnpMode->MultipleTxSupported = FALSE;\r
+\r
+ // MediaPresent checks for cable connection and partner link\r
+ SnpMode->MediaPresentSupported = TRUE;\r
+ SnpMode->MediaPresent = FALSE;\r
+\r
+ // Set broadcast address\r
+ SetMem (&SnpMode->BroadcastAddress, sizeof (EFI_MAC_ADDRESS), 0xFF);\r
+\r
+ // Assign fields for device path\r
+ Lan91xPath->Lan91x.MacAddress = SnpMode->PermanentAddress;\r
+ Lan91xPath->Lan91x.IfType = SnpMode->IfType;\r
+\r
+ // Initialise the protocol\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &LanDriver->ControllerHandle,\r
+ &gEfiSimpleNetworkProtocolGuid, Snp,\r
+ &gEfiDevicePathProtocolGuid, Lan91xPath,\r
+ NULL\r
+ );\r
+\r
+ // Say what the status of loading the protocol structure is\r
+ if (EFI_ERROR(Status)) {\r
+ FreePool (LanDriver);\r
+ }\r
+\r
+ return Status;\r
+}\r