This patch fixes wrong condition because of UINT16 value to integer promotion. NumberMcFilters is UINT16 value, so when bitwise shift operator applied to small integer type, the operation is preceded by integral promotion. This is described in MISRA-C:2004 guideline as Rule 10.5: "If the bitwise operators ~ and << are applied to an operand of underlying type unsigned char or unsigned short, the result shall be immediately cast to the underlying type of the operand." A simple fix for this issue would be the following: if ((UINT16)(UsbEthFunDescriptor.NumberMcFilters << 1) == 0) But this patch proposes to use bitwise AND operation with a proper bit mask rather than shifting to prevent similar mistakes in future. Cc: Richard Ho <richardho@ami.com> Cc: Rebecca Cran <rebecca@bsdio.com> Signed-off-by: Mike Maslenkin <mike.maslenkin@gmail.com>
1806 lines
52 KiB
C
1806 lines
52 KiB
C
/** @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;
|
|
}
|