arduino stuffs
Diffstat (limited to 'libraries/Ethernet/src/socket.cpp')
-rw-r--r--libraries/Ethernet/src/socket.cpp539
1 files changed, 539 insertions, 0 deletions
diff --git a/libraries/Ethernet/src/socket.cpp b/libraries/Ethernet/src/socket.cpp
new file mode 100644
index 0000000..f059dc9
--- /dev/null
+++ b/libraries/Ethernet/src/socket.cpp
@@ -0,0 +1,539 @@
+/* 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;
+}
+