eth.c 11.4 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 66 67 68 69 70 71 72
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);
}

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
#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

95 96 97 98 99 100 101 102
/*
 * CPU and board-specific Ethernet initializations.  Aliased function
 * signals caller to move on
 */
static int __def_eth_init(bd_t *bis)
{
	return -1;
}
103 104
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")));
105

106 107 108 109 110 111
#ifdef CONFIG_API
static struct {
	uchar data[PKTSIZE];
	int length;
} eth_rcv_bufs[PKTBUFSRX];

112
static unsigned int eth_rcv_current, eth_rcv_last;
113 114
#endif

W
wdenk 已提交
115 116 117 118 119 120 121
static struct eth_device *eth_devices, *eth_current;

struct eth_device *eth_get_dev(void)
{
	return eth_current;
}

122
struct eth_device *eth_get_dev_by_name(const char *devname)
M
Marian Balakowicz 已提交
123 124 125
{
	struct eth_device *dev, *target_dev;

126 127
	BUG_ON(devname == NULL);

M
Marian Balakowicz 已提交
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
	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 已提交
144 145 146 147 148 149 150 151 152 153
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 已提交
154
		if (dev->index == index) {
A
Andy Fleming 已提交
155 156 157 158 159 160 161 162 163
			target_dev = dev;
			break;
		}
		dev = dev->next;
	} while (dev != eth_devices);

	return target_dev;
}

164
int eth_get_dev_index(void)
W
wdenk 已提交
165
{
166
	if (!eth_current)
M
Michael Walle 已提交
167
		return -1;
W
wdenk 已提交
168

M
Michael Walle 已提交
169
	return eth_current->index;
W
wdenk 已提交
170 171
}

172
static void eth_current_changed(void)
W
wdenk 已提交
173
{
M
Mike Frysinger 已提交
174 175 176 177 178
	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);
179
	}
M
Mike Frysinger 已提交
180 181 182 183 184 185
	/*
	 * remove the variable completely if there is no active
	 * interface
	 */
	else if (act != NULL)
		setenv("ethact", NULL);
186 187
}

188 189 190 191 192 193
int eth_write_hwaddr(struct eth_device *dev, const char *base_name,
		   int eth_number)
{
	unsigned char env_enetaddr[6];
	int ret = 0;

194
	eth_getenv_enetaddr_by_index(base_name, eth_number, env_enetaddr);
195 196 197

	if (memcmp(env_enetaddr, "\0\0\0\0\0\0", 6)) {
		if (memcmp(dev->enetaddr, "\0\0\0\0\0\0", 6) &&
198
				memcmp(dev->enetaddr, env_enetaddr, 6)) {
199 200 201 202 203 204 205 206 207 208 209 210
			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);
	}

	if (dev->write_hwaddr &&
211 212
			!eth_mac_skip(eth_number) &&
			is_valid_ether_addr(dev->enetaddr))
213 214 215 216 217
		ret = dev->write_hwaddr(dev);

	return ret;
}

218 219 220
int eth_register(struct eth_device *dev)
{
	struct eth_device *d;
221
	static int index;
222

223
	assert(strlen(dev->name) < sizeof(dev->name));
224

225 226 227
	if (!eth_devices) {
		eth_current = eth_devices = dev;
		eth_current_changed();
W
wdenk 已提交
228
	} else {
229
		for (d = eth_devices; d->next != eth_devices; d = d->next)
230
			;
W
wdenk 已提交
231 232 233 234 235
		d->next = dev;
	}

	dev->state = ETH_STATE_INIT;
	dev->next  = eth_devices;
M
Michael Walle 已提交
236
	dev->index = index++;
W
wdenk 已提交
237 238 239 240

	return 0;
}

241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
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;
}

