mnpio.c

来自「EFI BIOS是Intel提出的下一代的BIOS标准。这里上传的Edk源代码是」· C语言 代码 · 共 1,165 行 · 第 1/2 页

C
1,165
字号
/*++

Copyright (c) 2005 - 2006, Intel Corporation                                                         
All rights reserved. This program and the accompanying materials                          
are licensed and made available under the terms and conditions of the BSD License         
which accompanies this distribution.  The full text of the license may be found at        
http://opensource.org/licenses/bsd-license.php                                            
                                                                                          
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             

Module Name:

  MnpIo.c

Abstract:

  Implementation of Managed Network Protocol I/O functions.

--*/

#include "MnpImpl.h"

BOOLEAN
MnpIsValidTxToken (
  IN MNP_INSTANCE_DATA                     *Instance,
  IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *Token
  )
/*++

Routine Description:

  Validates the Mnp transmit token.

Arguments:

  Instance - Pointer to the Mnp instance context data.
  Token    - Pointer to the transmit token to check.

Returns:

  The Token is valid or not.

--*/
{
  MNP_SERVICE_DATA                  *MnpServiceData;
  EFI_SIMPLE_NETWORK_MODE           *SnpMode;
  EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData;
  UINT32                            Index;
  UINT32                            TotalLength;
  EFI_MANAGED_NETWORK_FRAGMENT_DATA *FragmentTable;

  MnpServiceData = Instance->MnpServiceData;
  NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);

  SnpMode = MnpServiceData->Snp->Mode;
  TxData  = Token->Packet.TxData;

  if ((Token->Event == NULL) || (TxData == NULL) || (TxData->FragmentCount == 0)) {
    //
    // The token is invalid if the Event is NULL, or the TxData is NULL, or
    // the fragment count is zero.
    //
    MNP_DEBUG_WARN (("MnpIsValidTxToken: Invalid Token.\n"));
    return FALSE;
  }

  if ((TxData->DestinationAddress != NULL) && (TxData->HeaderLength != 0)) {
    //
    // The token is invalid if the HeaderLength isn't zero while the DestinationAddress
    // is NULL (The destination address is already put into the packet).
    //
    MNP_DEBUG_WARN (("MnpIsValidTxToken: DestinationAddress isn't NULL, HeaderLength must be 0.\n"));
    return FALSE;
  }

  TotalLength   = 0;
  FragmentTable = TxData->FragmentTable;
  for (Index = 0; Index < TxData->FragmentCount; Index++) {

    if ((FragmentTable[Index].FragmentLength == 0) || (FragmentTable[Index].FragmentBuffer == NULL)) {
      //
      // The token is invalid if any FragmentLength is zero or any FragmentBuffer is NULL.
      //
      MNP_DEBUG_WARN (("MnpIsValidTxToken: Invalid FragmentLength or FragmentBuffer.\n"));
      return FALSE;
    }

    TotalLength += FragmentTable[Index].FragmentLength;
  }

  if ((TxData->DestinationAddress == NULL) && (FragmentTable[0].FragmentLength < TxData->HeaderLength)) {
    //
    // Media header is split between fragments.
    //
    return FALSE;
  }

  if (TotalLength != (TxData->DataLength + TxData->HeaderLength)) {
    //
    // The length calculated from the fragment information doesn't equal to the
    // sum of the DataLength and the HeaderLength.
    //
    MNP_DEBUG_WARN (("MnpIsValidTxData: Invalid Datalength compared with ""the sum of fragment length.\n"));
    return FALSE;
  }

  if (TxData->DataLength > MnpServiceData->Mtu) {
    //
    // The total length is larger than the MTU.
    //
    MNP_DEBUG_WARN (("MnpIsValidTxData: TxData->DataLength exceeds Mtu.\n"));
    return FALSE;
  }

  return TRUE;
}

VOID
MnpBuildTxPacket (
  IN  MNP_SERVICE_DATA                   *MnpServiceData,
  IN  EFI_MANAGED_NETWORK_TRANSMIT_DATA  *TxData,
  OUT UINT8                              **PktBuf,
  OUT UINT32                             *PktLen
  )
