summaryrefslogtreecommitdiffstats
path: root/drivers/net/efi-snp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/efi-snp.c')
-rw-r--r--drivers/net/efi-snp.c89
1 files changed, 73 insertions, 16 deletions
diff --git a/drivers/net/efi-snp.c b/drivers/net/efi-snp.c
index def2714bee..476015f1c2 100644
--- a/drivers/net/efi-snp.c
+++ b/drivers/net/efi-snp.c
@@ -11,7 +11,7 @@
#include <net.h>
#include <init.h>
#include <efi.h>
-#include <efi/efi.h>
+#include <efi/efi-payload.h>
#include <efi/efi-device.h>
struct efi_network_statistics {
@@ -69,10 +69,10 @@ struct efi_simple_network_mode {
uint32_t ReceiveFilterSetting;
uint32_t MaxMCastFilterCount;
uint32_t MCastFilterCount;
- efi_mac_address MCastFilter[MAX_MCAST_FILTER_CNT];
- efi_mac_address CurrentAddress;
- efi_mac_address BroadcastAddress;
- efi_mac_address PermanentAddress;
+ struct efi_mac_address MCastFilter[MAX_MCAST_FILTER_CNT];
+ struct efi_mac_address CurrentAddress;
+ struct efi_mac_address BroadcastAddress;
+ struct efi_mac_address PermanentAddress;
uint8_t IfType;
bool MacAddressChangeable;
bool MultipleTxSupported;
@@ -92,14 +92,14 @@ struct efi_simple_network {
efi_status_t (EFIAPI *shutdown) (struct efi_simple_network *This);
efi_status_t (EFIAPI *receive_filters) (struct efi_simple_network *This,
uint32_t Enable, uint32_t Disable, bool ResetMCastFilter,
- unsigned long MCastFilterCnt, efi_mac_address *MCastFilter);
+ unsigned long MCastFilterCnt, struct efi_mac_address *MCastFilter);
efi_status_t (EFIAPI *station_address) (struct efi_simple_network *This,
- bool Reset, efi_mac_address *New);
+ bool Reset, struct efi_mac_address *New);
efi_status_t (EFIAPI *statistics) (struct efi_simple_network *This,
bool Reset, unsigned long *StatisticsSize,
struct efi_network_statistics *StatisticsTable);
efi_status_t (EFIAPI *mcast_ip_to_mac) (struct efi_simple_network *This,
- bool IPv6, efi_ip_address *IP, efi_mac_address *MAC);
+ bool IPv6, union efi_ip_address *IP, struct efi_mac_address *MAC);
efi_status_t (EFIAPI *nvdata) (struct efi_simple_network *This,
bool ReadWrite, unsigned long Offset, unsigned long BufferSize,
void *Buffer);
@@ -107,19 +107,20 @@ struct efi_simple_network {
uint32_t *InterruptStatus, void **TxBuf);
efi_status_t (EFIAPI *transmit) (struct efi_simple_network *This,
unsigned long HeaderSize, unsigned long BufferSize, void *Buffer,
- efi_mac_address *SrcAddr, efi_mac_address *DestAddr,
+ struct efi_mac_address *SrcAddr, struct efi_mac_address *DestAddr,
uint16_t *Protocol);
efi_status_t (EFIAPI *receive) (struct efi_simple_network *This,
unsigned long *HeaderSize, unsigned long *BufferSize, void *Buffer,
- efi_mac_address *SrcAddr, efi_mac_address *DestAddr, uint16_t *Protocol);
+ struct efi_mac_address *SrcAddr, struct efi_mac_address *DestAddr, uint16_t *Protocol);
void *WaitForPacket;
struct efi_simple_network_mode *Mode;
};
struct efi_snp_priv {
- struct device_d *dev;
+ struct device *dev;
struct eth_device edev;
struct efi_simple_network *snp;
+ void *rx_buf;
};
static inline struct efi_snp_priv *to_priv(struct eth_device *edev)
@@ -134,6 +135,9 @@ static int efi_snp_eth_send(struct eth_device *edev, void *packet, int length)
void *txbuf;
uint64_t start;
+ if (!priv->snp->Mode->MediaPresent)
+ return -ENOMEDIUM;
+
efiret = priv->snp->transmit(priv->snp, 0, length, packet, NULL, NULL, NULL);
if (EFI_ERROR(efiret)) {
dev_err(priv->dev, "failed to send: %s\n", efi_strerror(efiret));
@@ -160,7 +164,7 @@ static int efi_snp_eth_rx(struct eth_device *edev)
long bufsize = PKTSIZE;
efi_status_t efiret;
- efiret = priv->snp->receive(priv->snp, NULL, &bufsize, NetRxPackets[0], NULL, NULL, NULL);
+ efiret = priv->snp->receive(priv->snp, NULL, &bufsize, priv->rx_buf, NULL, NULL, NULL);
if (efiret == EFI_NOT_READY)
return 0;
@@ -169,11 +173,37 @@ static int efi_snp_eth_rx(struct eth_device *edev)
return -efi_errno(efiret);
}
- net_receive(edev, NetRxPackets[0], bufsize);
+ net_receive(edev, priv->rx_buf, bufsize);
return 0;
}
+static efi_guid_t snp_guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID;
+
+static int efi_snp_open_exclusive(struct efi_device *efidev)
+{
+ void *interface;
+ efi_status_t efiret;
+
+ /*
+ * Try to re-open SNP exlusively to close any active MNP protocol instance
+ * that may compete for packet polling
+ */
+ efiret = BS->open_protocol(efidev->handle, &snp_guid,
+ &interface, efi_parent_image, NULL, EFI_OPEN_PROTOCOL_EXCLUSIVE);
+ if (EFI_ERROR(efiret)) {
+ dev_err(&efidev->dev, "failed to open exclusively: %s\n", efi_strerror(efiret));
+ return -efi_errno(efiret);
+ }
+
+ return 0;
+}
+
+static void efi_snp_close_exclusive(struct efi_device *efidev)
+{
+ BS->close_protocol(efidev->handle, &snp_guid, efi_parent_image, NULL);
+}
+
static int efi_snp_eth_open(struct eth_device *edev)
{
struct efi_snp_priv *priv = to_priv(edev);
@@ -190,7 +220,7 @@ static int efi_snp_eth_open(struct eth_device *edev)
}
efiret = priv->snp->station_address(priv->snp, false,
- (efi_mac_address *)priv->snp->Mode->PermanentAddress.Addr );
+ (struct efi_mac_address *)priv->snp->Mode->PermanentAddress.Addr );
if (EFI_ERROR(efiret)) {
dev_err(priv->dev, "failed to set MAC address: %s\n",
efi_strerror(efiret));
@@ -231,6 +261,20 @@ static int efi_snp_set_ethaddr(struct eth_device *edev, const unsigned char *adr
return 0;
}
+static int efi_snp_pause(struct efi_device *efidev)
+{
+ efi_snp_close_exclusive(efidev);
+
+ return 0;
+}
+
+static int efi_snp_continue(struct efi_device *efidev)
+{
+ efi_snp_open_exclusive(efidev);
+
+ return 0;
+}
+
static int efi_snp_probe(struct efi_device *efidev)
{
struct eth_device *edev;
@@ -242,6 +286,7 @@ static int efi_snp_probe(struct efi_device *efidev)
priv = xzalloc(sizeof(struct efi_snp_priv));
priv->snp = efidev->protocol;
priv->dev = &efidev->dev;
+ priv->rx_buf = xmalloc(PKTSIZE);
dev_dbg(&efidev->dev, "perm: %02x:%02x:%02x:%02x:%02x:%02x\n",
priv->snp->Mode->PermanentAddress.Addr[0],
@@ -269,16 +314,28 @@ static int efi_snp_probe(struct efi_device *efidev)
edev->get_ethaddr = efi_snp_get_ethaddr;
edev->set_ethaddr = efi_snp_set_ethaddr;
+ ret = efi_snp_open_exclusive(efidev);
+ if (ret)
+ return ret;
+
ret = eth_register(edev);
return ret;
}
+static void efi_snp_remove(struct efi_device *efidev)
+{
+ efi_snp_close_exclusive(efidev);
+}
+
static struct efi_driver efi_snp_driver = {
- .driver = {
+ .driver = {
.name = "efi-snp",
},
- .probe = efi_snp_probe,
+ .probe = efi_snp_probe,
+ .remove = efi_snp_remove,
+ .dev_pause = efi_snp_pause,
+ .dev_continue = efi_snp_continue,
.guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID,
};
device_efi_driver(efi_snp_driver);