270 271 272 273 274 275 276 277
static void eth_env_init(bd_t *bis)
{
	const char *s;

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

W
wdenk 已提交
278 279
int eth_initialize(bd_t *bis)
{
M
Michael Walle 已提交
280
	int num_devices = 0;
W
wdenk 已提交
281 282 283
	eth_devices = NULL;
	eth_current = NULL;

284
	bootstage_mark(BOOTSTAGE_ID_NET_ETH_START);
285
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
286 287
	miiphy_init();
#endif
A
Andy Fleming 已提交
288 289 290 291 292

#ifdef CONFIG_PHYLIB
	phy_init();
#endif

293 294
	eth_env_init(bis);

B
Ben Warren 已提交
295 296 297 298 299 300 301 302 303 304 305 306
	/*
	 * 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");
307

W
wdenk 已提交
308
	if (!eth_devices) {
309
		puts("No ethernet found.\n");
310
		bootstage_error(BOOTSTAGE_ID_NET_ETH_START);
W
wdenk 已提交
311 312
	} else {
		struct eth_device *dev = eth_devices;
313
		char *ethprime = getenv("ethprime");
W
wdenk 已提交
314

315
		bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT);
W
wdenk 已提交
316
		do {
M
Michael Walle 已提交
317
			if (dev->index)
318
				puts(", ");
W
wdenk 已提交
319 320 321

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

322
			if (ethprime && strcmp(dev->name, ethprime) == 0) {
W
wdenk 已提交
323
				eth_current = dev;
324
				puts(" [PRIME]");
W
wdenk 已提交
325 326
			}

327
			if (strchr(dev->name, ' '))
328 329
				puts("\nWarning: eth device name has a space!"
					"\n");
330

M
Michael Walle 已提交
331
			if (eth_write_hwaddr(dev, "eth", dev->index))
332
				puts("\nWarning: failed to set MAC address\n");
W
wdenk 已提交
333 334

			dev = dev->next;
M
Michael Walle 已提交
335
			num_devices++;
336
		} while (dev != eth_devices);
W
wdenk 已提交
337

338
		eth_current_changed();
339
		putc('\n');
W
wdenk 已提交
340 341
	}

M
Michael Walle 已提交
342
	return num_devices;
W
wdenk 已提交
343 344
}

D
David Updegraff 已提交
345 346 347
#ifdef CONFIG_MCAST_TFTP
/* Multicast.
 * mcast_addr: multicast ipaddr from which multicast Mac is made
348
 * join: 1=join, 0=leave.
D
David Updegraff 已提交
349
 */
350
int eth_mcast_join(IPaddr_t mcast_ip, u8 join)
D
David Updegraff 已提交
351
{
352
	u8 mcast_mac[6];
353
	if (!eth_current || !eth_current->mcast)
D
David Updegraff 已提交
354 355 356 357 358 359 360 361 362 363
		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);
}

364 365
/* 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 已提交
366 367 368
 * some other adapter -- hash tables
 */
#define CRCPOLY_LE 0xedb88320
369
u32 ether_crc(size_t len, unsigned char const *p)
D
David Updegraff 已提交
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
{
	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 已提交
390 391 392

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

395
	if (!eth_current) {
396
		puts("No ethernet found.\n");
397
		return -1;
398
	}
W
wdenk 已提交
399

400 401 402 403 404
	/* Sync environment with network devices */
	dev = eth_devices;
	do {
		uchar env_enetaddr[6];

M
Michael Walle 已提交
405
		if (eth_getenv_enetaddr_by_index("eth", dev->index,
406
						 env_enetaddr))
407 408 409 410 411
			memcpy(dev->enetaddr, env_enetaddr, 6);

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

W
wdenk 已提交
412 413
	old_current = eth_current;
	do {
R
Robin Getz 已提交
414
		debug("Trying %s\n", eth_current->name);
W
wdenk 已提交
415

416
		if (eth_current->init(eth_current, bis) >= 0) {
W
wdenk 已提交
417 418
			eth_current->state = ETH_STATE_ACTIVE;

419
			return 0;
W
wdenk 已提交
420
		}
R
Robin Getz 已提交
421
		debug("FAIL\n");
W
wdenk 已提交
422 423 424 425

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

426
	return -1;
W
wdenk 已提交
427 428 429 430 431 432 433 434 435 436 437 438
}

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

	eth_current->halt(eth_current);

	eth_current->state = ETH_STATE_PASSIVE;
}

439
int eth_send(void *packet, int length)
W
wdenk 已提交
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
{
	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);
}

455
#ifdef CONFIG_API
456
static void eth_save_packet(void *packet, int length)
457
{
458
	char *p = packet;
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
	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;
}

474
int eth_receive(void *packet, int length)
475
{
476
	char *p = packet;
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
	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;
	}

	if (length < eth_rcv_bufs[eth_rcv_current].length)
		return -1;

	length = eth_rcv_bufs[eth_rcv_current].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 */

W
wdenk 已提交
502 503
void eth_try_another(int first_restart)
{
504
	static struct eth_device *first_failed;
R
Remy Bohmer 已提交
505
	char *ethrotate;
506 507 508 509 510

	/*
	 * Do not rotate between network interfaces when
	 * 'ethrotate' variable is set to 'no'.
	 */
511 512
	ethrotate = getenv("ethrotate");
	if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0))
513
		return;
W
wdenk 已提交
514 515 516 517

	if (!eth_current)
		return;

518
	if (first_restart)
W
wdenk 已提交
519 520 521 522
		first_failed = eth_current;

	eth_current = eth_current->next;

523
	eth_current_changed();
524

525
	if (first_failed == eth_current)
W
wdenk 已提交
526 527 528
		NetRestartWrap = 1;
}

529 530
void eth_set_current(void)
{
531 532 533
	static char *act;
	static int  env_changed_id;
	struct eth_device *old_current;
H
Heiko Schocher 已提交
534
	int	env_id;
535 536 537 538

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

H
Heiko Schocher 已提交
539 540 541 542 543
	env_id = get_env_id();
	if ((act == NULL) || (env_changed_id != env_id)) {
		act = getenv("ethact");
		env_changed_id = env_id;
	}
544 545 546 547 548 549 550 551 552
	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);
	}

553
	eth_current_changed();
554 555
}

556
char *eth_get_name(void)
557
{
558
	return eth_current ? eth_current->name : "unknown";
559
}