eth.c 11.7 KB
Newer Older
W
wdenk 已提交
1
/*
2
 * (C) Copyright 2001-2010
W
wdenk 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 *
 * 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 as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <common.h>
#include <command.h>
#include <net.h>
27
#include <miiphy.h>
A
Andy Fleming 已提交
28
#include <phy.h>
W
wdenk 已提交
29

30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
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(char *name, uchar *enetaddr)
{
	eth_parse_enetaddr(getenv(name), enetaddr);
	return is_valid_ether_addr(enetaddr);
}

int eth_setenv_enetaddr(char *name, const uchar *enetaddr)
{
	char buf[20];

	sprintf(buf, "%pM", enetaddr);

	return setenv(name, buf);
}
56

57 58
int eth_getenv_enetaddr_by_index(const char *base_name, int index,
				 uchar *enetaddr)
59 60
{
	char enetvar[32];
61
	sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index);
62 63
	return eth_getenv_enetaddr(enetvar, enetaddr);
}
64

65
static inline int eth_setenv_enetaddr_by_index(const char *base_name, int index,
66 67 68 69 70 71 72 73
				 uchar *enetaddr)
{
	char enetvar[32];
	sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index);
	return eth_setenv_enetaddr(enetvar, enetaddr);
}


74 75 76 77 78 79 80 81
static int eth_mac_skip(int index)
{
	char enetvar[15];
	char *skip_state;
	sprintf(enetvar, index ? "eth%dmacskip" : "ethmacskip", index);
	return ((skip_state = getenv(enetvar)) != NULL);
}

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
#ifdef CONFIG_RANDOM_MACADDR
void eth_random_enetaddr(uchar *enetaddr)
{
	uint32_t rval;

	srand(get_timer(0));

	rval = rand();
	enetaddr[0] = rval & 0xff;
	enetaddr[1] = (rval >> 8) & 0xff;
	enetaddr[2] = (rval >> 16) & 0xff;

	rval = rand();
	enetaddr[3] = rval & 0xff;
	enetaddr[4] = (rval >> 8) & 0xff;
	enetaddr[5] = (rval >> 16) & 0xff;

	/* make sure it's local and unicast */
	enetaddr[0] = (enetaddr[0] | 0x02) & ~0x01;
}
#endif

104 105 106 107 108 109 110 111
/*
 * CPU and board-specific Ethernet initializations.  Aliased function
 * signals caller to move on
 */
static int __def_eth_init(bd_t *bis)
{
	return -1;
}
112 113
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")));
114

115 116 117 118 119 120
#ifdef CONFIG_API
static struct {
	uchar data[PKTSIZE];
	int length;
} eth_rcv_bufs[PKTBUFSRX];

121
static unsigned int eth_rcv_current, eth_rcv_last;
122 123
#endif

124 125
static struct eth_device *eth_devices;
struct eth_device *eth_current;
W
wdenk 已提交
126

127
struct eth_device *eth_get_dev_by_name(const char *devname)
M
Marian Balakowicz 已提交
128 129 130
{
	struct eth_device *dev, *target_dev;

131 132
	BUG_ON(devname == NULL);

M
Marian Balakowicz 已提交
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
	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;
}

A
Andy Fleming 已提交
149 150 151 152 153 154 155 156 157 158
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 {
M
Michael Walle 已提交
159
		if (dev->index == index) {
A
Andy Fleming 已提交
160 161 162 163 164 165 166 167 168
			target_dev = dev;
			break;
		}
		dev = dev->next;
	} while (dev != eth_devices);

	return target_dev;
}

169
int eth_get_dev_index(void)
W
wdenk 已提交
170
{
171
	if (!eth_current)
M
Michael Walle 已提交
172
		return -1;
W
wdenk 已提交
173

M
Michael Walle 已提交
174
	return eth_current->index;
W
wdenk 已提交
175 176
}

177
static void eth_current_changed(void)
W
wdenk 已提交
178
{
M
Mike Frysinger 已提交
179 180 181 182 183
	char *act = getenv("ethact");
	/* update current ethernet name */
	if (eth_current) {
		if (act == NULL || strcmp(act, eth_current->name) != 0)
			setenv("ethact", eth_current->name);
184
	}
M
Mike Frysinger 已提交
185 186 187 188 189 190
	/*
	 * remove the variable completely if there is no active
	 * interface
	 */
	else if (act != NULL)
		setenv("ethact", NULL);
191 192
}

