summaryrefslogtreecommitdiffstats
path: root/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndisFunction.c
diff options
context:
space:
mode:
Diffstat (limited to 'MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndisFunction.c')
-rw-r--r--MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndisFunction.c1721
1 files changed, 1721 insertions, 0 deletions
diff --git a/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndisFunction.c b/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndisFunction.c
new file mode 100644
index 0000000000..b3632233ad
--- /dev/null
+++ b/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndisFunction.c
@@ -0,0 +1,1721 @@
+/** @file
+ This file contains code for USB Ethernet descriptor
+ and specific requests implement.
+
+ Copyright (c) 2023, American Megatrends International LLC. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "UsbRndis.h"
+
+UINT16 gStopBulkInCnt = 0;
+UINT16 gBlockBulkInCnt = 0;
+
+/**
+ Load All of device descriptor.
+
+ @param[in] UsbIo A pointer to the EFI_USB_IO_PROTOCOL instance.
+ @param[out] ConfigDesc A pointer to the configuration descriptor.
+
+ @retval EFI_SUCCESS The request executed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed because the
+ buffer specified by DescriptorLength and Descriptor
+ is not large enough to hold the result of the request.
+ @retval EFI_TIMEOUT A timeout occurred executing the request.
+ @retval EFI_DEVICE_ERROR The request failed due to a device error. The transfer
+ status is returned in Status.
+**/
+EFI_STATUS
+LoadAllDescriptor (
+ IN EFI_USB_IO_PROTOCOL *UsbIo,
+ OUT EFI_USB_CONFIG_DESCRIPTOR **ConfigDesc
+ )
+{
+ EFI_STATUS Status;
+ UINT32 TransStatus;
+ EFI_USB_CONFIG_DESCRIPTOR Tmp;
+
+ Status = UsbIo->UsbGetConfigDescriptor (UsbIo, &Tmp);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a:UsbGetConfigDescriptor status = %r\n", __func__, Status));
+ return Status;
+ }
+
+ Status = gBS->AllocatePool (
+ EfiBootServicesData,
+ Tmp.TotalLength,
+ (VOID **)ConfigDesc
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: AllocatePool status = %r\n", __func__, Status));
+ return Status;
+ }
+
+ Status = UsbGetDescriptor (
+ UsbIo,
+ USB_DESC_TYPE_CONFIG << 8 | (Tmp.ConfigurationValue - 1), // zero based
+ 0,
+ Tmp.TotalLength,
+ *ConfigDesc,
+ &TransStatus
+ );
+ return Status;
+}
+
+/**
+ Returns pointer to the next descriptor for the pack of USB descriptors
+ located in continues memory segment
+
+ @param[in] Desc A pointer to the CONFIG_DESCRIPTOR instance.
+ @param[in, out] Offset A pointer to the sum of descriptor length.
+
+ @retval TRUE The request executed successfully.
+ @retval FALSE No next descriptor.
+
+**/
+BOOLEAN
+NextDescriptor (
+ IN EFI_USB_CONFIG_DESCRIPTOR *Desc,
+ IN OUT UINTN *Offset
+ )
+{
+ if ((Desc == NULL) || (*Offset >= Desc->TotalLength)) {
+ return FALSE;
+ }
+
+ if (((EFI_USB_CONFIG_DESCRIPTOR *)((char *)Desc+*Offset))->Length == 0) {
+ return FALSE;
+ }
+
+ *Offset += ((EFI_USB_CONFIG_DESCRIPTOR *)((char *)Desc+*Offset))->Length;
+ if ( *Offset >= Desc->TotalLength ) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ Read Function descriptor
+
+ @param[in] Config A pointer to all of configuration.
+ @param[in] FunDescriptorType USB CDC class descriptor SubType.
+ @param[out] DataBuffer A pointer to the Data of corresponding to device capability.
+
+ @retval EFI_SUCCESS The device capability descriptor was retrieved
+ successfully.
+ @retval EFI_UNSUPPORTED No supported.
+ @retval EFI_NOT_FOUND The device capability descriptor was not found.
+
+**/
+EFI_STATUS
+GetFunctionalDescriptor (
+ IN EFI_USB_CONFIG_DESCRIPTOR *Config,
+ IN UINT8 FunDescriptorType,
+ OUT VOID *DataBuffer
+ )
+{
+ EFI_STATUS Status;
+ UINTN Offset;
+ EFI_USB_INTERFACE_DESCRIPTOR *Interface;
+
+ Status = EFI_NOT_FOUND;
+
+ for (Offset = 0; NextDescriptor (Config, &Offset);) {
+ Interface = (EFI_USB_INTERFACE_DESCRIPTOR *)((UINT8 *)Config + Offset);
+ if (Interface->DescriptorType == CS_INTERFACE) {
+ if (((USB_HEADER_FUN_DESCRIPTOR *)Interface)->DescriptorSubtype == FunDescriptorType) {
+ switch (FunDescriptorType) {
+ case HEADER_FUN_DESCRIPTOR:
+ CopyMem (
+ DataBuffer,
+ (USB_HEADER_FUN_DESCRIPTOR *)Interface,
+ sizeof (USB_HEADER_FUN_DESCRIPTOR)
+ );
+ return EFI_SUCCESS;
+ case UNION_FUN_DESCRIPTOR:
+ CopyMem (
+ DataBuffer,
+ (USB_UNION_FUN_DESCRIPTOR *)Interface,
+ ((USB_UNION_FUN_DESCRIPTOR *)Interface)->FunctionLength
+ );
+ return EFI_SUCCESS;
+ case ETHERNET_FUN_DESCRIPTOR:
+ CopyMem (
+ DataBuffer,
+ (USB_ETHERNET_FUN_DESCRIPTOR *)Interface,
+ sizeof (USB_ETHERNET_FUN_DESCRIPTOR)
+ );
+ return EFI_SUCCESS;
+ default:
+ Status = EFI_UNSUPPORTED;
+ break;
+ }
+ }
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Get USB Ethernet IO endpoint and USB CDC data IO endpoint.
+
+ @param[in] UsbIo A pointer to the EFI_USB_IO_PROTOCOL instance.
+ @param[in, out] UsbRndisDevice A pointer to the USB_RNDIS_DEVICE instance.
+
+**/
+VOID
+GetEndpoint (
+ IN EFI_USB_IO_PROTOCOL *UsbIo,
+ IN OUT USB_RNDIS_DEVICE *UsbRndisDevice
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Index;
+ UINT32 Result;
+ EFI_USB_INTERFACE_DESCRIPTOR Interface;
+ EFI_USB_ENDPOINT_DESCRIPTOR Endpoint;
+
+ Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a:UsbGetInterfaceDescriptor status = %r\n", __func__, Status));
+ return;
+ }
+
+ if (Interface.NumEndpoints == 0 ) {
+ Status = UsbSetInterface (UsbIo, 1, 0, &Result);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a:UsbSetInterface status = %r\n", __func__, Status));
+ return;
+ }
+
+ Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a:UsbGetInterfaceDescriptor status = %r\n", __func__, Status));
+ return;
+ }
+ }
+
+ for (Index = 0; Index < Interface.NumEndpoints; Index++) {
+ Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &Endpoint);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a:UsbGetEndpointDescriptor status = %r\n", __func__, Status));
+ return;
+ }
+
+ switch ((Endpoint.Attributes & (BIT0 | BIT1))) {
+ case USB_ENDPOINT_BULK:
+ if (Endpoint.EndpointAddress & BIT7) {
+ UsbRndisDevice->BulkInEndpoint = Endpoint.EndpointAddress;
+ } else {
+ UsbRndisDevice->BulkOutEndpoint = Endpoint.EndpointAddress;
+ }
+
+ break;
+ case USB_ENDPOINT_INTERRUPT:
+ UsbRndisDevice->InterrupEndpoint = Endpoint.EndpointAddress;
+ break;
+ }
+ }
+}
+
+/**
+ Async USB transfer callback routine.
+
+ @param[in] Data Data received or sent via the USB Asynchronous Transfer, if the
+ transfer completed successfully.
+ @param[in] DataLength The length of Data received or sent via the Asynchronous
+ Transfer, if transfer successfully completes.
+ @param[in] Context Data passed from UsbAsyncInterruptTransfer() request.
+ @param[in] Status Indicates the result of the asynchronous transfer.
+
+ @retval EFI_SUCCESS The asynchronous USB transfer request has been successfully executed.
+ @retval EFI_DEVICE_ERROR The asynchronous USB transfer request failed.
+
+**/
+EFI_STATUS
+EFIAPI
+InterruptCallback (
+ IN VOID *Data,
+ IN UINTN DataLength,
+ IN VOID *Context,
+ IN UINT32 Status
+ )
+{
+ if ((Data == NULL) || (Context == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (((EFI_USB_DEVICE_REQUEST *)Data)->Request == 0) {
+ CopyMem (
+ (EFI_USB_DEVICE_REQUEST *)Context,
+ (EFI_USB_DEVICE_REQUEST *)Data,
+ sizeof (EFI_USB_DEVICE_REQUEST)
+ );
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is used to manage a USB device with an interrupt transfer pipe.
+
+ @param[in] This A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+ @param[in] IsNewTransfer If TRUE, a new transfer will be submitted to USB controller. If
+ FALSE, the interrupt transfer is deleted from the device's interrupt
+ transfer queue.
+ @param[in] PollingInterval Indicates the periodic rate, in milliseconds, that the transfer is to be
+ executed.This parameter is required when IsNewTransfer is TRUE. The
+ value must be between 1 to 255, otherwise EFI_INVALID_PARAMETER is returned.
+ The units are in milliseconds.
+ @param[in] Requst A pointer to the EFI_USB_DEVICE_REQUEST data.
+
+ @retval EFI_SUCCESS The asynchronous USB transfer request transfer has been successfully executed.
+ @retval EFI_DEVICE_ERROR The asynchronous USB transfer request failed.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbRndisInterrupt (
+ IN EDKII_USB_ETHERNET_PROTOCOL *This,
+ IN BOOLEAN IsNewTransfer,
+ IN UINTN PollingInterval,
+ IN EFI_USB_DEVICE_REQUEST *Requst
+ )
+{
+ EFI_STATUS Status;
+ USB_RNDIS_DEVICE *UsbRndisDevice;
+ UINTN DataLength;
+
+ UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+ DataLength = 0;
+
+ if (IsNewTransfer) {
+ DataLength = sizeof (EFI_USB_DEVICE_REQUEST) + sizeof (USB_CONNECT_SPEED_CHANGE);
+ Status = UsbRndisDevice->UsbIo->UsbAsyncInterruptTransfer (
+ UsbRndisDevice->UsbIo,
+ UsbRndisDevice->InterrupEndpoint,
+ IsNewTransfer,
+ PollingInterval,
+ DataLength,
+ InterruptCallback,
+ Requst
+ );
+
+ if (Status == EFI_INVALID_PARAMETER) {
+ // Because of Stacked AsyncInterrupt request are not supported
+ Status = UsbRndisDevice->UsbIo->UsbAsyncInterruptTransfer (
+ UsbRndisDevice->UsbIo,
+ UsbRndisDevice->InterrupEndpoint,
+ 0,
+ 0,
+ 0,
+ NULL,
+ NULL
+ );
+ }
+ } else {
+ Status = UsbRndisDevice->UsbIo->UsbAsyncInterruptTransfer (
+ UsbRndisDevice->UsbIo,
+ UsbRndisDevice->InterrupEndpoint,
+ IsNewTransfer,
+ 0,
+ 0,
+ NULL,
+ NULL
+ );
+ }
+
+ return Status;
+}
+
+/**
+ This function is used to read USB interrupt transfer before the response RNDIS message.
+
+ @param[in] UsbRndisDevice A pointer to the USB_RNDIS_DEVICE instance.
+
+ @retval EFI_SUCCESS The USB interrupt transfer has been successfully executed.
+ @retval EFI_DEVICE_ERROR The USB interrupt transfer failed.
+
+**/
+EFI_STATUS
+EFIAPI
+ReadRndisResponseInterrupt (
+ IN USB_RNDIS_DEVICE *UsbRndisDevice
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Data[2];
+ UINT32 UsbStatus;
+ UINTN DataLength;
+
+ DataLength = 8;
+
+ ZeroMem (Data, sizeof (Data));
+
+ Status = UsbRndisDevice->UsbIo->UsbSyncInterruptTransfer (
+ UsbRndisDevice->UsbIo,
+ UsbRndisDevice->InterrupEndpoint,
+ &Data,
+ &DataLength,
+ 0x20,
+ &UsbStatus
+ );
+
+ return Status;
+}
+
+/**
+ Retrieves the USB Ethernet Mac Address.
+
+ @param[in] This A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+ @param[out] MacAddress A pointer to the caller allocated USB Ethernet Mac Address.
+
+ @retval EFI_SUCCESS The USB Header Functional descriptor was retrieved successfully.
+ @retval EFI_INVALID_PARAMETER UsbHeaderFunDescriptor is NULL.
+ @retval EFI_NOT_FOUND The USB Header Functional descriptor was not found.
+
+**/
+EFI_STATUS
+EFIAPI
+GetUsbEthMacAddress (
+ IN EDKII_USB_ETHERNET_PROTOCOL *This,
+ OUT EFI_MAC_ADDRESS *MacAddress
+ )
+{
+ EFI_STATUS Status;
+ USB_RNDIS_DEVICE *UsbRndisDevice;
+ USB_ETHERNET_FUN_DESCRIPTOR UsbEthDescriptor;
+ CHAR16 *Data;
+ CHAR16 *DataPtr;
+ CHAR16 TmpStr[1];
+ UINT8 Index;
+ UINT8 Hi;
+ UINT8 Low;
+
+ REMOTE_NDIS_QUERY_MAC_MSG RndisQueryMsg;
+ REMOTE_NDIS_QUERY_MAC_CMPLT RndisQueryMsgCmplt;
+
+ UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+
+ ZeroMem (&RndisQueryMsg, sizeof (REMOTE_NDIS_QUERY_MAC_MSG));
+ ZeroMem (&RndisQueryMsgCmplt, sizeof (REMOTE_NDIS_QUERY_MAC_CMPLT));
+
+ RndisQueryMsg.QueryMsg.MessageType = RNDIS_QUERY_MSG;
+ RndisQueryMsg.QueryMsg.MessageLength = sizeof (REMOTE_NDIS_QUERY_MAC_MSG);
+ RndisQueryMsg.QueryMsg.RequestID = UsbRndisDevice->RequestId;
+ RndisQueryMsg.QueryMsg.Oid = OID_802_3_CURRENT_ADDRESS;
+
+ RndisQueryMsgCmplt.QueryCmplt.MessageType = RNDIS_QUERY_CMPLT;
+ RndisQueryMsgCmplt.QueryCmplt.MessageLength = sizeof (REMOTE_NDIS_QUERY_MAC_CMPLT);
+
+ Status = RndisControlMsg (
+ UsbRndisDevice,
+ (REMOTE_NDIS_MSG_HEADER *)&RndisQueryMsg,
+ (REMOTE_NDIS_MSG_HEADER *)&RndisQueryMsgCmplt
+ );
+ if (!EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_INFO, "Success to get Mac address from RNDIS message.\n"));
+ for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+ MacAddress->Addr[Index] = RndisQueryMsgCmplt.Addr[Index];
+ }
+
+ UsbRndisDevice->RequestId++;
+ return Status;
+ }
+
+ // If it is not support the OID_802_3_CURRENT_ADDRESS.
+ // To check USB Ethernet functional Descriptor
+ Status = This->UsbEthFunDescriptor (This, &UsbEthDescriptor);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a:UsbEthFunDescriptor status = %r\n", __func__, Status));
+ return Status;
+ }
+
+ Status = UsbRndisDevice->UsbIo->UsbGetStringDescriptor (
+ UsbRndisDevice->UsbIo,
+ 0x409, // English-US Language ID
+ UsbEthDescriptor.MacAddress,
+ &Data
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a:UsbGetStringDescriptor status = %r\n", __func__, Status));
+ return Status;
+ }
+
+ DataPtr = Data;
+ for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+ CopyMem (TmpStr, DataPtr, sizeof (CHAR16));
+ DataPtr++;
+ Hi = (UINT8)StrHexToUintn (TmpStr);
+ CopyMem (TmpStr, DataPtr, sizeof (CHAR16));
+ DataPtr++;
+ Low = (UINT8)StrHexToUintn (TmpStr);
+ MacAddress->Addr[Index] = (Hi << 4) | Low;
+ }
+
+ return Status;
+}
+
+/**
+ Retrieves the USB Ethernet Bulk transfer data size.
+
+ @param[in] This A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+ @param[out] BulkSize A pointer to the Bulk transfer data size.
+
+ @retval EFI_SUCCESS The bulk transfer data size was retrieved successfully.
+ @retval other Failed to retrieve the bulk transfer data size.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbEthBulkSize (
+ IN EDKII_USB_ETHERNET_PROTOCOL *This,
+ OUT UINTN *BulkSize
+ )
+{
+ EFI_STATUS Status;
+ USB_ETHERNET_FUN_DESCRIPTOR UsbEthFunDescriptor;
+ USB_RNDIS_DEVICE *UsbRndisDevice;
+
+ REMOTE_NDIS_QUERY_MAX_TOTAL_SIZE_MSG RndisQueryMsg;
+ REMOTE_NDIS_QUERY_MAX_TOTAL_SIZE_CMPLT RndisQueryMsgCmplt;
+
+ UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+
+ ZeroMem (&RndisQueryMsg, sizeof (REMOTE_NDIS_QUERY_MAX_TOTAL_SIZE_MSG));
+ ZeroMem (&RndisQueryMsgCmplt, sizeof (REMOTE_NDIS_QUERY_MAX_TOTAL_SIZE_CMPLT));
+
+ RndisQueryMsg.QueryMsg.MessageType = RNDIS_QUERY_MSG;
+ RndisQueryMsg.QueryMsg.MessageLength = sizeof (REMOTE_NDIS_QUERY_MAX_TOTAL_SIZE_MSG);
+ RndisQueryMsg.QueryMsg.RequestID = UsbRndisDevice->RequestId;
+ RndisQueryMsg.QueryMsg.Oid = OID_GEN_MAXIMUM_TOTAL_SIZE;
+
+ RndisQueryMsgCmplt.QueryCmplt.MessageType = RNDIS_QUERY_CMPLT;
+ RndisQueryMsgCmplt.QueryCmplt.MessageLength = sizeof (REMOTE_NDIS_QUERY_MAX_TOTAL_SIZE_CMPLT);
+
+ Status = RndisControlMsg (
+ UsbRndisDevice,
+ (REMOTE_NDIS_MSG_HEADER *)&RndisQueryMsg,
+ (REMOTE_NDIS_MSG_HEADER *)&RndisQueryMsgCmplt
+ );
+ if (!EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_INFO, "Success to get Max Total size : %X \n", RndisQueryMsgCmplt.MaxTotalSize));
+ *BulkSize = RndisQueryMsgCmplt.MaxTotalSize;
+ UsbRndisDevice->RequestId++;
+ return Status;
+ }
+
+ Status = This->UsbEthFunDescriptor (This, &UsbEthFunDescriptor);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *BulkSize = (UINTN)UsbEthFunDescriptor.MaxSegmentSize;
+ return Status;
+}
+
+/**
+ Retrieves the USB Header functional Descriptor.
+
+ @param[in] This A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+ @param[out] UsbHeaderFunDescriptor A pointer to the caller allocated USB Header Functional Descriptor.
+
+ @retval EFI_SUCCESS The USB Header Functional descriptor was retrieved successfully.
+ @retval EFI_INVALID_PARAMETER UsbHeaderFunDescriptor is NULL.
+ @retval EFI_NOT_FOUND The USB Header Functional descriptor was not found.
+
+**/
+EFI_STATUS
+EFIAPI
+GetUsbHeaderFunDescriptor (
+ IN EDKII_USB_ETHERNET_PROTOCOL *This,
+ OUT USB_HEADER_FUN_DESCRIPTOR *UsbHeaderFunDescriptor
+ )
+{
+ EFI_STATUS Status;
+ USB_RNDIS_DEVICE *UsbRndisDevice;
+
+ UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+
+ if (UsbHeaderFunDescriptor == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetFunctionalDescriptor (
+ UsbRndisDevice->Config,
+ HEADER_FUN_DESCRIPTOR,
+ UsbHeaderFunDescriptor
+ );
+ return Status;
+}
+
+/**
+ Retrieves the USB Union functional Descriptor.
+
+ @param[in] This A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+ @param[out] UsbUnionFunDescriptor A pointer to the caller allocated USB Union Functional Descriptor.
+
+ @retval EFI_SUCCESS The USB Union Functional descriptor was retrieved successfully.
+ @retval EFI_INVALID_PARAMETER UsbUnionFunDescriptor is NULL.
+ @retval EFI_NOT_FOUND The USB Union Functional descriptor was not found.
+
+**/
+EFI_STATUS
+EFIAPI
+GetUsbUnionFunDescriptor (
+ IN EDKII_USB_ETHERNET_PROTOCOL *This,
+ OUT USB_UNION_FUN_DESCRIPTOR *UsbUnionFunDescriptor
+ )
+{
+ EFI_STATUS Status;
+ USB_RNDIS_DEVICE *UsbRndisDevice;
+
+ UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+
+ if (UsbUnionFunDescriptor == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetFunctionalDescriptor (
+ UsbRndisDevice->Config,
+ UNION_FUN_DESCRIPTOR,
+ UsbUnionFunDescriptor
+ );
+ return Status;
+}
+
+/**
+ Retrieves the USB Ethernet functional Descriptor.
+
+ This function get the Mac Address, Ethernet statistics, maximum segment size,
+ number of multicast filters, and number of pattern filters from Ethernet
+ functional Descriptor.
+
+ @param[in] This A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+ @param[out] UsbEthFunDescriptor A pointer to the caller allocated USB Ethernet Functional Descriptor.
+
+ @retval EFI_SUCCESS The USB Ethernet Functional descriptor was retrieved successfully.
+ @retval EFI_INVALID_PARAMETER UsbEthFunDescriptor is NULL.
+ @retval EFI_NOT_FOUND The USB Ethernet Functional descriptor was not found.
+
+**/
+EFI_STATUS
+EFIAPI
+GetUsbRndisFunDescriptor (
+ IN EDKII_USB_ETHERNET_PROTOCOL *This,
+ OUT USB_ETHERNET_FUN_DESCRIPTOR *UsbEthFunDescriptor
+ )
+{
+ EFI_STATUS Status;
+ USB_RNDIS_DEVICE *UsbRndisDevice;
+
+ UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+
+ if (UsbEthFunDescriptor == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetFunctionalDescriptor (
+ UsbRndisDevice->Config,
+ ETHERNET_FUN_DESCRIPTOR,
+ UsbEthFunDescriptor
+ );
+ return Status;
+}
+
+/**
+ This request sets the Ethernet device multicast filters as specified in the
+ sequential list of 48 bit Ethernet multicast addresses.
+
+ @param[in] This A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+ @param[in] Value Number of filters.
+ @param[in] McastAddr A pointer to the value of the multicast addresses.
+
+ @retval EFI_SUCCESS The request executed successfully.
+ @retval EFI_TIMEOUT A timeout occurred executing the request.
+ @retval EFI_DEVICE_ERROR The request failed due to a device error.
+ @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+ @retval EFI_UNSUPPORTED Not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SetUsbRndisMcastFilter (
+ IN EDKII_USB_ETHERNET_PROTOCOL *This,
+ IN UINT16 Value,
+ IN VOID *McastAddr
+ )
+{
+ EFI_STATUS Status;
+ EFI_USB_DEVICE_REQUEST Request;
+ UINT32 TransStatus;
+ USB_ETHERNET_FUN_DESCRIPTOR UsbEthFunDescriptor;
+ USB_RNDIS_DEVICE *UsbRndisDevice;
+
+ UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+
+ Status = This->UsbEthFunDescriptor (This, &UsbEthFunDescriptor);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if ((UsbEthFunDescriptor.NumberMcFilters << 1) == 0) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Request.RequestType = USB_ETHERNET_SET_REQ_TYPE;
+ Request.Request = SET_ETH_MULTICAST_FILTERS_REQ;
+ Request.Value = Value;
+ Request.Index = UsbRndisDevice->NumOfInterface;
+ Request.Length = Value * 6;
+
+ return UsbRndisDevice->UsbIo->UsbControlTransfer (
+ UsbRndisDevice->UsbIo,
+ &Request,
+ EfiUsbDataOut,
+ USB_ETHERNET_TRANSFER_TIMEOUT,
+ McastAddr,
+ Request.Length,
+ &TransStatus
+ );
+}
+
+/**
+ This request sets up the specified Ethernet power management pattern filter as
+ described in the data structure.
+
+ @param[in] This A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+ @param[in] Value Number of filters.
+ @param[in] Length Size of the power management pattern filter data.
+ @param[in] PatternFilter A pointer to the power management pattern filter structure.
+
+ @retval EFI_SUCCESS The request executed successfully.
+ @retval EFI_TIMEOUT A timeout occurred executing the request.
+ @retval EFI_DEVICE_ERROR The request failed due to a device error.
+ @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+ @retval EFI_UNSUPPORTED Not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SetUsbRndisPowerFilter (
+ IN EDKII_USB_ETHERNET_PROTOCOL *This,
+ IN UINT16 Value,
+ IN UINT16 Length,
+ IN VOID *PatternFilter
+ )
+{
+ EFI_USB_DEVICE_REQUEST Request;
+ UINT32 TransStatus;
+ USB_RNDIS_DEVICE *UsbRndisDevice;
+
+ UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+
+ Request.RequestType = USB_ETHERNET_SET_REQ_TYPE;
+ Request.Request = SET_ETH_POWER_MANAGEMENT_PATTERN_FILTER_REQ;
+ Request.Value = Value;
+ Request.Index = UsbRndisDevice->NumOfInterface;
+ Request.Length = Length;
+
+ return UsbRndisDevice->UsbIo->UsbControlTransfer (
+ UsbRndisDevice->UsbIo,
+ &Request,
+ EfiUsbDataOut,
+ USB_ETHERNET_TRANSFER_TIMEOUT,
+ PatternFilter,
+ Length,
+ &TransStatus
+ );
+}
+
+/**
+ This request retrieves the status of the specified Ethernet power management
+ pattern filter from the device.
+
+ @param[in] This A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+ @param[in] Value The filter number.
+ @param[out] PatternActive A pointer to the pattern active boolean.
+
+ @retval EFI_SUCCESS The request executed successfully.
+ @retval EFI_TIMEOUT A timeout occurred executing the request.
+ @retval EFI_DEVICE_ERROR The request failed due to a device error.
+ @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+ @retval EFI_UNSUPPORTED Not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+GetUsbRndisPowerFilter (
+ IN EDKII_USB_ETHERNET_PROTOCOL *This,
+ IN UINT16 Value,
+ OUT BOOLEAN *PatternActive
+ )
+{
+ EFI_USB_DEVICE_REQUEST Request;
+ UINT32 TransStatus;
+ USB_RNDIS_DEVICE *UsbRndisDevice;
+
+ UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+
+ Request.RequestType = USB_ETHERNET_GET_REQ_TYPE;
+ Request.Request = GET_ETH_POWER_MANAGEMENT_PATTERN_FILTER_REQ;
+ Request.Value = Value;
+ Request.Index = UsbRndisDevice->NumOfInterface;
+ Request.Length = USB_ETH_POWER_FILTER_LENGTH;
+
+ return UsbRndisDevice->UsbIo->UsbControlTransfer (
+ UsbRndisDevice->UsbIo,
+ &Request,
+ EfiUsbDataIn,
+ USB_ETHERNET_TRANSFER_TIMEOUT,
+ PatternActive,
+ USB_ETH_POWER_FILTER_LENGTH,
+ &TransStatus
+ );
+}
+
+BIT_MAP gTable[] = {
+ { PXE_OPFLAGS_RECEIVE_FILTER_UNICAST, NDIS_PACKET_TYPE_DIRECTED },
+ { PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST, NDIS_PACKET_TYPE_BROADCAST },
+ { PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST, NDIS_PACKET_TYPE_MULTICAST },
+ { PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS, NDIS_PACKET_TYPE_PROMISCUOUS },
+ { PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST, NDIS_PACKET_TYPE_ALL_MULTICAST },
+};
+
+/**
+
+ Converts PXE filter settings to RNDIS values
+
+ @param[in] Value PXE filter data.
+ @param[out] CdcFilter A pointer to the Ethernet Packet Filter Bitmap value converted by PXE_OPFLAGS.
+
+**/
+VOID
+ConvertFilter (
+ IN UINT16 Value,
+ OUT UINT16 *CdcFilter
+ )
+{
+ UINT32 Index;
+ UINT32 Count;
+
+ Count = sizeof (gTable)/sizeof (gTable[0]);
+
+ for (Index = 0; (gTable[Index].Src != 0) && (Index < Count); Index++) {
+ if (gTable[Index].Src & Value) {
+ *CdcFilter |= gTable[Index].Dst;
+ }
+ }
+}
+
+/**
+
+ Updates Filter settings on the device.
+
+ @param[in] Cdb A pointer to the command descriptor block.
+ @param[in] Nic A pointer to the Network interface controller data.
+
+ @retval EFI_STATUS
+
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiReceiveFilter (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *McastList;
+ UINT8 Count;
+ UINT8 Index1;
+ UINT8 Index2;
+ UINT64 CpbAddr;
+ UINT32 CpbSize;
+ UINT16 SetFilter;
+ PXE_CPB_RECEIVE_FILTERS *Cpb;
+ USB_ETHERNET_FUN_DESCRIPTOR UsbEthFunDescriptor;
+
+ Count = 0;
+ CpbAddr = Cdb->CPBaddr;
+ CpbSize = Cdb->CPBsize;
+ SetFilter = (UINT16)(Cdb->OpFlags & 0x1F);
+ 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);
+ } else {
+ Nic->McastCount = 0;
+ }
+
+ Nic->UsbEth->UsbEthFunDescriptor (Nic->UsbEth, &UsbEthFunDescriptor);
+ if ((UsbEthFunDescriptor.NumberMcFilters << 1) == 0) {
+ Nic->RxFilter |= PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST;
+ DEBUG ((DEBUG_INFO, "SetUsbEthPacketFilter Nic %lx Nic->UsbEth %lx ", Nic, Nic->UsbEth));
+ 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 EFI_SUCCESS;
+}
+
+/**
+ This request is used to configure device Ethernet packet filter settings.
+
+ @param[in] This A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+ @param[in] Value Packet Filter Bitmap.
+
+ @retval EFI_SUCCESS The request executed successfully.
+ @retval EFI_TIMEOUT A timeout occurred executing the request.
+ @retval EFI_DEVICE_ERROR The request failed due to a device error.
+ @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+ @retval EFI_UNSUPPORTED Not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SetUsbRndisPacketFilter (
+ IN EDKII_USB_ETHERNET_PROTOCOL *This,
+ IN UINT16 Value
+ )
+{
+ return EFI_SUCCESS;
+}
+
+/**
+ This request is used to retrieve a statistic based on the feature selector.
+
+ @param[in] This A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+ @param[in] FeatureSelector Value of the feature selector.
+ @param[out] Statistic A pointer to the 32 bit unsigned integer.
+
+ @retval EFI_SUCCESS The request executed successfully.
+ @retval EFI_TIMEOUT A timeout occurred executing the request.
+ @retval EFI_DEVICE_ERROR The request failed due to a device error.
+ @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+ @retval EFI_UNSUPPORTED Not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+GetRndisStatistic (
+ IN EDKII_USB_ETHERNET_PROTOCOL *This,
+ IN UINT16 FeatureSelector,
+ OUT VOID *Statistic
+ )
+{
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is called when UndiStart is invoked.
+
+ @param[in] Cdb A pointer to the command descriptor block.
+ @param[in] Nic A pointer to the Network interface controller data.
+
+ @retval EFI_SUCCESS The request executed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiStart (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ )
+{
+ EFI_STATUS Status;
+
+ DEBUG ((DEBUG_INFO, "RndisUndiStart Nic %lx Cdb %lx Nic State %x\n", Nic, Cdb, Nic->State));
+
+ // Issue Rndis Reset and bring the device to RNDIS_BUS_INITIALIZED state
+ Status = RndisUndiReset (Cdb, Nic);
+ if (EFI_ERROR (Status)) {
+ RndisUndiReset (Cdb, Nic);
+ }
+
+ Status = RndisUndiInitialize (Cdb, Nic);
+ if (EFI_ERROR (Status)) {
+ RndisUndiInitialize (Cdb, Nic);
+ }
+
+ RndisUndiShutdown (Cdb, Nic);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is called when Undistop is invoked.
+
+ @param[in] Cdb A pointer to the command descriptor block.
+ @param[in] Nic A pointer to the Network interface controller data.
+
+ @retval EFI_SUCCESS The request executed successfully.
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiStop (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ )
+{
+ DEBUG ((DEBUG_INFO, "RndisUndiStop State %x\n", Nic->State));
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is called when UndiGetInitInfo is invoked.
+
+ @param[in] Cdb A pointer to the command descriptor block.
+ @param[in] Nic A pointer to the Network interface controller data.
+
+ @retval EFI_SUCCESS The request executed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiGetInitInfo (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ )
+{
+ EDKII_USB_ETHERNET_PROTOCOL *UsbEthDevice;
+ USB_RNDIS_DEVICE *UsbRndisDevice;
+ PXE_DB_GET_INIT_INFO *Db;
+
+ DEBUG ((DEBUG_INFO, "RndisUndiGetInitInfo\n"));
+
+ UsbEthDevice = Nic->UsbEth;
+ UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (UsbEthDevice);
+
+ Db = (PXE_DB_GET_INIT_INFO *)(UINTN)Cdb->DBaddr;
+
+ Db->FrameDataLen = UsbRndisDevice->MaxTransferSize - sizeof (REMOTE_NDIS_PACKET_MSG) - PXE_MAC_HEADER_LEN_ETHER;
+ // Limit Max MTU size to 1500 bytes as RNDIS spec.
+ if (Db->FrameDataLen > PXE_MAX_TXRX_UNIT_ETHER) {
+ Db->FrameDataLen = PXE_MAX_TXRX_UNIT_ETHER;
+ }
+
+ DEBUG ((DEBUG_INFO, "Db->FrameDataLen %x\n", Db->FrameDataLen));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is called when RndisUndiGetConfigInfo is invoked.
+
+ @param[in] Cdb A pointer to the command descriptor block.
+ @param[in] Nic A pointer to the Network interface controller data.
+
+ @retval EFI_SUCCESS The request executed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiGetConfigInfo (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ )
+{
+ DEBUG ((DEBUG_INFO, "RndisUndiGetConfigInfo\n"));
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is called when UndiInitialize is invoked.
+
+ @param[in] Cdb A pointer to the command descriptor block.
+ @param[in] Nic A pointer to the Network interface controller data.
+
+ @retval EFI_SUCCESS The request executed successfully.
+ @retval EFI_UNSUPPORTED Not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiInitialize (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ )
+{
+ EDKII_USB_ETHERNET_PROTOCOL *UsbEthDriver;
+ USB_RNDIS_DEVICE *UsbRndisDevice;
+ REMOTE_NDIS_INITIALIZE_MSG RndisInitMsg;
+ REMOTE_NDIS_INITIALIZE_CMPLT RndisInitMsgCmplt;
+ EFI_STATUS Status;
+
+ DEBUG ((DEBUG_INFO, "RndisUndiInitialize\n"));
+
+ UsbEthDriver = Nic->UsbEth;
+ UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (UsbEthDriver);
+
+ ZeroMem (&RndisInitMsg, sizeof (REMOTE_NDIS_INITIALIZE_MSG));
+ ZeroMem (&RndisInitMsgCmplt, sizeof (REMOTE_NDIS_INITIALIZE_CMPLT));
+
+ RndisInitMsg.MessageType = RNDIS_INITIALIZE_MSG;
+ RndisInitMsg.MessageLength = sizeof (REMOTE_NDIS_INITIALIZE_MSG);
+ RndisInitMsg.RequestID = UsbRndisDevice->RequestId;
+ RndisInitMsg.MajorVersion = RNDIS_MAJOR_VERSION;
+ RndisInitMsg.MinorVersion = RNDIS_MINOR_VERSION;
+ RndisInitMsg.MaxTransferSize = RNDIS_MAX_TRANSFER_SIZE;
+
+ RndisInitMsgCmplt.MessageType = RNDIS_INITIALIZE_CMPLT;
+ RndisInitMsgCmplt.MessageLength = sizeof (REMOTE_NDIS_INITIALIZE_CMPLT);
+
+ Status = RndisControlMsg (UsbRndisDevice, (REMOTE_NDIS_MSG_HEADER *)&RndisInitMsg, (REMOTE_NDIS_MSG_HEADER *)&RndisInitMsgCmplt);
+
+ UsbRndisDevice->RequestId++;
+
+ if (EFI_ERROR (Status) || (RndisInitMsgCmplt.Status & 0x80000000)) {
+ return Status;
+ }
+
+ // Only Wired Medium is supported
+ if (RndisInitMsgCmplt.Medium) {
+ return EFI_UNSUPPORTED;
+ }
+
+ UsbRndisDevice->Medium = RndisInitMsgCmplt.Medium;
+ UsbRndisDevice->MaxPacketsPerTransfer = RndisInitMsgCmplt.MaxPacketsPerTransfer;
+ UsbRndisDevice->MaxTransferSize = RndisInitMsgCmplt.MaxTransferSize;
+ UsbRndisDevice->PacketAlignmentFactor = RndisInitMsgCmplt.PacketAlignmentFactor;
+
+ DEBUG ((DEBUG_INFO, "Medium : %x \n", RndisInitMsgCmplt.Medium));
+ DEBUG ((DEBUG_INFO, "MaxPacketsPerTransfer : %x \n", RndisInitMsgCmplt.MaxPacketsPerTransfer));
+ DEBUG ((DEBUG_INFO, "MaxTransferSize : %x\n", RndisInitMsgCmplt.MaxTransferSize));
+ DEBUG ((DEBUG_INFO, "PacketAlignmentFactor : %x\n", RndisInitMsgCmplt.PacketAlignmentFactor));
+
+ return Status;
+}
+
+/**
+ This function is called when UndiReset is invoked.
+
+ @param[in] Cdb A pointer to the command descriptor block.
+ @param[in] Nic A pointer to the Network interface controller data.
+
+ @retval EFI_SUCCESS The request executed successfully.
+ @retval EFI_DEVICE_ERROR The request failed due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiReset (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ )
+{
+ EDKII_USB_ETHERNET_PROTOCOL *UsbEthDriver;
+ USB_RNDIS_DEVICE *UsbRndisDevice;
+ REMOTE_NDIS_RESET_MSG RndisResetMsg;
+ REMOTE_NDIS_RESET_CMPLT RndisResetCmplt;
+ EFI_STATUS Status;
+
+ DEBUG ((DEBUG_INFO, "RndisUndiReset\n"));
+
+ UsbEthDriver = Nic->UsbEth;
+ UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (UsbEthDriver);
+
+ ZeroMem (&RndisResetMsg, sizeof (REMOTE_NDIS_RESET_MSG));
+ ZeroMem (&RndisResetCmplt, sizeof (REMOTE_NDIS_RESET_CMPLT));
+
+ RndisResetMsg.MessageType = RNDIS_RESET_MSG;
+ RndisResetMsg.MessageLength = sizeof (REMOTE_NDIS_RESET_MSG);
+
+ RndisResetCmplt.MessageType = RNDIS_RESET_CMPLT;
+ RndisResetCmplt.MessageLength = sizeof (REMOTE_NDIS_RESET_CMPLT);
+
+ Status = RndisControlMsg (UsbRndisDevice, (REMOTE_NDIS_MSG_HEADER *)&RndisResetMsg, (REMOTE_NDIS_MSG_HEADER *)&RndisResetCmplt);
+
+ UsbRndisDevice->RequestId = 1; // Let's start with 1
+
+ if (EFI_ERROR (Status) || (RndisResetCmplt.Status & 0x80000000)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is called when UndiShutdown is invoked.
+
+ @param[in] Cdb A pointer to the command descriptor block.
+ @param[in] Nic A pointer to the Network interface controller data.
+
+ @retval EFI_SUCCESS The request executed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiShutdown (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ )
+{
+ EDKII_USB_ETHERNET_PROTOCOL *UsbEthDriver;
+ USB_RNDIS_DEVICE *UsbRndisDevice;
+ REMOTE_NDIS_HALT_MSG RndisHltMsg;
+ EFI_STATUS Status;
+
+ DEBUG ((DEBUG_INFO, "RndisUndiShutdown\n"));
+
+ UsbEthDriver = Nic->UsbEth;
+ UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (UsbEthDriver);
+
+ ZeroMem (&RndisHltMsg, sizeof (REMOTE_NDIS_HALT_MSG));
+
+ RndisHltMsg.MessageType = RNDIS_HLT_MSG;
+ RndisHltMsg.MessageLength = sizeof (REMOTE_NDIS_HALT_MSG);
+
+ Status = RndisControlMsg (UsbRndisDevice, (REMOTE_NDIS_MSG_HEADER *)&RndisHltMsg, NULL);
+
+ if (Status == EFI_DEVICE_ERROR) {
+ Status = EFI_SUCCESS;
+ }
+
+ UsbRndisDevice->RequestId = 1;
+ return Status;
+}
+
+/**
+ Update the Media connection.
+
+ @param[in] Cdb A pointer to the command descriptor block.
+ @param[in] Nic A pointer to the Network interface controller data.
+
+ @retval EFI_SUCCESS The request executed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiGetStatus (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ )
+{
+ Cdb->StatFlags &= ~(PXE_STATFLAGS_GET_STATUS_NO_MEDIA);
+ return EFI_SUCCESS;
+}
+
+/**
+ Transmit the data after appending RNDIS header.
+
+ @param[in] Cdb A pointer to the command descriptor block.
+ @param[in] This A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+ @param[in] BulkOutData A pointer to the buffer of data that will be transmitted to USB
+ device or received from USB device.
+ @param[in, out] DataLength A pointer to the PacketLength.
+
+ @retval EFI_SUCCESS The request executed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiTransmit (
+ IN PXE_CDB *Cdb,
+ IN EDKII_USB_ETHERNET_PROTOCOL *This,
+ IN VOID *BulkOutData,
+ IN OUT UINTN *DataLength
+ )
+{
+ EFI_STATUS Status;
+ USB_RNDIS_DEVICE *UsbRndisDevice;
+ REMOTE_NDIS_PACKET_MSG *RndisPacketMsg;
+ UINTN TransferLength;
+
+ DEBUG ((DEBUG_INFO, "RndisUndiTransmit DataLength : %x\n", *DataLength));
+
+ UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+
+ RndisPacketMsg = AllocateZeroPool (sizeof (REMOTE_NDIS_PACKET_MSG) + *DataLength);
+ if (RndisPacketMsg == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ RndisPacketMsg->MessageType = RNDIS_PACKET_MSG;
+ RndisPacketMsg->MessageLength = sizeof (REMOTE_NDIS_PACKET_MSG) + (UINT32)*DataLength;
+ RndisPacketMsg->DataOffset = sizeof (REMOTE_NDIS_PACKET_MSG) - 8;
+ RndisPacketMsg->DataLength = (UINT32)*DataLength;
+
+ CopyMem (
+ ((UINT8 *)RndisPacketMsg) + sizeof (REMOTE_NDIS_PACKET_MSG),
+ BulkOutData,
+ *DataLength
+ );
+
+ TransferLength = RndisPacketMsg->MessageLength;
+
+ Status = RndisTransmitDataMsg (
+ UsbRndisDevice,
+ (REMOTE_NDIS_MSG_HEADER *)RndisPacketMsg,
+ &TransferLength
+ );
+
+ DEBUG ((DEBUG_INFO, "\nRndisUndiTransmit TransferLength %lx\n", TransferLength));
+
+ FreePool (RndisPacketMsg);
+
+ return Status;
+}
+
+/**
+ Receives and removes RNDIS header and returns the raw data.
+
+ @param[in] Cdb A pointer to the command descriptor block.
+ @param[in] This A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+ @param[in, out] BulkInData A pointer to the buffer of data that will be transmitted to USB
+ device or received from USB device.
+ @param[in, out] DataLength A pointer to the PacketLength.
+
+ @retval EFI_SUCCESS The request executed successfully.
+ @retval EFI_BUFFER_TOO_SMALL The user provided buffer is too small
+ @retval EFI_NOT_FOUND No buffer was found in the list.
+
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiReceive (
+ IN PXE_CDB *Cdb,
+ IN EDKII_USB_ETHERNET_PROTOCOL *This,
+ IN OUT VOID *BulkInData,
+ IN OUT UINTN *DataLength
+ )
+{
+ EFI_STATUS Status;
+ USB_RNDIS_DEVICE *UsbRndisDevice;
+ REMOTE_NDIS_PACKET_MSG *RndisPacketMsg;
+ UINTN TransferLength;
+ VOID *Buffer;
+ PACKET_LIST *HeadPacket;
+ PACKET_LIST *PacketList;
+
+ // Check if there is any outstanding packet to receive
+ // The buffer allocated has a linked List followed by the packet.
+
+ UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+ Buffer = NULL;
+ HeadPacket = NULL;
+
+ while (1) {
+ Buffer = AllocateZeroPool (sizeof (PACKET_LIST) + sizeof (REMOTE_NDIS_PACKET_MSG) + UsbRndisDevice->MaxTransferSize);
+ if (Buffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ RndisPacketMsg = (REMOTE_NDIS_PACKET_MSG *)(sizeof (PACKET_LIST) + (UINT8 *)Buffer);
+ PacketList = (PACKET_LIST *)Buffer;
+ PacketList->PacketStartBuffer = (UINT8 *)Buffer + sizeof (PACKET_LIST);
+ // Save the original address for freeing it up
+ PacketList->OrgBuffer = (UINT8 *)Buffer;
+ TransferLength = UsbRndisDevice->MaxTransferSize;
+
+ Status = RndisReceiveDataMsg (
+ UsbRndisDevice,
+ (REMOTE_NDIS_MSG_HEADER *)RndisPacketMsg,
+ &TransferLength
+ );
+
+ if (EFI_ERROR (Status) || (TransferLength == 0)) {
+ FreePool (Buffer);
+ break;
+ }
+
+ // Collect all the RNDIS packet in Linked list.
+ if ((RndisPacketMsg->MessageType == RNDIS_PACKET_MSG) &&
+ (RndisPacketMsg->DataOffset == sizeof (REMOTE_NDIS_PACKET_MSG) - RNDIS_RESERVED_BYTE_LENGTH) &&
+ (TransferLength >= RndisPacketMsg->MessageLength))
+ {
+ // Insert Packet
+ PacketList->RemainingLength = TransferLength;
+ InsertTailList (&UsbRndisDevice->ReceivePacketList, Buffer);
+ } else {
+ FreePool (Buffer);
+ }
+ }
+
+ while (!IsListEmpty (&UsbRndisDevice->ReceivePacketList)) {
+ HeadPacket = (PACKET_LIST *)GetFirstNode (&UsbRndisDevice->ReceivePacketList);
+
+ RndisPacketMsg = (REMOTE_NDIS_PACKET_MSG *)(UINT8 *)HeadPacket->PacketStartBuffer;
+
+ PrintRndisMsg ((REMOTE_NDIS_MSG_HEADER *)RndisPacketMsg);
+
+ // Check whether the packet is valid RNDIS packet.
+ if ((HeadPacket->RemainingLength > sizeof (REMOTE_NDIS_PACKET_MSG)) && (RndisPacketMsg->MessageType == RNDIS_PACKET_MSG) &&
+ (RndisPacketMsg->DataOffset == (sizeof (REMOTE_NDIS_PACKET_MSG) - RNDIS_RESERVED_BYTE_LENGTH)) &&
+ (HeadPacket->RemainingLength >= RndisPacketMsg->MessageLength))
+ {
+ if (*DataLength >= RndisPacketMsg->DataLength) {
+ CopyMem (
+ BulkInData,
+ (UINT8 *)RndisPacketMsg + (RndisPacketMsg->DataOffset + RNDIS_RESERVED_BYTE_LENGTH),
+ RndisPacketMsg->DataLength
+ );
+
+ *DataLength = RndisPacketMsg->DataLength;
+
+ HeadPacket->RemainingLength = HeadPacket->RemainingLength - RndisPacketMsg->MessageLength;
+ HeadPacket->PacketStartBuffer = (UINT8 *)RndisPacketMsg + RndisPacketMsg->MessageLength;
+
+ return EFI_SUCCESS;
+ } else {
+ *DataLength = RndisPacketMsg->DataLength;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ }
+
+ RemoveEntryList (&HeadPacket->PacketList);
+ FreePool ((PACKET_LIST *)HeadPacket->OrgBuffer);
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ This is a dummy function which just returns. Unimplemented EDKII_USB_ETHERNET_PROTOCOL functions
+ point to this function.
+
+ @param[in] Cdb A pointer to the command descriptor block.
+ @param[in] Nic A pointer to the Network interface controller data.
+
+ @retval EFI_SUCCESS The request executed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+RndisDummyReturn (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ )
+{
+ DEBUG ((DEBUG_INFO, "RndisDummyReturn called\n"));
+ return EFI_SUCCESS;
+}
+
+/**
+ This function send the RNDIS command through the device's control endpoint
+
+ @param[in] UsbRndisDevice A pointer to the USB_RNDIS_DEVICE instance.
+ @param[in] RndisMsg A pointer to the REMOTE_NDIS_MSG_HEADER data.
+ @param[out] RndisMsgResponse A pointer to the REMOTE_NDIS_MSG_HEADER data for getting responses.
+
+ @retval EFI_SUCCESS The bulk transfer has been successfully executed.
+
+**/
+EFI_STATUS
+RndisControlMsg (
+ IN USB_RNDIS_DEVICE *UsbRndisDevice,
+ IN REMOTE_NDIS_MSG_HEADER *RndisMsg,
+ OUT REMOTE_NDIS_MSG_HEADER *RndisMsgResponse
+ )
+{
+ EFI_USB_IO_PROTOCOL *UsbIo;
+ EFI_USB_DEVICE_REQUEST DevReq;
+ UINT32 UsbStatus;
+ EFI_STATUS Status;
+ UINT32 SaveResponseType;
+ UINT32 SaveResponseLength;
+ UINT32 Index;
+ REMOTE_NDIS_INITIALIZE_CMPLT *RndisInitCmplt;
+
+ UsbIo = UsbRndisDevice->UsbIo;
+ SaveResponseType = 0;
+ SaveResponseLength = 0;
+ RndisInitCmplt = (REMOTE_NDIS_INITIALIZE_CMPLT *)RndisMsgResponse;
+
+ if (RndisMsgResponse != NULL) {
+ SaveResponseType = RndisMsgResponse->MessageType;
+ SaveResponseLength = RndisMsgResponse->MessageLength;
+ }
+
+ ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));
+
+ DevReq.RequestType = USB_REQ_TYPE_CLASS | USB_TARGET_INTERFACE;
+ DevReq.Request = SEND_ENCAPSULATED_COMMAND;
+ DevReq.Value = 0;
+ DevReq.Index = 0;
+ DevReq.Length = (UINT16)RndisMsg->MessageLength;
+
+ PrintRndisMsg (RndisMsg);
+
+ Status = UsbIo->UsbControlTransfer (
+ UsbIo,
+ &DevReq,
+ EfiUsbDataOut,
+ USB_ETHERNET_TRANSFER_TIMEOUT,
+ RndisMsg,
+ RndisMsg->MessageLength,
+ &UsbStatus
+ );
+
+ DEBUG ((DEBUG_INFO, "RndisControlMsg: UsbStatus : %x Status : %r RndisMsgResponse : %lx\n", UsbStatus, Status, RndisMsgResponse));
+
+ // Error or no response expected
+ if ((EFI_ERROR (Status)) || (RndisMsgResponse == NULL)) {
+ DEBUG ((DEBUG_INFO, "RndisControlMsg: UsbStatus : %x Status : %r\n", UsbStatus, Status));
+ return Status;
+ }
+
+ for (Index = 0; Index < (RNDIS_CONTROL_TIMEOUT/100); Index++) {
+ ReadRndisResponseInterrupt (UsbRndisDevice);
+ ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));
+
+ DevReq.RequestType = USB_ENDPOINT_DIR_IN | USB_REQ_TYPE_CLASS | USB_TARGET_INTERFACE;
+ DevReq.Request = GET_ENCAPSULATED_RESPONSE;
+ DevReq.Value = 0;
+ DevReq.Index = 0;
+ DevReq.Length = (UINT16)RndisMsgResponse->MessageLength;
+
+ Status = UsbIo->UsbControlTransfer (
+ UsbIo,
+ &DevReq,
+ EfiUsbDataIn,
+ USB_ETHERNET_TRANSFER_TIMEOUT,
+ RndisMsgResponse,
+ RndisMsgResponse->MessageLength,
+ &UsbStatus
+ );
+
+ DEBUG ((DEBUG_INFO, "RndisControlMsg Response: UsbStatus : %x Status : %r \n", UsbStatus, Status));
+
+ PrintRndisMsg (RndisMsgResponse);
+
+ if (!EFI_ERROR (Status)) {
+ if ((RndisInitCmplt->RequestID != ((REMOTE_NDIS_INITIALIZE_CMPLT *)RndisMsg)->RequestID) || (RndisInitCmplt->MessageType != SaveResponseType)) {
+ DEBUG ((DEBUG_INFO, "Retry the response\n"));
+
+ RndisMsgResponse->MessageType = SaveResponseType;
+ RndisMsgResponse->MessageLength = SaveResponseLength;
+ continue;
+ }
+ }
+
+ return Status;
+ }
+
+ DEBUG ((DEBUG_INFO, "RndisControlMsg: TimeOut\n"));
+
+ return EFI_TIMEOUT;
+}
+
+/**
+ This function send the RNDIS command through the device's Data endpoint
+
+ @param[in] UsbRndisDevice A pointer to the USB_RNDIS_DEVICE instance.
+ @param[in] RndisMsg A pointer to the REMOTE_NDIS_MSG_HEADER to send out.
+ @param[in, out] TransferLength The length of the RndisMsg data to transfer.
+
+ @retval EFI_SUCCESS The request executed successfully.
+
+**/
+EFI_STATUS
+RndisTransmitDataMsg (
+ IN USB_RNDIS_DEVICE *UsbRndisDevice,
+ IN REMOTE_NDIS_MSG_HEADER *RndisMsg,
+ IN OUT UINTN *TransferLength
+ )
+{
+ EFI_STATUS Status;
+ UINT32 UsbStatus;
+
+ if (UsbRndisDevice->BulkInEndpoint == 0) {
+ GetEndpoint (UsbRndisDevice->UsbIoCdcData, UsbRndisDevice);
+ }
+
+ PrintRndisMsg (RndisMsg);
+
+ Status = UsbRndisDevice->UsbIoCdcData->UsbBulkTransfer (
+ UsbRndisDevice->UsbIoCdcData,
+ UsbRndisDevice->BulkOutEndpoint,
+ RndisMsg,
+ TransferLength,
+ USB_TX_ETHERNET_BULK_TIMEOUT,
+ &UsbStatus
+ );
+
+ if (Status == EFI_SUCCESS) {
+ gStopBulkInCnt = MAXIMUM_STOPBULKIN_CNT; // After sending cmd ,we will polling receive package for MAXIMUM_STOPBULKIN_CNT times
+ }
+
+ return Status;
+}
+
+/**
+ This function send the RNDIS command through the device's Data endpoint
+
+ @param[in] UsbRndisDevice A pointer to the USB_RNDIS_DEVICE instance.
+ @param[in, out] RndisMsg A pointer to the REMOTE_NDIS_MSG_HEADER to send out.
+ @param[in, out] TransferLength The length of the RndisMsg data to transfer.
+
+ @retval EFI_SUCCESS The request executed successfully.
+
+**/
+EFI_STATUS
+RndisReceiveDataMsg (
+ IN USB_RNDIS_DEVICE *UsbRndisDevice,
+ IN OUT REMOTE_NDIS_MSG_HEADER *RndisMsg,
+ IN OUT UINTN *TransferLength
+ )
+{
+ EFI_STATUS Status;
+ UINT32 UsbStatus;
+
+ UsbStatus = 0;
+
+ if (UsbRndisDevice->BulkInEndpoint == 0) {
+ GetEndpoint (UsbRndisDevice->UsbIoCdcData, UsbRndisDevice);
+ }
+
+ // Use gStopBulkInCnt to stop BulkIn command
+ if ((gStopBulkInCnt != 0) || LAN_BULKIN_CMD_CONTROL) {
+ Status = UsbRndisDevice->UsbIoCdcData->UsbBulkTransfer (
+ UsbRndisDevice->UsbIoCdcData,
+ UsbRndisDevice->BulkInEndpoint,
+ RndisMsg,
+ TransferLength,
+ USB_RX_ETHERNET_BULK_TIMEOUT,
+ &UsbStatus
+ );
+
+ if (!EFI_ERROR (Status)) {
+ gStopBulkInCnt = MINIMUM_STOPBULKIN_CNT;
+ } else {
+ gStopBulkInCnt--;
+ }
+ } else {
+ Status = EFI_TIMEOUT;
+ *TransferLength = 0;
+ gBlockBulkInCnt++;
+ }
+
+ if (gBlockBulkInCnt > BULKIN_CMD_POLLING_CNT) {
+ gStopBulkInCnt = MINIMUM_STOPBULKIN_CNT;
+ gBlockBulkInCnt = 0;
+ }
+
+ PrintRndisMsg (RndisMsg);
+
+ return Status;
+}
+
+/**
+ Prints RNDIS Header and Data
+
+ @param[in] RndisMsg A pointer to the REMOTE_NDIS_MSG_HEADER data.
+
+**/
+VOID
+PrintRndisMsg (
+ IN REMOTE_NDIS_MSG_HEADER *RndisMsg
+ )
+{
+ UINTN Length;
+ REMOTE_NDIS_QUERY_CMPLT *RndisQueryCmplt;
+
+ Length = 0;
+
+ switch (RndisMsg->MessageType) {
+ case RNDIS_PACKET_MSG:
+ DEBUG ((DEBUG_INFO, "RNDIS_PACKET_MSG:\n"));
+ Length = sizeof (REMOTE_NDIS_PACKET_MSG) + 0x14;
+ break;
+ case RNDIS_INITIALIZE_MSG:
+ DEBUG ((DEBUG_INFO, "RNDIS_INITIALIZE_MSG:\n"));
+ Length = sizeof (REMOTE_NDIS_INITIALIZE_MSG);
+ break;
+ case RNDIS_INITIALIZE_CMPLT:
+ DEBUG ((DEBUG_INFO, "RNDIS_INITIALIZE_CMPLT:\n"));
+ Length = sizeof (REMOTE_NDIS_INITIALIZE_CMPLT);
+ break;
+ case RNDIS_HLT_MSG:
+ DEBUG ((DEBUG_INFO, "RNDIS_HLT_MSG:\n"));
+ Length = sizeof (REMOTE_NDIS_HALT_MSG);
+ break;
+ case RNDIS_QUERY_MSG:
+ DEBUG ((DEBUG_INFO, "RNDIS_QUERY_MSG:\n"));
+ Length = sizeof (REMOTE_NDIS_QUERY_MSG);
+ break;
+ case RNDIS_QUERY_CMPLT:
+ DEBUG ((DEBUG_INFO, "RNDIS_QUERY_CMPLT:\n"));
+ RndisQueryCmplt = (REMOTE_NDIS_QUERY_CMPLT *)RndisMsg;
+ Length = sizeof (REMOTE_NDIS_QUERY_CMPLT) + RndisQueryCmplt->InformationBufferLength;
+ break;
+ case RNDIS_SET_MSG:
+ DEBUG ((DEBUG_INFO, "RNDIS_SET_MSG:\n"));
+ Length = sizeof (REMOTE_NDIS_SET_MSG);
+ break;
+ case RNDIS_SET_CMPLT:
+ DEBUG ((DEBUG_INFO, "RNDIS_SET_CMPLT:\n"));
+ Length = sizeof (REMOTE_NDIS_SET_CMPLT);
+ break;
+ case RNDIS_RESET_MSG:
+ DEBUG ((DEBUG_INFO, "RNDIS_RESET_MSG:\n"));
+ Length = sizeof (REMOTE_NDIS_RESET_MSG);
+ break;
+ case RNDIS_RESET_CMPLT:
+ DEBUG ((DEBUG_INFO, "RNDIS_RESET_CMPLT:\n"));
+ Length = sizeof (REMOTE_NDIS_RESET_CMPLT);
+ break;
+ case RNDIS_INDICATE_STATUS_MSG:
+ DEBUG ((DEBUG_INFO, "RNDIS_INDICATE_STATUS_MSG:\n"));
+ Length = sizeof (REMOTE_NDIS_INDICATE_STATUS_MSG);
+ break;
+ case RNDIS_KEEPALIVE_MSG:
+ DEBUG ((DEBUG_INFO, "RNDIS_KEEPALIVE_MSG:\n"));
+ Length = sizeof (REMOTE_NDIS_KEEPALIVE_MSG);
+ break;
+ case RNDIS_KEEPALIVE_CMPLT:
+ DEBUG ((DEBUG_INFO, "RNDIS_KEEPALIVE_CMPLT:\n"));
+ Length = sizeof (REMOTE_NDIS_KEEPALIVE_CMPLT);
+ }
+
+ if (Length) {
+ UINTN Index;
+ Index = 0;
+ for ( ; Length; Length -= 4, Index++) {
+ DEBUG ((DEBUG_INFO, "%8X\t", *((UINT32 *)RndisMsg + Index)));
+ if (((Index % 4) == 3) && (Index != 0)) {
+ DEBUG ((DEBUG_INFO, "\n"));
+ }
+
+ if ((Length < 8) && (Length > 4)) {
+ UINT32 Data32;
+ Index++;
+ Data32 = *((UINT32 *)RndisMsg + Index);
+ DEBUG ((DEBUG_INFO, "%8X\t", Data32));
+ break;
+ }
+ }
+
+ if ((Index % 4) != 0) {
+ DEBUG ((DEBUG_INFO, "\n"));
+ }
+ }
+}