Zoltan Hudak / ENC28J60-EMAC

Dependents:   MQTT_Hello MQTT_HelloENC28J60

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers enc28j60_emac.cpp Source File

enc28j60_emac.cpp

00001 /*
00002  * Copyright (c) 2019 Tobias Jaster
00003  *
00004  * Modified by Zoltan Hudak
00005  *
00006  * Licensed under the Apache License, Version 2.0 (the "License");
00007  * you may not use this file except in compliance with the License.
00008  * You may obtain a copy of the License at
00009  *
00010  *     http://www.apache.org/licenses/LICENSE-2.0
00011  *
00012  * Unless required by applicable law or agreed to in writing, software
00013  * distributed under the License is distributed on an "AS IS" BASIS,
00014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015  * See the License for the specific language governing permissions and
00016  * limitations under the License.
00017  */
00018 #include "mbed_interface.h"
00019 #include "mbed_wait_api.h"
00020 #include "mbed_assert.h"
00021 #include "netsocket/nsapi_types.h"
00022 #include "mbed_shared_queues.h"
00023 #include "enc28j60_emac.h"
00024 
00025 /**
00026  * @brief
00027  * @note
00028  * @param
00029  * @retval
00030  */
00031 ENC28J60_EMAC::ENC28J60_EMAC() :
00032     _enc28j60(new ENC28J60(ENC28J60_MOSI, ENC28J60_MISO, ENC28J60_SCK, ENC28J60_CS)),
00033     _prev_link_status_up(PHY_STATE_LINK_DOWN),
00034     _link_status_task_handle(0),
00035     _receive_task_handle(0),
00036     _memory_manager(NULL)
00037 { }
00038 
00039 /**
00040  * @brief
00041  * @note    Allocates buffer chain from a pool and filles it with incoming packet.
00042  * @param
00043  * @retval  Data from incoming packet
00044  */
00045 emac_mem_buf_t* ENC28J60_EMAC::low_level_input()
00046 {
00047     emac_mem_buf_t*     chain;
00048     emac_mem_buf_t*     buf;
00049     packet_t            packet;
00050 
00051     if (_enc28j60->getPacketInfo(&packet) != ENC28J60_ERROR_OK) {
00052         return NULL;
00053     }
00054 
00055     if (packet.payload.len == 0) {
00056         return NULL;
00057     }
00058 
00059     // Allocate a buffer chain from the memory pool.
00060     chain = _memory_manager->alloc_pool(packet.payload.len, ENC28J60_BUFF_ALIGNMENT);
00061     buf = chain;
00062 
00063     // Iterate through the buffer chain and fill it with packet payload.
00064     while (buf != NULL) {
00065         packet.payload.buf = (uint8_t*)_memory_manager->get_ptr(buf);
00066         packet.payload.len = (uint16_t) (_memory_manager->get_len(buf));
00067 
00068         // Fill the ethernet stack buffer with packet payload received by ENC28J60.
00069         _enc28j60->readPacket(&packet);
00070 
00071         buf = _memory_manager->get_next(buf);
00072     }
00073 
00074     // Return the buffer chain filled with packet payload.
00075     return chain;
00076 }
00077 
00078 /**
00079  * @brief   Receive task.
00080  * @note    Passes packet payload received by ENC28J60 to the ethernet stack
00081  * @param
00082  * @retval
00083  */
00084 void ENC28J60_EMAC::receive_task()
00085 {
00086     emac_mem_buf_t*     payload;
00087 
00088     _ethLockMutex.lock();
00089     payload = low_level_input();
00090     if (payload != NULL) {
00091         if (_emac_link_input_cb) {
00092             _emac_link_input_cb(payload);   // pass packet payload to the ethernet stack
00093             _memory_manager->free(payload);
00094         }
00095     }
00096 
00097     _enc28j60->freeRxBuffer();  // make room in ENC28J60 receive buffer for new packets
00098     _ethLockMutex.unlock();
00099 }
00100 
00101 
00102 /**
00103  * @brief
00104  * @note    Passes a packet payload from the ethernet stack to ENC28J60 for transmittion
00105  * @param
00106  * @retval
00107  */
00108 bool ENC28J60_EMAC::link_out(emac_mem_buf_t* buf)
00109 {
00110     emac_mem_buf_t*     chain = buf;
00111     packet_t            packet;
00112     enc28j60_error_t    error;
00113 
00114     if (buf == NULL) {
00115         return false;
00116     }
00117 
00118     _ethLockMutex.lock();
00119     // Iterate through the buffer chain and fill the packet with payload.
00120     while (buf != NULL) {
00121         packet.payload.buf = (uint8_t *) (_memory_manager->get_ptr(buf));
00122         packet.payload.len = _memory_manager->get_len(buf);
00123         error = _enc28j60->loadPacketInTxBuffer(&packet);
00124         if (error != ENC28J60_ERROR_OK) {
00125             _memory_manager->free(chain);
00126             _ethLockMutex.unlock();
00127             return false;
00128         }
00129 
00130         error = _enc28j60->transmitPacket(&packet);
00131         if (error != ENC28J60_ERROR_OK) {
00132             _memory_manager->free(chain);
00133             _ethLockMutex.unlock();
00134             return false;
00135         }
00136 
00137         buf = _memory_manager->get_next(buf);
00138     }
00139 
00140     _memory_manager->free(chain);
00141     _ethLockMutex.unlock();
00142 
00143     return true;
00144 }
00145 
00146 /**
00147  * @brief
00148  * @note
00149  * @param
00150  * @retval
00151  */
00152 void ENC28J60_EMAC::link_status_task()
00153 {
00154     uint16_t    phy_basic_status_reg_value = 0;
00155     bool        current_link_status_up = false;
00156 
00157     /* Get current status */
00158 
00159     _ethLockMutex.lock();
00160     _enc28j60->phyRead(PHSTAT2, &phy_basic_status_reg_value);
00161 
00162     current_link_status_up = (bool) ((phy_basic_status_reg_value & PHSTAT2_LSTAT) != 0);
00163 
00164     /* Compare with previous state */
00165     if (current_link_status_up != _prev_link_status_up) {
00166         if (_emac_link_state_cb) {
00167             _emac_link_state_cb(current_link_status_up);
00168         }
00169 
00170         _prev_link_status_up = current_link_status_up;
00171     }
00172 
00173     _ethLockMutex.unlock();
00174 }
00175 
00176 /**
00177  * @brief
00178  * @note
00179  * @param
00180  * @retval
00181  */
00182 bool ENC28J60_EMAC::power_up()
00183 {
00184     volatile uint32_t   timeout = 500;
00185     _enc28j60->writeOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV);
00186     while (_enc28j60->readReg(ESTAT_CLKRDY) == 0) {
00187         ThisThread::sleep_for(1ms);
00188         timeout--;
00189         if (timeout == 0) {
00190             return false;
00191         }
00192     }
00193 
00194     /* Trigger thread to deal with any RX packets that arrived
00195      * before receiver_thread was started */
00196     _receive_task_handle = mbed::mbed_event_queue()->call_every
00197         (
00198             RECEIVE_TASK_PERIOD_MS,
00199             mbed::callback(this, &ENC28J60_EMAC::receive_task)
00200         );
00201 
00202     _prev_link_status_up = PHY_STATE_LINK_DOWN;
00203     mbed::mbed_event_queue()->call(mbed::callback(this, &ENC28J60_EMAC::link_status_task));
00204 
00205     /* Allow the Link Status task to detect the initial link state */
00206     ThisThread::sleep_for(10ms);
00207     _link_status_task_handle = mbed::mbed_event_queue()->call_every
00208         (
00209             LINK_STATUS_TASK_PERIOD_MS,
00210             mbed::callback(this, &ENC28J60_EMAC::link_status_task)
00211         );
00212 
00213     return true;
00214 }
00215 
00216 /**
00217  * @brief
00218  * @note
00219  * @param
00220  * @retval
00221  */
00222 uint32_t ENC28J60_EMAC::get_mtu_size() const
00223 {
00224     return ENC28J60_ETH_MTU_SIZE;
00225 }
00226 
00227 /**
00228  * @brief
00229  * @note
00230  * @param
00231  * @retval
00232  */
00233 uint32_t ENC28J60_EMAC::get_align_preference() const
00234 {
00235     return ENC28J60_BUFF_ALIGNMENT;
00236 }
00237 
00238 /**
00239  * @brief
00240  * @note
00241  * @param
00242  * @retval
00243  */
00244 void ENC28J60_EMAC::get_ifname(char* name, uint8_t size) const
00245 {
00246     memcpy(name, ENC28J60_ETH_IF_NAME, (size < sizeof(ENC28J60_ETH_IF_NAME)) ? size : sizeof(ENC28J60_ETH_IF_NAME));
00247 }
00248 
00249 /**
00250  * @brief
00251  * @note
00252  * @param
00253  * @retval
00254  */
00255 uint8_t ENC28J60_EMAC::get_hwaddr_size() const
00256 {
00257     return ENC28J60_HWADDR_SIZE;
00258 }
00259 
00260 /**
00261  * @brief
00262  * @note
00263  * @param
00264  * @retval
00265  */
00266 bool ENC28J60_EMAC::get_hwaddr(uint8_t* addr) const
00267 {
00268     enc28j60_error_t    error = _enc28j60->readMacAddr((char*)addr);
00269     if (error == ENC28J60_ERROR_OK) {
00270         return true;
00271     }
00272     else {
00273         return false;
00274     }
00275 }
00276 
00277 /**
00278  * @brief
00279  * @note
00280  * @param
00281  * @retval
00282  */
00283 void ENC28J60_EMAC::set_hwaddr(const uint8_t* addr)
00284 {
00285     if (!addr) {
00286         return;
00287     }
00288 
00289     memcpy(_hwaddr, addr, sizeof _hwaddr);
00290     _ethLockMutex.lock();
00291 
00292     enc28j60_error_t    error = _enc28j60->writeMacAddr((char*)addr);
00293     _ethLockMutex.unlock();
00294     if (error) {
00295         return;
00296     }
00297 }
00298 
00299 /**
00300  * @brief
00301  * @note
00302  * @param
00303  * @retval
00304  */
00305 void ENC28J60_EMAC::set_link_input_cb(emac_link_input_cb_t input_cb)
00306 {
00307     _emac_link_input_cb = input_cb;
00308 }
00309 
00310 /**
00311  * @brief
00312  * @note
00313  * @param
00314  * @retval
00315  */
00316 void ENC28J60_EMAC::set_link_state_cb(emac_link_state_change_cb_t state_cb)
00317 {
00318     _emac_link_state_cb = state_cb;
00319 }
00320 
00321 /**
00322  * @brief
00323  * @note
00324  * @param
00325  * @retval
00326  */
00327 void ENC28J60_EMAC::add_multicast_group(const uint8_t* addr)
00328 {
00329     // No action for now
00330 }
00331 
00332 /**
00333  * @brief
00334  * @note
00335  * @param
00336  * @retval
00337  */
00338 void ENC28J60_EMAC::remove_multicast_group(const uint8_t* addr)
00339 {
00340     // No action for now
00341 }
00342 
00343 /**
00344  * @brief
00345  * @note
00346  * @param
00347  * @retval
00348  */
00349 void ENC28J60_EMAC::set_all_multicast(bool all)
00350 {
00351     // No action for now
00352 }
00353 
00354 /**
00355  * @brief
00356  * @note
00357  * @param
00358  * @retval
00359  */
00360 void ENC28J60_EMAC::power_down()
00361 {
00362     _enc28j60->disableMacRecv();
00363     if (_enc28j60->readReg(ESTAT_RXBUSY) != 0) {
00364         _enc28j60->enableMacRecv();
00365         return;
00366     }
00367 
00368     if (_enc28j60->readReg(ECON1_TXRTS) != 0) {
00369         _enc28j60->enableMacRecv();
00370         return;
00371     }
00372 
00373     _enc28j60->writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_VRPS);
00374     _enc28j60->writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV);
00375 }
00376 
00377 /**
00378  * @brief
00379  * @note
00380  * @param
00381  * @retval
00382  */
00383 void ENC28J60_EMAC::set_memory_manager(EMACMemoryManager& mem_mngr)
00384 {
00385     _memory_manager = &mem_mngr;
00386 }
00387 
00388 /**
00389  * @brief
00390  * @note
00391  * @param
00392  * @retval
00393  */
00394 ENC28J60_EMAC& ENC28J60_EMAC::get_instance()
00395 {
00396     static ENC28J60_EMAC    emac;
00397     return emac;
00398 }
00399 
00400 EMAC& EMAC::get_default_instance()
00401 {
00402     return ENC28J60_EMAC::get_instance();
00403 }