/*++

Routine Description:

  Build the packet to transmit from the TxData passed in.

Arguments:

  MnpServiceData - Pointer to the mnp service context data.
  TxData         - Pointer to the transmit data containing the information to
                   build the packet.
  PktBuf         - Pointer to record the address of the packet.
  PktLen         - Pointer to a UINT32 variable used to record the packet's length.

Returns:

  None.

--*/
{
  EFI_SIMPLE_NETWORK_MODE *SnpMode;
  UINT8                   *DstPos;
  UINT16                  Index;

  if ((TxData->DestinationAddress == NULL) && (TxData->FragmentCount == 1)) {
    //
    // Media header is in FragmentTable and there is only one fragment,
    // use fragment buffer directly.
    //
    *PktBuf = TxData->FragmentTable[0].FragmentBuffer;
    *PktLen = TxData->FragmentTable[0].FragmentLength;
  } else {
    //
    // Either media header isn't in FragmentTable or there is more than
    // one fragment, copy the data into the packet buffer. Reserve the
    // media header space if necessary.
    //
    SnpMode = MnpServiceData->Snp->Mode;
    DstPos  = MnpServiceData->TxBuf;

    *PktLen = 0;
    if (TxData->DestinationAddress != NULL) {
      //
      // If dest address is not NULL, move DstPos to reserve space for the
      // media header. Add the media header length to buflen.
      //
      DstPos += SnpMode->MediaHeaderSize;
      *PktLen += SnpMode->MediaHeaderSize;
    }

    for (Index = 0; Index < TxData->FragmentCount; Index++) {
      //
      // Copy the data.
      //
      NetCopyMem (
        DstPos,
        TxData->FragmentTable[Index].FragmentBuffer,
        TxData->FragmentTable[Index].FragmentLength
        );
      DstPos += TxData->FragmentTable[Index].FragmentLength;
    }

    //
    // Set the buffer pointer and the buffer length.
    //
    *PktBuf = MnpServiceData->TxBuf;
    *PktLen += TxData->DataLength + TxData->HeaderLength;
  }
}

EFI_STATUS
MnpSyncSendPacket (
  IN MNP_SERVICE_DATA                      *MnpServiceData,
  IN UINT8                                 *Packet,
  IN UINT32                                Length,
  IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *Token
  )
/*++

Routine Description:

  Synchronously send out the packet.

Arguments:

  MnpServiceData - Pointer to the mnp service context data.
  Packet         - Pointer to the pakcet buffer.
  Length         - The length of the packet.
  Token          - Pointer to the token the packet generated from.

Returns:

  EFI_SUCCESS      - The packet is sent out.
  EFI_TIMEOUT      - Time out occurs, the packet isn't sent.
  EFI_DEVICE_ERROR - An unexpected network error occurs.

--*/
{
  EFI_STATUS                        Status;
  EFI_SIMPLE_NETWORK_PROTOCOL       *Snp;
  EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData;
  UINT32                            HeaderSize;
  UINT8                             *TxBuf;

  Snp         = MnpServiceData->Snp;
  TxData      = Token->Packet.TxData;

  HeaderSize  = Snp->Mode->MediaHeaderSize - TxData->HeaderLength;

  //
  // Start the timeout event.
  //
  Status = gBS->SetTimer (
                  MnpServiceData->TxTimeoutEvent,
                  TimerRelative,
                  MNP_TX_TIMEOUT_TIME
                  );
  if (EFI_ERROR (Status)) {

    goto SIGNAL_TOKEN;
  }

  for (;;) {
    //
    // Transmit the packet through SNP.
    //
    Status = Snp->Transmit (
                    Snp,
                    HeaderSize,
                    Length,
                    Packet,
                    TxData->SourceAddress,
                    TxData->DestinationAddress,
                    &TxData->ProtocolType
                    );
    if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) {

      Status = EFI_DEVICE_ERROR;
      break;
    }

    //
    // If Status is EFI_SUCCESS, the packet is put in the transmit queue.
    // if Status is EFI_NOT_READY, the transmit engine of the network interface is busy.
    // Both need to sync SNP.
    //
    TxBuf = NULL;
    do {
      //
      // Get the recycled transmit buffer status.
      //
      Snp->GetStatus (Snp, NULL, &TxBuf);

      if (!EFI_ERROR (gBS->CheckEvent (MnpServiceData->TxTimeoutEvent))) {

        Status = EFI_TIMEOUT;
        break;
      }
    } while (TxBuf == NULL);

    if ((Status == EFI_SUCCESS) || (Status == EFI_TIMEOUT)) {

      break;
    } else {
      //
      // Status is EFI_NOT_READY. Restart the timer event and call Snp->Transmit again.
      //
      gBS->SetTimer (
            MnpServiceData->TxTimeoutEvent,
            TimerRelative,
            MNP_TX_TIMEOUT_TIME
            );
    }
  }

  //
  // Cancel the timer event.
  //
  gBS->SetTimer (MnpServiceData->TxTimeoutEvent, TimerCancel, 0);

