From mboxrd@z Thu Jan 1 00:00:00 1970 Return-path: Received: from metis.ext.pengutronix.de ([2001:6f8:1178:4:290:27ff:fe1d:cc33]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1X5VPC-0006cA-34 for barebox@lists.infradead.org; Fri, 11 Jul 2014 07:38:09 +0000 From: Sascha Hauer Date: Fri, 11 Jul 2014 09:36:26 +0200 Message-Id: <1405064190-1977-4-git-send-email-s.hauer@pengutronix.de> In-Reply-To: <1405064190-1977-1-git-send-email-s.hauer@pengutronix.de> References: <1405064190-1977-1-git-send-email-s.hauer@pengutronix.de> List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "barebox" Errors-To: barebox-bounces+u.kleine-koenig=pengutronix.de@lists.infradead.org Subject: [PATCH 3/7] net: Add EFI Simple Network Protocol Driver To: barebox@lists.infradead.org This adds a network driver which uses the EFI Simple Network Protocol to implement networking for barebox. Signed-off-by: Sascha Hauer --- drivers/net/Kconfig | 4 + drivers/net/Makefile | 1 + drivers/net/efi-snp.c | 296 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 301 insertions(+) create mode 100644 drivers/net/efi-snp.c diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 5f0c41b..43409a8 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -170,6 +170,10 @@ config DRIVER_NET_TAP bool "tap Ethernet driver" depends on LINUX +config DRIVER_NET_EFI_SNP + bool "EFI SNP ethernet driver" + depends on ARCH_EFI + config DRIVER_NET_TSE depends on NIOS2 bool "Altera TSE ethernet driver" diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 33bb5c8..1b85778 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -26,3 +26,4 @@ obj-$(CONFIG_DRIVER_NET_SMC911X) += smc911x.o obj-$(CONFIG_DRIVER_NET_SMC91111) += smc91111.o obj-$(CONFIG_DRIVER_NET_TAP) += tap.o obj-$(CONFIG_DRIVER_NET_TSE) += altera_tse.o +obj-$(CONFIG_DRIVER_NET_EFI_SNP) += efi-snp.o diff --git a/drivers/net/efi-snp.c b/drivers/net/efi-snp.c new file mode 100644 index 0000000..5b96fbf --- /dev/null +++ b/drivers/net/efi-snp.c @@ -0,0 +1,296 @@ +/* + * efi-snp.c - Simple Network Protocol driver for EFI + * + * Copyright (c) 2014 Sascha Hauer , Pengutronix + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct efi_network_statistics { + uint64_t RxTotalFrames; + uint64_t RxGoodFrames; + uint64_t RxUndersizeFrames; + uint64_t RxOversizeFrames; + uint64_t RxDroppedFrames; + uint64_t RxUnicastFrames; + uint64_t RxBroadcastFrames; + uint64_t RxMulticastFrames; + uint64_t RxCrcErrorFrames; + uint64_t RxTotalBytes; + uint64_t TxTotalFrames; + uint64_t TxGoodFrames; + uint64_t TxUndersizeFrames; + uint64_t TxOversizeFrames; + uint64_t TxDroppedFrames; + uint64_t TxUnicastFrames; + uint64_t TxBroadcastFrames; + uint64_t TxMulticastFrames; + uint64_t TxCrcErrorFrames; + uint64_t TxTotalBytes; + uint64_t Collisions; + uint64_t UnsupportedProtocol; +}; + +enum efi_simple_network_state { + EfiSimpleNetworkStopped, + EfiSimpleNetworkStarted, + EfiSimpleNetworkInitialized, + EfiSimpleNetworkMaxState +}; + +#define EFI_SIMPLE_NETWORK_RECEIVE_UNICAST 0x01 +#define EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST 0x02 +#define EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST 0x04 +#define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS 0x08 +#define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST 0x10 + +#define EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT 0x01 +#define EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT 0x02 +#define EFI_SIMPLE_NETWORK_COMMAND_INTERRUPT 0x04 +#define EFI_SIMPLE_NETWORK_SOFTWARE_INTERRUPT 0x08 + +#define MAX_MCAST_FILTER_CNT 16 +struct efi_simple_network_mode { + uint32_t State; + uint32_t HwAddressSize; + uint32_t MediaHeaderSize; + uint32_t MaxPacketSize; + uint32_t NvRamSize; + uint32_t NvRamAccessSize; + uint32_t ReceiveFilterMask; + 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; + uint8_t IfType; + bool MacAddressChangeable; + bool MultipleTxSupported; + bool MediaPresentSupported; + bool MediaPresent; +}; + +#define EFI_SIMPLE_NETWORK_INTERFACE_REVISION 0x00010000 + +struct efi_simple_network { + uint64_t Revision; + efi_status_t (EFIAPI *start) (struct efi_simple_network *This); + efi_status_t (EFIAPI *stop) (struct efi_simple_network *This); + efi_status_t (EFIAPI *initialize) (struct efi_simple_network *This, + unsigned long ExtraRxBufferSize, unsigned long ExtraTxBufferSize); + efi_status_t (EFIAPI *reset) (struct efi_simple_network *This, bool ExtendedVerification); + 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); + efi_status_t (EFIAPI *station_address) (struct efi_simple_network *This, + bool Reset, 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); + efi_status_t (EFIAPI *nvdata) (struct efi_simple_network *This, + bool ReadWrite, unsigned long Offset, unsigned long BufferSize, + void *Buffer); + efi_status_t (EFIAPI *get_status) (struct efi_simple_network *This, + 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, + 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); + void *WaitForPacket; + struct efi_simple_network_mode *Mode; +}; + +struct efi_snp_priv { + struct device_d *dev; + struct eth_device edev; + struct efi_simple_network *snp; +}; + +static inline struct efi_snp_priv *to_priv(struct eth_device *edev) +{ + return container_of(edev, struct efi_snp_priv, edev); +} + +static int efi_snp_eth_send(struct eth_device *edev, void *packet, int length) +{ + struct efi_snp_priv *priv = to_priv(edev); + efi_status_t efiret; + void *txbuf; + uint64_t start; + + 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)); + return -efi_errno(efiret); + } + + start = get_time_ns(); + + while (!is_timeout(start, SECOND)) { + uint32_t irq; + priv->snp->get_status(priv->snp, &irq, &txbuf); + if (txbuf) + return 0; + } + + dev_err(priv->dev, "tx time out\n"); + + return -ETIMEDOUT; +} + +static int efi_snp_eth_rx(struct eth_device *edev) +{ + struct efi_snp_priv *priv = to_priv(edev); + long bufsize = PKTSIZE; + efi_status_t efiret; + + efiret = priv->snp->receive(priv->snp, NULL, &bufsize, NetRxPackets[0], NULL, NULL, NULL); + if (efiret == EFI_NOT_READY) + return 0; + + if (EFI_ERROR(efiret)) { + dev_err(priv->dev, "failed to receive: %s\n", efi_strerror(efiret)); + return -efi_errno(efiret); + } + + net_receive(edev, NetRxPackets[0], bufsize); + + return 0; +} + +static int efi_snp_eth_open(struct eth_device *edev) +{ + struct efi_snp_priv *priv = to_priv(edev); + uint32_t mask; + efi_status_t efiret; + + priv->snp->shutdown(priv->snp); + priv->snp->stop(priv->snp); + priv->snp->start(priv->snp); + efiret = priv->snp->initialize(priv->snp, 0, 0); + if (EFI_ERROR(efiret)) { + dev_err(priv->dev, "Initialize failed with: %s\n", efi_strerror(efiret)); + return -efi_errno(efiret); + } + + efiret = priv->snp->station_address(priv->snp, false, + (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)); + return -efi_errno(efiret); + } + + mask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | + EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST | + EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS; + efiret = priv->snp->receive_filters(priv->snp, mask, 0, 0, 0, 0); + if (EFI_ERROR(efiret)) { + dev_err(priv->dev, "failed to set receive filters: %s\n", + efi_strerror(efiret)); + return -efi_errno(efiret); + } + + return 0; +} + +static void efi_snp_eth_halt(struct eth_device *edev) +{ + struct efi_snp_priv *priv = to_priv(edev); + + priv->snp->stop(priv->snp); +} + +static int efi_snp_get_ethaddr(struct eth_device *edev, unsigned char *adr) +{ + struct efi_snp_priv *priv = to_priv(edev); + + memcpy(adr, priv->snp->Mode->PermanentAddress.Addr, 6); + + return 0; +} + +static int efi_snp_set_ethaddr(struct eth_device *edev, unsigned char *adr) +{ + return 0; +} + +int efi_snp_probe(struct efi_device *efidev) +{ + struct eth_device *edev; + struct efi_snp_priv *priv; + int ret; + + dev_dbg(&efidev->dev, "efi_snp_probe\n"); + + priv = xzalloc(sizeof(struct efi_snp_priv)); + priv->snp = efidev->protocol; + priv->dev = &efidev->dev; + + dev_dbg(&efidev->dev, "perm: %02x:%02x:%02x:%02x:%02x:%02x\n", + priv->snp->Mode->PermanentAddress.Addr[0], + priv->snp->Mode->PermanentAddress.Addr[1], + priv->snp->Mode->PermanentAddress.Addr[2], + priv->snp->Mode->PermanentAddress.Addr[3], + priv->snp->Mode->PermanentAddress.Addr[4], + priv->snp->Mode->PermanentAddress.Addr[5]); + dev_dbg(&efidev->dev, "curr: %02x:%02x:%02x:%02x:%02x:%02x\n", + priv->snp->Mode->CurrentAddress.Addr[0], + priv->snp->Mode->CurrentAddress.Addr[1], + priv->snp->Mode->CurrentAddress.Addr[2], + priv->snp->Mode->CurrentAddress.Addr[3], + priv->snp->Mode->CurrentAddress.Addr[4], + priv->snp->Mode->CurrentAddress.Addr[5]); + + edev = &priv->edev; + edev->priv = priv; + edev->parent = &efidev->dev; + + edev->open = efi_snp_eth_open; + edev->send = efi_snp_eth_send; + edev->recv = efi_snp_eth_rx; + edev->halt = efi_snp_eth_halt; + edev->get_ethaddr = efi_snp_get_ethaddr; + edev->set_ethaddr = efi_snp_set_ethaddr; + + ret = eth_register(edev); + + return ret; +} + +static struct efi_driver efi_snp_driver = { + .driver = { + .name = "efi-snp", + }, + .probe = efi_snp_probe, + .guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID, +}; +device_efi_driver(efi_snp_driver); -- 2.0.0 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox