| /** @file | |
| This file contains code for UNDI command based on UEFI specification. | |
| Copyright (c) 2023, American Megatrends International LLC. All rights reserved.<BR> | |
| 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; | |
| } |