
UCLASS_ETH "efi_netdev" U-Boot driver. This driver can control a handle with a simple_network_protocol installed and working. See efi_netdev_create.
Signed-off-by: Adriano Cordova adriano.cordova@canonical.com --- include/efi_loader.h | 13 +++ lib/efi_driver/efi_net_device.c | 159 ++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 93f223389a..3bb95cfeb9 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -634,6 +634,18 @@ int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc, const char *pdevname); /* Called by bootefi to make GOP (graphical) interface available */ efi_status_t efi_gop_register(void); + +/** + * struct efi_netdev_plat - attributes of a network device + * + * @handle: handle of the controller on which this driver is installed + * @snp: simple network protocol proxied by this driver + */ +struct efi_netdev_plat { + efi_handle_t handle; + struct efi_simple_network *snp; + void *buffer; +}; /* Called to register an EFI network device */ int efi_net_register(void *ctx, struct event *event); /* Called to unregister an EFI network device */ @@ -647,6 +659,7 @@ efi_status_t efi_ipconfig_register(const efi_handle_t handle, /* Called by efi_net_register to make the http protocol available */ efi_status_t efi_http_register(const efi_handle_t handle, struct efi_service_binding_protocol *http_service_binding); + /* Called by bootefi to make the watchdog available */ efi_status_t efi_watchdog_register(void); efi_status_t efi_initrd_register(void); diff --git a/lib/efi_driver/efi_net_device.c b/lib/efi_driver/efi_net_device.c index 298eded265..af51b1f855 100644 --- a/lib/efi_driver/efi_net_device.c +++ b/lib/efi_driver/efi_net_device.c @@ -7,11 +7,146 @@ #include <dm.h> #include <efi_driver.h> #include <malloc.h> +#include <net.h> #include <dm/device-internal.h> #include <dm/root.h> #include <dm/tag.h> #include <dm/uclass-internal.h>
+#define DEFAULT_EFI_NET_BUFFER_SIZE 1024 + +int efi_net_start(struct udevice *dev) +{ + struct efi_netdev_plat *plat; + efi_status_t ret; + + plat = dev_get_plat(dev); + if (!plat || !plat->snp) + return -1; + + ret = plat->snp->start(plat->snp); + if (ret != EFI_SUCCESS) + return -1; + ret = plat->snp->initialize(plat->snp, 0, 0); + if (ret != EFI_SUCCESS) + return -1; + + return 0; +} + +int efi_net_send(struct udevice *dev, void *packet, int length) +{ + struct efi_netdev_plat *plat; + efi_status_t ret; + + plat = dev_get_plat(dev); + if (!plat || !plat->snp) + return -1; + + ret = plat->snp->transmit(plat->snp, 0, length, packet, NULL, NULL, NULL); + if (ret != EFI_SUCCESS) + return -1; + + return 0; +} + +int efi_net_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct efi_netdev_plat *plat; + efi_status_t ret; + size_t buffer_size; + + plat = dev_get_plat(dev); + if (!plat || !plat->snp) + return -1; + + if (plat->buffer) + free(plat->buffer); + + buffer_size = DEFAULT_EFI_NET_BUFFER_SIZE; + plat->buffer = calloc(1, buffer_size); + ret = plat->snp->receive(plat->snp, NULL, &buffer_size, plat->buffer, + NULL, NULL, NULL); + + if (ret == EFI_BUFFER_TOO_SMALL) { + free(plat->buffer); + plat->buffer = calloc(1, buffer_size); + ret = plat->snp->receive(plat->snp, NULL, &buffer_size, plat->buffer, + NULL, NULL, NULL); + } + + if (ret != EFI_SUCCESS || ret != EFI_NOT_READY) + return -1; + + *packetp = plat->buffer; + return buffer_size; +} + +void efi_net_stop(struct udevice *dev) +{ + struct efi_netdev_plat *plat; + + plat = dev_get_plat(dev); + if (!plat || !plat->snp) + return; + + plat->snp->stop(plat->snp); +} + +/** + * efi_netdev_create() - create a net udevice for a handle + * + * @handle: handle + * @interface: simple network protocol + * Return: status code + */ +static efi_status_t +efi_netdev_create(efi_handle_t handle, void *interface) +{ + struct udevice *dev = NULL, *parent = dm_root(); + efi_status_t ret; + char *name; + struct efi_netdev_plat *plat; + static int devnum; + + name = calloc(1, 18); /* strlen("efinet#2147483648") + 1 */ + if (!name) + return EFI_OUT_OF_RESOURCES; + sprintf(name, "efinet#%d", devnum); + devnum++; + + /* Create driver model udevice for the EFI block io device */ + if (eth_create_device(parent, "efi_netdev", name, &dev)) { + ret = EFI_OUT_OF_RESOURCES; + free(name); + goto err; + } + + plat = dev_get_plat(dev); + plat->handle = handle; + plat->snp = interface; + + if (efi_link_dev(handle, dev)) { + ret = EFI_OUT_OF_RESOURCES; + goto err; + } + + if (device_probe(dev)) { + ret = EFI_DEVICE_ERROR; + goto err; + } + EFI_PRINT("%s: net udevice '%s' created\n", __func__, dev->name); + + return EFI_SUCCESS; + +err: + efi_unlink_dev(handle); + if (dev) + device_unbind(dev); + + return ret; +} + /** * efi_net_bind_drv() - TODO * @@ -26,6 +161,14 @@ static efi_status_t efi_net_bind_drv( { EFI_PRINT("%s: handle %p, interface %p\n", __func__, handle, interface);
+ efi_status_t ret; + + if (!handle->dev) { + ret = efi_netdev_create(handle, interface); + if (ret != EFI_SUCCESS) + return ret; + } + return EFI_SUCCESS; }
@@ -71,6 +214,22 @@ efi_net_init_drv(struct efi_driver_binding_extended_protocol *this) return EFI_SUCCESS; }
+/* Net device driver operators */ +static const struct eth_ops efi_eth_ops = { + .start = efi_net_start, + .send = efi_net_send, + .recv = efi_net_recv, + .stop = efi_net_stop, +}; + +/* Identify as net device driver */ +U_BOOT_DRIVER(efi_netdev) = { + .name = "efi_netdev", + .id = UCLASS_ETH, + .ops = &efi_eth_ops, + .plat_auto = sizeof(struct efi_netdev_plat), +}; + /* EFI driver operators */ static const struct efi_driver_ops driver_ops = { .protocol = &efi_net_guid,