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