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+
2634namespace 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 ;
0 commit comments