+ @retval EFI_SUCCESS UdpIo is created successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Fails to create UdpIo because of limited\r
+ resources or configuration failure.\r
+**/\r
+EFI_STATUS\r
+Dhcp4InstanceCreateUdpIo (\r
+ IN OUT DHCP_PROTOCOL *Instance\r
+ )\r
+{\r
+ DHCP_SERVICE *DhcpSb;\r
+ EFI_STATUS Status;\r
+ VOID *Udp4;\r
+\r
+ ASSERT (Instance->Token != NULL);\r
+\r
+ DhcpSb = Instance->Service;\r
+ Instance->UdpIo = UdpIoCreateIo (\r
+ DhcpSb->Controller,\r
+ DhcpSb->Image,\r
+ Dhcp4InstanceConfigUdpIo,\r
+ UDP_IO_UDP4_VERSION,\r
+ Instance\r
+ );\r
+ if (Instance->UdpIo == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ } else {\r
+ Status = gBS->OpenProtocol (\r
+ Instance->UdpIo->UdpHandle,\r
+ &gEfiUdp4ProtocolGuid,\r
+ (VOID **) &Udp4,\r
+ Instance->Service->Image,\r
+ Instance->Handle,\r
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ UdpIoFreeIo (Instance->UdpIo);\r
+ Instance->UdpIo = NULL;\r
+ }\r
+ return Status;\r
+ }\r
+}\r
+\r
+/**\r
+ Callback of Dhcp packet. Does nothing.\r
+\r
+ @param Arg The context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+DhcpDummyExtFree (\r
+ IN VOID *Arg\r
+ )\r
+{\r
+}\r
+\r
+/**\r
+ Callback of UdpIoRecvDatagram() that handles a Dhcp4 packet.\r
+\r
+ Only BOOTP responses will be handled that correspond to the Xid of the request\r
+ sent out. The packet will be queued to the response queue.\r
+\r
+ @param UdpPacket The Dhcp4 packet.\r
+ @param EndPoint Udp4 address pair.\r
+ @param IoStatus Status of the input.\r
+ @param Context Extra info for the input.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PxeDhcpInput (\r
+ NET_BUF *UdpPacket,\r
+ UDP_END_POINT *EndPoint,\r
+ EFI_STATUS IoStatus,\r
+ VOID *Context\r
+ )\r
+{\r
+ DHCP_PROTOCOL *Instance;\r
+ EFI_DHCP4_HEADER *Head;\r
+ NET_BUF *Wrap;\r
+ EFI_DHCP4_PACKET *Packet;\r
+ EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token;\r
+ UINT32 Len;\r
+ EFI_STATUS Status;\r
+\r
+ Wrap = NULL;\r
+ Instance = (DHCP_PROTOCOL *) Context;\r
+ Token = Instance->Token;\r
+\r
+ //\r
+ // Don't restart receive if error occurs or DHCP is destroyed.\r
+ //\r
+ if (EFI_ERROR (IoStatus)) {\r
+ return ;\r
+ }\r
+\r
+ ASSERT (UdpPacket != NULL);\r
+\r
+ //\r
+ // Validate the packet received\r
+ //\r
+ if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {\r
+ goto RESTART;\r
+ }\r
+\r
+ //\r
+ // Copy the DHCP message to a continuous memory block, make the buffer size\r
+ // of the EFI_DHCP4_PACKET a multiple of 4-byte.\r
+ //\r
+ Len = NET_ROUNDUP (sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER), 4);\r
+ Wrap = NetbufAlloc (Len);\r
+ if (Wrap == NULL) {\r
+ goto RESTART;\r
+ }\r
+\r
+ Packet = (EFI_DHCP4_PACKET *) NetbufAllocSpace (Wrap, Len, NET_BUF_TAIL);\r
+ ASSERT (Packet != NULL);\r
+\r
+ Packet->Size = Len;\r
+ Head = &Packet->Dhcp4.Header;\r
+ Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head);\r
+\r
+ if (Packet->Length != UdpPacket->TotalSize) {\r
+ goto RESTART;\r
+ }\r
+\r
+ //\r
+ // Is this packet the answer to our packet?\r
+ //\r
+ if ((Head->OpCode != BOOTP_REPLY) ||\r
+ (Head->Xid != Token->Packet->Dhcp4.Header.Xid) ||\r
+ (CompareMem (&Token->Packet->Dhcp4.Header.ClientHwAddr[0], Head->ClientHwAddr, Head->HwAddrLen) != 0)) {\r
+ goto RESTART;\r
+ }\r
+\r
+ //\r
+ // Validate the options and retrieve the interested options\r
+ //\r
+ if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&\r
+ (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&\r
+ EFI_ERROR (DhcpValidateOptions (Packet, NULL))) {\r
+\r
+ goto RESTART;\r
+ }\r
+\r
+ //\r
+ // Keep this packet in the ResponseQueue.\r
+ //\r
+ NET_GET_REF (Wrap);\r
+ NetbufQueAppend (&Instance->ResponseQueue, Wrap);\r
+\r
+RESTART:\r
+\r
+ NetbufFree (UdpPacket);\r
+\r
+ if (Wrap != NULL) {\r
+ NetbufFree (Wrap);\r
+ }\r
+\r
+ Status = UdpIoRecvDatagram (Instance->UdpIo, PxeDhcpInput, Instance, 0);\r
+ if (EFI_ERROR (Status)) {\r
+ PxeDhcpDone (Instance);\r
+ }\r
+}\r
+\r
+/**\r
+ Complete a Dhcp4 transaction and signal the upper layer.\r
+\r
+ @param Instance Dhcp4 instance.\r
+\r
+**/\r
+VOID\r
+PxeDhcpDone (\r
+ IN DHCP_PROTOCOL *Instance\r
+ )\r
+{\r
+ EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token;\r
+\r
+ Token = Instance->Token;\r
+\r
+ Token->ResponseCount = Instance->ResponseQueue.BufNum;\r
+ if (Token->ResponseCount != 0) {\r
+ Token->ResponseList = (EFI_DHCP4_PACKET *) AllocatePool (Instance->ResponseQueue.BufSize);\r
+ if (Token->ResponseList == NULL) {\r
+ Token->Status = EFI_OUT_OF_RESOURCES;\r
+ goto SIGNAL_USER;\r
+ }\r
+\r
+ //\r
+ // Copy the received DHCP responses.\r
+ //\r
+ NetbufQueCopy (&Instance->ResponseQueue, 0, Instance->ResponseQueue.BufSize, (UINT8 *) Token->ResponseList);\r
+ Token->Status = EFI_SUCCESS;\r
+ } else {\r
+ Token->ResponseList = NULL;\r
+ Token->Status = EFI_TIMEOUT;\r
+ }\r
+\r
+SIGNAL_USER:\r
+ //\r
+ // Clean up the resources dedicated for this transmit receive transaction.\r
+ //\r
+ NetbufQueFlush (&Instance->ResponseQueue);\r
+ UdpIoCleanIo (Instance->UdpIo);\r
+ gBS->CloseProtocol (\r
+ Instance->UdpIo->UdpHandle,\r
+ &gEfiUdp4ProtocolGuid,\r
+ Instance->Service->Image,\r
+ Instance->Handle\r
+ );\r
+ UdpIoFreeIo (Instance->UdpIo);\r
+ Instance->UdpIo = NULL;\r
+ Instance->Token = NULL;\r
+\r
+ if (Token->CompletionEvent != NULL) {\r
+ gBS->SignalEvent (Token->CompletionEvent);\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Transmits a DHCP formatted packet and optionally waits for responses.\r
+\r
+ The TransmitReceive() function is used to transmit a DHCP packet and optionally\r
+ wait for the response from servers. This function does not change the state of\r
+ the EFI DHCPv4 Protocol driver and thus can be used at any time.\r
+\r
+ @param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance.\r
+ @param[in] Token Pointer to the EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN structure.\r
+\r
+ @retval EFI_SUCCESS The packet was successfully queued for transmission.\r
+ @retval EFI_INVALID_PARAMETER Some parameter is NULL.\r
+ @retval EFI_NOT_READY The previous call to this function has not finished yet. Try to call\r
+ this function after collection process completes.\r
+ @retval EFI_NO_MAPPING The default station address is not available yet.\r
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval Others Some other unexpected error occurred.\r