summaryrefslogtreecommitdiffstats
path: root/NetworkPkg/SnpDxe/Transmit.c
diff options
context:
space:
mode:
Diffstat (limited to 'NetworkPkg/SnpDxe/Transmit.c')
-rw-r--r--NetworkPkg/SnpDxe/Transmit.c353
1 files changed, 353 insertions, 0 deletions
diff --git a/NetworkPkg/SnpDxe/Transmit.c b/NetworkPkg/SnpDxe/Transmit.c
new file mode 100644
index 0000000000..44fdd71f41
--- /dev/null
+++ b/NetworkPkg/SnpDxe/Transmit.c
@@ -0,0 +1,353 @@
+/** @file
+ Implementation of transmitting a packet.
+
+Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Snp.h"
+
+
+/**
+ Call UNDI to create the meadia header for the given data buffer.
+
+ @param Snp Pointer to SNP driver structure.
+ @param MacHeaderPtr Address where the media header will be filled in.
+ @param HeaderSize Size of the memory at MacHeaderPtr.
+ @param Buffer Data buffer pointer.
+ @param BufferSize Size of data in the Buffer
+ @param DestAddr Address of the destination mac address buffer.
+ @param SrcAddr Address of the source mac address buffer.
+ @param ProtocolPtr Address of the protocol type.
+
+ @retval EFI_SUCCESS Successfully completed the undi call.
+ @retval Other Error return from undi call.
+
+**/
+EFI_STATUS
+PxeFillHeader (
+ SNP_DRIVER *Snp,
+ VOID *MacHeaderPtr,
+ UINTN HeaderSize,
+ VOID *Buffer,
+ UINTN BufferSize,
+ EFI_MAC_ADDRESS *DestAddr,
+ EFI_MAC_ADDRESS *SrcAddr,
+ UINT16 *ProtocolPtr
+ )
+{
+ PXE_CPB_FILL_HEADER_FRAGMENTED *Cpb;
+
+ Cpb = Snp->Cpb;
+ if (SrcAddr != NULL) {
+ CopyMem (
+ (VOID *) Cpb->SrcAddr,
+ (VOID *) SrcAddr,
+ Snp->Mode.HwAddressSize
+ );
+ } else {
+ CopyMem (
+ (VOID *) Cpb->SrcAddr,
+ (VOID *) &(Snp->Mode.CurrentAddress),
+ Snp->Mode.HwAddressSize
+ );
+ }
+
+ CopyMem (
+ (VOID *) Cpb->DestAddr,
+ (VOID *) DestAddr,
+ Snp->Mode.HwAddressSize
+ );
+
+ //
+ // we need to do the byte swapping
+ //
+ Cpb->Protocol = (UINT16) PXE_SWAP_UINT16 (*ProtocolPtr);
+
+ Cpb->PacketLen = (UINT32) (BufferSize);
+ Cpb->MediaHeaderLen = (UINT16) HeaderSize;
+
+ Cpb->FragCnt = 2;
+ Cpb->reserved = 0;
+
+ Cpb->FragDesc[0].FragAddr = (UINT64)(UINTN) MacHeaderPtr;
+ Cpb->FragDesc[0].FragLen = (UINT32) HeaderSize;
+ Cpb->FragDesc[1].FragAddr = (UINT64)(UINTN) Buffer;
+ Cpb->FragDesc[1].FragLen = (UINT32) BufferSize;
+
+ Cpb->FragDesc[0].reserved = Cpb->FragDesc[1].reserved = 0;
+
+ Snp->Cdb.OpCode = PXE_OPCODE_FILL_HEADER;
+ Snp->Cdb.OpFlags = PXE_OPFLAGS_FILL_HEADER_FRAGMENTED;
+
+ Snp->Cdb.DBsize = PXE_DBSIZE_NOT_USED;
+ Snp->Cdb.DBaddr = PXE_DBADDR_NOT_USED;
+
+ Snp->Cdb.CPBsize = (UINT16) sizeof (PXE_CPB_FILL_HEADER_FRAGMENTED);
+ Snp->Cdb.CPBaddr = (UINT64)(UINTN) Cpb;
+
+ Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ Snp->Cdb.IFnum = Snp->IfNum;
+ Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nSnp->undi.fill_header() "));
+
+ (*Snp->IssueUndi32Command) ((UINT64) (UINTN) &Snp->Cdb);
+
+ switch (Snp->Cdb.StatCode) {
+ case PXE_STATCODE_SUCCESS:
+ return EFI_SUCCESS;
+
+ case PXE_STATCODE_INVALID_PARAMETER:
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nSnp->undi.fill_header() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ return EFI_INVALID_PARAMETER;
+
+ default:
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nSnp->undi.fill_header() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+}
+
+
+/**
+ This routine calls undi to transmit the given data buffer
+
+ @param Snp pointer to SNP driver structure
+ @param Buffer data buffer pointer
+ @param BufferSize Size of data in the Buffer
+
+ @retval EFI_SUCCESS if successfully completed the undi call
+ @retval Other error return from undi call.
+
+**/
+EFI_STATUS
+PxeTransmit (
+ SNP_DRIVER *Snp,
+ VOID *Buffer,
+ UINTN BufferSize
+ )
+{
+ PXE_CPB_TRANSMIT *Cpb;
+ EFI_STATUS Status;
+
+ Cpb = Snp->Cpb;
+ Cpb->FrameAddr = (UINT64) (UINTN) Buffer;
+ Cpb->DataLen = (UINT32) BufferSize;
+
+ Cpb->MediaheaderLen = 0;
+ Cpb->reserved = 0;
+
+ Snp->Cdb.OpFlags = PXE_OPFLAGS_TRANSMIT_WHOLE;
+
+ Snp->Cdb.CPBsize = (UINT16) sizeof (PXE_CPB_TRANSMIT);
+ Snp->Cdb.CPBaddr = (UINT64)(UINTN) Cpb;
+
+ Snp->Cdb.OpCode = PXE_OPCODE_TRANSMIT;
+ Snp->Cdb.DBsize = PXE_DBSIZE_NOT_USED;
+ Snp->Cdb.DBaddr = PXE_DBADDR_NOT_USED;
+
+ Snp->Cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ Snp->Cdb.IFnum = Snp->IfNum;
+ Snp->Cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nSnp->undi.transmit() "));
+ DEBUG ((EFI_D_NET, "\nSnp->Cdb.OpCode == %x", Snp->Cdb.OpCode));
+ DEBUG ((EFI_D_NET, "\nSnp->Cdb.CPBaddr == %LX", Snp->Cdb.CPBaddr));
+ DEBUG ((EFI_D_NET, "\nSnp->Cdb.DBaddr == %LX", Snp->Cdb.DBaddr));
+ DEBUG ((EFI_D_NET, "\nCpb->FrameAddr == %LX\n", Cpb->FrameAddr));
+
+ (*Snp->IssueUndi32Command) ((UINT64) (UINTN) &Snp->Cdb);
+
+ DEBUG ((EFI_D_NET, "\nexit Snp->undi.transmit() "));
+
+ //
+ // we will unmap the buffers in get_status call, not here
+ //
+ switch (Snp->Cdb.StatCode) {
+ case PXE_STATCODE_SUCCESS:
+ return EFI_SUCCESS;
+
+ case PXE_STATCODE_BUFFER_FULL:
+ case PXE_STATCODE_QUEUE_FULL:
+ case PXE_STATCODE_BUSY:
+ Status = EFI_NOT_READY;
+ DEBUG (
+ (EFI_D_NET,
+ "\nSnp->undi.transmit() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+ break;
+
+ default:
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nSnp->undi.transmit() %xh:%xh\n",
+ Snp->Cdb.StatFlags,
+ Snp->Cdb.StatCode)
+ );
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ return Status;
+}
+
+/**
+ Places a packet in the transmit queue of a network interface.
+
+ This function places the packet specified by Header and Buffer on the transmit
+ queue. If HeaderSize is nonzero and HeaderSize is not equal to
+ This->Mode->MediaHeaderSize, then EFI_INVALID_PARAMETER will be returned. If
+ BufferSize is less than This->Mode->MediaHeaderSize, then EFI_BUFFER_TOO_SMALL
+ will be returned. If Buffer is NULL, then EFI_INVALID_PARAMETER will be
+ returned. If HeaderSize is nonzero and DestAddr or Protocol is NULL, then
+ EFI_INVALID_PARAMETER will be returned. If the transmit engine of the network
+ interface is busy, then EFI_NOT_READY will be returned. If this packet can be
+ accepted by the transmit engine of the network interface, the packet contents
+ specified by Buffer will be placed on the transmit queue of the network
+ interface, and EFI_SUCCESS will be returned. GetStatus() can be used to
+ determine when the packet has actually been transmitted. The contents of the
+ Buffer must not be modified until the packet has actually been transmitted.
+ The Transmit() function performs nonblocking I/O. A caller who wants to perform
+ blocking I/O, should call Transmit(), and then GetStatus() until the
+ transmitted buffer shows up in the recycled transmit buffer.
+ If the driver has not been initialized, EFI_DEVICE_ERROR will be returned.
+
+ @param This A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
+ @param HeaderSize The size, in bytes, of the media header to be filled in by the
+ Transmit() function. If HeaderSize is nonzero, then it must
+ be equal to This->Mode->MediaHeaderSize and the DestAddr and
+ Protocol parameters must not be NULL.
+ @param BufferSize The size, in bytes, of the entire packet (media header and
+ data) to be transmitted through the network interface.
+ @param Buffer A pointer to the packet (media header followed by data) to be
+ transmitted. This parameter cannot be NULL. If HeaderSize is
+ zero, then the media header in Buffer must already be filled
+ in by the caller. If HeaderSize is nonzero, then the media
+ header will be filled in by the Transmit() function.
+ @param SrcAddr The source HW MAC address. If HeaderSize is zero, then this
+ parameter is ignored. If HeaderSize is nonzero and SrcAddr
+ is NULL, then This->Mode->CurrentAddress is used for the
+ source HW MAC address.
+ @param DestAddr The destination HW MAC address. If HeaderSize is zero, then
+ this parameter is ignored.
+ @param Protocol The type of header to build. If HeaderSize is zero, then this
+ parameter is ignored. See RFC 1700, section "Ether Types,"
+ for examples.
+
+ @retval EFI_SUCCESS The packet was placed on the transmit queue.
+ @retval EFI_NOT_STARTED The network interface has not been started.
+ @retval EFI_NOT_READY The network interface is too busy to accept this
+ transmit request.
+ @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small.
+ @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported
+ value.
+ @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
+ @retval EFI_UNSUPPORTED This function is not supported by the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SnpUndi32Transmit (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN UINTN HeaderSize,
+ IN UINTN BufferSize,
+ IN VOID *Buffer,
+ IN EFI_MAC_ADDRESS *SrcAddr, OPTIONAL
+ IN EFI_MAC_ADDRESS *DestAddr, OPTIONAL
+ IN UINT16 *Protocol OPTIONAL
+ )
+{
+ SNP_DRIVER *Snp;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (Snp == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ switch (Snp->Mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ if (Buffer == NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ if (BufferSize < Snp->Mode.MediaHeaderSize) {
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto ON_EXIT;
+ }
+
+ //
+ // if the HeaderSize is non-zero, we need to fill up the header and for that
+ // we need the destination address and the protocol
+ //
+ if (HeaderSize != 0) {
+ if (HeaderSize != Snp->Mode.MediaHeaderSize || DestAddr == 0 || Protocol == 0) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Status = PxeFillHeader (
+ Snp,
+ Buffer,
+ HeaderSize,
+ (UINT8 *) Buffer + HeaderSize,
+ BufferSize - HeaderSize,
+ DestAddr,
+ SrcAddr,
+ Protocol
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ Status = PxeTransmit (Snp, Buffer, BufferSize);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}