diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/Makefile | 7 | ||||
-rw-r--r-- | net/bootp.c | 4 | ||||
-rw-r--r-- | net/eth-uclass.c (renamed from net/eth.c) | 596 | ||||
-rw-r--r-- | net/eth_common.c | 166 | ||||
-rw-r--r-- | net/eth_internal.h | 40 | ||||
-rw-r--r-- | net/eth_legacy.c | 439 |
6 files changed, 658 insertions, 594 deletions
diff --git a/net/Makefile b/net/Makefile index e9cc8ada96..f03d608326 100644 --- a/net/Makefile +++ b/net/Makefile @@ -12,7 +12,12 @@ obj-$(CONFIG_CMD_NET) += arp.o obj-$(CONFIG_CMD_NET) += bootp.o obj-$(CONFIG_CMD_CDP) += cdp.o obj-$(CONFIG_CMD_DNS) += dns.o -obj-$(CONFIG_CMD_NET) += eth.o +ifdef CONFIG_DM_ETH +obj-$(CONFIG_CMD_NET) += eth-uclass.o +else +obj-$(CONFIG_CMD_NET) += eth_legacy.o +endif +obj-$(CONFIG_CMD_NET) += eth_common.o obj-$(CONFIG_CMD_LINK_LOCAL) += link_local.o obj-$(CONFIG_CMD_NET) += net.o obj-$(CONFIG_CMD_NFS) += nfs.o diff --git a/net/bootp.c b/net/bootp.c index 8aeddb08ea..f2978a23ce 100644 --- a/net/bootp.c +++ b/net/bootp.c @@ -949,6 +949,7 @@ static void dhcp_send_request_packet(struct bootp_hdr *bp_offer) net_write_ip(&bp->bp_giaddr, zero_ip); memcpy(bp->bp_chaddr, net_ethaddr, 6); + copy_filename(bp->bp_file, net_boot_file_name, sizeof(bp->bp_file)); /* * ID is the id of the OFFER packet @@ -995,6 +996,9 @@ static void dhcp_handler(uchar *pkt, unsigned dest, struct in_addr sip, debug("DHCPHandler: got DHCP packet: (src=%d, dst=%d, len=%d) state: " "%d\n", src, dest, len, dhcp_state); + if (net_read_ip(&bp->bp_yiaddr).s_addr == 0) + return; + switch (dhcp_state) { case SELECTING: /* diff --git a/net/eth.c b/net/eth-uclass.c index 45fe6e3c1c..a356a08826 100644 --- a/net/eth.c +++ b/net/eth-uclass.c @@ -7,113 +7,13 @@ */ #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> +#include "eth_internal.h" -DECLARE_GLOBAL_DATA_PTR; - -void eth_parse_enetaddr(const char *addr, uchar *enetaddr) -{ - char *end; - int i; - - for (i = 0; i < 6; ++i) { - enetaddr[i] = addr ? simple_strtoul(addr, &end, 16) : 0; - if (addr) - addr = (*end) ? end + 1 : end; - } -} - -int eth_getenv_enetaddr(const char *name, uchar *enetaddr) -{ - eth_parse_enetaddr(getenv(name), enetaddr); - return is_valid_ethaddr(enetaddr); -} - -int eth_setenv_enetaddr(const char *name, const uchar *enetaddr) -{ - char buf[20]; - - sprintf(buf, "%pM", enetaddr); - - return setenv(name, buf); -} - -int eth_getenv_enetaddr_by_index(const char *base_name, int index, - uchar *enetaddr) -{ - char enetvar[32]; - sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index); - return eth_getenv_enetaddr(enetvar, enetaddr); -} - -static inline int eth_setenv_enetaddr_by_index(const char *base_name, int index, - uchar *enetaddr) -{ - char enetvar[32]; - sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index); - return eth_setenv_enetaddr(enetvar, enetaddr); -} - -static int eth_mac_skip(int index) -{ - char enetvar[15]; - char *skip_state; - - sprintf(enetvar, index ? "eth%dmacskip" : "ethmacskip", index); - skip_state = getenv(enetvar); - return skip_state != NULL; -} - -static void eth_current_changed(void); - -/* - * CPU and board-specific Ethernet initializations. Aliased function - * signals caller to move on - */ -static int __def_eth_init(bd_t *bis) -{ - return -1; -} -int cpu_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init"))); -int board_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init"))); - -static void eth_common_init(void) -{ - bootstage_mark(BOOTSTAGE_ID_NET_ETH_START); -#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB) - miiphy_init(); -#endif - -#ifdef CONFIG_PHYLIB - phy_init(); -#endif - - /* - * If board-specific initialization exists, call it. - * If not, call a CPU-specific one - */ - if (board_eth_init != __def_eth_init) { - if (board_eth_init(gd->bd) < 0) - printf("Board Net Initialization Failed\n"); - } else if (cpu_eth_init != __def_eth_init) { - if (cpu_eth_init(gd->bd) < 0) - printf("CPU Net Initialization Failed\n"); - } else { -#ifndef CONFIG_DM_ETH - printf("Net Initialization Skipped\n"); -#endif - } -} - -#ifdef CONFIG_DM_ETH /** * struct eth_device_priv - private structure for each Ethernet device * @@ -144,7 +44,7 @@ static struct eth_uclass_priv *eth_get_uclass_priv(void) return uc->priv; } -static void eth_set_current_to_next(void) +void eth_set_current_to_next(void) { struct eth_uclass_priv *uc_priv; @@ -177,7 +77,7 @@ struct udevice *eth_get_dev(void) * In case it was not probed, we will attempt to do so. * dev may be NULL to unset the active device. */ -static void eth_set_dev(struct udevice *dev) +void eth_set_dev(struct udevice *dev) { if (dev && !device_active(dev)) { eth_errno = device_probe(dev); @@ -647,493 +547,3 @@ UCLASS_DRIVER(eth) = { .per_device_auto_alloc_size = sizeof(struct eth_device_priv), .flags = DM_UC_FLAG_SEQ_ALIAS, }; -#endif /* #ifdef CONFIG_DM_ETH */ - -#ifndef CONFIG_DM_ETH - -#ifdef CONFIG_API -static struct { - uchar data[PKTSIZE]; - int length; -} eth_rcv_bufs[PKTBUFSRX]; - -static unsigned int eth_rcv_current, eth_rcv_last; -#endif - -static struct eth_device *eth_devices; -struct eth_device *eth_current; - -static void eth_set_current_to_next(void) -{ - eth_current = eth_current->next; -} - -static void eth_set_dev(struct eth_device *dev) -{ - eth_current = dev; -} - -struct eth_device *eth_get_dev_by_name(const char *devname) -{ - struct eth_device *dev, *target_dev; - - BUG_ON(devname == NULL); - - if (!eth_devices) - return NULL; - - dev = eth_devices; - target_dev = NULL; - do { - if (strcmp(devname, dev->name) == 0) { - target_dev = dev; - break; - } - dev = dev->next; - } while (dev != eth_devices); - - return target_dev; -} - -struct eth_device *eth_get_dev_by_index(int index) -{ - struct eth_device *dev, *target_dev; - - if (!eth_devices) - return NULL; - - dev = eth_devices; - target_dev = NULL; - do { - if (dev->index == index) { - target_dev = dev; - break; - } - dev = dev->next; - } while (dev != eth_devices); - - return target_dev; -} - -int eth_get_dev_index(void) -{ - if (!eth_current) - return -1; - - 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) -{ - unsigned char env_enetaddr[6]; - int ret = 0; - - eth_getenv_enetaddr_by_index(base_name, eth_number, env_enetaddr); - - if (!is_zero_ethaddr(env_enetaddr)) { - if (!is_zero_ethaddr(dev->enetaddr) && - memcmp(dev->enetaddr, env_enetaddr, 6)) { - printf("\nWarning: %s MAC addresses don't match:\n", - dev->name); - printf("Address in SROM is %pM\n", - dev->enetaddr); - printf("Address in environment is %pM\n", - env_enetaddr); - } - - memcpy(dev->enetaddr, env_enetaddr, 6); - } else if (is_valid_ethaddr(dev->enetaddr)) { - eth_setenv_enetaddr_by_index(base_name, eth_number, - dev->enetaddr); - } 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)) { - if (!is_valid_ethaddr(dev->enetaddr)) { - printf("\nError: %s address %pM illegal value\n", - dev->name, dev->enetaddr); - return -EINVAL; - } - - ret = dev->write_hwaddr(dev); - if (ret) - printf("\nWarning: %s failed to set MAC address\n", - dev->name); - } - - return ret; -} - -int eth_register(struct eth_device *dev) -{ - struct eth_device *d; - static int index; - - assert(strlen(dev->name) < sizeof(dev->name)); - - if (!eth_devices) { - eth_devices = dev; - eth_current = dev; - eth_current_changed(); - } else { - for (d = eth_devices; d->next != eth_devices; d = d->next) - ; - d->next = dev; - } - - dev->state = ETH_STATE_INIT; - dev->next = eth_devices; - dev->index = index++; - - return 0; -} - -int eth_unregister(struct eth_device *dev) -{ - struct eth_device *cur; - - /* No device */ - if (!eth_devices) - return -ENODEV; - - for (cur = eth_devices; cur->next != eth_devices && cur->next != dev; - cur = cur->next) - ; - - /* Device not found */ - if (cur->next != dev) - return -ENODEV; - - cur->next = dev->next; - - if (eth_devices == dev) - eth_devices = dev->next == eth_devices ? NULL : dev->next; - - if (eth_current == dev) { - eth_current = eth_devices; - eth_current_changed(); - } - - return 0; -} - -int eth_initialize(void) -{ - int num_devices = 0; - - eth_devices = NULL; - eth_current = NULL; - eth_common_init(); - - if (!eth_devices) { - puts("No ethernet found.\n"); - bootstage_error(BOOTSTAGE_ID_NET_ETH_START); - } else { - struct eth_device *dev = eth_devices; - char *ethprime = getenv("ethprime"); - - bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT); - do { - if (dev->index) - puts(", "); - - printf("%s", dev->name); - - if (ethprime && strcmp(dev->name, ethprime) == 0) { - eth_current = dev; - puts(" [PRIME]"); - } - - if (strchr(dev->name, ' ')) - puts("\nWarning: eth device name has a space!" - "\n"); - - eth_write_hwaddr(dev, "eth", dev->index); - - dev = dev->next; - num_devices++; - } while (dev != eth_devices); - - eth_current_changed(); - putc('\n'); - } - - return num_devices; -} - -#ifdef CONFIG_MCAST_TFTP -/* Multicast. - * mcast_addr: multicast ipaddr from which multicast Mac is made - * join: 1=join, 0=leave. - */ -int eth_mcast_join(struct in_addr mcast_ip, int join) -{ - u8 mcast_mac[6]; - if (!eth_current || !eth_current->mcast) - return -1; - mcast_mac[5] = htonl(mcast_ip.s_addr) & 0xff; - mcast_mac[4] = (htonl(mcast_ip.s_addr)>>8) & 0xff; - mcast_mac[3] = (htonl(mcast_ip.s_addr)>>16) & 0x7f; - mcast_mac[2] = 0x5e; - mcast_mac[1] = 0x0; - mcast_mac[0] = 0x1; - return eth_current->mcast(eth_current, mcast_mac, join); -} - -/* the 'way' for ethernet-CRC-32. Spliced in from Linux lib/crc32.c - * and this is the ethernet-crc method needed for TSEC -- and perhaps - * some other adapter -- hash tables - */ -#define CRCPOLY_LE 0xedb88320 -u32 ether_crc(size_t len, unsigned char const *p) -{ - int i; - u32 crc; - crc = ~0; - while (len--) { - crc ^= *p++; - for (i = 0; i < 8; i++) - crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); - } - /* an reverse the bits, cuz of way they arrive -- last-first */ - crc = (crc >> 16) | (crc << 16); - crc = (crc >> 8 & 0x00ff00ff) | (crc << 8 & 0xff00ff00); - crc = (crc >> 4 & 0x0f0f0f0f) | (crc << 4 & 0xf0f0f0f0); - crc = (crc >> 2 & 0x33333333) | (crc << 2 & 0xcccccccc); - crc = (crc >> 1 & 0x55555555) | (crc << 1 & 0xaaaaaaaa); - return crc; -} - -#endif - - -int eth_init(void) -{ - struct eth_device *old_current; - - if (!eth_current) { - puts("No ethernet found.\n"); - return -ENODEV; - } - - old_current = eth_current; - do { - debug("Trying %s\n", eth_current->name); - - if (eth_current->init(eth_current, gd->bd) >= 0) { - eth_current->state = ETH_STATE_ACTIVE; - - return 0; - } - debug("FAIL\n"); - - eth_try_another(0); - } while (old_current != eth_current); - - return -ETIMEDOUT; -} - -void eth_halt(void) -{ - if (!eth_current) - return; - - eth_current->halt(eth_current); - - 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) - return -ENODEV; - - return eth_current->send(eth_current, packet, length); -} - -int eth_rx(void) -{ - if (!eth_current) - return -ENODEV; - - return eth_current->recv(eth_current); -} -#endif /* ifndef CONFIG_DM_ETH */ - -#ifdef CONFIG_API -static void eth_save_packet(void *packet, int length) -{ - char *p = packet; - int i; - - if ((eth_rcv_last+1) % PKTBUFSRX == eth_rcv_current) - return; - - if (PKTSIZE < length) - return; - - for (i = 0; i < length; i++) - eth_rcv_bufs[eth_rcv_last].data[i] = p[i]; - - eth_rcv_bufs[eth_rcv_last].length = length; - eth_rcv_last = (eth_rcv_last + 1) % PKTBUFSRX; -} - -int eth_receive(void *packet, int length) -{ - char *p = packet; - void *pp = push_packet; - int i; - - if (eth_rcv_current == eth_rcv_last) { - push_packet = eth_save_packet; - eth_rx(); - push_packet = pp; - - if (eth_rcv_current == eth_rcv_last) - return -1; - } - - length = min(eth_rcv_bufs[eth_rcv_current].length, length); - - for (i = 0; i < length; i++) - p[i] = eth_rcv_bufs[eth_rcv_current].data[i]; - - eth_rcv_current = (eth_rcv_current + 1) % PKTBUFSRX; - return length; -} -#endif /* CONFIG_API */ - -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) - setenv("ethact", eth_get_name()); - } - /* - * remove the variable completely if there is no active - * interface - */ - else if (act != NULL) - setenv("ethact", NULL); -} - -void eth_try_another(int first_restart) -{ - static void *first_failed; - char *ethrotate; - - /* - * Do not rotate between network interfaces when - * 'ethrotate' variable is set to 'no'. - */ - ethrotate = getenv("ethrotate"); - if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0)) - return; - - if (!eth_get_dev()) - return; - - if (first_restart) - first_failed = eth_get_dev(); - - eth_set_current_to_next(); - - eth_current_changed(); - - if (first_failed == eth_get_dev()) - net_restart_wrap = 1; -} - -void eth_set_current(void) -{ - static char *act; - static int env_changed_id; - int env_id; - - env_id = get_env_id(); - if ((act == NULL) || (env_changed_id != env_id)) { - act = getenv("ethact"); - env_changed_id = env_id; - } - - if (act == NULL) { - char *ethprime = getenv("ethprime"); - void *dev = NULL; - - if (ethprime) - dev = eth_get_dev_by_name(ethprime); - if (dev) - eth_set_dev(dev); - else - eth_set_dev(NULL); - } else { - eth_set_dev(eth_get_dev_by_name(act)); - } - - eth_current_changed(); -} - -const char *eth_get_name(void) -{ - return eth_get_dev() ? eth_get_dev()->name : "unknown"; -} diff --git a/net/eth_common.c b/net/eth_common.c new file mode 100644 index 0000000000..288090155e --- /dev/null +++ b/net/eth_common.c @@ -0,0 +1,166 @@ +/* + * (C) Copyright 2001-2015 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * Joe Hershberger, National Instruments + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <miiphy.h> +#include <net.h> +#include "eth_internal.h" + +void eth_parse_enetaddr(const char *addr, uchar *enetaddr) +{ + char *end; + int i; + + for (i = 0; i < 6; ++i) { + enetaddr[i] = addr ? simple_strtoul(addr, &end, 16) : 0; + if (addr) + addr = (*end) ? end + 1 : end; + } +} + +int eth_getenv_enetaddr(const char *name, uchar *enetaddr) +{ + eth_parse_enetaddr(getenv(name), enetaddr); + return is_valid_ethaddr(enetaddr); +} + +int eth_setenv_enetaddr(const char *name, const uchar *enetaddr) +{ + char buf[20]; + + sprintf(buf, "%pM", enetaddr); + + return setenv(name, buf); +} + +int eth_getenv_enetaddr_by_index(const char *base_name, int index, + uchar *enetaddr) +{ + char enetvar[32]; + sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index); + return eth_getenv_enetaddr(enetvar, enetaddr); +} + +int eth_setenv_enetaddr_by_index(const char *base_name, int index, + uchar *enetaddr) +{ + char enetvar[32]; + sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index); + return eth_setenv_enetaddr(enetvar, enetaddr); +} + +void eth_common_init(void) +{ + bootstage_mark(BOOTSTAGE_ID_NET_ETH_START); +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB) + miiphy_init(); +#endif + +#ifdef CONFIG_PHYLIB + phy_init(); +#endif +} + +int eth_mac_skip(int index) +{ + char enetvar[15]; + char *skip_state; + + sprintf(enetvar, index ? "eth%dmacskip" : "ethmacskip", index); + skip_state = getenv(enetvar); + return skip_state != NULL; +} + +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) + setenv("ethact", eth_get_name()); + } + /* + * remove the variable completely if there is no active + * interface + */ + else if (act != NULL) + setenv("ethact", NULL); +} + +void eth_try_another(int first_restart) +{ + static void *first_failed; + char *ethrotate; + + /* + * Do not rotate between network interfaces when + * 'ethrotate' variable is set to 'no'. + */ + ethrotate = getenv("ethrotate"); + if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0)) + return; + + if (!eth_get_dev()) + return; + + if (first_restart) + first_failed = eth_get_dev(); + + eth_set_current_to_next(); + + eth_current_changed(); + + if (first_failed == eth_get_dev()) + net_restart_wrap = 1; +} + +void eth_set_current(void) +{ + static char *act; + static int env_changed_id; + int env_id; + + env_id = get_env_id(); + if ((act == NULL) || (env_changed_id != env_id)) { + act = getenv("ethact"); + env_changed_id = env_id; + } + + if (act == NULL) { + char *ethprime = getenv("ethprime"); + void *dev = NULL; + + if (ethprime) + dev = eth_get_dev_by_name(ethprime); + if (dev) + eth_set_dev(dev); + else + eth_set_dev(NULL); + } else { + eth_set_dev(eth_get_dev_by_name(act)); + } + + eth_current_changed(); +} + +const char *eth_get_name(void) +{ + return eth_get_dev() ? eth_get_dev()->name : "unknown"; +} diff --git a/net/eth_internal.h b/net/eth_internal.h new file mode 100644 index 0000000000..6e4753cd0d --- /dev/null +++ b/net/eth_internal.h @@ -0,0 +1,40 @@ +/* + * (C) Copyright 2001-2015 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * Joe Hershberger, National Instruments + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ETH_INTERNAL_H +#define __ETH_INTERNAL_H + +/* Do init that is common to driver model and legacy networking */ +void eth_common_init(void); + +/** + * eth_setenv_enetaddr_by_index() - set the MAC address envrionment variable + * + * This sets up an environment variable with the given MAC address (@enetaddr). + * The environment variable to be set is defined by <@base_name><@index>addr. + * If @index is 0 it is omitted. For common Ethernet this means ethaddr, + * eth1addr, etc. + * + * @base_name: Base name for variable, typically "eth" + * @index: Index of interface being updated (>=0) + * @enetaddr: Pointer to MAC address to put into the variable + * @return 0 if OK, other value on error + */ +int eth_setenv_enetaddr_by_index(const char *base_name, int index, + uchar *enetaddr); + +int eth_mac_skip(int index); +void eth_current_changed(void); +#ifdef CONFIG_DM_ETH +void eth_set_dev(struct udevice *dev); +#else +void eth_set_dev(struct eth_device *dev); +#endif +void eth_set_current_to_next(void); + +#endif diff --git a/net/eth_legacy.c b/net/eth_legacy.c new file mode 100644 index 0000000000..bdcd6eaafc --- /dev/null +++ b/net/eth_legacy.c @@ -0,0 +1,439 @@ +/* + * (C) Copyright 2001-2015 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * Joe Hershberger, National Instruments + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <environment.h> +#include <net.h> +#include <phy.h> +#include <asm/errno.h> +#include "eth_internal.h" + +DECLARE_GLOBAL_DATA_PTR; + +/* + * CPU and board-specific Ethernet initializations. Aliased function + * signals caller to move on + */ +static int __def_eth_init(bd_t *bis) +{ + return -1; +} +int cpu_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init"))); +int board_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init"))); + +#ifdef CONFIG_API +static struct { + uchar data[PKTSIZE]; + int length; +} eth_rcv_bufs[PKTBUFSRX]; + +static unsigned int eth_rcv_current, eth_rcv_last; +#endif + +static struct eth_device *eth_devices; +struct eth_device *eth_current; + +void eth_set_current_to_next(void) +{ + eth_current = eth_current->next; +} + +void eth_set_dev(struct eth_device *dev) +{ + eth_current = dev; +} + +struct eth_device *eth_get_dev_by_name(const char *devname) +{ + struct eth_device *dev, *target_dev; + + BUG_ON(devname == NULL); + + if (!eth_devices) + return NULL; + + dev = eth_devices; + target_dev = NULL; + do { + if (strcmp(devname, dev->name) == 0) { + target_dev = dev; + break; + } + dev = dev->next; + } while (dev != eth_devices); + + return target_dev; +} + +struct eth_device *eth_get_dev_by_index(int index) +{ + struct eth_device *dev, *target_dev; + + if (!eth_devices) + return NULL; + + dev = eth_devices; + target_dev = NULL; + do { + if (dev->index == index) { + target_dev = dev; + break; + } + dev = dev->next; + } while (dev != eth_devices); + + return target_dev; +} + +int eth_get_dev_index(void) +{ + if (!eth_current) + return -1; + + 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) +{ + unsigned char env_enetaddr[6]; + int ret = 0; + + eth_getenv_enetaddr_by_index(base_name, eth_number, env_enetaddr); + + if (!is_zero_ethaddr(env_enetaddr)) { + if (!is_zero_ethaddr(dev->enetaddr) && + memcmp(dev->enetaddr, env_enetaddr, 6)) { + printf("\nWarning: %s MAC addresses don't match:\n", + dev->name); + printf("Address in SROM is %pM\n", + dev->enetaddr); + printf("Address in environment is %pM\n", + env_enetaddr); + } + + memcpy(dev->enetaddr, env_enetaddr, 6); + } else if (is_valid_ethaddr(dev->enetaddr)) { + eth_setenv_enetaddr_by_index(base_name, eth_number, + dev->enetaddr); + } 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)) { + if (!is_valid_ethaddr(dev->enetaddr)) { + printf("\nError: %s address %pM illegal value\n", + dev->name, dev->enetaddr); + return -EINVAL; + } + + ret = dev->write_hwaddr(dev); + if (ret) + printf("\nWarning: %s failed to set MAC address\n", + dev->name); + } + + return ret; +} + +int eth_register(struct eth_device *dev) +{ + struct eth_device *d; + static int index; + + assert(strlen(dev->name) < sizeof(dev->name)); + + if (!eth_devices) { + eth_devices = dev; + eth_current = dev; + eth_current_changed(); + } else { + for (d = eth_devices; d->next != eth_devices; d = d->next) + ; + d->next = dev; + } + + dev->state = ETH_STATE_INIT; + dev->next = eth_devices; + dev->index = index++; + + return 0; +} + +int eth_unregister(struct eth_device *dev) +{ + struct eth_device *cur; + + /* No device */ + if (!eth_devices) + return -ENODEV; + + for (cur = eth_devices; cur->next != eth_devices && cur->next != dev; + cur = cur->next) + ; + + /* Device not found */ + if (cur->next != dev) + return -ENODEV; + + cur->next = dev->next; + + if (eth_devices == dev) + eth_devices = dev->next == eth_devices ? NULL : dev->next; + + if (eth_current == dev) { + eth_current = eth_devices; + eth_current_changed(); + } + + return 0; +} + +int eth_initialize(void) +{ + int num_devices = 0; + + eth_devices = NULL; + eth_current = NULL; + eth_common_init(); + /* + * If board-specific initialization exists, call it. + * If not, call a CPU-specific one + */ + if (board_eth_init != __def_eth_init) { + if (board_eth_init(gd->bd) < 0) + printf("Board Net Initialization Failed\n"); + } else if (cpu_eth_init != __def_eth_init) { + if (cpu_eth_init(gd->bd) < 0) + printf("CPU Net Initialization Failed\n"); + } else { + printf("Net Initialization Skipped\n"); + } + + if (!eth_devices) { + puts("No ethernet found.\n"); + bootstage_error(BOOTSTAGE_ID_NET_ETH_START); + } else { + struct eth_device *dev = eth_devices; + char *ethprime = getenv("ethprime"); + + bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT); + do { + if (dev->index) + puts(", "); + + printf("%s", dev->name); + + if (ethprime && strcmp(dev->name, ethprime) == 0) { + eth_current = dev; + puts(" [PRIME]"); + } + + if (strchr(dev->name, ' ')) + puts("\nWarning: eth device name has a space!" + "\n"); + + eth_write_hwaddr(dev, "eth", dev->index); + + dev = dev->next; + num_devices++; + } while (dev != eth_devices); + + eth_current_changed(); + putc('\n'); + } + + return num_devices; +} + +#ifdef CONFIG_MCAST_TFTP +/* Multicast. + * mcast_addr: multicast ipaddr from which multicast Mac is made + * join: 1=join, 0=leave. + */ +int eth_mcast_join(struct in_addr mcast_ip, int join) +{ + u8 mcast_mac[6]; + if (!eth_current || !eth_current->mcast) + return -1; + mcast_mac[5] = htonl(mcast_ip.s_addr) & 0xff; + mcast_mac[4] = (htonl(mcast_ip.s_addr)>>8) & 0xff; + mcast_mac[3] = (htonl(mcast_ip.s_addr)>>16) & 0x7f; + mcast_mac[2] = 0x5e; + mcast_mac[1] = 0x0; + mcast_mac[0] = 0x1; + return eth_current->mcast(eth_current, mcast_mac, join); +} + +/* the 'way' for ethernet-CRC-32. Spliced in from Linux lib/crc32.c + * and this is the ethernet-crc method needed for TSEC -- and perhaps + * some other adapter -- hash tables + */ +#define CRCPOLY_LE 0xedb88320 +u32 ether_crc(size_t len, unsigned char const *p) +{ + int i; + u32 crc; + crc = ~0; + while (len--) { + crc ^= *p++; + for (i = 0; i < 8; i++) + crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); + } + /* an reverse the bits, cuz of way they arrive -- last-first */ + crc = (crc >> 16) | (crc << 16); + crc = (crc >> 8 & 0x00ff00ff) | (crc << 8 & 0xff00ff00); + crc = (crc >> 4 & 0x0f0f0f0f) | (crc << 4 & 0xf0f0f0f0); + crc = (crc >> 2 & 0x33333333) | (crc << 2 & 0xcccccccc); + crc = (crc >> 1 & 0x55555555) | (crc << 1 & 0xaaaaaaaa); + return crc; +} + +#endif + + +int eth_init(void) +{ + struct eth_device *old_current; + + if (!eth_current) { + puts("No ethernet found.\n"); + return -ENODEV; + } + + old_current = eth_current; + do { + debug("Trying %s\n", eth_current->name); + + if (eth_current->init(eth_current, gd->bd) >= 0) { + eth_current->state = ETH_STATE_ACTIVE; + + return 0; + } + debug("FAIL\n"); + + eth_try_another(0); + } while (old_current != eth_current); + + return -ETIMEDOUT; +} + +void eth_halt(void) +{ + if (!eth_current) + return; + + eth_current->halt(eth_current); + + 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) + return -ENODEV; + + return eth_current->send(eth_current, packet, length); +} + +int eth_rx(void) +{ + if (!eth_current) + return -ENODEV; + + return eth_current->recv(eth_current); +} + +#ifdef CONFIG_API +static void eth_save_packet(void *packet, int length) +{ + char *p = packet; + int i; + + if ((eth_rcv_last+1) % PKTBUFSRX == eth_rcv_current) + return; + + if (PKTSIZE < length) + return; + + for (i = 0; i < length; i++) + eth_rcv_bufs[eth_rcv_last].data[i] = p[i]; + + eth_rcv_bufs[eth_rcv_last].length = length; + eth_rcv_last = (eth_rcv_last + 1) % PKTBUFSRX; +} + +int eth_receive(void *packet, int length) +{ + char *p = packet; + void *pp = push_packet; + int i; + + if (eth_rcv_current == eth_rcv_last) { + push_packet = eth_save_packet; + eth_rx(); + push_packet = pp; + + if (eth_rcv_current == eth_rcv_last) + return -1; + } + + length = min(eth_rcv_bufs[eth_rcv_current].length, length); + + for (i = 0; i < length; i++) + p[i] = eth_rcv_bufs[eth_rcv_current].data[i]; + + eth_rcv_current = (eth_rcv_current + 1) % PKTBUFSRX; + return length; +} +#endif /* CONFIG_API */ |