mirror of https://github.com/ARMmbed/mbed-os.git
Add multicast filter support to STM32 EMAC (#408)
parent
2f3a3c3302
commit
2fd9d83098
|
@ -42,6 +42,10 @@
|
||||||
"eth-phy-duplex-status": {
|
"eth-phy-duplex-status": {
|
||||||
"help" : "Duplex mask information in eth-phy-status-register",
|
"help" : "Duplex mask information in eth-phy-status-register",
|
||||||
"value" : "0x0010"
|
"value" : "0x0010"
|
||||||
|
},
|
||||||
|
"max-mcast-subscribes": {
|
||||||
|
"help" : "Maximum supported number of multicast addresses that the application can subscribe to",
|
||||||
|
"value" : "8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"target_overrides": {
|
"target_overrides": {
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "platform/mbed_power_mgmt.h"
|
#include "platform/mbed_power_mgmt.h"
|
||||||
#include "platform/mbed_error.h"
|
#include "platform/mbed_error.h"
|
||||||
#include "CacheAlignedBuffer.h"
|
#include "CacheAlignedBuffer.h"
|
||||||
|
#include "MbedCRC.h"
|
||||||
|
|
||||||
#include "stm32xx_emac_config.h"
|
#include "stm32xx_emac_config.h"
|
||||||
#include "stm32xx_emac.h"
|
#include "stm32xx_emac.h"
|
||||||
|
@ -294,6 +295,12 @@ bool STM32_EMAC::low_level_init_successful()
|
||||||
/* HAL_ETH_Init returns TIMEOUT when Ethernet cable is not plugged */;
|
/* HAL_ETH_Init returns TIMEOUT when Ethernet cable is not plugged */;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set MAC address
|
||||||
|
writeMACAddress(MACAddr, &EthHandle.Instance->MACA0HR, &EthHandle.Instance->MACA0LR);
|
||||||
|
|
||||||
|
// Enable multicast hash and perfect filter
|
||||||
|
EthHandle.Instance->MACFFR = ETH_MACFFR_HM | ETH_MACFFR_HPF;
|
||||||
|
|
||||||
uint32_t TempRegisterValue;
|
uint32_t TempRegisterValue;
|
||||||
if (HAL_ETH_ReadPHYRegister(&EthHandle, 2, &TempRegisterValue) != HAL_OK) {
|
if (HAL_ETH_ReadPHYRegister(&EthHandle, 2, &TempRegisterValue) != HAL_OK) {
|
||||||
tr_error("HAL_ETH_ReadPHYRegister 2 issue");
|
tr_error("HAL_ETH_ReadPHYRegister 2 issue");
|
||||||
|
@ -364,6 +371,12 @@ bool STM32_EMAC::low_level_init_successful()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set MAC address
|
||||||
|
writeMACAddress(MACAddr, &EthHandle.Instance->MACA0HR, &EthHandle.Instance->MACA0LR);
|
||||||
|
|
||||||
|
// Enable multicast hash and perfect filter
|
||||||
|
EthHandle.Instance->MACPFR = ETH_MACPFR_HMC | ETH_MACPFR_HPF;
|
||||||
|
|
||||||
memset(&TxConfig, 0, sizeof(ETH_TxPacketConfig));
|
memset(&TxConfig, 0, sizeof(ETH_TxPacketConfig));
|
||||||
TxConfig.Attributes = ETH_TX_PACKETS_FEATURES_CSUM | ETH_TX_PACKETS_FEATURES_CRCPAD;
|
TxConfig.Attributes = ETH_TX_PACKETS_FEATURES_CSUM | ETH_TX_PACKETS_FEATURES_CRCPAD;
|
||||||
TxConfig.ChecksumCtrl = ETH_CHECKSUM_IPHDR_PAYLOAD_INSERT_PHDR_CALC;
|
TxConfig.ChecksumCtrl = ETH_CHECKSUM_IPHDR_PAYLOAD_INSERT_PHDR_CALC;
|
||||||
|
@ -934,17 +947,68 @@ void STM32_EMAC::set_link_state_cb(emac_link_state_change_cb_t state_cb)
|
||||||
|
|
||||||
void STM32_EMAC::add_multicast_group(const uint8_t *addr)
|
void STM32_EMAC::add_multicast_group(const uint8_t *addr)
|
||||||
{
|
{
|
||||||
/* No-op at this stage */
|
if(numSubscribedMcastMacs >= MBED_CONF_STM32_EMAC_MAX_MCAST_SUBSCRIBES)
|
||||||
|
{
|
||||||
|
tr_error("Out of multicast group entries (currently have %d). Increase the 'stm32-emac.max-mcast-subscribes' JSON option!", MBED_CONF_STM32_EMAC_MAX_MCAST_SUBSCRIBES);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(mcastMacs[numSubscribedMcastMacs++].data(), addr, 6);
|
||||||
|
populateMcastFilterRegs();
|
||||||
}
|
}
|
||||||
|
|
||||||
void STM32_EMAC::remove_multicast_group(const uint8_t *addr)
|
void STM32_EMAC::remove_multicast_group(const uint8_t *addr)
|
||||||
{
|
{
|
||||||
/* No-op at this stage */
|
// Find MAC address in the subscription list
|
||||||
|
auto macsEndIter = std::begin(mcastMacs) + numSubscribedMcastMacs;
|
||||||
|
auto toRemoveIter = std::find_if(std::begin(mcastMacs), macsEndIter, [&](auto element) {
|
||||||
|
return memcmp(element.data(), addr, 6) == 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(toRemoveIter == macsEndIter)
|
||||||
|
{
|
||||||
|
tr_warning("Tried to remove mcast group that was not added");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap the MAC addr to be removed to the end of the list, if it is not there already
|
||||||
|
auto lastElementIter = macsEndIter - 1;
|
||||||
|
if(toRemoveIter != std::begin(mcastMacs) && toRemoveIter != lastElementIter)
|
||||||
|
{
|
||||||
|
std::swap(*toRemoveIter, *lastElementIter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'remove' the last element by changing the length
|
||||||
|
numSubscribedMcastMacs--;
|
||||||
|
|
||||||
|
// Rebuild the MAC registers with that MAC removed.
|
||||||
|
// Technically it would be more performance efficient to remove just this MAC address, but that gets complex
|
||||||
|
// once you throw the hash filter into the mix. Unless you are subscribed to insane numbers of mcast addrs,
|
||||||
|
// it's easier to just rebuild it all.
|
||||||
|
populateMcastFilterRegs();
|
||||||
}
|
}
|
||||||
|
|
||||||
void STM32_EMAC::set_all_multicast(bool all)
|
void STM32_EMAC::set_all_multicast(bool all)
|
||||||
{
|
{
|
||||||
/* No-op at this stage */
|
#if defined(ETH_IP_VERSION_V2)
|
||||||
|
if(all)
|
||||||
|
{
|
||||||
|
EthHandle.Instance->MACPFR |= ETH_MACPFR_PM;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EthHandle.Instance->MACPFR &= ~ETH_MACPFR_PM;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if(all)
|
||||||
|
{
|
||||||
|
EthHandle.Instance->MACFFR |= ETH_MACFFR_PM;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EthHandle.Instance->MACFFR &= ~ETH_MACFFR_PM;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void STM32_EMAC::power_down()
|
void STM32_EMAC::power_down()
|
||||||
|
@ -966,6 +1030,100 @@ STM32_EMAC &STM32_EMAC::get_instance()
|
||||||
return emac;
|
return emac;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void STM32_EMAC::populateMcastFilterRegs() {
|
||||||
|
const size_t NUM_PERFECT_FILTER_REGS = 3;
|
||||||
|
|
||||||
|
const size_t numPerfectFilterMacs = std::min(NUM_PERFECT_FILTER_REGS, numSubscribedMcastMacs);
|
||||||
|
const size_t numHashFilterMacs = numSubscribedMcastMacs - numPerfectFilterMacs;
|
||||||
|
|
||||||
|
for(size_t perfFiltIdx = 0; perfFiltIdx < NUM_PERFECT_FILTER_REGS; ++perfFiltIdx)
|
||||||
|
{
|
||||||
|
// Find MAC addr registers (they aren't in an array :/)
|
||||||
|
uint32_t volatile * highReg;
|
||||||
|
uint32_t volatile * lowReg;
|
||||||
|
|
||||||
|
if(perfFiltIdx == 0)
|
||||||
|
{
|
||||||
|
highReg = &EthHandle.Instance->MACA1HR;
|
||||||
|
lowReg = &EthHandle.Instance->MACA1LR;
|
||||||
|
}
|
||||||
|
else if(perfFiltIdx == 1)
|
||||||
|
{
|
||||||
|
highReg = &EthHandle.Instance->MACA2HR;
|
||||||
|
lowReg = &EthHandle.Instance->MACA2LR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
highReg = &EthHandle.Instance->MACA3HR;
|
||||||
|
lowReg = &EthHandle.Instance->MACA3LR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(perfFiltIdx < numPerfectFilterMacs)
|
||||||
|
{
|
||||||
|
tr_debug("Using perfect filtering for %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8,
|
||||||
|
mcastMacs[perfFiltIdx][0], mcastMacs[perfFiltIdx][1], mcastMacs[perfFiltIdx][2],
|
||||||
|
mcastMacs[perfFiltIdx][3], mcastMacs[perfFiltIdx][4], mcastMacs[perfFiltIdx][5]);
|
||||||
|
writeMACAddress(mcastMacs[perfFiltIdx].data(), highReg, lowReg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Write zeroes to disable this mac addr entry
|
||||||
|
*highReg = 0;
|
||||||
|
*lowReg = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ETH_IP_VERSION_V2)
|
||||||
|
uint32_t volatile * hashRegs[] = {
|
||||||
|
&EthHandle.Instance->MACHT1R,
|
||||||
|
&EthHandle.Instance->MACHT0R
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
uint32_t volatile * hashRegs[] = {
|
||||||
|
&EthHandle.Instance->MACHTHR,
|
||||||
|
&EthHandle.Instance->MACHTLR
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Reset hash filter regs
|
||||||
|
*hashRegs[0] = 0;
|
||||||
|
*hashRegs[1] = 0;
|
||||||
|
|
||||||
|
// Note: as always, the datasheet description of how to do this CRC was vague and slightly wrong.
|
||||||
|
// This forum thread figured it out: https://community.st.com/t5/stm32-mcus-security/calculating-ethernet-multicast-filter-hash-value/td-p/416984
|
||||||
|
// What the datasheet SHOULD say is:
|
||||||
|
// Compute the Ethernet CRC-32 of the MAC address, with initial value of 1s, final XOR of ones, and input reflection on but output reflection off
|
||||||
|
// Then, take the upper 6 bits and use that to index the hash table.
|
||||||
|
|
||||||
|
mbed::MbedCRC<POLY_32BIT_ANSI> crcCalc(0xFFFFFFFF, 0xFFFFFFFF, true, false);
|
||||||
|
for(size_t hashFiltIdx = 0; hashFiltIdx < numHashFilterMacs; ++hashFiltIdx)
|
||||||
|
{
|
||||||
|
auto & currMacAddr = mcastMacs[hashFiltIdx + numPerfectFilterMacs];
|
||||||
|
|
||||||
|
tr_debug("Using hash filtering for %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8,
|
||||||
|
currMacAddr[0], currMacAddr[1], currMacAddr[2],
|
||||||
|
currMacAddr[3], currMacAddr[4], currMacAddr[5]);
|
||||||
|
|
||||||
|
// Compute Ethernet CRC-32 of the MAC address
|
||||||
|
uint32_t crc;
|
||||||
|
crcCalc.compute(currMacAddr.data(), currMacAddr.size(), &crc);
|
||||||
|
|
||||||
|
// Take upper 6 bits
|
||||||
|
uint32_t hashVal = crc >> 26;
|
||||||
|
|
||||||
|
// Set correct bit in hash filter
|
||||||
|
*hashRegs[hashVal >> 5] |= (1 << (hashVal & 0x1F));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void STM32_EMAC::writeMACAddress(const uint8_t *MAC, volatile uint32_t *addrHighReg, volatile uint32_t *addrLowReg) {
|
||||||
|
/* Set MAC addr bits 32 to 47 */
|
||||||
|
*addrHighReg = (static_cast<uint32_t>(MAC[5]) << 8) | static_cast<uint32_t>(MAC[4]) | ETH_MACA1HR_AE_Msk;
|
||||||
|
/* Set MAC addr bits 0 to 31 */
|
||||||
|
*addrLowReg = (static_cast<uint32_t>(MAC[3]) << 24) | (static_cast<uint32_t>(MAC[2]) << 16) |
|
||||||
|
(static_cast<uint32_t>(MAC[1]) << 8) | static_cast<uint32_t>(MAC[0]);
|
||||||
|
}
|
||||||
|
|
||||||
// Weak so a module can override
|
// Weak so a module can override
|
||||||
MBED_WEAK EMAC &EMAC::get_default_instance()
|
MBED_WEAK EMAC &EMAC::get_default_instance()
|
||||||
{
|
{
|
||||||
|
|
|
@ -162,6 +162,13 @@ private:
|
||||||
void enable_interrupts();
|
void enable_interrupts();
|
||||||
void disable_interrupts();
|
void disable_interrupts();
|
||||||
|
|
||||||
|
// Populate multicast filter registers with the contents of mcastMacs.
|
||||||
|
// Uses the perfect filter registers first, then the hash filter.
|
||||||
|
void populateMcastFilterRegs();
|
||||||
|
|
||||||
|
// Write a MAC address into a high and low register pair on the MAC.
|
||||||
|
static void writeMACAddress(uint8_t const * MAC, uint32_t volatile * addrHighReg, uint32_t volatile * addrLowReg);
|
||||||
|
|
||||||
mbed_rtos_storage_thread_t thread_cb;
|
mbed_rtos_storage_thread_t thread_cb;
|
||||||
#if defined (STM32F767xx) || defined (STM32F769xx) || defined (STM32F777xx)\
|
#if defined (STM32F767xx) || defined (STM32F769xx) || defined (STM32F777xx)\
|
||||||
|| defined (STM32F779xx)
|
|| defined (STM32F779xx)
|
||||||
|
@ -176,6 +183,10 @@ private:
|
||||||
|
|
||||||
uint32_t phy_status;
|
uint32_t phy_status;
|
||||||
int phy_task_handle; /**< Handle for phy task event */
|
int phy_task_handle; /**< Handle for phy task event */
|
||||||
|
|
||||||
|
// Multicast subscribe information
|
||||||
|
std::array<uint8_t, 6> mcastMacs[MBED_CONF_STM32_EMAC_MAX_MCAST_SUBSCRIBES];
|
||||||
|
size_t numSubscribedMcastMacs;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* STM32_EMAC_H_ */
|
#endif /* STM32_EMAC_H_ */
|
||||||
|
|
Loading…
Reference in New Issue