193 194 195 196 197 198
int eth_write_hwaddr(struct eth_device *dev, const char *base_name,
		   int eth_number)
{
	unsigned char env_enetaddr[6];
	int ret = 0;

199
	eth_getenv_enetaddr_by_index(base_name, eth_number, env_enetaddr);
200 201 202

	if (memcmp(env_enetaddr, "\0\0\0\0\0\0", 6)) {
		if (memcmp(dev->enetaddr, "\0\0\0\0\0\0", 6) &&
203
				memcmp(dev->enetaddr, env_enetaddr, 6)) {
204 205 206 207 208 209 210 211 212
			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);
213 214 215 216 217
	} else if (is_valid_ether_addr(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);
218 219 220
	}

	if (dev->write_hwaddr &&
221 222
			!eth_mac_skip(eth_number) &&
			is_valid_ether_addr(dev->enetaddr))
223 224 225 226 227
		ret = dev->write_hwaddr(dev);

	return ret;
}

228 229 230
int eth_register(struct eth_device *dev)
{
	struct eth_device *d;
231
	static int index;
232

233
	assert(strlen(dev->name) < sizeof(dev->name));
234

235 236 237
	if (!eth_devices) {
		eth_current = eth_devices = dev;
		eth_current_changed();
W
wdenk 已提交
238
	} else {
239
		for (d = eth_devices; d->next != eth_devices; d = d->next)
240
			;
W
wdenk 已提交
241 242 243 244 245
		d->next = dev;
	}

	dev->state = ETH_STATE_INIT;
	dev->next  = eth_devices;
M
Michael Walle 已提交
246
	dev->index = index++;
W
wdenk 已提交
247 248 249 250

	return 0;
}

251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
int eth_unregister(struct eth_device *dev)
{
	struct eth_device *cur;

	/* No device */
	if (!eth_devices)
		return -1;

	for (cur = eth_devices; cur->next != eth_devices && cur->next != dev;
	     cur = cur->next)
		;

	/* Device not found */
	if (cur->next != dev)
		return -1;

	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;
}

280 281 282 283 284 285 286 287
static void eth_env_init(bd_t *bis)
{
	const char *s;

	if ((s = getenv("bootfile")) != NULL)
		copy_filename(BootFile, s, sizeof(BootFile));
}

