Petfilamachine/libraries/Ethernet/src/socket.cpp

540 lines
15 KiB
C++

/* Copyright 2018 Paul Stoffregen
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <Arduino.h>
#include "Ethernet.h"
#include "utility/w5100.h"
#if ARDUINO >= 156 && !defined(ARDUINO_ARCH_PIC32)
extern void yield(void);
#else
#define yield()
#endif
// TODO: randomize this when not using DHCP, but how?
static uint16_t local_port = 49152; // 49152 to 65535
typedef struct {
uint16_t RX_RSR; // Number of bytes received
uint16_t RX_RD; // Address to read
uint16_t TX_FSR; // Free space ready for transmit
uint8_t RX_inc; // how much have we advanced RX_RD
} socketstate_t;
static socketstate_t state[MAX_SOCK_NUM];
static uint16_t getSnTX_FSR(uint8_t s);
static uint16_t getSnRX_RSR(uint8_t s);
static void write_data(uint8_t s, uint16_t offset, const uint8_t *data, uint16_t len);
static void read_data(uint8_t s, uint16_t src, uint8_t *dst, uint16_t len);
/*****************************************/
/* Socket management */
/*****************************************/
void EthernetClass::socketPortRand(uint16_t n)
{
n &= 0x3FFF;
local_port ^= n;
//Serial.printf("socketPortRand %d, srcport=%d\n", n, local_port);
}
uint8_t EthernetClass::socketBegin(uint8_t protocol, uint16_t port)
{
uint8_t s, status[MAX_SOCK_NUM], chip, maxindex=MAX_SOCK_NUM;
// first check hardware compatibility
chip = W5100.getChip();
if (!chip) return MAX_SOCK_NUM; // immediate error if no hardware detected
#if MAX_SOCK_NUM > 4
if (chip == 51) maxindex = 4; // W5100 chip never supports more than 4 sockets
#endif
//Serial.printf("W5000socket begin, protocol=%d, port=%d\n", protocol, port);
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
// look at all the hardware sockets, use any that are closed (unused)
for (s=0; s < maxindex; s++) {
status[s] = W5100.readSnSR(s);
if (status[s] == SnSR::CLOSED) goto makesocket;
}
//Serial.printf("W5000socket step2\n");
// as a last resort, forcibly close any already closing
for (s=0; s < maxindex; s++) {
uint8_t stat = status[s];
if (stat == SnSR::LAST_ACK) goto closemakesocket;
if (stat == SnSR::TIME_WAIT) goto closemakesocket;
if (stat == SnSR::FIN_WAIT) goto closemakesocket;
if (stat == SnSR::CLOSING) goto closemakesocket;
}
#if 0
Serial.printf("W5000socket step3\n");
// next, use any that are effectively closed
for (s=0; s < MAX_SOCK_NUM; s++) {
uint8_t stat = status[s];
// TODO: this also needs to check if no more data
if (stat == SnSR::CLOSE_WAIT) goto closemakesocket;
}
#endif
SPI.endTransaction();
return MAX_SOCK_NUM; // all sockets are in use
closemakesocket:
//Serial.printf("W5000socket close\n");
W5100.execCmdSn(s, Sock_CLOSE);
makesocket:
//Serial.printf("W5000socket %d\n", s);
EthernetServer::server_port[s] = 0;
delayMicroseconds(250); // TODO: is this needed??
W5100.writeSnMR(s, protocol);
W5100.writeSnIR(s, 0xFF);
if (port > 0) {
W5100.writeSnPORT(s, port);
} else {
// if don't set the source port, set local_port number.
if (++local_port < 49152) local_port = 49152;
W5100.writeSnPORT(s, local_port);
}
W5100.execCmdSn(s, Sock_OPEN);
state[s].RX_RSR = 0;
state[s].RX_RD = W5100.readSnRX_RD(s); // always zero?
state[s].RX_inc = 0;
state[s].TX_FSR = 0;
//Serial.printf("W5000socket prot=%d, RX_RD=%d\n", W5100.readSnMR(s), state[s].RX_RD);
SPI.endTransaction();
return s;
}
// multicast version to set fields before open thd
uint8_t EthernetClass::socketBeginMulticast(uint8_t protocol, IPAddress ip, uint16_t port)
{
uint8_t s, status[MAX_SOCK_NUM], chip, maxindex=MAX_SOCK_NUM;
// first check hardware compatibility
chip = W5100.getChip();
if (!chip) return MAX_SOCK_NUM; // immediate error if no hardware detected
#if MAX_SOCK_NUM > 4
if (chip == 51) maxindex = 4; // W5100 chip never supports more than 4 sockets
#endif
//Serial.printf("W5000socket begin, protocol=%d, port=%d\n", protocol, port);
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
// look at all the hardware sockets, use any that are closed (unused)
for (s=0; s < maxindex; s++) {
status[s] = W5100.readSnSR(s);
if (status[s] == SnSR::CLOSED) goto makesocket;
}
//Serial.printf("W5000socket step2\n");
// as a last resort, forcibly close any already closing
for (s=0; s < maxindex; s++) {
uint8_t stat = status[s];
if (stat == SnSR::LAST_ACK) goto closemakesocket;
if (stat == SnSR::TIME_WAIT) goto closemakesocket;
if (stat == SnSR::FIN_WAIT) goto closemakesocket;
if (stat == SnSR::CLOSING) goto closemakesocket;
}
#if 0
Serial.printf("W5000socket step3\n");
// next, use any that are effectively closed
for (s=0; s < MAX_SOCK_NUM; s++) {
uint8_t stat = status[s];
// TODO: this also needs to check if no more data
if (stat == SnSR::CLOSE_WAIT) goto closemakesocket;
}
#endif
SPI.endTransaction();
return MAX_SOCK_NUM; // all sockets are in use
closemakesocket:
//Serial.printf("W5000socket close\n");
W5100.execCmdSn(s, Sock_CLOSE);
makesocket:
//Serial.printf("W5000socket %d\n", s);
EthernetServer::server_port[s] = 0;
delayMicroseconds(250); // TODO: is this needed??
W5100.writeSnMR(s, protocol);
W5100.writeSnIR(s, 0xFF);
if (port > 0) {
W5100.writeSnPORT(s, port);
} else {
// if don't set the source port, set local_port number.
if (++local_port < 49152) local_port = 49152;
W5100.writeSnPORT(s, local_port);
}
// Calculate MAC address from Multicast IP Address
byte mac[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0x00 };
mac[3] = ip[1] & 0x7F;
mac[4] = ip[2];
mac[5] = ip[3];
W5100.writeSnDIPR(s, ip.raw_address()); //239.255.0.1
W5100.writeSnDPORT(s, port);
W5100.writeSnDHAR(s, mac);
W5100.execCmdSn(s, Sock_OPEN);
state[s].RX_RSR = 0;
state[s].RX_RD = W5100.readSnRX_RD(s); // always zero?
state[s].RX_inc = 0;
state[s].TX_FSR = 0;
//Serial.printf("W5000socket prot=%d, RX_RD=%d\n", W5100.readSnMR(s), state[s].RX_RD);
SPI.endTransaction();
return s;
}
// Return the socket's status
//
uint8_t EthernetClass::socketStatus(uint8_t s)
{
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
uint8_t status = W5100.readSnSR(s);
SPI.endTransaction();
return status;
}
// Immediately close. If a TCP connection is established, the
// remote host is left unaware we closed.
//
void EthernetClass::socketClose(uint8_t s)
{
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
W5100.execCmdSn(s, Sock_CLOSE);
SPI.endTransaction();
}
// Place the socket in listening (server) mode
//
uint8_t EthernetClass::socketListen(uint8_t s)
{
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
if (W5100.readSnSR(s) != SnSR::INIT) {
SPI.endTransaction();
return 0;
}
W5100.execCmdSn(s, Sock_LISTEN);
SPI.endTransaction();
return 1;
}
// establish a TCP connection in Active (client) mode.
//
void EthernetClass::socketConnect(uint8_t s, uint8_t * addr, uint16_t port)
{
// set destination IP
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
W5100.writeSnDIPR(s, addr);
W5100.writeSnDPORT(s, port);
W5100.execCmdSn(s, Sock_CONNECT);
SPI.endTransaction();
}
// Gracefully disconnect a TCP connection.
//
void EthernetClass::socketDisconnect(uint8_t s)
{
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
W5100.execCmdSn(s, Sock_DISCON);
SPI.endTransaction();
}
/*****************************************/
/* Socket Data Receive Functions */
/*****************************************/
static uint16_t getSnRX_RSR(uint8_t s)
{
#if 1
uint16_t val, prev;
prev = W5100.readSnRX_RSR(s);
while (1) {
val = W5100.readSnRX_RSR(s);
if (val == prev) {
return val;
}
prev = val;
}
#else
uint16_t val = W5100.readSnRX_RSR(s);
return val;
#endif
}
static void read_data(uint8_t s, uint16_t src, uint8_t *dst, uint16_t len)
{
uint16_t size;
uint16_t src_mask;
uint16_t src_ptr;
//Serial.printf("read_data, len=%d, at:%d\n", len, src);
src_mask = (uint16_t)src & W5100.SMASK;
src_ptr = W5100.RBASE(s) + src_mask;
if (W5100.hasOffsetAddressMapping() || src_mask + len <= W5100.SSIZE) {
W5100.read(src_ptr, dst, len);
} else {
size = W5100.SSIZE - src_mask;
W5100.read(src_ptr, dst, size);
dst += size;
W5100.read(W5100.RBASE(s), dst, len - size);
}
}
// Receive data. Returns size, or -1 for no data, or 0 if connection closed
//
int EthernetClass::socketRecv(uint8_t s, uint8_t *buf, int16_t len)
{
// Check how much data is available
int ret = state[s].RX_RSR;
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
if (ret < len) {
uint16_t rsr = getSnRX_RSR(s);
ret = rsr - state[s].RX_inc;
state[s].RX_RSR = ret;
//Serial.printf("Sock_RECV, RX_RSR=%d, RX_inc=%d\n", ret, state[s].RX_inc);
}
if (ret == 0) {
// No data available.
uint8_t status = W5100.readSnSR(s);
if ( status == SnSR::LISTEN || status == SnSR::CLOSED ||
status == SnSR::CLOSE_WAIT ) {
// The remote end has closed its side of the connection,
// so this is the eof state
ret = 0;
} else {
// The connection is still up, but there's no data waiting to be read
ret = -1;
}
} else {
if (ret > len) ret = len; // more data available than buffer length
uint16_t ptr = state[s].RX_RD;
if (buf) read_data(s, ptr, buf, ret);
ptr += ret;
state[s].RX_RD = ptr;
state[s].RX_RSR -= ret;
uint16_t inc = state[s].RX_inc + ret;
if (inc >= 250 || state[s].RX_RSR == 0) {
state[s].RX_inc = 0;
W5100.writeSnRX_RD(s, ptr);
W5100.execCmdSn(s, Sock_RECV);
//Serial.printf("Sock_RECV cmd, RX_RD=%d, RX_RSR=%d\n",
// state[s].RX_RD, state[s].RX_RSR);
} else {
state[s].RX_inc = inc;
}
}
SPI.endTransaction();
//Serial.printf("socketRecv, ret=%d\n", ret);
return ret;
}
uint16_t EthernetClass::socketRecvAvailable(uint8_t s)
{
uint16_t ret = state[s].RX_RSR;
if (ret == 0) {
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
uint16_t rsr = getSnRX_RSR(s);
SPI.endTransaction();
ret = rsr - state[s].RX_inc;
state[s].RX_RSR = ret;
//Serial.printf("sockRecvAvailable s=%d, RX_RSR=%d\n", s, ret);
}
return ret;
}
// get the first byte in the receive queue (no checking)
//
uint8_t EthernetClass::socketPeek(uint8_t s)
{
uint8_t b;
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
uint16_t ptr = state[s].RX_RD;
W5100.read((ptr & W5100.SMASK) + W5100.RBASE(s), &b, 1);
SPI.endTransaction();
return b;
}
/*****************************************/
/* Socket Data Transmit Functions */
/*****************************************/
static uint16_t getSnTX_FSR(uint8_t s)
{
uint16_t val, prev;
prev = W5100.readSnTX_FSR(s);
while (1) {
val = W5100.readSnTX_FSR(s);
if (val == prev) {
state[s].TX_FSR = val;
return val;
}
prev = val;
}
}
static void write_data(uint8_t s, uint16_t data_offset, const uint8_t *data, uint16_t len)
{
uint16_t ptr = W5100.readSnTX_WR(s);
ptr += data_offset;
uint16_t offset = ptr & W5100.SMASK;
uint16_t dstAddr = offset + W5100.SBASE(s);
if (W5100.hasOffsetAddressMapping() || offset + len <= W5100.SSIZE) {
W5100.write(dstAddr, data, len);
} else {
// Wrap around circular buffer
uint16_t size = W5100.SSIZE - offset;
W5100.write(dstAddr, data, size);
W5100.write(W5100.SBASE(s), data + size, len - size);
}
ptr += len;
W5100.writeSnTX_WR(s, ptr);
}
/**
* @brief This function used to send the data in TCP mode
* @return 1 for success else 0.
*/
uint16_t EthernetClass::socketSend(uint8_t s, const uint8_t * buf, uint16_t len)
{
uint8_t status=0;
uint16_t ret=0;
uint16_t freesize=0;
if (len > W5100.SSIZE) {
ret = W5100.SSIZE; // check size not to exceed MAX size.
} else {
ret = len;
}
// if freebuf is available, start.
do {
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
freesize = getSnTX_FSR(s);
status = W5100.readSnSR(s);
SPI.endTransaction();
if ((status != SnSR::ESTABLISHED) && (status != SnSR::CLOSE_WAIT)) {
ret = 0;
break;
}
yield();
} while (freesize < ret);
// copy data
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
write_data(s, 0, (uint8_t *)buf, ret);
W5100.execCmdSn(s, Sock_SEND);
/* +2008.01 bj */
while ( (W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK ) {
/* m2008.01 [bj] : reduce code */
if ( W5100.readSnSR(s) == SnSR::CLOSED ) {
SPI.endTransaction();
return 0;
}
SPI.endTransaction();
yield();
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
}
/* +2008.01 bj */
W5100.writeSnIR(s, SnIR::SEND_OK);
SPI.endTransaction();
return ret;
}
uint16_t EthernetClass::socketSendAvailable(uint8_t s)
{
uint8_t status=0;
uint16_t freesize=0;
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
freesize = getSnTX_FSR(s);
status = W5100.readSnSR(s);
SPI.endTransaction();
if ((status == SnSR::ESTABLISHED) || (status == SnSR::CLOSE_WAIT)) {
return freesize;
}
return 0;
}
uint16_t EthernetClass::socketBufferData(uint8_t s, uint16_t offset, const uint8_t* buf, uint16_t len)
{
//Serial.printf(" bufferData, offset=%d, len=%d\n", offset, len);
uint16_t ret =0;
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
uint16_t txfree = getSnTX_FSR(s);
if (len > txfree) {
ret = txfree; // check size not to exceed MAX size.
} else {
ret = len;
}
write_data(s, offset, buf, ret);
SPI.endTransaction();
return ret;
}
bool EthernetClass::socketStartUDP(uint8_t s, uint8_t* addr, uint16_t port)
{
if ( ((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00) && (addr[3] == 0x00)) ||
((port == 0x00)) ) {
return false;
}
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
W5100.writeSnDIPR(s, addr);
W5100.writeSnDPORT(s, port);
SPI.endTransaction();
return true;
}
bool EthernetClass::socketSendUDP(uint8_t s)
{
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
W5100.execCmdSn(s, Sock_SEND);
/* +2008.01 bj */
while ( (W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK ) {
if (W5100.readSnIR(s) & SnIR::TIMEOUT) {
/* +2008.01 [bj]: clear interrupt */
W5100.writeSnIR(s, (SnIR::SEND_OK|SnIR::TIMEOUT));
SPI.endTransaction();
//Serial.printf("sendUDP timeout\n");
return false;
}
SPI.endTransaction();
yield();
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
}
/* +2008.01 bj */
W5100.writeSnIR(s, SnIR::SEND_OK);
SPI.endTransaction();
//Serial.printf("sendUDP ok\n");
/* Sent ok */
return true;
}