Mike Maslenkin e07948255c MdeModulePkg: UsbNetwork: fix Ethernet functional descriptor processing
This patch fixes wrong condition because of UINT16 value to integer
promotion. NumberMcFilters is UINT16 value, so when bitwise shift operator
applied to small integer type, the operation is preceded by integral
promotion. This is described in MISRA-C:2004 guideline as Rule 10.5:
"If the bitwise operators ~ and << are applied to an operand of underlying
type unsigned char or unsigned short, the result shall be immediately cast
to the underlying type of the operand."

A simple fix for this issue would be the following:
  if ((UINT16)(UsbEthFunDescriptor.NumberMcFilters << 1) == 0)

But this patch proposes to use bitwise AND operation with a proper bit mask
rather than shifting to prevent similar mistakes in future.

Cc: Richard Ho <richardho@ami.com>
Cc: Rebecca Cran <rebecca@bsdio.com>
Signed-off-by: Mike Maslenkin <mike.maslenkin@gmail.com>
2023-10-16 22:49:33 +00:00

1722 lines
56 KiB
C

/** @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 & MAC_FILTERS_MASK) == 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 & MAC_FILTERS_MASK) == 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"));
}
}
}