/** @file | |
Implementation of the SNP.GetStatus() function and its private helpers if | |
any. | |
Copyright (C) 2013, Red Hat, Inc. | |
Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <Library/BaseLib.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include "VirtioNet.h" | |
/** | |
Reads the current interrupt status and recycled transmit buffer status from | |
a network interface. | |
@param This The protocol instance pointer. | |
@param InterruptStatus A pointer to the bit mask of the currently active | |
interrupts If this is NULL, the interrupt status will | |
not be read from the device. If this is not NULL, the | |
interrupt status will be read from the device. When | |
the interrupt status is read, it will also be | |
cleared. Clearing the transmit interrupt does not | |
empty the recycled transmit buffer array. | |
@param TxBuf Recycled transmit buffer address. The network | |
interface will not transmit if its internal recycled | |
transmit buffer array is full. Reading the transmit | |
buffer does not clear the transmit interrupt. If this | |
is NULL, then the transmit buffer status will not be | |
read. If there are no transmit buffers to recycle and | |
TxBuf is not NULL, * TxBuf will be set to NULL. | |
@retval EFI_SUCCESS The status of the network interface was | |
retrieved. | |
@retval EFI_NOT_STARTED The network interface has not been started. | |
@retval EFI_INVALID_PARAMETER One or more of the parameters has an | |
unsupported value. | |
@retval EFI_DEVICE_ERROR The command could not be sent to the network | |
interface. | |
@retval EFI_UNSUPPORTED This function is not supported by the network | |
interface. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
VirtioNetGetStatus ( | |
IN EFI_SIMPLE_NETWORK_PROTOCOL *This, | |
OUT UINT32 *InterruptStatus OPTIONAL, | |
OUT VOID **TxBuf OPTIONAL | |
) | |
{ | |
VNET_DEV *Dev; | |
EFI_TPL OldTpl; | |
EFI_STATUS Status; | |
UINT16 RxCurUsed; | |
UINT16 TxCurUsed; | |
EFI_PHYSICAL_ADDRESS DeviceAddress; | |
if (This == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Dev = VIRTIO_NET_FROM_SNP (This); | |
OldTpl = gBS->RaiseTPL (TPL_CALLBACK); | |
switch (Dev->Snm.State) { | |
case EfiSimpleNetworkStopped: | |
Status = EFI_NOT_STARTED; | |
goto Exit; | |
case EfiSimpleNetworkStarted: | |
Status = EFI_DEVICE_ERROR; | |
goto Exit; | |
default: | |
break; | |
} | |
// | |
// update link status | |
// | |
if (Dev->Snm.MediaPresentSupported) { | |
UINT16 LinkStatus; | |
Status = VIRTIO_CFG_READ (Dev, LinkStatus, &LinkStatus); | |
if (EFI_ERROR (Status)) { | |
goto Exit; | |
} | |
Dev->Snm.MediaPresent = | |
(BOOLEAN)((LinkStatus & VIRTIO_NET_S_LINK_UP) != 0); | |
} | |
// | |
// virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device | |
// | |
MemoryFence (); | |
RxCurUsed = *Dev->RxRing.Used.Idx; | |
TxCurUsed = *Dev->TxRing.Used.Idx; | |
MemoryFence (); | |
if (InterruptStatus != NULL) { | |
// | |
// report the receive interrupt if there is data available for reception, | |
// report the transmit interrupt if we have transmitted at least one buffer | |
// | |
*InterruptStatus = 0; | |
if (Dev->RxLastUsed != RxCurUsed) { | |
*InterruptStatus |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; | |
} | |
if (Dev->TxLastUsed != TxCurUsed) { | |
ASSERT (Dev->TxCurPending > 0); | |
*InterruptStatus |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; | |
} | |
} | |
if (TxBuf != NULL) { | |
if (Dev->TxLastUsed == TxCurUsed) { | |
*TxBuf = NULL; | |
} else { | |
UINT16 UsedElemIdx; | |
UINT32 DescIdx; | |
// | |
// fetch the first descriptor among those that the hypervisor reports | |
// completed | |
// | |
ASSERT (Dev->TxCurPending > 0); | |
ASSERT (Dev->TxCurPending <= Dev->TxMaxPending); | |
UsedElemIdx = Dev->TxLastUsed++ % Dev->TxRing.QueueSize; | |
DescIdx = Dev->TxRing.Used.UsedElem[UsedElemIdx].Id; | |
ASSERT (DescIdx < (UINT32)(2 * Dev->TxMaxPending - 1)); | |
// | |
// get the device address that has been enqueued for the caller's | |
// transmit buffer | |
// | |
DeviceAddress = Dev->TxRing.Desc[DescIdx + 1].Addr; | |
// | |
// now this descriptor can be used again to enqueue a transmit buffer | |
// | |
Dev->TxFreeStack[--Dev->TxCurPending] = (UINT16)DescIdx; | |
// | |
// Unmap the device address and perform the reverse mapping to find the | |
// caller buffer address. | |
// | |
Status = VirtioNetUnmapTxBuf ( | |
Dev, | |
TxBuf, | |
DeviceAddress | |
); | |
if (EFI_ERROR (Status)) { | |
// | |
// VirtioNetUnmapTxBuf should never fail, if we have reached here | |
// that means our internal state has been corrupted | |
// | |
ASSERT (FALSE); | |
Status = EFI_DEVICE_ERROR; | |
goto Exit; | |
} | |
} | |
} | |
Status = EFI_SUCCESS; | |
Exit: | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} |