|
| 1 | +#include "W5500EMAC.h" |
| 2 | +#include <Arduino.h> |
| 3 | +#include <SPI.h> |
| 4 | + |
| 5 | +#ifndef W5500_CS |
| 6 | +#define W5500_CS PIN_SPI_SS |
| 7 | +#endif |
| 8 | + |
| 9 | +#define W5500_BUFF_ALIGNMENT 4U |
| 10 | +#define W5500_ETH_MTU_SIZE 1500U |
| 11 | +#define W5500_ETH_IF_NAME "W5500" |
| 12 | + |
| 13 | +using namespace mbed; |
| 14 | +using namespace rtos; |
| 15 | +using namespace std::chrono_literals; |
| 16 | + |
| 17 | +#define W5500_RECEIVE_TASK_PERIOD_MS 20ms |
| 18 | +#define W5500_LINK_STATUS_TASK_PERIOD_MS 500ms |
| 19 | + |
| 20 | +#define SPI_ETHERNET_SETTINGS SPISettings(20000000, MSBFIRST, SPI_MODE0) |
| 21 | + |
| 22 | +W5500EMAC::W5500EMAC(SPIClass &_spi) : spi(_spi), driver(W5500_CS, _spi) { |
| 23 | +} |
| 24 | + |
| 25 | +W5500EMAC& W5500EMAC::get_instance(void) { |
| 26 | + static W5500EMAC emac; |
| 27 | + return emac; |
| 28 | +} |
| 29 | + |
| 30 | +/** |
| 31 | + * Return maximum transmission unit |
| 32 | + * |
| 33 | + * @return MTU in bytes |
| 34 | + */ |
| 35 | +uint32_t W5500EMAC::get_mtu_size(void) const { |
| 36 | + return W5500_ETH_MTU_SIZE; |
| 37 | +} |
| 38 | + |
| 39 | +/** |
| 40 | + * Gets memory buffer alignment preference |
| 41 | + * |
| 42 | + * Gets preferred memory buffer alignment of the Emac device. IP stack may |
| 43 | + * or may not align link out memory buffer chains using the alignment. |
| 44 | + * |
| 45 | + * @return Memory alignment requirement in bytes |
| 46 | + */ |
| 47 | +uint32_t W5500EMAC::get_align_preference(void) const { |
| 48 | + return W5500_BUFF_ALIGNMENT; |
| 49 | +} |
| 50 | + |
| 51 | +/** |
| 52 | + * Return interface name |
| 53 | + * |
| 54 | + * @param name Pointer to where the name should be written |
| 55 | + * @param size Maximum number of character to copy |
| 56 | + */ |
| 57 | +void W5500EMAC::get_ifname(char *name, uint8_t size) const { |
| 58 | + memcpy(name, W5500_ETH_IF_NAME, (size < sizeof(W5500_ETH_IF_NAME)) ? size : sizeof(W5500_ETH_IF_NAME)); |
| 59 | +} |
| 60 | + |
| 61 | +/** |
| 62 | + * Returns size of the underlying interface HW address size. |
| 63 | + * |
| 64 | + * @return HW address size in bytes |
| 65 | + */ |
| 66 | +uint8_t W5500EMAC::get_hwaddr_size(void) const { |
| 67 | + return W5500_HWADDR_SIZE; |
| 68 | +} |
| 69 | + |
| 70 | +/** |
| 71 | + * Return interface-supplied HW address |
| 72 | + * |
| 73 | + * Copies HW address to provided memory, @param addr has to be of correct |
| 74 | + * size see @a get_hwaddr_size |
| 75 | + * |
| 76 | + * HW address need not be provided if this interface does not have its own |
| 77 | + * HW address configuration; stack will choose address from central system |
| 78 | + * configuration if the function returns false and does not write to addr. |
| 79 | + * |
| 80 | + * @param addr HW address for underlying interface |
| 81 | + * @return true if HW address is available |
| 82 | + */ |
| 83 | +bool W5500EMAC::get_hwaddr(uint8_t *addr) const { |
| 84 | + memcpy(addr, macAddr, W5500_HWADDR_SIZE); |
| 85 | + return true; |
| 86 | +} |
| 87 | + |
| 88 | +/** |
| 89 | + * Set HW address for interface |
| 90 | + * |
| 91 | + * Provided address has to be of correct size, see @a get_hwaddr_size |
| 92 | + * |
| 93 | + * Called to set the MAC address to actually use - if @a get_hwaddr is |
| 94 | + * provided the stack would normally use that, but it could be overridden, |
| 95 | + * eg for test purposes. |
| 96 | + * |
| 97 | + * @param addr Address to be set |
| 98 | + */ |
| 99 | +void W5500EMAC::set_hwaddr(const uint8_t *addr) { |
| 100 | + memcpy(macAddr, addr, W5500_HWADDR_SIZE); |
| 101 | +} |
| 102 | + |
| 103 | +/** |
| 104 | + * Initializes the HW |
| 105 | + * |
| 106 | + * @return True on success, False in case of an error. |
| 107 | + */ |
| 108 | +bool W5500EMAC::power_up(void) { |
| 109 | + |
| 110 | + spi.begin(); |
| 111 | + spi.beginTransaction(SPI_ETHERNET_SETTINGS); |
| 112 | + bool ret = driver.begin(macAddr); |
| 113 | + spi.endTransaction(); |
| 114 | + if (!ret) |
| 115 | + return false; |
| 116 | + receiveTaskHandle = mbed::mbed_event_queue()->call_every(W5500_RECEIVE_TASK_PERIOD_MS, mbed::callback(this, &W5500EMAC::receiveTask)); |
| 117 | + linkStatusTaskHandle = mbed::mbed_event_queue()->call_every(W5500_LINK_STATUS_TASK_PERIOD_MS, mbed::callback(this, &W5500EMAC::linkStatusTask)); |
| 118 | + return true; |
| 119 | +} |
| 120 | + |
| 121 | +/** |
| 122 | + * Deinitializes the HW |
| 123 | + * |
| 124 | + */ |
| 125 | +void W5500EMAC::power_down(void) { |
| 126 | + mbed::mbed_event_queue()->cancel(receiveTaskHandle); |
| 127 | + mbed::mbed_event_queue()->cancel(linkStatusTaskHandle); |
| 128 | + driver.end(); |
| 129 | +} |
| 130 | + |
| 131 | +/** |
| 132 | + * Sends the packet over the link |
| 133 | + * |
| 134 | + * That can not be called from an interrupt context. |
| 135 | + * |
| 136 | + * @param buf Packet to be send |
| 137 | + * @return True if the packet was send successfully, False otherwise |
| 138 | + */ |
| 139 | +bool W5500EMAC::link_out(emac_mem_buf_t *buf) { |
| 140 | + |
| 141 | + if (buf == NULL) |
| 142 | + return false; |
| 143 | + |
| 144 | + // If buffer is chained or not aligned then make a contiguous aligned copy of it |
| 145 | + if (memoryManager->get_next(buf) || reinterpret_cast<uint32_t>(memoryManager->get_ptr(buf)) % W5500_BUFF_ALIGNMENT) { |
| 146 | + emac_mem_buf_t *copy_buf; |
| 147 | + copy_buf = memoryManager->alloc_heap(memoryManager->get_total_len(buf), W5500_BUFF_ALIGNMENT); |
| 148 | + if (NULL == copy_buf) { |
| 149 | + memoryManager->free(buf); |
| 150 | + return false; |
| 151 | + } |
| 152 | + |
| 153 | + // Copy to new buffer and free original |
| 154 | + memoryManager->copy(copy_buf, buf); |
| 155 | + memoryManager->free(buf); |
| 156 | + buf = copy_buf; |
| 157 | + } |
| 158 | + |
| 159 | + uint16_t len = memoryManager->get_len(buf); |
| 160 | + uint8_t *data = (uint8_t*) (memoryManager->get_ptr(buf)); |
| 161 | + spi.beginTransaction(SPI_ETHERNET_SETTINGS); |
| 162 | + bool ret = driver.sendFrame(data, len) == len; |
| 163 | + spi.endTransaction(); |
| 164 | + memoryManager->free(buf); |
| 165 | + |
| 166 | + return ret; |
| 167 | +} |
| 168 | + |
| 169 | +void W5500EMAC::linkStatusTask() { |
| 170 | + spi.beginTransaction(SPI_ETHERNET_SETTINGS); |
| 171 | + static bool linked = false; |
| 172 | + if (linked != driver.isLinked()) { |
| 173 | + linked = driver.isLinked(); |
| 174 | + emac_link_state_cb(linked); |
| 175 | + } |
| 176 | + spi.endTransaction(); |
| 177 | +} |
| 178 | + |
| 179 | +void W5500EMAC::receiveTask() { |
| 180 | + if (!emac_link_input_cb) |
| 181 | + return; |
| 182 | + spi.beginTransaction(SPI_ETHERNET_SETTINGS); |
| 183 | + emac_mem_buf_t *buf = driver.readFrame(memoryManager); |
| 184 | + spi.endTransaction(); |
| 185 | + if (buf != NULL) { |
| 186 | + emac_link_input_cb(buf); |
| 187 | + } |
| 188 | +} |
| 189 | + |
| 190 | +/** |
| 191 | + * Sets a callback that needs to be called for packets received for that |
| 192 | + * interface |
| 193 | + * |
| 194 | + * @param input_cb Function to be register as a callback |
| 195 | + */ |
| 196 | +void W5500EMAC::set_link_input_cb(emac_link_input_cb_t input_cb) { |
| 197 | + emac_link_input_cb = input_cb; |
| 198 | +} |
| 199 | + |
| 200 | +/** |
| 201 | + * Sets a callback that needs to be called on link status changes for given |
| 202 | + * interface |
| 203 | + * |
| 204 | + * @param state_cb Function to be register as a callback |
| 205 | + */ |
| 206 | +void W5500EMAC::set_link_state_cb(emac_link_state_change_cb_t state_cb) { |
| 207 | + emac_link_state_cb = state_cb; |
| 208 | +} |
| 209 | + |
| 210 | +/** Add device to a multicast group |
| 211 | + * |
| 212 | + * @param address A multicast group hardware address |
| 213 | + */ |
| 214 | +void W5500EMAC::add_multicast_group(const uint8_t *address) { |
| 215 | + |
| 216 | +} |
| 217 | + |
| 218 | +/** Remove device from a multicast group |
| 219 | + * |
| 220 | + * @param address A multicast group hardware address |
| 221 | + */ |
| 222 | +void W5500EMAC::remove_multicast_group(const uint8_t *address) { |
| 223 | + |
| 224 | +} |
| 225 | + |
| 226 | +/** Request reception of all multicast packets |
| 227 | + * |
| 228 | + * @param all True to receive all multicasts |
| 229 | + * False to receive only multicasts addressed to specified groups |
| 230 | + */ |
| 231 | +void W5500EMAC::set_all_multicast(bool all) { |
| 232 | + |
| 233 | +} |
| 234 | + |
| 235 | +/** Sets memory manager that is used to handle memory buffers |
| 236 | + * |
| 237 | + * @param mem_mngr Pointer to memory manager |
| 238 | + */ |
| 239 | +void W5500EMAC::set_memory_manager(EMACMemoryManager &mem_mngr) { |
| 240 | + memoryManager = &mem_mngr; |
| 241 | +} |
| 242 | + |
| 243 | +EMAC& EMAC::get_default_instance() { |
| 244 | + return W5500EMAC::get_instance(); |
| 245 | +} |
0 commit comments