2 * (C) Copyright 2001-2010
3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5 * SPDX-License-Identifier: GPL-2.0+
13 #include <asm/errno.h>
15 void eth_parse_enetaddr(const char *addr
, uchar
*enetaddr
)
20 for (i
= 0; i
< 6; ++i
) {
21 enetaddr
[i
] = addr
? simple_strtoul(addr
, &end
, 16) : 0;
23 addr
= (*end
) ? end
+ 1 : end
;
27 int eth_getenv_enetaddr(char *name
, uchar
*enetaddr
)
29 eth_parse_enetaddr(getenv(name
), enetaddr
);
30 return is_valid_ether_addr(enetaddr
);
33 int eth_setenv_enetaddr(char *name
, const uchar
*enetaddr
)
37 sprintf(buf
, "%pM", enetaddr
);
39 return setenv(name
, buf
);
42 int eth_getenv_enetaddr_by_index(const char *base_name
, int index
,
46 sprintf(enetvar
, index
? "%s%daddr" : "%saddr", base_name
, index
);
47 return eth_getenv_enetaddr(enetvar
, enetaddr
);
50 static inline int eth_setenv_enetaddr_by_index(const char *base_name
, int index
,
54 sprintf(enetvar
, index
? "%s%daddr" : "%saddr", base_name
, index
);
55 return eth_setenv_enetaddr(enetvar
, enetaddr
);
59 static int eth_mac_skip(int index
)
63 sprintf(enetvar
, index
? "eth%dmacskip" : "ethmacskip", index
);
64 return ((skip_state
= getenv(enetvar
)) != NULL
);
68 * CPU and board-specific Ethernet initializations. Aliased function
69 * signals caller to move on
71 static int __def_eth_init(bd_t
*bis
)
75 int cpu_eth_init(bd_t
*bis
) __attribute__((weak
, alias("__def_eth_init")));
76 int board_eth_init(bd_t
*bis
) __attribute__((weak
, alias("__def_eth_init")));
82 } eth_rcv_bufs
[PKTBUFSRX
];
84 static unsigned int eth_rcv_current
, eth_rcv_last
;
87 static struct eth_device
*eth_devices
;
88 struct eth_device
*eth_current
;
90 struct eth_device
*eth_get_dev_by_name(const char *devname
)
92 struct eth_device
*dev
, *target_dev
;
94 BUG_ON(devname
== NULL
);
102 if (strcmp(devname
, dev
->name
) == 0) {
107 } while (dev
!= eth_devices
);
112 struct eth_device
*eth_get_dev_by_index(int index
)
114 struct eth_device
*dev
, *target_dev
;
122 if (dev
->index
== index
) {
127 } while (dev
!= eth_devices
);
132 int eth_get_dev_index(void)
137 return eth_current
->index
;
140 static void eth_current_changed(void)
142 char *act
= getenv("ethact");
143 /* update current ethernet name */
145 if (act
== NULL
|| strcmp(act
, eth_current
->name
) != 0)
146 setenv("ethact", eth_current
->name
);
149 * remove the variable completely if there is no active
152 else if (act
!= NULL
)
153 setenv("ethact", NULL
);
156 static int eth_address_set(unsigned char *addr
)
158 return memcmp(addr
, "\0\0\0\0\0\0", 6);
161 int eth_write_hwaddr(struct eth_device
*dev
, const char *base_name
,
164 unsigned char env_enetaddr
[6];
167 eth_getenv_enetaddr_by_index(base_name
, eth_number
, env_enetaddr
);
169 if (eth_address_set(env_enetaddr
)) {
170 if (eth_address_set(dev
->enetaddr
) &&
171 memcmp(dev
->enetaddr
, env_enetaddr
, 6)) {
172 printf("\nWarning: %s MAC addresses don't match:\n",
174 printf("Address in SROM is %pM\n",
176 printf("Address in environment is %pM\n",
180 memcpy(dev
->enetaddr
, env_enetaddr
, 6);
181 } else if (is_valid_ether_addr(dev
->enetaddr
)) {
182 eth_setenv_enetaddr_by_index(base_name
, eth_number
,
184 printf("\nWarning: %s using MAC address from net device\n",
186 } else if (!(eth_address_set(dev
->enetaddr
))) {
187 printf("\nError: %s address not set.\n",
192 if (dev
->write_hwaddr
&& !eth_mac_skip(eth_number
)) {
193 if (!is_valid_ether_addr(dev
->enetaddr
)) {
194 printf("\nError: %s address %pM illegal value\n",
195 dev
->name
, dev
->enetaddr
);
199 ret
= dev
->write_hwaddr(dev
);
201 printf("\nWarning: %s failed to set MAC address\n", dev
->name
);
207 int eth_register(struct eth_device
*dev
)
209 struct eth_device
*d
;
212 assert(strlen(dev
->name
) < sizeof(dev
->name
));
215 eth_current
= eth_devices
= dev
;
216 eth_current_changed();
218 for (d
= eth_devices
; d
->next
!= eth_devices
; d
= d
->next
)
223 dev
->state
= ETH_STATE_INIT
;
224 dev
->next
= eth_devices
;
225 dev
->index
= index
++;
230 int eth_unregister(struct eth_device
*dev
)
232 struct eth_device
*cur
;
238 for (cur
= eth_devices
; cur
->next
!= eth_devices
&& cur
->next
!= dev
;
242 /* Device not found */
243 if (cur
->next
!= dev
)
246 cur
->next
= dev
->next
;
248 if (eth_devices
== dev
)
249 eth_devices
= dev
->next
== eth_devices
? NULL
: dev
->next
;
251 if (eth_current
== dev
) {
252 eth_current
= eth_devices
;
253 eth_current_changed();
259 static void eth_env_init(bd_t
*bis
)
263 if ((s
= getenv("bootfile")) != NULL
)
264 copy_filename(BootFile
, s
, sizeof(BootFile
));
267 int eth_initialize(bd_t
*bis
)
273 bootstage_mark(BOOTSTAGE_ID_NET_ETH_START
);
274 #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB)
285 * If board-specific initialization exists, call it.
286 * If not, call a CPU-specific one
288 if (board_eth_init
!= __def_eth_init
) {
289 if (board_eth_init(bis
) < 0)
290 printf("Board Net Initialization Failed\n");
291 } else if (cpu_eth_init
!= __def_eth_init
) {
292 if (cpu_eth_init(bis
) < 0)
293 printf("CPU Net Initialization Failed\n");
295 printf("Net Initialization Skipped\n");
298 puts("No ethernet found.\n");
299 bootstage_error(BOOTSTAGE_ID_NET_ETH_START
);
301 struct eth_device
*dev
= eth_devices
;
302 char *ethprime
= getenv("ethprime");
304 bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT
);
309 printf("%s", dev
->name
);
311 if (ethprime
&& strcmp(dev
->name
, ethprime
) == 0) {
316 if (strchr(dev
->name
, ' '))
317 puts("\nWarning: eth device name has a space!"
320 eth_write_hwaddr(dev
, "eth", dev
->index
);
324 } while (dev
!= eth_devices
);
326 eth_current_changed();
333 #ifdef CONFIG_MCAST_TFTP
335 * mcast_addr: multicast ipaddr from which multicast Mac is made
336 * join: 1=join, 0=leave.
338 int eth_mcast_join(IPaddr_t mcast_ip
, u8 join
)
341 if (!eth_current
|| !eth_current
->mcast
)
343 mcast_mac
[5] = htonl(mcast_ip
) & 0xff;
344 mcast_mac
[4] = (htonl(mcast_ip
)>>8) & 0xff;
345 mcast_mac
[3] = (htonl(mcast_ip
)>>16) & 0x7f;
349 return eth_current
->mcast(eth_current
, mcast_mac
, join
);
352 /* the 'way' for ethernet-CRC-32. Spliced in from Linux lib/crc32.c
353 * and this is the ethernet-crc method needed for TSEC -- and perhaps
354 * some other adapter -- hash tables
356 #define CRCPOLY_LE 0xedb88320
357 u32
ether_crc(size_t len
, unsigned char const *p
)
364 for (i
= 0; i
< 8; i
++)
365 crc
= (crc
>> 1) ^ ((crc
& 1) ? CRCPOLY_LE
: 0);
367 /* an reverse the bits, cuz of way they arrive -- last-first */
368 crc
= (crc
>> 16) | (crc
<< 16);
369 crc
= (crc
>> 8 & 0x00ff00ff) | (crc
<< 8 & 0xff00ff00);
370 crc
= (crc
>> 4 & 0x0f0f0f0f) | (crc
<< 4 & 0xf0f0f0f0);
371 crc
= (crc
>> 2 & 0x33333333) | (crc
<< 2 & 0xcccccccc);
372 crc
= (crc
>> 1 & 0x55555555) | (crc
<< 1 & 0xaaaaaaaa);
379 int eth_init(bd_t
*bis
)
381 struct eth_device
*old_current
, *dev
;
384 puts("No ethernet found.\n");
388 /* Sync environment with network devices */
391 uchar env_enetaddr
[6];
393 if (eth_getenv_enetaddr_by_index("eth", dev
->index
,
395 memcpy(dev
->enetaddr
, env_enetaddr
, 6);
398 } while (dev
!= eth_devices
);
400 old_current
= eth_current
;
402 debug("Trying %s\n", eth_current
->name
);
404 if (eth_current
->init(eth_current
, bis
) >= 0) {
405 eth_current
->state
= ETH_STATE_ACTIVE
;
412 } while (old_current
!= eth_current
);
422 eth_current
->halt(eth_current
);
424 eth_current
->state
= ETH_STATE_PASSIVE
;
427 int eth_send(void *packet
, int length
)
432 return eth_current
->send(eth_current
, packet
, length
);
440 return eth_current
->recv(eth_current
);
444 static void eth_save_packet(void *packet
, int length
)
449 if ((eth_rcv_last
+1) % PKTBUFSRX
== eth_rcv_current
)
452 if (PKTSIZE
< length
)
455 for (i
= 0; i
< length
; i
++)
456 eth_rcv_bufs
[eth_rcv_last
].data
[i
] = p
[i
];
458 eth_rcv_bufs
[eth_rcv_last
].length
= length
;
459 eth_rcv_last
= (eth_rcv_last
+ 1) % PKTBUFSRX
;
462 int eth_receive(void *packet
, int length
)
465 void *pp
= push_packet
;
468 if (eth_rcv_current
== eth_rcv_last
) {
469 push_packet
= eth_save_packet
;
473 if (eth_rcv_current
== eth_rcv_last
)
477 length
= min(eth_rcv_bufs
[eth_rcv_current
].length
, length
);
479 for (i
= 0; i
< length
; i
++)
480 p
[i
] = eth_rcv_bufs
[eth_rcv_current
].data
[i
];
482 eth_rcv_current
= (eth_rcv_current
+ 1) % PKTBUFSRX
;
485 #endif /* CONFIG_API */
487 void eth_try_another(int first_restart
)
489 static struct eth_device
*first_failed
;
493 * Do not rotate between network interfaces when
494 * 'ethrotate' variable is set to 'no'.
496 ethrotate
= getenv("ethrotate");
497 if ((ethrotate
!= NULL
) && (strcmp(ethrotate
, "no") == 0))
504 first_failed
= eth_current
;
506 eth_current
= eth_current
->next
;
508 eth_current_changed();
510 if (first_failed
== eth_current
)
514 void eth_set_current(void)
517 static int env_changed_id
;
518 struct eth_device
*old_current
;
521 if (!eth_current
) /* XXX no current */
524 env_id
= get_env_id();
525 if ((act
== NULL
) || (env_changed_id
!= env_id
)) {
526 act
= getenv("ethact");
527 env_changed_id
= env_id
;
530 old_current
= eth_current
;
532 if (strcmp(eth_current
->name
, act
) == 0)
534 eth_current
= eth_current
->next
;
535 } while (old_current
!= eth_current
);
538 eth_current_changed();
541 char *eth_get_name(void)
543 return eth_current
? eth_current
->name
: "unknown";