W
wdenk 已提交
288 289
int eth_initialize(bd_t *bis)
{
M
Michael Walle 已提交
290
	int num_devices = 0;
W
wdenk 已提交
291 292 293
	eth_devices = NULL;
	eth_current = NULL;

294
	bootstage_mark(BOOTSTAGE_ID_NET_ETH_START);
295
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
296 297
	miiphy_init();
#endif
A
Andy Fleming 已提交
298 299 300 301 302

#ifdef CONFIG_PHYLIB
	phy_init();
#endif

303 304
	eth_env_init(bis);

B
Ben Warren 已提交
305 306 307 308 309 310 311 312 313 314 315 316
	/*
	 * 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(bis) < 0)
			printf("Board Net Initialization Failed\n");
	} else if (cpu_eth_init != __def_eth_init) {
		if (cpu_eth_init(bis) < 0)
			printf("CPU Net Initialization Failed\n");
	} else
		printf("Net Initialization Skipped\n");
317

W
wdenk 已提交
318
	if (!eth_devices) {
319
		puts("No ethernet found.\n");
320
		bootstage_error(BOOTSTAGE_ID_NET_ETH_START);
W
wdenk 已提交
321 322
	} else {
		struct eth_device *dev = eth_devices;
323
		char *ethprime = getenv("ethprime");
W
wdenk 已提交
324

325
		bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT);
W
wdenk 已提交
326
		do {
M
Michael Walle 已提交
327
			if (dev->index)
328
				puts(", ");
W
wdenk 已提交
329 330 331

			printf("%s", dev->name);

332
			if (ethprime && strcmp(dev->name, ethprime) == 0) {
W
wdenk 已提交
333
				eth_current = dev;
334
				puts(" [PRIME]");
W
wdenk 已提交
335 336
			}

337
			if (strchr(dev->name, ' '))
338 339
				puts("\nWarning: eth device name has a space!"
					"\n");
340

M
Michael Walle 已提交
341
			if (eth_write_hwaddr(dev, "eth", dev->index))
342
				puts("\nWarning: failed to set MAC address\n");
W
wdenk 已提交
343 344

			dev = dev->next;
M
Michael Walle 已提交
345
			num_devices++;
346
		} while (dev != eth_devices);
W
wdenk 已提交
347

348
		eth_current_changed();
349
		putc('\n');
W
wdenk 已提交
350 351
	}

M
Michael Walle 已提交
352
	return num_devices;
W
wdenk 已提交
353 354
}

D
David Updegraff 已提交
355 356 357
#ifdef CONFIG_MCAST_TFTP
/* Multicast.
 * mcast_addr: multicast ipaddr from which multicast Mac is made
358
 * join: 1=join, 0=leave.
D
David Updegraff 已提交
359
 */
360
int eth_mcast_join(IPaddr_t mcast_ip, u8 join)
D
David Updegraff 已提交
361
{
362
	u8 mcast_mac[6];
363
	if (!eth_current || !eth_current->mcast)
D
David Updegraff 已提交
364 365 366 367 368 369 370 371 372 373
		return -1;
	mcast_mac[5] = htonl(mcast_ip) & 0xff;
	mcast_mac[4] = (htonl(mcast_ip)>>8) & 0xff;
	mcast_mac[3] = (htonl(mcast_ip)>>16) & 0x7f;
	mcast_mac[2] = 0x5e;
	mcast_mac[1] = 0x0;
	mcast_mac[0] = 0x1;
	return eth_current->mcast(eth_current, mcast_mac, join);
}

374 375
/* 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
D
David Updegraff 已提交
376 377 378
 * some other adapter -- hash tables
 */
#define CRCPOLY_LE 0xedb88320
379
u32 ether_crc(size_t len, unsigned char const *p)
D
David Updegraff 已提交
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
{
	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

W
wdenk 已提交
400 401 402

int eth_init(bd_t *bis)
{
403
	struct eth_device *old_current, *dev;
W
wdenk 已提交
404

405
	if (!eth_current) {
406
		puts("No ethernet found.\n");
407
		return -1;
408
	}
W
wdenk 已提交
409

410 411 412 413 414
	/* Sync environment with network devices */
	dev = eth_devices;
	do {
		uchar env_enetaddr[6];

M
Michael Walle 已提交
415
		if (eth_getenv_enetaddr_by_index("eth", dev->index,
416
						 env_enetaddr))
417 418 419 420 421
			memcpy(dev->enetaddr, env_enetaddr, 6);

		dev = dev->next;
	} while (dev != eth_devices);

W
wdenk 已提交
422 423
	old_current = eth_current;
	do {
R
Robin Getz 已提交
424
		debug("Trying %s\n", eth_current->name);
W
wdenk 已提交
425

426
		if (eth_current->init(eth_current, bis) >= 0) {
W
wdenk 已提交
427 428
			eth_current->state = ETH_STATE_ACTIVE;

429
			return 0;
W
wdenk 已提交
430
		}
R
Robin Getz 已提交
431
		debug("FAIL\n");
W
wdenk 已提交
432 433 434 435

		eth_try_another(0);
	} while (old_current != eth_current);

436
	return -1;
W
wdenk 已提交
437 438 439 440 441 442 443 444 445 446 447 448
}

void eth_halt(void)
{
	if (!eth_current)
		return;

	eth_current->halt(eth_current);

	eth_current->state = ETH_STATE_PASSIVE;
}

449
int eth_send(void *packet, int length)
W
wdenk 已提交
450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
{
	if (!eth_current)
		return -1;

	return eth_current->send(eth_current, packet, length);
}

int eth_rx(void)
{
	if (!eth_current)
		return -1;

	return eth_current->recv(eth_current);
}

465
#ifdef CONFIG_API
466
static void eth_save_packet(void *packet, int length)
467
{
468
	char *p = packet;
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
	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;
}

484
int eth_receive(void *packet, int length)
485
{
486
	char *p = packet;
487 488 489 490 491 492 493 494 495 496 497 498
	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;
	}

499
	length = min(eth_rcv_bufs[eth_rcv_current].length, length);
500 501 502 503 504 505 506 507 508

	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 */

W
wdenk 已提交
509 510
void eth_try_another(int first_restart)
{
511
	static struct eth_device *first_failed;
R
Remy Bohmer 已提交
512
	char *ethrotate;
513 514 515 516 517

	/*
	 * Do not rotate between network interfaces when
	 * 'ethrotate' variable is set to 'no'.
	 */
518 519
	ethrotate = getenv("ethrotate");
	if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0))
520
		return;
W
wdenk 已提交
521 522 523 524

	if (!eth_current)
		return;

525
	if (first_restart)
W
wdenk 已提交
526 527 528 529
		first_failed = eth_current;

	eth_current = eth_current->next;

530
	eth_current_changed();
531

532
	if (first_failed == eth_current)
W
wdenk 已提交
533 534 535
		NetRestartWrap = 1;
}

536 537
void eth_set_current(void)
{
538 539 540
	static char *act;
	static int  env_changed_id;
	struct eth_device *old_current;
H
Heiko Schocher 已提交
541
	int	env_id;
542 543 544 545

	if (!eth_current)	/* XXX no current */
		return;

H
Heiko Schocher 已提交
546 547 548 549 550
	env_id = get_env_id();
	if ((act == NULL) || (env_changed_id != env_id)) {
		act = getenv("ethact");
		env_changed_id = env_id;
	}
551 552 553 554 555 556 557 558 559
	if (act != NULL) {
		old_current = eth_current;
		do {
			if (strcmp(eth_current->name, act) == 0)
				return;
			eth_current = eth_current->next;
		} while (old_current != eth_current);
	}

560
	eth_current_changed();
561 562
}

563
char *eth_get_name(void)
564
{
565
	return eth_current ? eth_current->name : "unknown";
566
}