/** @file This file contains code for UNDI command based on UEFI specification. Copyright (c) 2023, American Megatrends International LLC. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "DriverBinding.h" // API table, defined in UEFI specification API_FUNC gUndiApiTable[] = { UndiGetState, UndiStart, UndiStop, UndiGetInitInfo, UndiGetConfigInfo, UndiInitialize, UndiReset, UndiShutdown, UndiInterruptEnable, UndiReceiveFilter, UndiStationAddress, UndiStatistics, UndiMcastIp2Mac, UndiNvData, UndiGetStatus, UndiFillHeader, UndiTransmit, UndiReceive }; /** Callback function for enable Rate Limiter. @param[in] Event Event whose notification function is being invoked @param[in] Context Pointer to the notification function's context **/ VOID EFIAPI UndiRateLimiterCallback ( IN EFI_EVENT Event, IN VOID *Context ) { NIC_DATA *Nic; Nic = Context; if (Nic->RateLimitingCreditCount < Nic->RateLimitingCredit) { Nic->RateLimitingCreditCount++; } } /** This command is used to determine the operational state of the UNDI. @param[in] Cdb A pointer to the command descriptor block. @param[in] Nic A pointer to the Network interface controller data. **/ VOID UndiGetState ( IN PXE_CDB *Cdb, IN NIC_DATA *Nic ) { EFI_STATUS Status; if ((Cdb->OpCode != PXE_OPCODE_GET_STATE) || (Cdb->StatCode != PXE_STATCODE_INITIALIZE) || (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) || (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) || (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) || (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED) || (Cdb->DBsize != PXE_DBSIZE_NOT_USED) || (Cdb->DBaddr != PXE_DBADDR_NOT_USED) || (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } else { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE; Cdb->StatCode = PXE_STATCODE_SUCCESS; } Cdb->StatFlags = Cdb->StatFlags | Nic->State; if (Nic->UsbEth->UsbEthUndi.UsbEthUndiGetState != NULL) { Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiGetState (Cdb, Nic); if (EFI_ERROR (Status)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } } } /** This command is used to change the UNDI operational state from stopped to started. @param[in] Cdb A pointer to the command descriptor block. @param[in] Nic A pointer to the Network interface controller data. **/ VOID UndiStart ( IN PXE_CDB *Cdb, IN NIC_DATA *Nic ) { PXE_CPB_START_31 *Cpb; EFI_STATUS Status; BOOLEAN EventError; if ((Cdb->OpCode != PXE_OPCODE_START) || (Cdb->StatCode != PXE_STATCODE_INITIALIZE) || (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) || (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) || (Cdb->CPBsize != sizeof (PXE_CPB_START_31)) || (Cdb->DBsize != PXE_DBSIZE_NOT_USED) || (Cdb->DBaddr != PXE_DBADDR_NOT_USED) || (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } if (Nic->State != PXE_STATFLAGS_GET_STATE_STOPPED) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_ALREADY_STARTED; return; } Cpb = (PXE_CPB_START_31 *)(UINTN)Cdb->CPBaddr; Nic->PxeStart.Delay = Cpb->Delay; Nic->PxeStart.Virt2Phys = Cpb->Virt2Phys; Nic->PxeStart.Block = Cpb->Block; Nic->PxeStart.Map_Mem = 0; Nic->PxeStart.UnMap_Mem = 0; Nic->PxeStart.Sync_Mem = Cpb->Sync_Mem; Nic->PxeStart.Unique_ID = Cpb->Unique_ID; EventError = FALSE; Status = EFI_SUCCESS; if (Nic->RateLimitingEnable == TRUE) { Status = gBS->CreateEvent ( EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY, UndiRateLimiterCallback, Nic, &Nic->RateLimiter ); if (!EFI_ERROR (Status)) { Status = gBS->SetTimer ( Nic->RateLimiter, TimerPeriodic, Nic->RateLimitingPollTimer * 10000 ); if (EFI_ERROR (Status)) { EventError = TRUE; } } } if ((Nic->UsbEth->UsbEthUndi.UsbEthUndiStart != NULL) && (EventError == FALSE)) { Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiStart (Cdb, Nic); } if (!EFI_ERROR (Status)) { // Initial the state for UNDI start. Nic->State = PXE_STATFLAGS_GET_STATE_STARTED; Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE; Cdb->StatCode = PXE_STATCODE_SUCCESS; } else { if (Nic->RateLimitingEnable == TRUE) { if (!EventError) { gBS->SetTimer (&Nic->RateLimiter, TimerCancel, 0); } if (Nic->RateLimiter) { gBS->CloseEvent (&Nic->RateLimiter); Nic->RateLimiter = 0; } } // Initial the state when UNDI start is fail Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_DEVICE_FAILURE; } } /** This command is used to change the UNDI operational state from started to stopped. @param[in] Cdb A pointer to the command descriptor block. @param[in] Nic A pointer to the Network interface controller data. **/ VOID UndiStop ( IN PXE_CDB *Cdb, IN NIC_DATA *Nic ) { EFI_STATUS Status; if ((Cdb->OpCode != PXE_OPCODE_STOP) || (Cdb->StatCode != PXE_STATCODE_INITIALIZE) || (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) || (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) || (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) || (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED) || (Cdb->DBsize != PXE_DBSIZE_NOT_USED) || (Cdb->DBaddr != PXE_DBADDR_NOT_USED) || (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } else { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE; Cdb->StatCode = PXE_STATCODE_SUCCESS; } if (Nic->State == PXE_STATFLAGS_GET_STATE_STOPPED) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_NOT_STARTED; return; } if (Nic->State == PXE_STATFLAGS_GET_STATE_INITIALIZED) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_NOT_SHUTDOWN; return; } Nic->PxeStart.Delay = 0; Nic->PxeStart.Virt2Phys = 0; Nic->PxeStart.Block = 0; Nic->PxeStart.Map_Mem = 0; Nic->PxeStart.UnMap_Mem = 0; Nic->PxeStart.Sync_Mem = 0; Nic->State = PXE_STATFLAGS_GET_STATE_STOPPED; if (Nic->RateLimitingEnable == TRUE) { gBS->SetTimer (&Nic->RateLimiter, TimerCancel, 0); gBS->CloseEvent (&Nic->RateLimiter); } if (Nic->UsbEth->UsbEthUndi.UsbEthUndiStop != NULL) { Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiStop (Cdb, Nic); if (EFI_ERROR (Status)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } } } /** This command is used to retrieve initialization information that is needed by drivers and applications to initialized UNDI. @param[in] Cdb A pointer to the command descriptor block. @param[in] Nic A pointer to the Network interface controller data. **/ VOID UndiGetInitInfo ( IN PXE_CDB *Cdb, IN NIC_DATA *Nic ) { PXE_DB_GET_INIT_INFO *Db; EFI_STATUS Status; if ((Cdb->OpCode != PXE_OPCODE_GET_INIT_INFO) || (Cdb->StatCode != PXE_STATCODE_INITIALIZE) || (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) || (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) || (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) || (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED) || (Cdb->DBsize != sizeof (PXE_DB_GET_INIT_INFO)) || (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } else { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE; Cdb->StatCode = PXE_STATCODE_SUCCESS; } if (Nic->State == PXE_STATFLAGS_GET_STATE_STOPPED) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_NOT_STARTED; return; } Db = (PXE_DB_GET_INIT_INFO *)(UINTN)Cdb->DBaddr; Db->MemoryRequired = MEMORY_REQUIRE; Db->FrameDataLen = PXE_MAX_TXRX_UNIT_ETHER; Db->LinkSpeeds[0] = 10; Db->LinkSpeeds[1] = 100; Db->LinkSpeeds[2] = 1000; Db->LinkSpeeds[3] = 0; Db->MediaHeaderLen = PXE_MAC_HEADER_LEN_ETHER; Db->HWaddrLen = PXE_HWADDR_LEN_ETHER; Db->MCastFilterCnt = MAX_MCAST_ADDRESS_CNT; Db->TxBufCnt = Nic->PxeInit.TxBufCnt; Db->TxBufSize = Nic->PxeInit.TxBufSize; Db->RxBufCnt = Nic->PxeInit.RxBufCnt; Db->RxBufSize = Nic->PxeInit.RxBufSize; Db->IFtype = PXE_IFTYPE_ETHERNET; Db->SupportedDuplexModes = PXE_DUPLEX_DEFAULT; Db->SupportedLoopBackModes = LOOPBACK_NORMAL; Cdb->StatFlags |= (PXE_STATFLAGS_CABLE_DETECT_SUPPORTED | PXE_STATFLAGS_GET_STATUS_NO_MEDIA_SUPPORTED); if (Nic->UsbEth->UsbEthUndi.UsbEthUndiGetInitInfo != NULL) { Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiGetInitInfo (Cdb, Nic); if (EFI_ERROR (Status)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } } } /** This command is used to retrieve configuration information about the NIC being controlled by the UNDI. @param[in] Cdb A pointer to the command descriptor block. @param[in] Nic A pointer to the Network interface controller data. **/ VOID UndiGetConfigInfo ( IN PXE_CDB *Cdb, IN NIC_DATA *Nic ) { PXE_DB_GET_CONFIG_INFO *Db; EFI_STATUS Status; if ((Cdb->OpCode != PXE_OPCODE_GET_CONFIG_INFO) || (Cdb->StatCode != PXE_STATCODE_INITIALIZE) || (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) || (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) || (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) || (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED) || (Cdb->DBsize != sizeof (PXE_DB_GET_CONFIG_INFO)) || (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } else { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE; Cdb->StatCode = PXE_STATCODE_SUCCESS; } if (Nic->State == PXE_STATFLAGS_GET_STATE_STOPPED) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_NOT_STARTED; return; } Db = (PXE_DB_GET_CONFIG_INFO *)(UINTN)Cdb->DBaddr; Db->pci.BusType = PXE_BUSTYPE_USB; if (Nic->UsbEth->UsbEthUndi.UsbEthUndiGetConfigInfo != NULL) { Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiGetConfigInfo (Cdb, Nic); if (EFI_ERROR (Status)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } } } /** This command resets the network adapter and initializes UNDI using the parameters supplied in the CPB. @param[in] Cdb A pointer to the command descriptor block. @param[in, out] Nic A pointer to the Network interface controller data. **/ VOID UndiInitialize ( IN PXE_CDB *Cdb, IN OUT NIC_DATA *Nic ) { PXE_CPB_INITIALIZE *Cpb; PXE_DB_INITIALIZE *Db; EFI_STATUS Status; if ((Cdb->OpCode != PXE_OPCODE_INITIALIZE) || (Cdb->StatCode != PXE_STATCODE_INITIALIZE) || (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) || (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) || (Cdb->CPBsize != sizeof (PXE_CPB_INITIALIZE))) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } else { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE; } if (Nic->State == PXE_STATFLAGS_GET_STATE_STOPPED) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_NOT_STARTED; return; } if ((Cdb->OpFlags != PXE_OPFLAGS_INITIALIZE_DETECT_CABLE) && (Cdb->OpFlags != PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } if (Nic->State == PXE_STATFLAGS_GET_STATE_INITIALIZED) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_ALREADY_INITIALIZED; return; } Cpb = (PXE_CPB_INITIALIZE *)(UINTN)Cdb->CPBaddr; Db = (PXE_DB_INITIALIZE *)(UINTN)Cdb->DBaddr; Nic->PxeInit.LinkSpeed = Cpb->LinkSpeed; Nic->PxeInit.DuplexMode = Cpb->DuplexMode; Nic->PxeInit.LoopBackMode = Cpb->LoopBackMode; Nic->PxeInit.MemoryAddr = Cpb->MemoryAddr; Nic->PxeInit.MemoryLength = Cpb->MemoryLength; Nic->PxeInit.TxBufCnt = TX_BUFFER_COUNT; Nic->PxeInit.TxBufSize = Nic->MaxSegmentSize; Nic->PxeInit.RxBufCnt = RX_BUFFER_COUNT; Nic->PxeInit.RxBufSize = Nic->MaxSegmentSize; Cdb->StatCode = Initialize (Cdb, Nic); Db->MemoryUsed = MEMORY_REQUIRE; Db->TxBufCnt = Nic->PxeInit.TxBufCnt; Db->TxBufSize = Nic->PxeInit.TxBufSize; Db->RxBufCnt = Nic->PxeInit.RxBufCnt; Db->RxBufSize = Nic->PxeInit.RxBufSize; Nic->RxFilter = PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST; Nic->CanTransmit = FALSE; if (Cdb->OpFlags == PXE_OPFLAGS_INITIALIZE_DETECT_CABLE) { if ((Nic->Request.Request == USB_CDC_NETWORK_CONNECTION) && (Nic->Request.Value == NETWORK_DISCONNECT)) { Nic->CableDetect = 0; } else if ((Nic->Request.Request == USB_CDC_NETWORK_CONNECTION) && (Nic->Request.Value == NETWORK_CONNECTED)) { Nic->CableDetect = 1; } if (Nic->CableDetect == 0) { Cdb->StatFlags |= PXE_STATFLAGS_INITIALIZED_NO_MEDIA; } } if (Cdb->StatCode != PXE_STATCODE_SUCCESS) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } else { Nic->State = PXE_STATFLAGS_GET_STATE_INITIALIZED; } if (Nic->UsbEth->UsbEthUndi.UsbEthUndiInitialize != NULL) { Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiInitialize (Cdb, Nic); if (EFI_ERROR (Status)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } } } /** Initialize Network interface controller data. @param[in] Cdb A pointer to the command descriptor block. @param[in, out] Nic A pointer to the Network interface controller data. @retval Status A value of Pxe statcode. **/ UINT16 Initialize ( IN PXE_CDB *Cdb, IN OUT NIC_DATA *Nic ) { UINTN Status; UINT32 Index; EFI_STATUS EfiStatus; Status = MapIt ( Nic, Nic->PxeInit.MemoryAddr, Nic->PxeInit.MemoryLength, TO_AND_FROM_DEVICE, (UINT64)(UINTN)&Nic->MappedAddr ); if (Status != 0) { return (UINT16)Status; } for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) { Nic->PermNodeAddress[Index] = Nic->MacAddr.Addr[Index]; } for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) { Nic->CurrentNodeAddress[Index] = Nic->PermNodeAddress[Index]; } for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) { Nic->BroadcastNodeAddress[Index] = 0xFF; } for (Index = PXE_HWADDR_LEN_ETHER; Index < PXE_MAC_LENGTH; Index++) { Nic->CurrentNodeAddress[Index] = 0; Nic->PermNodeAddress[Index] = 0; Nic->BroadcastNodeAddress[Index] = 0; } if (Nic->UsbEth->UsbEthInitialize != NULL) { EfiStatus = Nic->UsbEth->UsbEthInitialize (Cdb, Nic); if (EFI_ERROR (EfiStatus)) { return PXE_STATFLAGS_COMMAND_FAILED; } } return (UINT16)Status; } /** This command resets the network adapter and reinitializes the UNDI with the same parameters provided in the Initialize command. @param[in] Cdb A pointer to the command descriptor block. @param[in] Nic A pointer to the Network interface controller data. **/ VOID UndiReset ( IN PXE_CDB *Cdb, IN NIC_DATA *Nic ) { EFI_STATUS Status; if ((Cdb->OpCode != PXE_OPCODE_RESET) || (Cdb->StatCode != PXE_STATCODE_INITIALIZE) || (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) || (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) || (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) || (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED) || (Cdb->DBsize != PXE_DBSIZE_NOT_USED) || (Cdb->DBaddr != PXE_DBADDR_NOT_USED)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } else { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE; Cdb->StatCode = PXE_STATCODE_SUCCESS; } if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) { Cdb->StatCode = PXE_STATCODE_NOT_INITIALIZED; Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; return; } if ((Cdb->OpFlags != PXE_OPFLAGS_NOT_USED) && (Cdb->OpFlags != PXE_OPFLAGS_RESET_DISABLE_INTERRUPTS) && (Cdb->OpFlags != PXE_OPFLAGS_RESET_DISABLE_FILTERS)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } if ((Cdb->OpFlags & PXE_OPFLAGS_RESET_DISABLE_FILTERS) == 0) { Nic->RxFilter = PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST; } if ((Cdb->OpFlags & PXE_OPFLAGS_RESET_DISABLE_INTERRUPTS) != 0) { Nic->InterrupOpFlag = 0; } if (Nic->UsbEth->UsbEthUndi.UsbEthUndiReset != NULL) { Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiReset (Cdb, Nic); if (EFI_ERROR (Status)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } } } /** The Shutdown command resets the network adapter and leaves it in a safe state for another driver to initialize. @param[in] Cdb A pointer to the command descriptor block. @param[in, out] Nic A pointer to the Network interface controller data. **/ VOID UndiShutdown ( IN PXE_CDB *Cdb, IN OUT NIC_DATA *Nic ) { EFI_STATUS Status; if ((Cdb->OpCode != PXE_OPCODE_SHUTDOWN) || (Cdb->StatCode != PXE_STATCODE_INITIALIZE) || (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) || (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) || (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) || (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED) || (Cdb->DBsize != PXE_DBSIZE_NOT_USED) || (Cdb->DBaddr != PXE_DBADDR_NOT_USED) || (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } else { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE; Cdb->StatCode = PXE_STATCODE_SUCCESS; } if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) { Cdb->StatCode = PXE_STATCODE_NOT_INITIALIZED; Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; return; } Nic->CanTransmit = FALSE; Nic->State = PXE_STATFLAGS_GET_STATE_STARTED; if (Nic->UsbEth->UsbEthUndi.UsbEthUndiShutdown != NULL) { Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiShutdown (Cdb, Nic); if (EFI_ERROR (Status)) { Cdb->StatCode = PXE_STATCODE_NOT_INITIALIZED; Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } } } /** The Interrupt Enables command can be used to read and/or change the current external interrupt enable settings. @param[in] Cdb A pointer to the command descriptor block. @param[in] Nic A pointer to the Network interface controller data. **/ VOID UndiInterruptEnable ( IN PXE_CDB *Cdb, IN NIC_DATA *Nic ) { EFI_STATUS Status; Cdb->StatCode = PXE_STATCODE_UNSUPPORTED; if (Nic->UsbEth->UsbEthUndi.UsbEthUndiInterruptEnable != NULL) { Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiInterruptEnable (Cdb, Nic); if (EFI_ERROR (Status)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } else { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE; Cdb->StatCode = PXE_STATCODE_SUCCESS; } } } /** This command is used to read and change receive filters and, if supported, read and change the multicast MAC address filter list. @param[in] Cdb A pointer to the command descriptor block. @param[in] Nic A pointer to the Network interface controller data. **/ VOID UndiReceiveFilter ( IN PXE_CDB *Cdb, IN NIC_DATA *Nic ) { UINT16 NewFilter; PXE_DB_RECEIVE_FILTERS *Db; EFI_STATUS Status; if ((Cdb->OpCode != PXE_OPCODE_RECEIVE_FILTERS) || (Cdb->StatCode != PXE_STATCODE_INITIALIZE) || (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) || (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8))) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } else { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE; Cdb->StatCode = PXE_STATCODE_SUCCESS; } if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) { Cdb->StatCode = PXE_STATCODE_NOT_INITIALIZED; Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; return; } NewFilter = (UINT16)(Cdb->OpFlags & 0x1F); switch (Cdb->OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_OPMASK) { case PXE_OPFLAGS_RECEIVE_FILTER_READ: if (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; } if ((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST) == 0) { if ((Cdb->DBsize != 0)) { Db = (PXE_DB_RECEIVE_FILTERS *)(UINTN)Cdb->DBaddr; CopyMem (Db, &Nic->McastList, Nic->McastCount); } } break; case PXE_OPFLAGS_RECEIVE_FILTER_ENABLE: if (NewFilter == 0) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } if (Cdb->CPBsize != 0) { if (((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) == 0) || ((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST) != 0) || ((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST) != 0) || ((Cdb->CPBsize % sizeof (PXE_MAC_ADDR)) != 0)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } } if ((Cdb->OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) != 0) { if (((Cdb->OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST) != 0) || ((Cdb->OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST) != 0)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } if ((Cdb->CPBsize == 0)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } } Cdb->StatCode = SetFilter (Nic, NewFilter, Cdb->CPBaddr, Cdb->CPBsize); break; case PXE_OPFLAGS_RECEIVE_FILTER_DISABLE: if (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; } break; default: Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; } Cdb->StatFlags = (PXE_STATFLAGS)(Cdb->StatFlags | Nic->RxFilter); if (Nic->UsbEth->UsbEthUndi.UsbEthUndiReceiveFilter != NULL) { Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiReceiveFilter (Cdb, Nic); if (EFI_ERROR (Status)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } } } /** Set PXE receive filter. @param[in] Nic A pointer to the Network interface controller data. @param[in] SetFilter PXE receive filter @param[in] CpbAddr Command Parameter Block Address @param[in] CpbSize Command Parameter Block Size **/ UINT16 SetFilter ( IN NIC_DATA *Nic, IN UINT16 SetFilter, IN UINT64 CpbAddr, IN UINT32 CpbSize ) { EFI_STATUS Status; UINT8 *McastList; UINT8 Count; UINT8 Index1; UINT8 Index2; PXE_CPB_RECEIVE_FILTERS *Cpb; USB_ETHERNET_FUN_DESCRIPTOR UsbEthFunDescriptor; Count = 0; Cpb = (PXE_CPB_RECEIVE_FILTERS *)(UINTN)CpbAddr; // The Cpb could be NULL.(ref:PXE_CPBADDR_NOT_USED) Nic->RxFilter = (UINT8)SetFilter; if (((SetFilter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) != 0) || (Cpb != NULL)) { if (Cpb != NULL) { Nic->McastCount = (UINT8)(CpbSize / PXE_MAC_LENGTH); CopyMem (&Nic->McastList, Cpb, Nic->McastCount); } Nic->UsbEth->UsbEthFunDescriptor (Nic->UsbEth, &UsbEthFunDescriptor); if ((UsbEthFunDescriptor.NumberMcFilters & MAC_FILTERS_MASK) == 0) { Nic->RxFilter |= PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST; Nic->UsbEth->SetUsbEthPacketFilter (Nic->UsbEth, Nic->RxFilter); } else { Status = gBS->AllocatePool (EfiBootServicesData, Nic->McastCount * 6, (VOID **)&McastList); if (EFI_ERROR (Status)) { return PXE_STATCODE_INVALID_PARAMETER; } if (Cpb != NULL) { for (Index1 = 0; Index1 < Nic->McastCount; Index1++) { for (Index2 = 0; Index2 < 6; Index2++) { McastList[Count++] = Cpb->MCastList[Index1][Index2]; } } } Nic->RxFilter |= PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST; if (Cpb != NULL) { Nic->UsbEth->SetUsbEthMcastFilter (Nic->UsbEth, Nic->McastCount, McastList); } Nic->UsbEth->SetUsbEthPacketFilter (Nic->UsbEth, Nic->RxFilter); FreePool (McastList); } } return PXE_STATCODE_SUCCESS; } /** This command is used to get current station and broadcast MAC addresses and, if supported, to change the current station MAC address. @param[in] Cdb A pointer to the command descriptor block. @param[in] Nic A pointer to the Network interface controller data. **/ VOID UndiStationAddress ( IN PXE_CDB *Cdb, IN NIC_DATA *Nic ) { PXE_CPB_STATION_ADDRESS *Cpb; PXE_DB_STATION_ADDRESS *Db; UINT16 Index; EFI_STATUS Status; if ((Cdb->OpCode != PXE_OPCODE_STATION_ADDRESS) || (Cdb->StatCode != PXE_STATCODE_INITIALIZE) || (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) || (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) || (Cdb->DBsize != sizeof (PXE_DB_STATION_ADDRESS))) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } else { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE; Cdb->StatCode = PXE_STATCODE_SUCCESS; } if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) { Cdb->StatCode = PXE_STATCODE_NOT_INITIALIZED; Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; return; } if (Cdb->OpFlags == PXE_OPFLAGS_STATION_ADDRESS_RESET) { if (CompareMem (&Nic->CurrentNodeAddress[0], &Nic->PermNodeAddress[0], PXE_MAC_LENGTH) != 0) { for (Index = 0; Index < PXE_MAC_LENGTH; Index++) { Nic->CurrentNodeAddress[Index] = Nic->PermNodeAddress[Index]; } } } if (Cdb->CPBaddr != 0) { Cpb = (PXE_CPB_STATION_ADDRESS *)(UINTN)Cdb->CPBaddr; for (Index = 0; Index < PXE_MAC_LENGTH; Index++) { Nic->CurrentNodeAddress[Index] = Cpb->StationAddr[Index]; } } if (Cdb->DBaddr != 0) { Db = (PXE_DB_STATION_ADDRESS *)(UINTN)Cdb->DBaddr; for (Index = 0; Index < PXE_MAC_LENGTH; Index++) { Db->StationAddr[Index] = Nic->CurrentNodeAddress[Index]; Db->BroadcastAddr[Index] = Nic->BroadcastNodeAddress[Index]; Db->PermanentAddr[Index] = Nic->PermNodeAddress[Index]; } } if (Nic->UsbEth->UsbEthUndi.UsbEthUndiStationAddress != NULL) { Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiStationAddress (Cdb, Nic); if (EFI_ERROR (Status)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } } } /** This command is used to read and clear the NIC traffic statistics. @param[in] Cdb A pointer to the command descriptor block. @param[in] Nic A pointer to the Network interface controller data. **/ VOID UndiStatistics ( IN PXE_CDB *Cdb, IN NIC_DATA *Nic ) { EFI_STATUS Status; if ((Cdb->OpCode != PXE_OPCODE_STATISTICS) || (Cdb->StatCode != PXE_STATCODE_INITIALIZE) || (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) || (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) || (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) || (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } else { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE; } if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) { Cdb->StatCode = PXE_STATCODE_NOT_INITIALIZED; Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; return; } if ((Cdb->OpFlags != PXE_OPFLAGS_STATISTICS_RESET) && (Cdb->OpFlags != PXE_OPFLAGS_STATISTICS_READ)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } Cdb->StatCode = Statistics (Nic, Cdb->DBaddr, Cdb->DBsize); if (Nic->UsbEth->UsbEthUndi.UsbEthUndiStatistics != NULL) { Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiStatistics (Cdb, Nic); if (EFI_ERROR (Status)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } } } /** Return data for DB data. @param[in] Nic A pointer to the Network interface controller data. @param[in] DbAddr Data Block Address. @param[in] DbSize Data Block Size. **/ UINT16 Statistics ( IN NIC_DATA *Nic, IN UINT64 DbAddr, IN UINT16 DbSize ) { PXE_DB_STATISTICS *DbStatistic; EFI_STATUS Status; DbStatistic = (PXE_DB_STATISTICS *)(UINTN)DbAddr; if (DbSize == 0) { return PXE_STATCODE_SUCCESS; } DbStatistic->Supported = 0x802; DbStatistic->Data[0x01] = Nic->RxFrame; DbStatistic->Data[0x0B] = Nic->TxFrame; if (Nic->UsbEth->UsbEthStatistics != NULL) { Status = Nic->UsbEth->UsbEthStatistics (Nic, DbAddr, DbSize); if (EFI_ERROR (Status)) { return PXE_STATFLAGS_COMMAND_FAILED; } } return PXE_STATCODE_SUCCESS; } /** Translate a multicast IPv4 or IPv6 address to a multicast MAC address. @param[in, out] Cdb A pointer to the command descriptor block. @param[in] Nic A pointer to the Network interface controller data. **/ VOID UndiMcastIp2Mac ( IN OUT PXE_CDB *Cdb, IN NIC_DATA *Nic ) { PXE_CPB_MCAST_IP_TO_MAC *Cpb; PXE_DB_MCAST_IP_TO_MAC *Db; UINT8 *Tmp; EFI_STATUS Status; if ((Cdb->OpCode != PXE_OPCODE_MCAST_IP_TO_MAC) || (Cdb->StatCode != PXE_STATCODE_INITIALIZE) || (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) || (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) || (Cdb->CPBsize != sizeof (PXE_CPB_MCAST_IP_TO_MAC)) || (Cdb->DBsize != sizeof (PXE_DB_MCAST_IP_TO_MAC))) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } else { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE; Cdb->StatCode = PXE_STATCODE_SUCCESS; } if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) { Cdb->StatCode = PXE_STATCODE_NOT_INITIALIZED; Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; return; } Cpb = (PXE_CPB_MCAST_IP_TO_MAC *)(UINTN)Cdb->CPBaddr; Db = (PXE_DB_MCAST_IP_TO_MAC *)(UINTN)Cdb->DBaddr; if ((Cdb->OpFlags & PXE_OPFLAGS_MCAST_IPV6_TO_MAC) != 0) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_UNSUPPORTED; return; } Tmp = (UINT8 *)(&Cpb->IP.IPv4); if ((Tmp[0] & 0xF0) != 0xE0) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CPB; } Db->MAC[0] = 0x01; Db->MAC[1] = 0x00; Db->MAC[2] = 0x5E; Db->MAC[3] = Tmp[1] & 0x7F; Db->MAC[4] = Tmp[2]; Db->MAC[5] = Tmp[3]; if (Nic->UsbEth->UsbEthUndi.UsbEthUndiMcastIp2Mac != NULL) { Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiMcastIp2Mac (Cdb, Nic); if (EFI_ERROR (Status)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } } } /** This command is used to read and write (if supported by NIC H/W) nonvolatile storage on the NIC. @param[in] Cdb A pointer to the command descriptor block. @param[in] Nic A pointer to the Network interface controller data. **/ VOID UndiNvData ( IN PXE_CDB *Cdb, IN NIC_DATA *Nic ) { EFI_STATUS Status; Cdb->StatCode = PXE_STATCODE_UNSUPPORTED; if (Nic->UsbEth->UsbEthUndi.UsbEthUndiNvData != NULL) { Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiNvData (Cdb, Nic); if (EFI_ERROR (Status)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } else { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE; Cdb->StatCode = PXE_STATCODE_SUCCESS; } } } /** This command returns the current interrupt status and/or the transmitted buffer addresses and the current media status. @param[in] Cdb A pointer to the command descriptor block. @param[in] Nic A pointer to the Network interface controller data. **/ VOID UndiGetStatus ( IN PXE_CDB *Cdb, IN NIC_DATA *Nic ) { PXE_DB_GET_STATUS *Db; PXE_DB_GET_STATUS TmpGetStatus; UINT16 NumEntries; UINTN Index; EFI_STATUS Status; if ((Cdb->OpCode != PXE_OPCODE_GET_STATUS) || (Cdb->StatCode != PXE_STATCODE_INITIALIZE) || (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) || (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) || (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) || (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } else { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE; Cdb->StatCode = PXE_STATCODE_SUCCESS; } if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) { Cdb->StatCode = PXE_STATCODE_NOT_INITIALIZED; Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; return; } TmpGetStatus.RxFrameLen = 0; TmpGetStatus.reserved = 0; Db = (PXE_DB_GET_STATUS *)(UINTN)Cdb->DBaddr; if ((Cdb->DBsize > 0) && (Cdb->DBsize < sizeof (UINT32) * 2)) { CopyMem (Db, &TmpGetStatus, Cdb->DBsize); } else { CopyMem (Db, &TmpGetStatus, sizeof (UINT32) * 2); } if ((Cdb->OpFlags & PXE_OPFLAGS_GET_TRANSMITTED_BUFFERS) != 0) { if (Cdb->DBsize == 0) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } NumEntries = Cdb->DBsize - sizeof (UINT64); Cdb->DBsize = sizeof (UINT32) * 2; for (Index = 0; NumEntries >= sizeof (UINT64); Index++, NumEntries -= sizeof (UINT64)) { if (Nic->TxBufferCount > 0) { Nic->TxBufferCount--; Db->TxBuffer[Index] = Nic->MediaHeader[Nic->TxBufferCount]; } } } if ((Cdb->OpFlags & PXE_OPFLAGS_GET_INTERRUPT_STATUS) != 0) { if (Nic->ReceiveStatus != 0) { Cdb->StatFlags |= PXE_STATFLAGS_GET_STATUS_RECEIVE; } } if ((Nic->Request.Request == USB_CDC_NETWORK_CONNECTION) && (Nic->Request.Value == NETWORK_DISCONNECT)) { Nic->CableDetect = 0; } else if ((Nic->Request.Request == USB_CDC_NETWORK_CONNECTION) && (Nic->Request.Value == NETWORK_CONNECTED)) { Nic->CableDetect = 1; } if ((Cdb->OpFlags & PXE_OPFLAGS_GET_MEDIA_STATUS) != 0) { if (Nic->CableDetect == 0) { Cdb->StatFlags |= PXE_STATFLAGS_GET_STATUS_NO_MEDIA; } } if (Nic->UsbEth->UsbEthUndi.UsbEthUndiGetStatus != NULL) { Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiGetStatus (Cdb, Nic); if (EFI_ERROR (Status)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } } } /** This command is used to fill the media header(s) in transmit packet(s). @param[in] Cdb A pointer to the command descriptor block. @param[in] Nic A pointer to the Network interface controller data. **/ VOID UndiFillHeader ( IN PXE_CDB *Cdb, IN NIC_DATA *Nic ) { PXE_CPB_FILL_HEADER *CpbFillHeader; PXE_CPB_FILL_HEADER_FRAGMENTED *CpbFill; ETHERNET_HEADER *MacHeader; UINTN Index; EFI_STATUS Status; if ((Cdb->OpCode != PXE_OPCODE_FILL_HEADER) || (Cdb->StatCode != PXE_STATCODE_INITIALIZE) || (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) || (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) || (Cdb->CPBsize != sizeof (PXE_CPB_FILL_HEADER_FRAGMENTED)) || (Cdb->DBsize != PXE_DBSIZE_NOT_USED) || (Cdb->DBaddr != PXE_DBADDR_NOT_USED)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } else { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE; Cdb->StatCode = PXE_STATCODE_SUCCESS; } if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) { Cdb->StatCode = PXE_STATCODE_NOT_INITIALIZED; Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; return; } if (Cdb->CPBsize == PXE_CPBSIZE_NOT_USED) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } if ((Cdb->OpFlags & PXE_OPFLAGS_FILL_HEADER_FRAGMENTED) != 0) { CpbFill = (PXE_CPB_FILL_HEADER_FRAGMENTED *)(UINTN)Cdb->CPBaddr; if ((CpbFill->FragCnt == 0) || (CpbFill->FragDesc[0].FragLen < PXE_MAC_HEADER_LEN_ETHER)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } MacHeader = (ETHERNET_HEADER *)(UINTN)CpbFill->FragDesc[0].FragAddr; MacHeader->Protocol = CpbFill->Protocol; for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) { MacHeader->DestAddr[Index] = CpbFill->DestAddr[Index]; MacHeader->SrcAddr[Index] = CpbFill->SrcAddr[Index]; } } else { CpbFillHeader = (PXE_CPB_FILL_HEADER *)(UINTN)Cdb->CPBaddr; MacHeader = (ETHERNET_HEADER *)(UINTN)CpbFillHeader->MediaHeader; MacHeader->Protocol = CpbFillHeader->Protocol; for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) { MacHeader->DestAddr[Index] = CpbFillHeader->DestAddr[Index]; MacHeader->SrcAddr[Index] = CpbFillHeader->SrcAddr[Index]; } } if (Nic->UsbEth->UsbEthUndi.UsbEthUndiFillHeader != NULL) { Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiFillHeader (Cdb, Nic); if (EFI_ERROR (Status)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } } } /** The Transmit command is used to place a packet into the transmit queue. @param[in] Cdb A pointer to the command descriptor block. @param[in] Nic A pointer to the Network interface controller data. **/ VOID UndiTransmit ( IN PXE_CDB *Cdb, IN NIC_DATA *Nic ) { EFI_STATUS Status; if ((Cdb->OpCode != PXE_OPCODE_TRANSMIT) || (Cdb->StatCode != PXE_STATCODE_INITIALIZE) || (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) || (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) || (Cdb->CPBsize != sizeof (PXE_CPB_TRANSMIT)) || (Cdb->DBsize != PXE_DBSIZE_NOT_USED) || (Cdb->DBaddr != PXE_DBADDR_NOT_USED)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } else { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE; Cdb->StatCode = PXE_STATCODE_SUCCESS; } if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_NOT_INITIALIZED; return; } if (Cdb->CPBsize == PXE_CPBSIZE_NOT_USED) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } if (Nic->UsbEth->UsbEthUndi.UsbEthUndiTransmit != NULL) { Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiTransmit (Cdb, Nic); if (EFI_ERROR (Status)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } return; } Cdb->StatCode = Transmit (Cdb, Nic, Cdb->CPBaddr, Cdb->OpFlags); if (Cdb->StatCode != PXE_STATCODE_SUCCESS) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } } /** Use USB Ethernet Protocol Bulk out command to transmit data. @param[in] Cdb A pointer to the command descriptor block. @param[in, out] Nic A pointer to the Network interface controller data. @param[in] CpbAddr Command Parameter Block Address. @param[in] OpFlags Operation Flags. **/ UINT16 Transmit ( IN PXE_CDB *Cdb, IN OUT NIC_DATA *Nic, IN UINT64 CpbAddr, IN UINT16 OpFlags ) { EFI_STATUS Status; PXE_CPB_TRANSMIT *Cpb; UINT64 BulkOutData; UINTN DataLength; UINTN TransmitLength; UINTN Map; UINT32 Counter; UINT16 StatCode; BulkOutData = 0; Counter = 0; Cpb = (PXE_CPB_TRANSMIT *)(UINTN)CpbAddr; if (Nic->CanTransmit) { return PXE_STATCODE_BUSY; } Nic->CanTransmit = TRUE; if ((OpFlags & PXE_OPFLAGS_TRANSMIT_FRAGMENTED) != 0) { return PXE_STATCODE_INVALID_PARAMETER; } Map = MapIt ( Nic, Cpb->FrameAddr, Cpb->DataLen + (UINT32)Cpb->MediaheaderLen, TO_DEVICE, (UINT64)(UINTN)&BulkOutData ); if (Map != 0) { Nic->CanTransmit = FALSE; return PXE_STATCODE_INVALID_PARAMETER; } if (Nic->TxBufferCount < MAX_XMIT_BUFFERS) { Nic->MediaHeader[Nic->TxBufferCount] = Cpb->FrameAddr; Nic->TxBufferCount++; } DataLength = (UINTN)(Cpb->DataLen + (UINT32)Cpb->MediaheaderLen); while (1) { if (Counter >= 3) { StatCode = PXE_STATCODE_BUSY; break; } TransmitLength = DataLength; Status = Nic->UsbEth->UsbEthTransmit (Cdb, Nic->UsbEth, (VOID *)(UINTN)BulkOutData, &TransmitLength); if (EFI_ERROR (Status)) { StatCode = PXE_STATFLAGS_COMMAND_FAILED; } if (Status == EFI_INVALID_PARAMETER) { StatCode = PXE_STATCODE_INVALID_PARAMETER; break; } if (Status == EFI_DEVICE_ERROR) { StatCode = PXE_STATCODE_DEVICE_FAILURE; break; } if (!EFI_ERROR (Status)) { Nic->TxFrame++; StatCode = PXE_STATCODE_SUCCESS; break; } Counter++; } UnMapIt ( Nic, Cpb->FrameAddr, Cpb->DataLen + (UINT32)Cpb->MediaheaderLen, TO_DEVICE, BulkOutData ); Nic->CanTransmit = FALSE; return StatCode; } /** When the network adapter has received a frame, this command is used to copy the frame into driver/application storage. @param[in] Cdb A pointer to the command descriptor block. @param[in] Nic A pointer to the Network interface controller data. **/ VOID UndiReceive ( IN PXE_CDB *Cdb, IN NIC_DATA *Nic ) { EFI_STATUS Status; if ((Cdb->OpCode != PXE_OPCODE_RECEIVE) || (Cdb->StatCode != PXE_STATCODE_INITIALIZE) || (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) || (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) || (Cdb->CPBsize != sizeof (PXE_CPB_RECEIVE)) || (Cdb->DBsize != sizeof (PXE_DB_RECEIVE)) || (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_INVALID_CDB; return; } else { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE; Cdb->StatCode = PXE_STATCODE_SUCCESS; } if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; Cdb->StatCode = PXE_STATCODE_NOT_INITIALIZED; return; } if (Nic->UsbEth->UsbEthUndi.UsbEthUndiReceive != NULL) { Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiReceive (Cdb, Nic); if (EFI_ERROR (Status)) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } return; } Cdb->StatCode = Receive (Cdb, Nic, Cdb->CPBaddr, Cdb->DBaddr); if (Cdb->StatCode != PXE_STATCODE_SUCCESS) { Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; } } /** Use USB Ethernet Protocol Bulk in command to receive data. @param[in] Cdb A pointer to the command descriptor block. @param[in, out] Nic A pointer to the Network interface controller data. @param[in] CpbAddr Command Parameter Block Address. @param[in, out] DbAddr Data Block Address. **/ UINT16 Receive ( IN PXE_CDB *Cdb, IN OUT NIC_DATA *Nic, IN UINT64 CpbAddr, IN OUT UINT64 DbAddr ) { EFI_STATUS Status; UINTN Index; PXE_FRAME_TYPE FrameType; PXE_CPB_RECEIVE *Cpb; PXE_DB_RECEIVE *Db; NIC_DEVICE *NicDevice; UINT8 *BulkInData; UINTN DataLength; ETHERNET_HEADER *Header; EFI_TPL OriginalTpl; FrameType = PXE_FRAME_TYPE_NONE; NicDevice = UNDI_DEV_FROM_NIC (Nic); BulkInData = NicDevice->ReceiveBuffer; DataLength = (UINTN)Nic->MaxSegmentSize; Cpb = (PXE_CPB_RECEIVE *)(UINTN)CpbAddr; Db = (PXE_DB_RECEIVE *)(UINTN)DbAddr; if (!BulkInData) { return PXE_STATCODE_INVALID_PARAMETER; } if ((Nic->RateLimitingCreditCount == 0) && (Nic->RateLimitingEnable == TRUE)) { return PXE_STATCODE_NO_DATA; } Status = Nic->UsbEth->UsbEthReceive (Cdb, Nic->UsbEth, (VOID *)BulkInData, &DataLength); if (EFI_ERROR (Status)) { Nic->ReceiveStatus = 0; if (Nic->RateLimitingEnable == TRUE) { OriginalTpl = gBS->RaiseTPL (TPL_NOTIFY); if (Nic->RateLimitingCreditCount != 0) { Nic->RateLimitingCreditCount--; } gBS->RestoreTPL (OriginalTpl); } return PXE_STATCODE_NO_DATA; } Nic->RxFrame++; if (DataLength != 0) { if (DataLength > Cpb->BufferLen) { DataLength = (UINTN)Cpb->BufferLen; } CopyMem ((UINT8 *)(UINTN)Cpb->BufferAddr, (UINT8 *)BulkInData, DataLength); Header = (ETHERNET_HEADER *)BulkInData; Db->FrameLen = (UINT32)DataLength; Db->MediaHeaderLen = PXE_MAC_HEADER_LEN_ETHER; for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) { if (Header->DestAddr[Index] != Nic->CurrentNodeAddress[Index]) { break; } } if (Index >= PXE_HWADDR_LEN_ETHER) { FrameType = PXE_FRAME_TYPE_UNICAST; } else { for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) { if (Header->DestAddr[Index] != Nic->BroadcastNodeAddress[Index]) { break; } } if (Index >= PXE_HWADDR_LEN_ETHER) { FrameType = PXE_FRAME_TYPE_BROADCAST; } else { if ((Header->DestAddr[0] & 1) == 1) { FrameType = PXE_FRAME_TYPE_FILTERED_MULTICAST; } else { FrameType = PXE_FRAME_TYPE_PROMISCUOUS; } } } Db->Type = FrameType; Db->Protocol = Header->Protocol; for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) { Db->SrcAddr[Index] = Header->SrcAddr[Index]; Db->DestAddr[Index] = Header->DestAddr[Index]; } } if (FrameType == PXE_FRAME_TYPE_NONE) { Nic->ReceiveStatus = 0; } else { Nic->ReceiveStatus = 1; } return PXE_STATCODE_SUCCESS; } /** Fill out PXE SW UNDI structure. @param[out] PxeSw A pointer to the PXE SW UNDI structure. **/ VOID PxeStructInit ( OUT PXE_SW_UNDI *PxeSw ) { PxeSw->Signature = PXE_ROMID_SIGNATURE; PxeSw->Len = (UINT8)sizeof (PXE_SW_UNDI); PxeSw->Fudge = 0; PxeSw->IFcnt = 0; PxeSw->IFcntExt = 0; PxeSw->Rev = PXE_ROMID_REV; PxeSw->MajorVer = PXE_ROMID_MAJORVER; PxeSw->MinorVer = PXE_ROMID_MINORVER; PxeSw->reserved1 = 0; PxeSw->Implementation = PXE_ROMID_IMP_SW_VIRT_ADDR | PXE_ROMID_IMP_FRAG_SUPPORTED | PXE_ROMID_IMP_CMD_LINK_SUPPORTED | PXE_ROMID_IMP_STATION_ADDR_SETTABLE | PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED | PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED | PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED | PXE_ROMID_IMP_FILTERED_MULTICAST_RX_SUPPORTED; PxeSw->EntryPoint = (UINT64)(UINTN)UndiApiEntry; PxeSw->reserved2[0] = 0; PxeSw->reserved2[1] = 0; PxeSw->reserved2[2] = 0; PxeSw->BusCnt = 1; PxeSw->BusType[0] = PXE_BUSTYPE_USB; PxeSw->Fudge = PxeSw->Fudge - CalculateSum8 ((VOID *)PxeSw, PxeSw->Len); } /** Update NIC number. @param[in] Nic A pointer to the Network interface controller data. @param[in, out] PxeSw A pointer to the PXE SW UNDI structure. **/ VOID UpdateNicNum ( IN NIC_DATA *Nic, IN OUT PXE_SW_UNDI *PxeSw ) { UINT16 NicNum; NicNum = (PxeSw->IFcnt | PxeSw->IFcntExt << 8); if (Nic == NULL) { if (NicNum > 0) { NicNum--; } PxeSw->IFcnt = (UINT8)(NicNum & 0xFF); // Get lower byte PxeSw->IFcntExt = (UINT8)((NicNum & 0xFF00) >> 8); // Get upper byte PxeSw->Fudge = (UINT8)(PxeSw->Fudge - CalculateSum8 ((VOID *)PxeSw, PxeSw->Len)); return; } NicNum++; PxeSw->IFcnt = (UINT8)(NicNum & 0xFF); // Get lower byte PxeSw->IFcntExt = (UINT8)((NicNum & 0xFF00) >> 8); // Get upper byte PxeSw->Fudge = (UINT8)(PxeSw->Fudge - CalculateSum8 ((VOID *)PxeSw, PxeSw->Len)); } /** UNDI API table entry. @param[in] Cdb A pointer to the command descriptor block. **/ EFI_STATUS EFIAPI UndiApiEntry ( IN UINT64 Cdb ) { PXE_CDB *CdbPtr; NIC_DATA *Nic; if (Cdb == 0) { return EFI_INVALID_PARAMETER; } CdbPtr = (PXE_CDB *)(UINTN)Cdb; Nic = &(gLanDeviceList[CdbPtr->IFnum]->NicInfo); gUndiApiTable[CdbPtr->OpCode](CdbPtr, Nic); return EFI_SUCCESS; } /** Map virtual memory address for DMA. This field can be set to zero if there is no mapping service. @param[in] Nic A pointer to the Network interface controller data. @param[in] MemAddr Virtual address to be mapped. @param[in] Size Size of memory to be mapped. @param[in] Direction Direction of data flow for this memory's usage: cpu->device, device->cpu or both ways. @param[out] MappedAddr Pointer to return the mapped device address. **/ UINTN MapIt ( IN NIC_DATA *Nic, IN UINT64 MemAddr, IN UINT32 Size, IN UINT32 Direction, OUT UINT64 MappedAddr ) { UINT64 *PhyAddr; PhyAddr = (UINT64 *)(UINTN)MappedAddr; if (Nic->PxeStart.Map_Mem == 0) { *PhyAddr = MemAddr; } else { ((void (*)(UINT64, UINT64, UINT32, UINT32, UINT64))(UINTN) Nic->PxeStart.Map_Mem)( Nic->PxeStart.Unique_ID, MemAddr, Size, Direction, MappedAddr ); } return PXE_STATCODE_SUCCESS; } /** Un-map previously mapped virtual memory address. This field can be set to zero only if the Map_Mem() service is also set to zero. @param[in] Nic A pointer to the Network interface controller data. @param[in] MemAddr Virtual address to be mapped. @param[in] Size Size of memory to be mapped. @param[in] Direction Direction of data flow for this memory's usage: cpu->device, device->cpu or both ways. @param[in] MappedAddr Pointer to return the mapped device address. **/ VOID UnMapIt ( IN NIC_DATA *Nic, IN UINT64 MemAddr, IN UINT32 Size, IN UINT32 Direction, IN UINT64 MappedAddr ) { if (Nic->PxeStart.UnMap_Mem != 0) { ((void (*)(UINT64, UINT64, UINT32, UINT32, UINT64))(UINTN) Nic->PxeStart.UnMap_Mem)( Nic->PxeStart.Unique_ID, MemAddr, Size, Direction, MappedAddr ); } return; }