#include <common.h>
#include <command.h>
#include <dm.h>
+#include <environment.h>
#include <net.h>
#include <miiphy.h>
#include <phy.h>
#include <asm/errno.h>
#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
DECLARE_GLOBAL_DATA_PTR;
}
}
-int eth_getenv_enetaddr(char *name, uchar *enetaddr)
+int eth_getenv_enetaddr(const char *name, uchar *enetaddr)
{
eth_parse_enetaddr(getenv(name), enetaddr);
return is_valid_ethaddr(enetaddr);
}
-int eth_setenv_enetaddr(char *name, const uchar *enetaddr)
+int eth_setenv_enetaddr(const char *name, const uchar *enetaddr)
{
char buf[20];
return eth_setenv_enetaddr(enetvar, enetaddr);
}
-static void eth_env_init(void)
-{
- const char *s;
-
- s = getenv("bootfile");
- if (s != NULL)
- copy_filename(net_boot_file_name, s,
- sizeof(net_boot_file_name));
-}
-
static int eth_mac_skip(int index)
{
char enetvar[15];
phy_init();
#endif
- eth_env_init();
-
/*
* If board-specific initialization exists, call it.
* If not, call a CPU-specific one
if (cpu_eth_init(gd->bd) < 0)
printf("CPU Net Initialization Failed\n");
} else {
+#ifndef CONFIG_DM_ETH
printf("Net Initialization Skipped\n");
+#endif
}
}
*/
static void eth_set_dev(struct udevice *dev)
{
- if (dev && !device_active(dev))
+ if (dev && !device_active(dev)) {
eth_errno = device_probe(dev);
+ if (eth_errno)
+ dev = NULL;
+ }
+
eth_get_uclass_priv()->current = dev;
}
const char *startp = NULL;
struct udevice *it;
struct uclass *uc;
+ int len = strlen("eth");
/* Must be longer than 3 to be an alias */
- if (strlen(devname) > strlen("eth")) {
- startp = devname + strlen("eth");
+ if (!strncmp(devname, "eth", len) && strlen(devname) > len) {
+ startp = devname + len;
seq = simple_strtoul(startp, &endp, 10);
}
* match an alias or it will match a literal name and we'll pick
* up the error when we try to probe again in eth_set_dev().
*/
- device_probe(it);
- /*
- * Check for the name or the sequence number to match
- */
+ if (device_probe(it))
+ continue;
+ /* Check for the name or the sequence number to match */
if (strcmp(it->name, devname) == 0 ||
(endp > startp && it->seq == seq))
return it;
return -EINVAL;
}
+ /*
+ * Drivers are allowed to decide not to implement this at
+ * run-time. E.g. Some devices may use it and some may not.
+ */
ret = eth_get_ops(dev)->write_hwaddr(dev);
+ if (ret == -ENOSYS)
+ ret = 0;
if (ret)
printf("\nWarning: %s failed to set MAC address\n",
dev->name);
return ret;
}
+static int on_ethaddr(const char *name, const char *value, enum env_op op,
+ int flags)
+{
+ int index;
+ int retval;
+ struct udevice *dev;
+
+ /* look for an index after "eth" */
+ index = simple_strtoul(name + 3, NULL, 10);
+
+ retval = uclass_find_device_by_seq(UCLASS_ETH, index, false, &dev);
+ if (!retval) {
+ struct eth_pdata *pdata = dev->platdata;
+ switch (op) {
+ case env_op_create:
+ case env_op_overwrite:
+ eth_parse_enetaddr(value, pdata->enetaddr);
+ break;
+ case env_op_delete:
+ memset(pdata->enetaddr, 0, 6);
+ }
+ }
+
+ return 0;
+}
+U_BOOT_ENV_CALLBACK(ethaddr, on_ethaddr);
+
int eth_init(void)
{
- struct udevice *current;
+ char *ethact = getenv("ethact");
+ char *ethrotate = getenv("ethrotate");
+ struct udevice *current = NULL;
struct udevice *old_current;
int ret = -ENODEV;
- current = eth_get_dev();
+ /*
+ * When 'ethrotate' variable is set to 'no' and 'ethact' variable
+ * is already set to an ethernet device, we should stick to 'ethact'.
+ */
+ if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0)) {
+ if (ethact) {
+ current = eth_get_dev_by_name(ethact);
+ if (!current)
+ return -EINVAL;
+ }
+ }
+
if (!current) {
- printf("No ethernet found.\n");
- return -ENODEV;
+ current = eth_get_dev();
+ if (!current) {
+ printf("No ethernet found.\n");
+ return -ENODEV;
+ }
}
old_current = current;
do {
- debug("Trying %s\n", current->name);
-
- if (device_active(current)) {
- uchar env_enetaddr[6];
- struct eth_pdata *pdata = current->platdata;
- int enetaddr_changed = 0;
-
- /* Sync environment with network device */
- if (eth_getenv_enetaddr_by_index("eth", current->seq,
- env_enetaddr)) {
- enetaddr_changed = memcmp(pdata->enetaddr,
- env_enetaddr, 6);
- memcpy(pdata->enetaddr, env_enetaddr, 6);
+ if (current) {
+ debug("Trying %s\n", current->name);
+
+ if (device_active(current)) {
+ ret = eth_get_ops(current)->start(current);
+ if (ret >= 0) {
+ struct eth_device_priv *priv =
+ current->uclass_priv;
+
+ priv->state = ETH_STATE_ACTIVE;
+ return 0;
+ }
} else {
- memset(env_enetaddr, 0, 6);
- enetaddr_changed = memcmp(pdata->enetaddr,
- env_enetaddr, 6);
- memset(pdata->enetaddr, 0, 6);
+ ret = eth_errno;
}
- if (enetaddr_changed)
- eth_write_hwaddr(current);
-
- ret = eth_get_ops(current)->start(current);
- if (ret >= 0) {
- struct eth_device_priv *priv =
- current->uclass_priv;
- priv->state = ETH_STATE_ACTIVE;
- return 0;
- }
+ debug("FAIL\n");
} else {
- ret = eth_errno;
+ debug("PROBE FAIL\n");
}
- debug("FAIL\n");
-
/*
* If ethrotate is enabled, this will change "current",
* otherwise we will drop out of this while loop immediately
priv->state = ETH_STATE_PASSIVE;
}
+int eth_is_active(struct udevice *dev)
+{
+ struct eth_device_priv *priv;
+
+ if (!dev || !device_active(dev))
+ return 0;
+
+ priv = dev_get_uclass_priv(dev);
+ return priv->state == ETH_STATE_ACTIVE;
+}
+
int eth_send(void *packet, int length)
{
struct udevice *current;
{
struct udevice *current;
uchar *packet;
+ int flags;
int ret;
int i;
return -EINVAL;
/* Process up to 32 packets at one time */
+ flags = ETH_RECV_CHECK_DEVICE;
for (i = 0; i < 32; i++) {
- ret = eth_get_ops(current)->recv(current, &packet);
+ ret = eth_get_ops(current)->recv(current, flags, &packet);
+ flags = 0;
if (ret > 0)
net_process_received_packet(packet, ret);
if (ret >= 0 && eth_get_ops(current)->free_pkt)
struct eth_pdata *pdata = dev->platdata;
unsigned char env_enetaddr[6];
+#if defined(CONFIG_NEEDS_MANUAL_RELOC)
+ struct eth_ops *ops = eth_get_ops(dev);
+ static int reloc_done;
+
+ if (!reloc_done) {
+ if (ops->start)
+ ops->start += gd->reloc_off;
+ if (ops->send)
+ ops->send += gd->reloc_off;
+ if (ops->recv)
+ ops->recv += gd->reloc_off;
+ if (ops->free_pkt)
+ ops->free_pkt += gd->reloc_off;
+ if (ops->stop)
+ ops->stop += gd->reloc_off;
+#ifdef CONFIG_MCAST_TFTP
+ if (ops->mcast)
+ ops->mcast += gd->reloc_off;
+#endif
+ if (ops->write_hwaddr)
+ ops->write_hwaddr += gd->reloc_off;
+ if (ops->read_rom_hwaddr)
+ ops->read_rom_hwaddr += gd->reloc_off;
+
+ reloc_done++;
+ }
+#endif
+
priv->state = ETH_STATE_INIT;
/* Check if the device has a MAC address in ROM */
printf("\nWarning: %s using MAC address from ROM\n",
dev->name);
} else if (is_zero_ethaddr(pdata->enetaddr)) {
+#ifdef CONFIG_NET_RANDOM_ETHADDR
+ net_random_ethaddr(pdata->enetaddr);
+ printf("\nWarning: %s (eth%d) using random MAC address - %pM\n",
+ dev->name, dev->seq, pdata->enetaddr);
+#else
printf("\nError: %s address not set.\n",
dev->name);
return -EINVAL;
+#endif
}
return 0;
static int eth_pre_remove(struct udevice *dev)
{
+ struct eth_pdata *pdata = dev->platdata;
+
eth_get_ops(dev)->stop(dev);
+ /* clear the MAC address */
+ memset(pdata->enetaddr, 0, 6);
+
return 0;
}
.per_device_auto_alloc_size = sizeof(struct eth_device_priv),
.flags = DM_UC_FLAG_SEQ_ALIAS,
};
-#endif
+#endif /* #ifdef CONFIG_DM_ETH */
#ifndef CONFIG_DM_ETH
return eth_current->index;
}
+static int on_ethaddr(const char *name, const char *value, enum env_op op,
+ int flags)
+{
+ int index;
+ struct eth_device *dev;
+
+ if (!eth_devices)
+ return 0;
+
+ /* look for an index after "eth" */
+ index = simple_strtoul(name + 3, NULL, 10);
+
+ dev = eth_devices;
+ do {
+ if (dev->index == index) {
+ switch (op) {
+ case env_op_create:
+ case env_op_overwrite:
+ eth_parse_enetaddr(value, dev->enetaddr);
+ break;
+ case env_op_delete:
+ memset(dev->enetaddr, 0, 6);
+ }
+ }
+ dev = dev->next;
+ } while (dev != eth_devices);
+
+ return 0;
+}
+U_BOOT_ENV_CALLBACK(ethaddr, on_ethaddr);
+
int eth_write_hwaddr(struct eth_device *dev, const char *base_name,
int eth_number)
{
} else if (is_valid_ethaddr(dev->enetaddr)) {
eth_setenv_enetaddr_by_index(base_name, eth_number,
dev->enetaddr);
- printf("\nWarning: %s using MAC address from net device\n",
- dev->name);
} else if (is_zero_ethaddr(dev->enetaddr)) {
+#ifdef CONFIG_NET_RANDOM_ETHADDR
+ net_random_ethaddr(dev->enetaddr);
+ printf("\nWarning: %s (eth%d) using random MAC address - %pM\n",
+ dev->name, eth_number, dev->enetaddr);
+#else
printf("\nError: %s address not set.\n",
dev->name);
return -EINVAL;
+#endif
}
if (dev->write_hwaddr && !eth_mac_skip(eth_number)) {
int eth_init(void)
{
- struct eth_device *old_current, *dev;
+ struct eth_device *old_current;
if (!eth_current) {
puts("No ethernet found.\n");
return -ENODEV;
}
- /* Sync environment with network devices */
- dev = eth_devices;
- do {
- uchar env_enetaddr[6];
- int enetaddr_changed = 0;
-
- if (eth_getenv_enetaddr_by_index("eth", dev->index,
- env_enetaddr)) {
- enetaddr_changed = memcmp(dev->enetaddr,
- env_enetaddr, 6);
- memcpy(dev->enetaddr, env_enetaddr, 6);
- } else {
- memset(env_enetaddr, 0, 6);
- enetaddr_changed = memcmp(dev->enetaddr,
- env_enetaddr, 6);
- memset(dev->enetaddr, 0, 6);
- }
- if (enetaddr_changed)
- eth_write_hwaddr(dev, "eth", dev->index);
-
- dev = dev->next;
- } while (dev != eth_devices);
-
old_current = eth_current;
do {
debug("Trying %s\n", eth_current->name);
eth_current->state = ETH_STATE_PASSIVE;
}
+int eth_is_active(struct eth_device *dev)
+{
+ return dev && dev->state == ETH_STATE_ACTIVE;
+}
+
int eth_send(void *packet, int length)
{
if (!eth_current)
static void eth_current_changed(void)
{
char *act = getenv("ethact");
+ char *ethrotate;
+
+ /*
+ * The call to eth_get_dev() below has a side effect of rotating
+ * ethernet device if uc_priv->current == NULL. This is not what
+ * we want when 'ethrotate' variable is 'no'.
+ */
+ ethrotate = getenv("ethrotate");
+ if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0))
+ return;
+
/* update current ethernet name */
if (eth_get_dev()) {
if (act == NULL || strcmp(act, eth_get_name()) != 0)