SIGNAL_TOKEN:

  Token->Status = Status;
  gBS->SignalEvent (Token->Event);

  return EFI_SUCCESS;
}

EFI_STATUS
MnpInstanceDeliverPacket (
  IN MNP_INSTANCE_DATA  *Instance
  )
/*++

Routine Description:

  Try to deliver the received packet to the instance.

Arguments:

  Instance - Pointer to the mnp instance context data.

Returns:

  EFI_SUCCESS          - The received packet is delivered, or there is no packet
                         to deliver, or there is no available receive token.
  EFI_OUT_OF_RESOURCES - The deliver fails due to lack of memory resource.

--*/
{
  MNP_SERVICE_DATA                      *MnpServiceData;
  MNP_RXDATA_WRAP                       *RxDataWrap;
  NET_BUF                               *DupNbuf;
  EFI_MANAGED_NETWORK_RECEIVE_DATA      *RxData;
  EFI_SIMPLE_NETWORK_MODE               *SnpMode;
  EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *RxToken;

  MnpServiceData = Instance->MnpServiceData;
  NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);

  if (NetMapIsEmpty (&Instance->RxTokenMap) || NetListIsEmpty (&Instance->RcvdPacketQueue)) {
    //
    // No pending received data or no available receive token, return.
    //
    return EFI_SUCCESS;
  }

  ASSERT (Instance->RcvdPacketQueueSize != 0);

  RxDataWrap = NET_LIST_HEAD (&Instance->RcvdPacketQueue, MNP_RXDATA_WRAP, WrapEntry);
  if (RxDataWrap->Nbuf->RefCnt > 2) {
    //
    // There are other instances share this Nbuf, duplicate to get a
    // copy to allow the instance to do R/W operations.
    //
    DupNbuf = MnpAllocNbuf (MnpServiceData);
    if (DupNbuf == NULL) {
      MNP_DEBUG_WARN (("MnpDeliverPacket: Failed to allocate a free Nbuf.\n"));

      return EFI_OUT_OF_RESOURCES;
    }

    //
    // Duplicate the net buffer.
    //
    NetbufDuplicate (RxDataWrap->Nbuf, DupNbuf, 0);
    MnpFreeNbuf (MnpServiceData, RxDataWrap->Nbuf);
    RxDataWrap->Nbuf = DupNbuf;
  }

  //
  // All resources are OK, remove the packet from the queue.
  //
  NetListRemoveHead (&Instance->RcvdPacketQueue);
  Instance->RcvdPacketQueueSize--;

  RxData  = &RxDataWrap->RxData;
  SnpMode = MnpServiceData->Snp->Mode;

  //
  // Set all the buffer pointers.
  //
  RxData->MediaHeader         = NetbufGetByte (RxDataWrap->Nbuf, 0, NULL);
  RxData->DestinationAddress  = RxData->MediaHeader;
  RxData->SourceAddress       = (UINT8 *) RxData->MediaHeader + SnpMode->HwAddressSize;
  RxData->PacketData          = (UINT8 *) RxData->MediaHeader + SnpMode->MediaHeaderSize;

  //
  // Insert this RxDataWrap into the delivered queue.
  //
  NetListInsertTail (&Instance->RxDeliveredPacketQueue, &RxDataWrap->WrapEntry);

  //
  // Get the receive token from the RxTokenMap.
  //
  RxToken = NetMapRemoveHead (&Instance->RxTokenMap, NULL);

  //
  // Signal this token's event.
  //
  RxToken->Packet.RxData  = &RxDataWrap->RxData;
  RxToken->Status         = EFI_SUCCESS;
  gBS->SignalEvent (RxToken->Event);

  return EFI_SUCCESS;
}

STATIC
VOID
MnpDeliverPacket (
  IN MNP_SERVICE_DATA  *MnpServiceData
  )
