Skip to content

Commit 2fd9d83

Browse files
author
Jamie Smith
authored
Add multicast filter support to STM32 EMAC (#408)
1 parent 2f3a3c3 commit 2fd9d83

File tree

3 files changed

+176
-3
lines changed

3 files changed

+176
-3
lines changed

connectivity/drivers/emac/TARGET_STM/mbed_lib.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@
4242
"eth-phy-duplex-status": {
4343
"help" : "Duplex mask information in eth-phy-status-register",
4444
"value" : "0x0010"
45+
},
46+
"max-mcast-subscribes": {
47+
"help" : "Maximum supported number of multicast addresses that the application can subscribe to",
48+
"value" : "8"
4549
}
4650
},
4751
"target_overrides": {

connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp

Lines changed: 161 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "platform/mbed_power_mgmt.h"
2929
#include "platform/mbed_error.h"
3030
#include "CacheAlignedBuffer.h"
31+
#include "MbedCRC.h"
3132

3233
#include "stm32xx_emac_config.h"
3334
#include "stm32xx_emac.h"
@@ -294,6 +295,12 @@ bool STM32_EMAC::low_level_init_successful()
294295
/* HAL_ETH_Init returns TIMEOUT when Ethernet cable is not plugged */;
295296
}
296297

298+
// Set MAC address
299+
writeMACAddress(MACAddr, &EthHandle.Instance->MACA0HR, &EthHandle.Instance->MACA0LR);
300+
301+
// Enable multicast hash and perfect filter
302+
EthHandle.Instance->MACFFR = ETH_MACFFR_HM | ETH_MACFFR_HPF;
303+
297304
uint32_t TempRegisterValue;
298305
if (HAL_ETH_ReadPHYRegister(&EthHandle, 2, &TempRegisterValue) != HAL_OK) {
299306
tr_error("HAL_ETH_ReadPHYRegister 2 issue");
@@ -364,6 +371,12 @@ bool STM32_EMAC::low_level_init_successful()
364371
return false;
365372
}
366373

374+
// Set MAC address
375+
writeMACAddress(MACAddr, &EthHandle.Instance->MACA0HR, &EthHandle.Instance->MACA0LR);
376+
377+
// Enable multicast hash and perfect filter
378+
EthHandle.Instance->MACPFR = ETH_MACPFR_HMC | ETH_MACPFR_HPF;
379+
367380
memset(&TxConfig, 0, sizeof(ETH_TxPacketConfig));
368381
TxConfig.Attributes = ETH_TX_PACKETS_FEATURES_CSUM | ETH_TX_PACKETS_FEATURES_CRCPAD;
369382
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)
934947

935948
void STM32_EMAC::add_multicast_group(const uint8_t *addr)
936949
{
937-
/* No-op at this stage */
950+
if(numSubscribedMcastMacs >= MBED_CONF_STM32_EMAC_MAX_MCAST_SUBSCRIBES)
951+
{
952+
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);
953+
return;
954+
}
955+
956+
memcpy(mcastMacs[numSubscribedMcastMacs++].data(), addr, 6);
957+
populateMcastFilterRegs();
938958
}
939959

940960
void STM32_EMAC::remove_multicast_group(const uint8_t *addr)
941961
{
942-
/* No-op at this stage */
962+
// Find MAC address in the subscription list
963+
auto macsEndIter = std::begin(mcastMacs) + numSubscribedMcastMacs;
964+
auto toRemoveIter = std::find_if(std::begin(mcastMacs), macsEndIter, [&](auto element) {
965+
return memcmp(element.data(), addr, 6) == 0;
966+
});
967+
968+
if(toRemoveIter == macsEndIter)
969+
{
970+
tr_warning("Tried to remove mcast group that was not added");
971+
return;
972+
}
973+
974+
// Swap the MAC addr to be removed to the end of the list, if it is not there already
975+
auto lastElementIter = macsEndIter - 1;
976+
if(toRemoveIter != std::begin(mcastMacs) && toRemoveIter != lastElementIter)
977+
{
978+
std::swap(*toRemoveIter, *lastElementIter);
979+
}
980+
981+
// 'remove' the last element by changing the length
982+
numSubscribedMcastMacs--;
983+
984+
// Rebuild the MAC registers with that MAC removed.
985+
// Technically it would be more performance efficient to remove just this MAC address, but that gets complex
986+
// once you throw the hash filter into the mix. Unless you are subscribed to insane numbers of mcast addrs,
987+
// it's easier to just rebuild it all.
988+
populateMcastFilterRegs();
943989
}
944990

