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