26#include <interfaces/endianness.h>
27#include <kernel/scheduler/scheduler.h>
33#include "kernel/kernel.h"
41static long long intoUntil(
int timeout)
43 return timeout == -1 ? -1 : getTime() +
msToNs(timeout);
50 bus_config.mode = SPI::Mode::MODE_0;
51 bus_config.bitOrder = SPI::Order::MSB_FIRST;
52 bus_config.byteOrder = SPI::Order::MSB_FIRST;
59 : intn(intn), slave(bus, cs, getSpiBusConfig(clock_divider))
64 for (
int i = 0; i < NUM_THREAD_WAIT_INFOS; i++)
66 wait_infos[i].sock_n = -1;
67 wait_infos[i].irq_mask = 0;
68 wait_infos[i].irq = 0;
69 wait_infos[i].thread =
nullptr;
73 for (
int i = 0; i < NUM_SOCKETS; i++)
75 socket_infos[i].mode = Wiz5500::SocketMode::CLOSED;
76 socket_infos[i].irq_mask = 0;
84 Lock<FastMutex> l(mutex);
90 Lock<FastMutex> l(mutex);
91 on_dest_unreachable = cb;
96 Lock<FastMutex> l(mutex);
103 Lock<FastMutex> l(mutex);
106 bool full_duplex = (phycfg & (1 << 2)) != 0;
107 bool based_100mbps = (phycfg & (1 << 1)) != 0;
108 bool link_up = (phycfg & (1 << 0)) != 0;
110 return {full_duplex, based_100mbps, link_up};
115 Lock<FastMutex> l(mutex);
124 for (
int i = 0; i < NUM_SOCKETS; i++)
132 intn_thread->IRQwakeup();
133 if (intn_thread->IRQgetPriority() >
134 miosix::Thread::IRQgetCurrentThread()->IRQgetPriority())
136 miosix::Scheduler::IRQfindNextThread();
143 Lock<FastMutex> l(mutex);
149 Lock<FastMutex> l(mutex);
155 Lock<FastMutex> l(mutex);
161 Lock<FastMutex> l(mutex);
166 uint16_t dst_port,
int timeout)
169 long long until = intoUntil(timeout);
171 Lock<FastMutex> l(mutex);
174 if (socket_infos[sock_n].mode != Wiz5500::SocketMode::CLOSED)
199 int irq = waitForSocketIrq(
200 l, sock_n, Wiz::Socket::Irq::CON | Wiz::Socket::Irq::DISCON, until);
203 if ((irq & Wiz::Socket::Irq::CON) == 0)
206 socket_infos[sock_n].mode = Wiz5500::SocketMode::TCP;
211 uint16_t& dst_port,
int timeout)
214 long long until = intoUntil(timeout);
216 Lock<FastMutex> l(mutex);
219 if (socket_infos[sock_n].mode != Wiz5500::SocketMode::CLOSED)
242 waitForSocketIrq(l, sock_n,
243 Wiz::Socket::Irq::CON | Wiz::Socket::Irq::DISCON |
244 Wiz::Socket::Irq::TIMEOUT,
248 if ((irq & Wiz::Socket::Irq::CON) == 0)
256 socket_infos[sock_n].mode = Wiz5500::SocketMode::TCP;
261 uint16_t dst_port,
int timeout)
266 Lock<FastMutex> l(mutex);
269 if (socket_infos[sock_n].mode != Wiz5500::SocketMode::CLOSED)
289 socket_infos[sock_n].mode = Wiz5500::SocketMode::UDP;
296 long long until = intoUntil(timeout);
298 Lock<FastMutex> l(mutex);
301 if (socket_infos[sock_n].mode == Wiz5500::SocketMode::CLOSED)
305 uint16_t start_addr =
318 int irq = waitForSocketIrq(l, sock_n, Wiz::Socket::Irq::SEND_OK, until);
321 if ((irq & Wiz::Socket::Irq::SEND_OK) == 0)
330 long long until = intoUntil(timeout);
332 Lock<FastMutex> l(mutex);
335 if (socket_infos[sock_n].mode != Wiz5500::SocketMode::TCP)
340 int irq = waitForSocketIrq(
341 l, sock_n, Wiz::Socket::Irq::RECV | Wiz::Socket::Irq::DISCON, until);
342 if ((irq & Wiz::Socket::Irq::RECV) == 0)
362 return recv_len < len ? recv_len : -1;
366 uint16_t& dst_port,
int timeout)
369 long long until = intoUntil(timeout);
371 Lock<FastMutex> l(mutex);
374 if (socket_infos[sock_n].mode != Wiz5500::SocketMode::UDP)
379 int irq = waitForSocketIrq(
380 l, sock_n, Wiz::Socket::Irq::RECV | Wiz::Socket::Irq::DISCON, until);
381 if ((irq & Wiz::Socket::Irq::RECV) == 0)
392 reinterpret_cast<uint8_t*
>(&dst_ip),
sizeof(
WizIp));
393 addr +=
sizeof(
WizIp);
395 reinterpret_cast<uint8_t*
>(&dst_port),
sizeof(uint16_t));
396 addr +=
sizeof(uint16_t);
401 reinterpret_cast<uint8_t*
>(&what),
sizeof(uint16_t));
402 addr +=
sizeof(uint16_t);
405 recv_len -=
sizeof(
WizIp) +
sizeof(uint16_t) +
sizeof(uint16_t);
408 uint16_t read_len = std::min(
static_cast<size_t>(recv_len), len);
425 long long until = intoUntil(timeout);
427 Lock<FastMutex> l(mutex);
430 if (socket_infos[sock_n].mode == Wiz5500::SocketMode::CLOSED)
436 waitForSocketIrq(l, sock_n, Wiz::Socket::Irq::DISCON, until);
437 socket_infos[sock_n].mode = Wiz5500::SocketMode::CLOSED;
440TimedWaitResult Wiz5500::waitForINTn(Lock<FastMutex>& l,
long long until)
442 TimedWaitResult result = TimedWaitResult::NoTimeout;
444 Unlock<FastMutex> ul(l);
445 FastInterruptDisableLock il;
447 intn_thread = Thread::IRQgetCurrentThread();
449 while (intn.value() != 0 && result == TimedWaitResult::NoTimeout)
451 long long now = getTime();
460 result = Thread::IRQenableIrqAndTimedWait(il, until);
465 intn_thread =
nullptr;
470int Wiz5500::waitForSocketIrq(miosix::Lock<miosix::FastMutex>& l,
int sock_n,
471 uint8_t irq_mask,
long long until)
474 if ((socket_infos[sock_n].irq_mask & irq_mask) != 0)
478 socket_infos[sock_n].irq_mask |= irq_mask;
480 socket_infos[sock_n].irq_mask);
482 Thread* this_thread = Thread::getCurrentThread();
486 while (i < NUM_THREAD_WAIT_INFOS)
488 if (wait_infos[i].sock_n == -1)
490 wait_infos[i].sock_n = sock_n;
491 wait_infos[i].irq_mask = irq_mask;
492 wait_infos[i].irq = 0;
493 wait_infos[i].thread = this_thread;
501 if (i == NUM_THREAD_WAIT_INFOS)
504 TimedWaitResult result = TimedWaitResult::NoTimeout;
506 if (interrupt_service_thread !=
nullptr)
509 while (wait_infos[i].irq == 0 && result == TimedWaitResult::NoTimeout &&
510 interrupt_service_thread != this_thread)
514 Unlock<FastMutex> ul(l);
515 result = Thread::timedWait(until);
519 Unlock<FastMutex> ul(l);
527 interrupt_service_thread = this_thread;
530 while (interrupt_service_thread == this_thread)
533 result = runInterruptServiceRoutine(l, until);
537 if (wait_infos[i].irq != 0 || result == TimedWaitResult::Timeout)
539 Thread* new_interrupt_service_thread =
nullptr;
541 for (
int j = 0; j < NUM_THREAD_WAIT_INFOS; j++)
543 if (wait_infos[j].irq == 0 && wait_infos[j].sock_n != -1 &&
546 new_interrupt_service_thread = wait_infos[j].thread;
553 interrupt_service_thread = new_interrupt_service_thread;
554 if (interrupt_service_thread)
555 interrupt_service_thread->wakeup();
560 wait_infos[i].sock_n = -1;
563 socket_infos[sock_n].irq_mask &= ~irq_mask;
565 socket_infos[sock_n].irq_mask);
567 return wait_infos[i].irq;
570TimedWaitResult Wiz5500::runInterruptServiceRoutine(Lock<FastMutex>& l,
574 if (waitForINTn(l, until) == TimedWaitResult::Timeout)
575 return TimedWaitResult::Timeout;
583 uint8_t sn_ir[NUM_SOCKETS] = {};
587 for (
int i = 0; i < NUM_SOCKETS; i++)
597 for (
int i = 0; i < NUM_THREAD_WAIT_INFOS; i++)
599 if (wait_infos[i].sock_n != -1)
601 int sock_n = wait_infos[i].sock_n;
602 int irq = sn_ir[sock_n] & wait_infos[i].irq_mask;
606 wait_infos[i].irq = irq;
607 wait_infos[i].thread->wakeup();
613 if (ir & Wiz::Common::Irq::CONFLICT)
615 auto cb = on_ip_conflict;
618 Unlock<FastMutex> ul(l);
623 if (ir & Wiz::Common::Irq::UNREACH)
625 auto cb = on_dest_unreachable;
631 Unlock<FastMutex> ul(l);
636 return TimedWaitResult::NoTimeout;
639void Wiz5500::spiRead(uint8_t block, uint16_t address, uint8_t* data,
652void Wiz5500::spiWrite(uint8_t block, uint16_t address,
const uint8_t* data,
665uint8_t Wiz5500::spiRead8(uint8_t block, uint16_t address)
668 spiRead(block, address, &data, 1);
672uint16_t Wiz5500::spiRead16(uint8_t block, uint16_t address)
675 spiRead(block, address,
reinterpret_cast<uint8_t*
>(&data),
677 return fromBigEndian16(data);
680WizIp Wiz5500::spiReadIp(uint8_t block, uint16_t address)
683 spiRead(block, address,
reinterpret_cast<uint8_t*
>(&data),
sizeof(
WizIp));
694void Wiz5500::spiWrite8(uint8_t block, uint16_t address, uint8_t data)
696 spiWrite(block, address, &data, 1);
699void Wiz5500::spiWrite16(uint8_t block, uint16_t address, uint16_t data)
701 data = toBigEndian16(data);
702 spiWrite(block, address,
reinterpret_cast<uint8_t*
>(&data),
706void Wiz5500::spiWriteIp(uint8_t block, uint16_t address,
WizIp data)
708 spiWrite(block, address,
reinterpret_cast<uint8_t*
>(&data),
sizeof(
WizIp));
711void Wiz5500::spiWriteMac(uint8_t block, uint16_t address,
WizMac data)
713 spiWrite(block, address,
reinterpret_cast<uint8_t*
>(&data),
sizeof(
WizMac));
constexpr long long INTN_TIMEOUT
Driver for STM32 low level SPI peripheral.
virtual void write16(uint16_t data)=0
Writes 16 bits to the bus.
virtual void deselect(GpioType cs)=0
Deselects the slave.
virtual uint8_t read()=0
Reads 8 bits from the bus.
virtual void select(GpioType cs)=0
Selects the slave.
virtual void write(uint8_t data)=0
Writes 8 bits to the bus.
virtual void configure(SPIBusConfig config)=0
Configures the bus with the provided configuration parameters.
ssize_t recvfrom(int sock_n, uint8_t *data, size_t len, WizIp &dst_ip, uint16_t &dst_port, int timeout=-1)
Receive data from the socket (works only in UDP).
std::function< void()> OnIpConflictCb
void setSourceIp(WizIp ip)
Set the device IP address.
bool openUdp(int sock_n, uint16_t src_port, WizIp dst_ip, uint16_t dst_port, int timeout=-1)
Open a simple UDP socket.
void setSourceMac(WizMac mac)
Set the device MAC address.
void setOnIpConflict(OnIpConflictCb cb)
Sets the callback to be invoked when the device detects an IP. conflict.
std::function< void(WizIp, uint16_t)> OnDestUnreachableCb
bool checkVersion()
Checks the VERSION register. Can be used to detect device presence.
void setGatewayIp(WizIp ip)
Set global gateway ip.
void reset()
Resets the device. Performs a software resets, resetting all registers and closing all sockets.
bool listenTcp(int sock_n, uint16_t src_port, WizIp &dst_ip, uint16_t &dst_port, int timeout=-1)
Listen for a single remote TCP connection.
void handleINTn()
Handle an interrupt from INTn.
Wiz5500(SPIBus &bus, miosix::GpioPin cs, miosix::GpioPin intn, SPI::ClockDivider clock_divider)
Build an instance of the driver.
ssize_t recv(int sock_n, uint8_t *data, size_t len, int timeout=-1)
Receive data from the socket (works only in TCP).
void setSubnetMask(WizIp mask)
Set global subnet mask.
void setOnDestUnreachable(OnDestUnreachableCb cb)
Sets the callback to be invoked when the device detects an unreachable host.
bool send(int sock_n, const uint8_t *data, size_t len, int timeout=-1)
Send data through the socket (works both in TCP and UDP).
PhyState getPhyState()
Get current PHY state, can be used to poll link status, and wait for link up.
void close(int sock_n, int timeout=-1)
Close a socket.
bool connectTcp(int sock_n, uint16_t src_port, WizIp dst_ip, uint16_t dst_port, int timeout=-1)
Connect to a remote socket via TCP.
void disableExternalInterrupt(unsigned int gpioPort, unsigned int gpioNum)
Disables external interrupts on the provided pin.
void enableExternalInterrupt(unsigned int gpioPort, unsigned int gpioNum, InterruptTrigger trigger, unsigned int priority)
Enables external interrupts on the provided pin. Remember to set the GPIO to input mode!
ClockDivider
SPI Clock divider.
uint8_t buildModeTcp(bool enable_no_delayed_ack)
uint8_t buildModeUdp(bool enable_multicast, bool block_broadcast, bool multicast_use_igmp_v1, bool block_unicast)
uint8_t buildControlWord(uint8_t block, bool write)
uint8_t getSocketTxBlock(int n)
uint8_t getSocketRxBlock(int n)
uint8_t getSocketRegBlock(int n)
This file includes all the types the logdecoder script will decode.
constexpr long long msToNs(long long ms)
Convert milliseconds to nanoseconds.
SPI Bus configuration for a specific slave.
SPI::ClockDivider clockDivider
< Peripheral clock division
GpioType cs
Chip select pin.
SPIBusInterface & bus
Bus on which the slave is connected.
Class representing an IPv4 ip.
Class representing an ethernet MAC address.