945991
void STM32_EMAC::set_all_multicast(bool all)
946992
{
947-
/* No-op at this stage */
993+
#if defined(ETH_IP_VERSION_V2)
994+
if(all)
995+
{
996+
EthHandle.Instance->MACPFR |= ETH_MACPFR_PM;
997+
}
998+
else
999+
{
1000+
EthHandle.Instance->MACPFR &= ~ETH_MACPFR_PM;
1001+
}
1002+
#else
1003+
if(all)
1004+
{
1005+
EthHandle.Instance->MACFFR |= ETH_MACFFR_PM;
1006+
}
1007+
else
1008+
{
1009+
EthHandle.Instance->MACFFR &= ~ETH_MACFFR_PM;
1010+
}
1011+
#endif
9481012
}
9491013

9501014
void STM32_EMAC::power_down()
@@ -966,6 +1030,100 @@ STM32_EMAC &STM32_EMAC::get_instance()
9661030
return emac;
9671031
}
9681032

1033+
void STM32_EMAC::populateMcastFilterRegs() {
1034+
const size_t NUM_PERFECT_FILTER_REGS = 3;
1035+
1036+
const size_t numPerfectFilterMacs = std::min(NUM_PERFECT_FILTER_REGS, numSubscribedMcastMacs);
1037+
const size_t numHashFilterMacs = numSubscribedMcastMacs - numPerfectFilterMacs;
1038+
1039+
for(size_t perfFiltIdx = 0; perfFiltIdx < NUM_PERFECT_FILTER_REGS; ++perfFiltIdx)
1040+
{
1041+
// Find MAC addr registers (they aren't in an array :/)
1042+
uint32_t volatile * highReg;
1043+
uint32_t volatile * lowReg;
1044+
1045+
if(perfFiltIdx == 0)
1046+
{
1047+
highReg = &EthHandle.Instance->MACA1HR;
1048+
lowReg = &EthHandle.Instance->MACA1LR;
1049+
}
1050+
else if(perfFiltIdx == 1)
1051+
{
1052+
highReg = &EthHandle.Instance->MACA2HR;
1053+
lowReg = &EthHandle.Instance->MACA2LR;
1054+
}
1055+
else
1056+
{
1057+
highReg = &EthHandle.Instance->MACA3HR;
1058+
lowReg = &EthHandle.Instance->MACA3LR;
1059+
}
1060+
1061+
if(perfFiltIdx < numPerfectFilterMacs)
1062+
{
1063+
tr_debug("Using perfect filtering for %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8,
1064+
mcastMacs[perfFiltIdx][0], mcastMacs[perfFiltIdx][1], mcastMacs[perfFiltIdx][2],
1065+
mcastMacs[perfFiltIdx][3], mcastMacs[perfFiltIdx][4], mcastMacs[perfFiltIdx][5]);
1066+
writeMACAddress(mcastMacs[perfFiltIdx].data(), highReg, lowReg);
1067+
}
1068+
else
1069+
{
1070+
// Write zeroes to disable this mac addr entry
1071+
*highReg = 0;
1072+
*lowReg = 0;
1073+
}
1074+
}
1075+
1076+
#if defined(ETH_IP_VERSION_V2)
1077+
uint32_t volatile * hashRegs[] = {
1078+
&EthHandle.Instance->MACHT1R,
1079+
&EthHandle.Instance->MACHT0R
1080+
};
1081+
#else
1082+
uint32_t volatile * hashRegs[] = {
1083+
&EthHandle.Instance->MACHTHR,
1084+
&EthHandle.Instance->MACHTLR
1085+
};
1086+
#endif
1087+
1088+
// Reset hash filter regs
1089+
*hashRegs[0] = 0;
1090+
*hashRegs[1] = 0;
1091+
1092+
// Note: as always, the datasheet description of how to do this CRC was vague and slightly wrong.
1093+
// This forum thread figured it out: https://community.st.com/t5/stm32-mcus-security/calculating-ethernet-multicast-filter-hash-value/td-p/416984
1094+
// What the datasheet SHOULD say is:
1095+
// 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
1096+
// Then, take the upper 6 bits and use that to index the hash table.
1097+
1098+
mbed::MbedCRC<POLY_32BIT_ANSI> crcCalc(0xFFFFFFFF, 0xFFFFFFFF, true, false);
1099+
for(size_t hashFiltIdx = 0; hashFiltIdx < numHashFilterMacs; ++hashFiltIdx)
1100+
{
1101+
auto & currMacAddr = mcastMacs[hashFiltIdx + numPerfectFilterMacs];
1102+
1103+
tr_debug("Using hash filtering for %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8,
1104+
currMacAddr[0], currMacAddr[1], currMacAddr[2],
1105+
currMacAddr[3], currMacAddr[4], currMacAddr[5]);
1106+
1107+
// Compute Ethernet CRC-32 of the MAC address
1108+
uint32_t crc;
1109+
crcCalc.compute(currMacAddr.data(), currMacAddr.size(), &crc);
1110+
1111+
// Take upper 6 bits
1112+
uint32_t hashVal = crc >> 26;
1113+
1114+
// Set correct bit in hash filter
1115+
*hashRegs[hashVal >> 5] |= (1 << (hashVal & 0x1F));
1116+
}
1117+
}
1118+
1119+
void STM32_EMAC::writeMACAddress(const uint8_t *MAC, volatile uint32_t *addrHighReg, volatile uint32_t *addrLowReg) {
1120+
/* Set MAC addr bits 32 to 47 */
1121+
*addrHighReg = (static_cast<uint32_t>(MAC[5]) << 8) | static_cast<uint32_t>(MAC[4]) | ETH_MACA1HR_AE_Msk;
1122+
/* Set MAC addr bits 0 to 31 */
1123+
*addrLowReg = (static_cast<uint32_t>(MAC[3]) << 24) | (static_cast<uint32_t>(MAC[2]) << 16) |
1124+
(static_cast<uint32_t>(MAC[1]) << 8) | static_cast<uint32_t>(MAC[0]);
1125+
}
1126+
9691127
// Weak so a module can override
9701128
MBED_WEAK EMAC &EMAC::get_default_instance()
9711129
{

connectivity/drivers/emac/TARGET_STM/stm32xx_emac.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,13 @@ class STM32_EMAC : public EMAC {
162162
void enable_interrupts();
163163
void disable_interrupts();
164164

165+
// Populate multicast filter registers with the contents of mcastMacs.
166+
// Uses the perfect filter registers first, then the hash filter.
167+
void populateMcastFilterRegs();
168+
169+
// Write a MAC address into a high and low register pair on the MAC.
170+
static void writeMACAddress(uint8_t const * MAC, uint32_t volatile * addrHighReg, uint32_t volatile * addrLowReg);
171+
165172
mbed_rtos_storage_thread_t thread_cb;
166173
#if defined (STM32F767xx) || defined (STM32F769xx) || defined (STM32F777xx)\
167174
|| defined (STM32F779xx)
@@ -176,6 +183,10 @@ class STM32_EMAC : public EMAC {
176183

177184
uint32_t phy_status;
178185
int phy_task_handle; /**< Handle for phy task event */
186+
187+
// Multicast subscribe information
188+
std::array<uint8_t, 6> mcastMacs[MBED_CONF_STM32_EMAC_MAX_MCAST_SUBSCRIBES];
189+
size_t numSubscribedMcastMacs;
179190
};
180191

181192
#endif /* STM32_EMAC_H_ */

0 commit comments

Comments
 (0)