Set_mac_address does not work!

Hi,

I need to be able to set the mac address on an Ethernet interface. OS6.6.0 added the command ‘set_mac_address’ which seemed to be the answer to my prayers, but on testing the command using a FRDM-K66 board, it doesn’t actually work!

The command executes correctly and returns a nsapi_error_t of NSAPI_ERROR_OK which the documentation says means that the command has completed successfully, however it doesn’t set the mac address, in fact it doesn’t actually do anything at all.

Could this be fixed please?

Regards

Roger

Mbed boards have a build in MAC address, so there is no need for set_mac_address.
Try If can connect your board.

From what I understand the Mbed OS generates a random MAC address based on the serial number of the chip, it is not a proper IEEE registered MAC address. This is fine for home hobbyists, but no use for commercial products.

If I’ve got this wrong and it is a proper IEEE MAC then I’ll be delighted to be corrected.

I think this depends from board vendor.

I had similar Problems as @rogermcardell, however I used STM32 boards where this problem is easily solvable.

STM32 way
The MAC address during startup is “generated” in the function mbed_mac_address(…) in platform\source\mbed_interface.c, where it is defined as WEAK. The standard STM32Emac overwrites this with a STM32-specific way of generating MAC addresses. However, there is another weak function definition, mbed_otp_mac_address(…), which can be used to set specific MAC addresses.

Freescale way
From my very brief search in the MBed OS source code, only the STM32 targets have this mechanism. For the FRDM-K66 board there is no such code or mechanism. In the file targets\TARGET_Freescale\TARGET_MCUXpresso_MCUS\TARGET_K66F\TARGET_FRDM\mbed_overrides.c the MAC address is generated and cannot be changed.

My opinion
My feeling is that a similar mechanism to the STM32 way would be very helpful also for other targets, but unfortunately I don’t have access to such devices currently to implement it.

it looks like the set_mac_address() should work, there is a function in the emac driver for the K66F:

if calls a function from the Kinetis fsl:

Just set a breakpoint here and check what is set.

Hi Johannes,

I set a breakpoint and found that the ENET_SetMacAddr function is run twice when ‘net->connect();’ is executed. This function is not called by ‘net->set_mac_address(my_macaddr,6);’ The first time round *macAddr is zero, the second time it has values but these do not seem to have any resemblance to the Mac address that is finally set.

At this point I got stuck, so I tried hard coding my new MacAddress into the ENET_SetMacAddr function, but then I get an error when ‘net->connect()’ is executed.

So all in all I’m none the wiser.

when the MCU is stopped at the BP, you can look for a window ‘call stack’ (MCUXpresso IDE?). In this window, you can click on the callers and see where the call comes finally from.
The first call with zeros is maybe from the initialization. I don’t know the K66f, maybe setting the MAC is somehow limited? Reading the MAC seems also not to be supported.

Hi Johannes,

Thanks for this. The first call is the initialization. The 2nd call is called as follows:

ENET_SetMacAddr is called from:

void Kinetis_EMAC::set_hwaddr(const uint8_t *addr)
{
memcpy(hwaddr, addr, sizeof hwaddr);
ENET_SetMacAddr(ENET, const_cast<uint8_t *>(addr));
}
located within kinetis_emac.cpp

This is called by:
mbed_if->emac->set_hwaddr(netif->hwaddr);
located within LWIPInterfaceEMAC.cpp

Which is called by:
Kinetis_EMAC::Kinetis_EMAC() : xTXDCountSem(ENET_TX_RING_LEN, ENET_TX_RING_LEN), hwaddr()
{
}
located within LWIPInterfaceEMAC.cpp

which is called by:
result = net->connect();
in main.cpp

I’ve come up with a work around!

I’m pretty sure it violates every good coding practice ever, while bypassing large lumps of the OS, however it actually works!

First of all I have modified the ENET_SetMacAddr function as follows:

void ENET_SetMacAddr(ENET_Type *base, uint8_t *macAddr)
{
uint32_t address;

// My added Code
if (macAddr[0] != 0)
{
macAddr[0] = MyMacAddress[0];
macAddr[1] = MyMacAddress[1];
macAddr[2] = MyMacAddress[2];
macAddr[3] = MyMacAddress[3];
macAddr[4] = MyMacAddress[4];
macAddr[5] = MyMacAddress[5];
}

/* Set physical address lower register. */
address = (uint32_t)(((uint32_t)macAddr[0] << 24U) | ((uint32_t)macAddr[1] << 16U) | ((uint32_t)macAddr[2] << 8U) | (uint32_t)macAddr[3]);
base->PALR = address;
/* Set physical address high register. */
address = (uint32_t)(((uint32_t)macAddr[4] << 8U) | ((uint32_t)macAddr[5]));
base->PAUR = address << ENET_PAUR_PADDR2_SHIFT;

}

This function lives within fsl_enet.c and at the top of this I’ve added a global vaiable:
uint8_t MyMacAddress[6];

Then at the top of my main program I’ve given access to this with:
extern uint8_t MyMacAddress[6];

Within my Main() function I then set the mac address like this:

MyMacAddress[0] = 0xAA;
MyMacAddress[1] = 0xAB;
MyMacAddress[2] = 0xAC;
MyMacAddress[3] = 0xAD;
MyMacAddress[4] = 0xAE;
MyMacAddress[5] = 0xAF;

This is done before net->connect(); is called and hey presto, it actually works and correctly sets the Mac Address!