]> git.karo-electronics.de Git - karo-tx-uboot.git/blob - net/eth.c
net: Rename helper function to be more clear
[karo-tx-uboot.git] / net / eth.c
1 /*
2  * (C) Copyright 2001-2010
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * SPDX-License-Identifier:     GPL-2.0+
6  */
7
8 #include <common.h>
9 #include <command.h>
10 #include <net.h>
11 #include <miiphy.h>
12 #include <phy.h>
13 #include <asm/errno.h>
14
15 void eth_parse_enetaddr(const char *addr, uchar *enetaddr)
16 {
17         char *end;
18         int i;
19
20         for (i = 0; i < 6; ++i) {
21                 enetaddr[i] = addr ? simple_strtoul(addr, &end, 16) : 0;
22                 if (addr)
23                         addr = (*end) ? end + 1 : end;
24         }
25 }
26
27 int eth_getenv_enetaddr(char *name, uchar *enetaddr)
28 {
29         eth_parse_enetaddr(getenv(name), enetaddr);
30         return is_valid_ether_addr(enetaddr);
31 }
32
33 int eth_setenv_enetaddr(char *name, const uchar *enetaddr)
34 {
35         char buf[20];
36
37         sprintf(buf, "%pM", enetaddr);
38
39         return setenv(name, buf);
40 }
41
42 int eth_getenv_enetaddr_by_index(const char *base_name, int index,
43                                  uchar *enetaddr)
44 {
45         char enetvar[32];
46         sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index);
47         return eth_getenv_enetaddr(enetvar, enetaddr);
48 }
49
50 static inline int eth_setenv_enetaddr_by_index(const char *base_name, int index,
51                                  uchar *enetaddr)
52 {
53         char enetvar[32];
54         sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index);
55         return eth_setenv_enetaddr(enetvar, enetaddr);
56 }
57
58
59 static int eth_mac_skip(int index)
60 {
61         char enetvar[15];
62         char *skip_state;
63         sprintf(enetvar, index ? "eth%dmacskip" : "ethmacskip", index);
64         return ((skip_state = getenv(enetvar)) != NULL);
65 }
66
67 /*
68  * CPU and board-specific Ethernet initializations.  Aliased function
69  * signals caller to move on
70  */
71 static int __def_eth_init(bd_t *bis)
72 {
73         return -1;
74 }
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")));
77
78 #ifdef CONFIG_API
79 static struct {
80         uchar data[PKTSIZE];
81         int length;
82 } eth_rcv_bufs[PKTBUFSRX];
83
84 static unsigned int eth_rcv_current, eth_rcv_last;
85 #endif
86
87 static struct eth_device *eth_devices;
88 struct eth_device *eth_current;
89
90 struct eth_device *eth_get_dev_by_name(const char *devname)
91 {
92         struct eth_device *dev, *target_dev;
93
94         BUG_ON(devname == NULL);
95
96         if (!eth_devices)
97                 return NULL;
98
99         dev = eth_devices;
100         target_dev = NULL;
101         do {
102                 if (strcmp(devname, dev->name) == 0) {
103                         target_dev = dev;
104                         break;
105                 }
106                 dev = dev->next;
107         } while (dev != eth_devices);
108
109         return target_dev;
110 }
111
112 struct eth_device *eth_get_dev_by_index(int index)
113 {
114         struct eth_device *dev, *target_dev;
115
116         if (!eth_devices)
117                 return NULL;
118
119         dev = eth_devices;
120         target_dev = NULL;
121         do {
122                 if (dev->index == index) {
123                         target_dev = dev;
124                         break;
125                 }
126                 dev = dev->next;
127         } while (dev != eth_devices);
128
129         return target_dev;
130 }
131
132 int eth_get_dev_index(void)
133 {
134         if (!eth_current)
135                 return -1;
136
137         return eth_current->index;
138 }
139
140 static void eth_current_changed(void)
141 {
142         char *act = getenv("ethact");
143         /* update current ethernet name */
144         if (eth_current) {
145                 if (act == NULL || strcmp(act, eth_current->name) != 0)
146                         setenv("ethact", eth_current->name);
147         }
148         /*
149          * remove the variable completely if there is no active
150          * interface
151          */
152         else if (act != NULL)
153                 setenv("ethact", NULL);
154 }
155
156 int eth_write_hwaddr(struct eth_device *dev, const char *base_name,
157                    int eth_number)
158 {
159         unsigned char env_enetaddr[6];
160         int ret = 0;
161
162         eth_getenv_enetaddr_by_index(base_name, eth_number, env_enetaddr);
163
164         if (!is_zero_ether_addr(env_enetaddr)) {
165                 if (!is_zero_ether_addr(dev->enetaddr) &&
166                     memcmp(dev->enetaddr, env_enetaddr, 6)) {
167                         printf("\nWarning: %s MAC addresses don't match:\n",
168                                 dev->name);
169                         printf("Address in SROM is         %pM\n",
170                                 dev->enetaddr);
171                         printf("Address in environment is  %pM\n",
172                                 env_enetaddr);
173                 }
174
175                 memcpy(dev->enetaddr, env_enetaddr, 6);
176         } else if (is_valid_ether_addr(dev->enetaddr)) {
177                 eth_setenv_enetaddr_by_index(base_name, eth_number,
178                                              dev->enetaddr);
179                 printf("\nWarning: %s using MAC address from net device\n",
180                         dev->name);
181         } else if (is_zero_ether_addr(dev->enetaddr)) {
182                 printf("\nError: %s address not set.\n",
183                        dev->name);
184                 return -EINVAL;
185         }
186
187         if (dev->write_hwaddr && !eth_mac_skip(eth_number)) {
188                 if (!is_valid_ether_addr(dev->enetaddr)) {
189                         printf("\nError: %s address %pM illegal value\n",
190                                  dev->name, dev->enetaddr);
191                         return -EINVAL;
192                 }
193
194                 ret = dev->write_hwaddr(dev);
195                 if (ret)
196                         printf("\nWarning: %s failed to set MAC address\n", dev->name);
197         }
198
199         return ret;
200 }
201
202 int eth_register(struct eth_device *dev)
203 {
204         struct eth_device *d;
205         static int index;
206
207         assert(strlen(dev->name) < sizeof(dev->name));
208
209         if (!eth_devices) {
210                 eth_current = eth_devices = dev;
211                 eth_current_changed();
212         } else {
213                 for (d = eth_devices; d->next != eth_devices; d = d->next)
214                         ;
215                 d->next = dev;
216         }
217
218         dev->state = ETH_STATE_INIT;
219         dev->next  = eth_devices;
220         dev->index = index++;
221
222         return 0;
223 }
224
225 int eth_unregister(struct eth_device *dev)
226 {
227         struct eth_device *cur;
228
229         /* No device */
230         if (!eth_devices)
231                 return -1;
232
233         for (cur = eth_devices; cur->next != eth_devices && cur->next != dev;
234              cur = cur->next)
235                 ;
236
237         /* Device not found */
238         if (cur->next != dev)
239                 return -1;
240
241         cur->next = dev->next;
242
243         if (eth_devices == dev)
244                 eth_devices = dev->next == eth_devices ? NULL : dev->next;
245
246         if (eth_current == dev) {
247                 eth_current = eth_devices;
248                 eth_current_changed();
249         }
250
251         return 0;
252 }
253
254 static void eth_env_init(bd_t *bis)
255 {
256         const char *s;
257
258         if ((s = getenv("bootfile")) != NULL)
259                 copy_filename(BootFile, s, sizeof(BootFile));
260 }
261
262 int eth_initialize(bd_t *bis)
263 {
264         int num_devices = 0;
265         eth_devices = NULL;
266         eth_current = NULL;
267
268         bootstage_mark(BOOTSTAGE_ID_NET_ETH_START);
269 #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB)
270         miiphy_init();
271 #endif
272
273 #ifdef CONFIG_PHYLIB
274         phy_init();
275 #endif
276
277         eth_env_init(bis);
278
279         /*
280          * If board-specific initialization exists, call it.
281          * If not, call a CPU-specific one
282          */
283         if (board_eth_init != __def_eth_init) {
284                 if (board_eth_init(bis) < 0)
285                         printf("Board Net Initialization Failed\n");
286         } else if (cpu_eth_init != __def_eth_init) {
287                 if (cpu_eth_init(bis) < 0)
288                         printf("CPU Net Initialization Failed\n");
289         } else
290                 printf("Net Initialization Skipped\n");
291
292         if (!eth_devices) {
293                 puts("No ethernet found.\n");
294                 bootstage_error(BOOTSTAGE_ID_NET_ETH_START);
295         } else {
296                 struct eth_device *dev = eth_devices;
297                 char *ethprime = getenv("ethprime");
298
299                 bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT);
300                 do {
301                         if (dev->index)
302                                 puts(", ");
303
304                         printf("%s", dev->name);
305
306                         if (ethprime && strcmp(dev->name, ethprime) == 0) {
307                                 eth_current = dev;
308                                 puts(" [PRIME]");
309                         }
310
311                         if (strchr(dev->name, ' '))
312                                 puts("\nWarning: eth device name has a space!"
313                                         "\n");
314
315                         eth_write_hwaddr(dev, "eth", dev->index);
316
317                         dev = dev->next;
318                         num_devices++;
319                 } while (dev != eth_devices);
320
321                 eth_current_changed();
322                 putc('\n');
323         }
324
325         return num_devices;
326 }
327
328 #ifdef CONFIG_MCAST_TFTP
329 /* Multicast.
330  * mcast_addr: multicast ipaddr from which multicast Mac is made
331  * join: 1=join, 0=leave.
332  */
333 int eth_mcast_join(IPaddr_t mcast_ip, u8 join)
334 {
335         u8 mcast_mac[6];
336         if (!eth_current || !eth_current->mcast)
337                 return -1;
338         mcast_mac[5] = htonl(mcast_ip) & 0xff;
339         mcast_mac[4] = (htonl(mcast_ip)>>8) & 0xff;
340         mcast_mac[3] = (htonl(mcast_ip)>>16) & 0x7f;
341         mcast_mac[2] = 0x5e;
342         mcast_mac[1] = 0x0;
343         mcast_mac[0] = 0x1;
344         return eth_current->mcast(eth_current, mcast_mac, join);
345 }
346
347 /* the 'way' for ethernet-CRC-32. Spliced in from Linux lib/crc32.c
348  * and this is the ethernet-crc method needed for TSEC -- and perhaps
349  * some other adapter -- hash tables
350  */
351 #define CRCPOLY_LE 0xedb88320
352 u32 ether_crc(size_t len, unsigned char const *p)
353 {
354         int i;
355         u32 crc;
356         crc = ~0;
357         while (len--) {
358                 crc ^= *p++;
359                 for (i = 0; i < 8; i++)
360                         crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
361         }
362         /* an reverse the bits, cuz of way they arrive -- last-first */
363         crc = (crc >> 16) | (crc << 16);
364         crc = (crc >> 8 & 0x00ff00ff) | (crc << 8 & 0xff00ff00);
365         crc = (crc >> 4 & 0x0f0f0f0f) | (crc << 4 & 0xf0f0f0f0);
366         crc = (crc >> 2 & 0x33333333) | (crc << 2 & 0xcccccccc);
367         crc = (crc >> 1 & 0x55555555) | (crc << 1 & 0xaaaaaaaa);
368         return crc;
369 }
370
371 #endif
372
373
374 int eth_init(bd_t *bis)
375 {
376         struct eth_device *old_current, *dev;
377
378         if (!eth_current) {
379                 puts("No ethernet found.\n");
380                 return -1;
381         }
382
383         /* Sync environment with network devices */
384         dev = eth_devices;
385         do {
386                 uchar env_enetaddr[6];
387
388                 if (eth_getenv_enetaddr_by_index("eth", dev->index,
389                                                  env_enetaddr))
390                         memcpy(dev->enetaddr, env_enetaddr, 6);
391
392                 dev = dev->next;
393         } while (dev != eth_devices);
394
395         old_current = eth_current;
396         do {
397                 debug("Trying %s\n", eth_current->name);
398
399                 if (eth_current->init(eth_current, bis) >= 0) {
400                         eth_current->state = ETH_STATE_ACTIVE;
401
402                         return 0;
403                 }
404                 debug("FAIL\n");
405
406                 eth_try_another(0);
407         } while (old_current != eth_current);
408
409         return -1;
410 }
411
412 void eth_halt(void)
413 {
414         if (!eth_current)
415                 return;
416
417         eth_current->halt(eth_current);
418
419         eth_current->state = ETH_STATE_PASSIVE;
420 }
421
422 int eth_send(void *packet, int length)
423 {
424         if (!eth_current)
425                 return -1;
426
427         return eth_current->send(eth_current, packet, length);
428 }
429
430 int eth_rx(void)
431 {
432         if (!eth_current)
433                 return -1;
434
435         return eth_current->recv(eth_current);
436 }
437
438 #ifdef CONFIG_API
439 static void eth_save_packet(void *packet, int length)
440 {
441         char *p = packet;
442         int i;
443
444         if ((eth_rcv_last+1) % PKTBUFSRX == eth_rcv_current)
445                 return;
446
447         if (PKTSIZE < length)
448                 return;
449
450         for (i = 0; i < length; i++)
451                 eth_rcv_bufs[eth_rcv_last].data[i] = p[i];
452
453         eth_rcv_bufs[eth_rcv_last].length = length;
454         eth_rcv_last = (eth_rcv_last + 1) % PKTBUFSRX;
455 }
456
457 int eth_receive(void *packet, int length)
458 {
459         char *p = packet;
460         void *pp = push_packet;
461         int i;
462
463         if (eth_rcv_current == eth_rcv_last) {
464                 push_packet = eth_save_packet;
465                 eth_rx();
466                 push_packet = pp;
467
468                 if (eth_rcv_current == eth_rcv_last)
469                         return -1;
470         }
471
472         length = min(eth_rcv_bufs[eth_rcv_current].length, length);
473
474         for (i = 0; i < length; i++)
475                 p[i] = eth_rcv_bufs[eth_rcv_current].data[i];
476
477         eth_rcv_current = (eth_rcv_current + 1) % PKTBUFSRX;
478         return length;
479 }
480 #endif /* CONFIG_API */
481
482 void eth_try_another(int first_restart)
483 {
484         static struct eth_device *first_failed;
485         char *ethrotate;
486
487         /*
488          * Do not rotate between network interfaces when
489          * 'ethrotate' variable is set to 'no'.
490          */
491         ethrotate = getenv("ethrotate");
492         if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0))
493                 return;
494
495         if (!eth_current)
496                 return;
497
498         if (first_restart)
499                 first_failed = eth_current;
500
501         eth_current = eth_current->next;
502
503         eth_current_changed();
504
505         if (first_failed == eth_current)
506                 NetRestartWrap = 1;
507 }
508
509 void eth_set_current(void)
510 {
511         static char *act;
512         static int  env_changed_id;
513         struct eth_device *old_current;
514         int     env_id;
515
516         if (!eth_current)       /* XXX no current */
517                 return;
518
519         env_id = get_env_id();
520         if ((act == NULL) || (env_changed_id != env_id)) {
521                 act = getenv("ethact");
522                 env_changed_id = env_id;
523         }
524         if (act != NULL) {
525                 old_current = eth_current;
526                 do {
527                         if (strcmp(eth_current->name, act) == 0)
528                                 return;
529                         eth_current = eth_current->next;
530                 } while (old_current != eth_current);
531         }
532
533         eth_current_changed();
534 }
535
536 char *eth_get_name(void)
537 {
538         return eth_current ? eth_current->name : "unknown";
539 }