Skip to content

Commit 786f156

Browse files
Phy task and Tx seem to work!
1 parent a3bfe45 commit 786f156

File tree

7 files changed

+228
-33
lines changed

7 files changed

+228
-33
lines changed

connectivity/drivers/emac/TARGET_STM/STM32EthMACv2.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ namespace mbed {
128128
memset(&desc, 0, sizeof(stm32_ethv2::EthRxDescriptor));
129129

130130
// Store buffer address
131-
desc.formats.toDMA.buffer1Addr = memory_manager->get_ptr(buffer);
131+
desc.formats.toDMA.buffer1Addr = buffer;
132132

133133
// Configure descriptor
134134
desc.formats.toDMA.buffer1Valid = true;

connectivity/drivers/emac/include/CompositeEMAC.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
#include "EMAC.h"
2121
#include "NonCopyable.h"
22+
#include "Thread.h"
23+
2224
#include <atomic>
2325

2426
namespace mbed
@@ -345,12 +347,22 @@ class CompositeEMAC : public EMAC
345347
/// Pointer to memory manager for the EMAC
346348
EMACMemoryManager * memory_manager = nullptr;
347349

350+
// Callbacks to the MAC
351+
emac_link_state_change_cb_t linkStateCallback{};
352+
emac_link_input_cb_t linkInputCallback{};
353+
348354
// Instances of each of the 4 component classes
349355
PhyDriver * phy = nullptr;
350356
RxDMA & rxDMA;
351357
TxDMA & txDMA;
352358
MACDriver & mac;
353359

360+
// Event queue handle for phy task
361+
int phyTaskHandle;
362+
363+
// Thread running inside the MAC. Processes interrupts (both Tx and Rx) and receives packets.
364+
rtos::Thread * macThread = nullptr;
365+
354366
// State of the MAC
355367
enum class PowerState {
356368
OFF = 0,
@@ -360,6 +372,14 @@ class CompositeEMAC : public EMAC
360372

361373
std::atomic<PowerState> state = PowerState::OFF;
362374

375+
// State of the link
376+
enum class LinkState {
377+
DOWN,
378+
UP
379+
};
380+
381+
std::atomic<LinkState> linkState = LinkState::DOWN;
382+
363383
// Multicast subscribe information
364384
MACAddress mcastMacs[MBED_CONF_NSAPI_EMAC_MAX_MCAST_SUBSCRIBES];
365385

@@ -368,12 +388,22 @@ class CompositeEMAC : public EMAC
368388
// and power up the MAC.
369389
size_t numSubscribedMcastMacs;
370390

391+
// Mutex that must be acquired before interacting with the MAC. This is used to protect against, for example,
392+
// one thread calling power_down() while the phy task is still running.
393+
rtos::Mutex macOpsMutex;
394+
371395
/// Subclass should call this when a receive interrupt is detected
372396
void rxISR();
373397

374398
/// Subclass should call this when a transmit complete interrupt is detected
375399
void txISR();
376400

401+
/// Called periodically to poll the phy and bring link up/down
402+
void phyTask();
403+
404+
/// Run in its own thread to service the MAC.
405+
void macTask();
406+
377407
/// Constructor. Should be called by subclass.
378408
CompositeEMAC(TxDMA & txDMA, RxDMA & rxDMA, MACDriver & macDriver):
379409
rxDMA(rxDMA),

connectivity/drivers/emac/sources/CompositeEMAC.cpp

Lines changed: 164 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,131 @@
1818
#include "CompositeEMAC.h"
1919

2020
#include <mbed_trace.h>
21+
#include <mbed_shared_queues.h>
22+
#include <ThisThread.h>
2123

2224
#include <algorithm>
2325

2426
#define TRACE_GROUP "CEMAC"
2527

28+
// Flags for the MAC thread
29+
static uint32_t THREAD_FLAG_TX_DESC_AVAILABLE = 1 << 0;
30+
static uint32_t THREAD_FLAG_RX_DESC_AVAILABLE = 1 << 1;
31+
static uint32_t THREAD_FLAG_RX_MEM_AVAILABLE = 1 << 2;
32+
static uint32_t THREAD_FLAG_SHUTDOWN = 1 << 3;
33+
2634
namespace mbed {
2735
// Defined in PhyDrivers.cpp
2836
CompositeEMAC::PhyDriver * mbed_get_eth_phy_driver();
2937

3038
void CompositeEMAC::rxISR() {
39+
// Note: Not locking mutex here as this is an ISR and should be able to run while the MAC thread is executing.
40+
if(rxDMA.rxHasPackets_ISR()) {
41+
// Reclaimable descriptor or complete packet detected.
42+
macThread->flags_set(THREAD_FLAG_RX_DESC_AVAILABLE);
43+
}
3144
}
3245

3346
void CompositeEMAC::txISR() {
47+
// Reclaimable Tx descriptor detected
48+
macThread->flags_set(THREAD_FLAG_TX_DESC_AVAILABLE);
49+
}
50+
51+
void CompositeEMAC::phyTask() {
52+
rtos::ScopedMutexLock lock(macOpsMutex);
53+
54+
// If the MAC has been powered off, bail immediately (this event is about to be canceled)
55+
if(state == PowerState::OFF) {
56+
return;
57+
}
58+
59+
bool phyLinkState = false;
60+
if(phy->checkLinkStatus(phyLinkState) != ErrCode::SUCCESS) {
61+
tr_error("phyTask(): Phy failed to check link status");
62+
}
63+
64+
if(linkState == LinkState::UP) {
65+
if(!phyLinkState) {
66+
tr_info("Link down");
67+
linkState = LinkState::DOWN;
68+
69+
if(mac.disable() != ErrCode::SUCCESS) {
70+
tr_error("phyTask(): Mac failed to disable");
71+
}
72+
73+
linkStateCallback(false);
74+
}
75+
}
76+
else { // LinkState::DOWN
77+
if(phyLinkState) {
78+
Duplex duplex;
79+
LinkSpeed speed;
80+
if(phy->checkLinkType(speed, duplex)!= ErrCode::SUCCESS) {
81+
tr_error("phyTask(): Phy failed to check link type");
82+
return;
83+
}
84+
85+
char const * speedStr;
86+
if(speed == LinkSpeed::LINK_10MBIT) {
87+
speedStr = "10Mbps";
88+
}
89+
else if(speed == LinkSpeed::LINK_100MBIT) {
90+
speedStr = "100Mbps";
91+
}
92+
else {
93+
speedStr = "1Gbps";
94+
}
95+
96+
tr_info("Link up at %s %s duplex", speedStr, duplex == Duplex::FULL ? "full" : "half");
97+
98+
linkState = LinkState::UP;
99+
if(mac.enable(speed, duplex) != ErrCode::SUCCESS) {
100+
tr_error("phyTask(): Mac failed to enable");
101+
}
102+
103+
linkStateCallback(true);
104+
}
105+
}
106+
}
107+
108+
void CompositeEMAC::macTask() {
109+
while(true)
110+
{
111+
// Wait for something to happen
112+
uint32_t flags = rtos::ThisThread::flags_wait_any(THREAD_FLAG_TX_DESC_AVAILABLE | THREAD_FLAG_SHUTDOWN | THREAD_FLAG_RX_DESC_AVAILABLE | THREAD_FLAG_RX_MEM_AVAILABLE);
113+
if(flags & THREAD_FLAG_SHUTDOWN)
114+
{
115+
return;
116+
}
117+
118+
// Now lock the mutex for the other cases
119+
rtos::ScopedMutexLock lock(macOpsMutex);
120+
121+
if(flags & THREAD_FLAG_RX_DESC_AVAILABLE)
122+
{
123+
// Receive any available packets.
124+
// Note that if the ISR was delayed, we might get multiple packets per ISR, so we need to loop.
125+
while(true)
126+
{
127+
auto * packet = rxDMA.dequeuePacket();
128+
if(!packet) {
129+
break;
130+
}
131+
132+
linkInputCallback(packet);
133+
134+
// Rebuild descriptors if possible
135+
rxDMA.rebuildDescriptors();
136+
}
137+
}
138+
if(flags & THREAD_FLAG_TX_DESC_AVAILABLE)
139+
{
140+
txDMA.reclaimTxDescs();
141+
}
142+
if(flags & THREAD_FLAG_RX_MEM_AVAILABLE) {
143+
rxDMA.rebuildDescriptors();
144+
}
145+
}
34146
}
35147

36148
void CompositeEMAC::get_ifname(char *name, uint8_t size) const {
@@ -42,6 +154,8 @@ namespace mbed {
42154
}
43155

44156
void CompositeEMAC::set_hwaddr(const uint8_t *addr) {
157+
rtos::ScopedMutexLock lock(macOpsMutex);
158+
45159
if(state != PowerState::ON_NO_LINK) {
46160
tr_err("MAC address can only be set after power up, before link up!");
47161
return;
@@ -54,11 +168,19 @@ namespace mbed {
54168

55169
bool CompositeEMAC::link_out(emac_mem_buf_t *buf)
56170
{
171+
rtos::ScopedMutexLock lock(macOpsMutex);
172+
const auto ret = txDMA.txPacket(buf);
57173

174+
if(ret != ErrCode::SUCCESS) {
175+
tr_warn("link_out(): Tx failed.");
176+
}
177+
return ret == ErrCode::SUCCESS;
58178
}
59179

60180
bool CompositeEMAC::power_up()
61181
{
182+
rtos::ScopedMutexLock lock(macOpsMutex);
183+
62184
if(state != PowerState::OFF) {
63185
tr_err("power_up(): Already powered up!");
64186
return false;
@@ -85,6 +207,12 @@ namespace mbed {
85207
return false;
86208
}
87209

210+
// Init DMA rungs
211+
if(txDMA.init() != ErrCode::SUCCESS || rxDMA.init() != ErrCode::SUCCESS) {
212+
tr_err("power_up(): Failed to init DMA!");
213+
return false;
214+
}
215+
88216
// Initialize the PHY
89217
phy->setMAC(&mac);
90218
if(phy->init() != ErrCode::SUCCESS) {
@@ -93,16 +221,32 @@ namespace mbed {
93221
}
94222

95223
state = PowerState::ON_NO_LINK;
224+
225+
// Start phy task
226+
phyTaskHandle = mbed_event_queue()->call_every(std::chrono::milliseconds(MBED_CONF_NSAPI_EMAC_PHY_POLL_PERIOD),
227+
callback(this, &CompositeEMAC::phyTask));
228+
229+
// Start MAC thread.
230+
// We want to run this thread at high priority since reclaiming descriptors generally needs to happen quickly
231+
// for the application to use the network at full speed.
232+
macThread = new rtos::Thread(osPriorityHigh, 2048, nullptr, "EMAC Thread");
233+
macThread->start(callback(this, &CompositeEMAC::macTask));
234+
96235
return true;
97236
}
98237

99238
void CompositeEMAC::power_down() {
100-
// TODO stop phy task
239+
// Stop MAC thread (don't need to lock mutex for this)
240+
macThread->flags_set(THREAD_FLAG_SHUTDOWN);
241+
macThread->join();
242+
delete macThread;
101243

102-
state = PowerState::OFF;
244+
rtos::ScopedMutexLock lock(macOpsMutex);
103245

104-
// TODO sync with other thread(s), ensure that no other threads are accessing the MAC
105-
// (lock mutex?)
246+
mbed_event_queue()->cancel(phyTaskHandle);
247+
248+
state = PowerState::OFF;
249+
linkState = LinkState::DOWN;
106250

107251
// Clear multicast filter, so that we start with a clean slate next time
108252
if(mac.clearMcastFilter() != ErrCode::SUCCESS) {
@@ -129,13 +273,25 @@ namespace mbed {
129273
}
130274

131275
void CompositeEMAC::set_link_input_cb(emac_link_input_cb_t input_cb) {
276+
if(state != PowerState::OFF) {
277+
tr_err("Not available while MAC is on!");
278+
return;
279+
}
280+
linkInputCallback = input_cb;
132281
}
133282

134283
void CompositeEMAC::set_link_state_cb(emac_link_state_change_cb_t state_cb) {
284+
if(state != PowerState::OFF) {
285+
tr_err("Not available while MAC is on!");
286+
return;
287+
}
288+
linkStateCallback = state_cb;
135289
}
136290

137291
void CompositeEMAC::add_multicast_group(const uint8_t *address)
138292
{
293+
rtos::ScopedMutexLock lock(macOpsMutex);
294+
139295
if(state == PowerState::OFF) {
140296
tr_err("Not available while MAC is off!");
141297
return;
@@ -159,6 +315,8 @@ namespace mbed {
159315
}
160316

161317
void CompositeEMAC::remove_multicast_group(const uint8_t *address) {
318+
rtos::ScopedMutexLock lock(macOpsMutex);
319+
162320
if(state == PowerState::OFF) {
163321
tr_err("Not available while MAC is off!");
164322
return;
@@ -205,6 +363,8 @@ namespace mbed {
205363
}
206364

207365
void CompositeEMAC::set_all_multicast(bool all) {
366+
rtos::ScopedMutexLock lock(macOpsMutex);
367+
208368
if(state == PowerState::OFF) {
209369
tr_err("Not available while MAC is off!");
210370
return;

connectivity/netsocket/include/netsocket/EMAC.h

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,10 @@ class EMAC {
118118
/**
119119
* Sends the packet over the link
120120
*
121-
* That can not be called from an interrupt context.
121+
* Will not be called from an interrupt context.
122122
*
123-
* @param buf Packet to be send
124-
* @return True if the packet was send successfully, False otherwise
123+
* @param buf Packet to be sent. This packet is now owned by the MAC and must be freed in all cases.
124+
* @return True if the packet was sent successfully, False otherwise
125125
*/
126126
virtual bool link_out(emac_mem_buf_t *buf) = 0;
127127

@@ -139,16 +139,24 @@ class EMAC {
139139
virtual void power_down() = 0;
140140

141141
/**
142-
* Sets a callback that needs to be called for packets received for that interface
142+
* Sets a callback that needs to be called for packets received for this interface.
143143
*
144-
* @param input_cb Function to be register as a callback
144+
* Note that the callback function will contain appropriate locking such that may be called from any OS thread.
145+
* However, it shall not be called from an interrupt.
146+
*
147+
* Also note that the callback will take ownership of the passed packet and must free it in all cases.
148+
*
149+
* @param input_cb Function to be registered as a callback
145150
*/
146151
virtual void set_link_input_cb(emac_link_input_cb_t input_cb) = 0;
147152

148153
/**
149154
* Sets a callback that needs to be called on link status changes for given interface
150155
*
151-
* @param state_cb Function to be register as a callback
156+
* Note that the callback function will contain appropriate locking such that may be called from any OS thread.
157+
* However, it shall not be called from an interrupt.
158+
*
159+
* @param state_cb Function to be registered as a callback
152160
*/
153161
virtual void set_link_state_cb(emac_link_state_change_cb_t state_cb) = 0;
154162

@@ -184,11 +192,4 @@ class EMAC {
184192
virtual void set_memory_manager(EMACMemoryManager &mem_mngr) = 0;
185193
};
186194

187-
188-
/** These need to be defined by targets wishing to provide an Ethernet driver using EMAC interface. It will
189-
* be used by the EMACInterface class's default constructor to initialize the networking subsystem.
190-
*/
191-
//extern const emac_interface_ops_t mbed_emac_eth_ops_default;
192-
//extern void *mbed_emac_eth_hw_default;
193-
194195
#endif /* EMAC_H */

connectivity/netsocket/mbed_lib.json5

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@
101101
"emac-phy-mdio-address": {
102102
"help": "MDIO address of the phy chip. Usually set with strapping pins. NOTE: 0 is *supposed* to be reserved as the general call address but lots of phy chips use it anyway.",
103103
"value": null
104+
},
105+
"emac-phy-poll-period": {
106+
"help": "How often (in ms) to poll the Ethernet PHY and check for link status changes.",
107+
"value": 100
104108
}
105109
},
106110
"target_overrides": {

0 commit comments

Comments
 (0)