CDP & LLDP send works, but can't receive ethernet packets (uboot)

      CDP & LLDP send works, but can't receive ethernet packets (uboot)

      Hello,

      I tried to use the CDP command in UBOOT and also implemented a LLDP command. Both commands send CDP or LLDP messages. The problem for me is that UBOOT does not receive any multicast (CDP, LLDP) Ethernet packets. This seems similar to this thread: forums.xilinx.com/t5/Zynq-All-…ve-data-uboot/td-p/681131
      Other protocols/commands works normal...like tftp, ping…and so on.

      Where is the function that receives the Ethernet frames for the efusA9X defined? Is it "drivers/net/fec_mxc.{c,h}"?

      How can I test this problem more in detail…like registers or tcpdump in uboot??
      Attached there is a zip with examples for a CDP and a LLDP message.


      Thanks.
      Files
      Yes, fec_mxc.c is the right file. As far as I understand the code, this driver in U-Boot is not prepared for multicast packets, yet. All multicast addresses are switched off, see function fec_init(), where gaddr1 and gaddr2 are set to zero. And the receive function does not handle any multicast packets, too. See fec_recv().

      So I believe you have to enhance the driver to work with multicast packets. First you have to set the gaddr registers to accept the packets. The controller seems to compute a 6 bit hash number from all bypassing MAC addresses. This value between 0 and 63 addresses one of 64 bits in the gaddr registers. If the corresponding bit is set, then the packet is accepted, otherwise ignored. And then you have to implement the processing of these packets in fec_recv(). First of all you have to make sure that you really want to accept this packet, because the hash matches more than one address. So you actually have to compare the MAC address against your real address mask. And only if this test passes, you can forward the packet data to the NetReceive() function.

      Maybe you can also have a look at the Linux driver to see how it is done there. There it is in drivers/net/ethernet/freescale/fec_main.c. Of course there the code is considerably more complex to handle more cases.

      Your F&S Support Team
      F&S Elektronik Systeme GmbH
      As this is an international forum, please try to post in English.
      Da dies ein internationales Forum ist, bitten wir darum, Beiträge möglichst in Englisch zu verfassen.
      I found the hash calculation definition in [2] and the calculation algorithm in [1]. I rewrite [1] to [3] and get a hash_high: 0x0 and hash_low: 0x400000.
      But this values does not let the FEC driver accept LLDP multicasts.

      Is [1] a generic algorithm for all FEC drivers, does it suit the efusA9X board? Or is my calculation in [3] incorrect?
      How can I force U-Boot to use only one Ethernet interface for example FEC0? Because if FEC0 fails it continues with FEC1.

      [1]:
      FEC Hash calculation: elixir.free-electrons.com/linu…reescale/fec_main.c#L2938
      [2]:
      nxp.com/docs/en/reference-manual/IMX6SXRM.pdf, page 1180, chapter 24.6.4.3.2
      [3]:

      Source Code

      1. #include <stdio.h>
      2. #define FEC_HASH_BITS 6
      3. #define CRC32_POLY 0xEDB88320
      4. int main (void)
      5. {
      6. struct netdev_hw_addr *ha;
      7. unsigned int i, bit, data, crc, tmp;
      8. unsigned char hash;
      9. unsigned int hash_high = 0, hash_low = 0;
      10. unsigned char addr[7] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e}; // LLDP
      11. unsigned char addr_len=1;
      12. // Add the addresses in hash register
      13. // calculate crc32 value of mac address
      14. crc = 0xffffffff;
      15. // for (i = 0; i < ndev->addr_len; i++) {
      16. for (i = 0; i < addr_len; i++) {
      17. // data = ha->addr[i];
      18. data = addr[i];
      19. for (bit = 0; bit < 8; bit++, data >>= 1) {
      20. crc = (crc >> 1) ^ (((crc ^ data) & 1) ? CRC32_POLY : 0);
      21. }
      22. }
      23. // only upper 6 bits (FEC_HASH_BITS) are used
      24. // which point to specific bit in the hash registers
      25. hash = (crc >> (32 - FEC_HASH_BITS)) & 0x3f;
      26. if (hash > 31)
      27. hash_high |= 1 << (hash - 32);
      28. else
      29. hash_low |= 1 << hash;
      30. printf("hash: 0x%X\n", hash);
      31. printf("hash_high: 0x%X\n", hash_high);
      32. printf("hash_low: 0x%X\n", hash_low);
      33. return 0;
      34. }
      How can I force U-Boot to use only one Ethernet interface for example FEC0? Because if FEC0 fails it continues with FEC1.


      I tried the following MAKRO in "include/configs/fsimx6sx.h".
      CONFIG_NET_DO_NOT_TRY_ANOTHER

      But if FEC0 is not connected or the counterpart is dead (starting switch) it still switch to interface FEC1.

      Is there an other option?

      Post was edited 1 time, last by “BrenkeM” ().

      Just as an idea, how about setting 0xffffffff in both hash registers? Then every multicast packet should be accepted and then you can check the real address later and still discard unwanted packets. Just to prove if multicast works at all. When this succeeds, then you can try again to compute the correct hash value. Unfortunately I have also not done this yet, so I'm at the same knowledge level as you.

      Regarding the automatic switching of FEC ports, why is this a problem? Isn't this what you usually want? That you can plug in the cable on any port and the system will automatically try the other port(s) if one port fails? Why should it stay on one port?

      Nevertheless, as far as I understand the code of eth_try_another(), you can prevent using a different port by setting environment variable ethrotate to "no".

      Your F&S Support Team
      F&S Elektronik Systeme GmbH
      As this is an international forum, please try to post in English.
      Da dies ein internationales Forum ist, bitten wir darum, Beiträge möglichst in Englisch zu verfassen.
      Status so far:

      Multicast works:

      Source Code

      1. // aktivate all multicast addresses
      2. writel(0xffffffff, &fec->eth->gaddr1);
      3. writel(0xffffffff, &fec->eth->gaddr2);


      Than I spend some time by "try and error" with all bits of the gaddr register. The bit to control if a LLDP frame will be rejected or accepted are is the following:

      Source Code

      1. // Set multicast address filter
      2. // multicast for LLDP: 01:80:c2:00:00:0e
      3. writel(0x00000000, &fec->eth->gaddr1);
      4. writel(0x00000008, &fec->eth->gaddr2);


      Furthermore, I checked community.st.com/thread/24942 to calculate the correct Hash for fec with the LLDP multicast address { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }. It seems to be 0x1E.

      But I do not know how to split/convert this hash for gaddr1 and gadd2 in "drivers/net/fec_mxc.c". According to [1] the hash results in hash_high: 0x0
      and hash_low: 0x40000000.

      -----------------------------------

      Regarding the ethernet interface switching:

      In my case LLDP frames are present on FEC0 and FEC1. But those on FEC1 are the wrong ones...and will misconfigure the system. So if FEC0 is defined and a TIMEOUT on FEC0 occurs the board switch to FEC1...gets the wrong LLDP frame and breaks the system.

      But I will try "ethrotate".


      Thanks so far.
      This is the Multicast CRC code from the Linux kernel:

      Source Code

      1. #define HASH_BITS 6 /* #bits in hash */
      2. #define CRC32_POLY 0xEDB88320
      3. ...
      4. /* calculate crc32 value of mac address */
      5. crc = 0xffffffff;
      6. for (i = 0; i < ndev->addr_len; i++) {
      7. data = ha->addr[i];
      8. for (bit = 0; bit < 8; bit++, data >>= 1) {
      9. crc = (crc >> 1) ^
      10. (((crc ^ data) & 1) ? CRC32_POLY : 0);
      11. }
      12. }
      13. /* only upper 6 bits (HASH_BITS) are used
      14. * which point to specific bit in he hash registers
      15. */
      16. hash = (crc >> (32 - HASH_BITS)) & 0x3f;
      17. if (hash > 31) {
      18. tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
      19. tmp |= 1 << (hash - 32);
      20. writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
      21. } else {
      22. tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_LOW);
      23. tmp |= 1 << hash;
      24. writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
      25. }


      Your F&S Support Team
      F&S Elektronik Systeme GmbH
      As this is an international forum, please try to post in English.
      Da dies ein internationales Forum ist, bitten wir darum, Beiträge möglichst in Englisch zu verfassen.