/*++

Routine Description:

  Deliver the received packet for the instances belonging to the MnpServiceData.

Arguments:

  MnpServiceData  - Pointer to the mnp service context data.

Returns:

  None.

--*/
{
  NET_LIST_ENTRY    *Entry;
  MNP_INSTANCE_DATA *Instance;

  NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);

  NET_LIST_FOR_EACH (Entry, &MnpServiceData->ChildrenList) {
    Instance = NET_LIST_USER_STRUCT (Entry, MNP_INSTANCE_DATA, InstEntry);
    NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);

    //
    // Try to deliver packet for this instance.
    //
    MnpInstanceDeliverPacket (Instance);
  }
}

VOID
EFIAPI
MnpRecycleRxData (
  IN EFI_EVENT  Event,
  IN VOID       *Context
  )
/*++

Routine Description:

  Recycle the RxData and other resources used to hold and deliver the received
  packet.

Arguments:

  Event   - The event this notify function registered to.
  Context - Pointer to the context data registerd to the Event.

Returns:

  None.

--*/
{
  MNP_RXDATA_WRAP   *RxDataWrap;
  MNP_SERVICE_DATA  *MnpServiceData;

  ASSERT (Context != NULL);

  RxDataWrap = (MNP_RXDATA_WRAP *) Context;
  NET_CHECK_SIGNATURE (RxDataWrap->Instance, MNP_INSTANCE_DATA_SIGNATURE);

  ASSERT (RxDataWrap->Nbuf != NULL);

  MnpServiceData = RxDataWrap->Instance->MnpServiceData;
  NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);

  //
  // Free this Nbuf.
  //
  MnpFreeNbuf (MnpServiceData, RxDataWrap->Nbuf);
  RxDataWrap->Nbuf = NULL;

  //
  // Close the recycle event.
  //
  gBS->CloseEvent (RxDataWrap->RxData.RecycleEvent);

  //
  // Remove this Wrap entry from the list.
  //
  NetListRemoveEntry (&RxDataWrap->WrapEntry);

  NetFreePool (RxDataWrap);
}

STATIC
VOID
MnpQueueRcvdPacket (
  IN MNP_INSTANCE_DATA  *Instance,
  IN MNP_RXDATA_WRAP    *RxDataWrap
  )
/*++

Routine Description:

  Queue the received packet into instance's receive queue.

Arguments:

  Instance   - Pointer to the mnp instance context data.
  RxDataWrap - Pointer to the Wrap structure containing the received data and
               other information.

Returns:

  None.

--*/
{
  MNP_RXDATA_WRAP *OldRxDataWrap;

  NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);

  //
  // Check the queue size. If it exceeds the limit, drop one packet
  // from the head.
  //
  if (Instance->RcvdPacketQueueSize == MNP_MAX_RCVD_PACKET_QUE_SIZE) {

    MNP_DEBUG_WARN (("MnpQueueRcvdPacket: Drop one packet bcz queue ""size limit reached.\n"));

    //
    // Get the oldest packet.
    //
    OldRxDataWrap = NET_LIST_HEAD (
                      &Instance->RcvdPacketQueue,
                      MNP_RXDATA_WRAP,
                      WrapEntry
                      );

    //
    // Recycle this OldRxDataWrap, this entry will be removed by the callee.
    //
    MnpRecycleRxData (NULL, (VOID *) OldRxDataWrap);
    Instance->RcvdPacketQueueSize--;
  }

  //
  // Update the timeout tick using the configured parameter.
  //
  RxDataWrap->TimeoutTick = Instance->ConfigData.ReceivedQueueTimeoutValue;

  //
  // Insert this Wrap into the instance queue.
  //
  NetListInsertTail (&Instance->RcvdPacketQueue, &RxDataWrap->WrapEntry);
  Instance->RcvdPacketQueueSize++;
}

STATIC
BOOLEAN
MnpMatchPacket (
  IN MNP_INSTANCE_DATA                 *Instance,
  IN EFI_MANAGED_NETWORK_RECEIVE_DATA  *RxData,
  IN MNP_GROUP_ADDRESS                 *GroupAddress OPTIONAL,
  IN UINT8                             PktAttr
  )
/*++

Routine Description:

  Match the received packet with the instance receive filters.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?