Suggestion for Ethernet Library improvement

Welocs

Well-known member
Hello there! =)

At my Ethernet-Work with multiple sockets at the same time, it came out for me that i have to change the number of sockets used for my requirements in Ethernet.h.
But this wasn't enough for me...i needed multiple sockets, but with different buffer sizes e.g.:
1. socket: 8k Buffer
2. socket: 2k
3. socket: 2k
4. socket: 2k
5. socket: 1k
6. socket: 1k
(sum of all sockets = 16k)

But the library wasn't written to be able to do this, so i changed the library.
It would be very nice, to implement this change (and maybe better coded :) ) in future updates of the Ethernet library!

I think i made the changes for both, non-fifo and fifo. I only use fifo, so maybe the non-fifo code must be debugged.
The Changed files a the following:
w5100.h (--> new definitions, SMASK and SSIZE are now arrays, new var "USESOCKET")
w5100.cpp (--> init(), write(), read() )
socket.cpp (--> read_data(), socketPeek(), write_data(), socketSend() )

I hope i didn't forgot to post any another changed file!

w5100.h:
Code:
/*
 * Copyright (c) 2010 by Cristian Maglie <c.maglie@bug.st>
 *
 * This file is free software; you can redistribute it and/or modify
 * it under the terms of either the GNU General Public License version 2
 * or the GNU Lesser General Public License version 2.1, both as
 * published by the Free Software Foundation.
 */

#ifndef	W5100_H_INCLUDED
#define	W5100_H_INCLUDED

#include <Arduino.h>
#include <SPI.h>

// Safe for all chips
//#define SPI_ETHERNET_SETTINGS SPISettings(14000000, MSBFIRST, SPI_MODE0)

// Safe for W5200 and W5500, but too fast for W5100
// uncomment this if you know you'll never need W5100 support
//#define SPI_ETHERNET_SETTINGS SPISettings(30000000, MSBFIRST, SPI_MODE0)
#define SPI_ETHERNET_SETTINGS SPISettings(40000000, MSBFIRST, SPI_MODE0)

#define MAX_SOCK_NUM 5
//#define W5200_4K_BUFFERS

//#define W5500_4K_BUFFERS
//#define W5500_8K_BUFFERS
//#define W5500_16K_BUFFERS
/*
#	uncomment to define sockets of differen sizes, e.g.:
#	socket 1 rx/tx = 12k
#	socket 2 rx/tx = 2k
#	socket 3 rx/tx = 2k
*/
#define W5500_MULTISOCKET
#define W5500_MULTISOCKET_AMOUNT		MAX_SOCK_NUM
#define W5500_MULTISOCKET_SIZE_BUFFER0	_8k
#define W5500_MULTISOCKET_SIZE_BUFFER1	_2k
#define W5500_MULTISOCKET_SIZE_BUFFER2	_2k
#define W5500_MULTISOCKET_SIZE_BUFFER3	_2k
#define W5500_MULTISOCKET_SIZE_BUFFER4	_2k
//#define W5500_MULTISOCKET_SIZE_BUFFER5	_1k
//#define W5500_MULTISOCKET_SIZE_BUFFER6	_1k
//#define W5500_MULTISOCKET_SIZE_BUFFER7	2048

#define _1k		0x400
#define _2k		0x800
#define _4k		0x1000
#define _6k		0x1800
#define _8k		0x2000
#define _10k	0x2800
#define _12k	0x3000
#define _14k	0x3800
#define _16k	0x4000

typedef uint8_t SOCKET;

class SnMR {
public:
  static const uint8_t CLOSE  = 0x00;
  static const uint8_t TCP    = 0x01;
  static const uint8_t UDP    = 0x02;
  static const uint8_t IPRAW  = 0x03;
  static const uint8_t MACRAW = 0x04;
  static const uint8_t PPPOE  = 0x05;
  static const uint8_t ND     = 0x20;
  static const uint8_t MULTI  = 0x80;
};

enum SockCMD {
  Sock_OPEN      = 0x01,
  Sock_LISTEN    = 0x02,
  Sock_CONNECT   = 0x04,
  Sock_DISCON    = 0x08,
  Sock_CLOSE     = 0x10,
  Sock_SEND      = 0x20,
  Sock_SEND_MAC  = 0x21,
  Sock_SEND_KEEP = 0x22,
  Sock_RECV      = 0x40
};

class SnIR {
public:
  static const uint8_t SEND_OK = 0x10;
  static const uint8_t TIMEOUT = 0x08;
  static const uint8_t RECV    = 0x04;
  static const uint8_t DISCON  = 0x02;
  static const uint8_t CON     = 0x01;
};

class SnSR {
public:
  static const uint8_t CLOSED      = 0x00;
  static const uint8_t INIT        = 0x13;
  static const uint8_t LISTEN      = 0x14;
  static const uint8_t SYNSENT     = 0x15;
  static const uint8_t SYNRECV     = 0x16;
  static const uint8_t ESTABLISHED = 0x17;
  static const uint8_t FIN_WAIT    = 0x18;
  static const uint8_t CLOSING     = 0x1A;
  static const uint8_t TIME_WAIT   = 0x1B;
  static const uint8_t CLOSE_WAIT  = 0x1C;
  static const uint8_t LAST_ACK    = 0x1D;
  static const uint8_t UDP         = 0x22;
  static const uint8_t IPRAW       = 0x32;
  static const uint8_t MACRAW      = 0x42;
  static const uint8_t PPPOE       = 0x5F;
};

class IPPROTO {
public:
  static const uint8_t IP   = 0;
  static const uint8_t ICMP = 1;
  static const uint8_t IGMP = 2;
  static const uint8_t GGP  = 3;
  static const uint8_t TCP  = 6;
  static const uint8_t PUP  = 12;
  static const uint8_t UDP  = 17;
  static const uint8_t IDP  = 22;
  static const uint8_t ND   = 77;
  static const uint8_t RAW  = 255;
};

class W5100Class {

public:
  static uint8_t init(void);

  inline void setGatewayIp(const uint8_t * addr) { writeGAR(addr); }
  inline void getGatewayIp(uint8_t * addr) { readGAR(addr); }

  inline void setSubnetMask(const uint8_t * addr) { writeSUBR(addr); }
  inline void getSubnetMask(uint8_t * addr) { readSUBR(addr); }

  inline void setMACAddress(const uint8_t * addr) { writeSHAR(addr); }
  inline void getMACAddress(uint8_t * addr) { readSHAR(addr); }

  inline void setIPAddress(const uint8_t * addr) { writeSIPR(addr); }
  inline void getIPAddress(uint8_t * addr) { readSIPR(addr); }

  inline void setRetransmissionTime(uint16_t timeout) { writeRTR(timeout); }
  inline void setRetransmissionCount(uint8_t retry) { writeRCR(retry); }

  static void execCmdSn(SOCKET s, SockCMD _cmd);


  // W5100 Registers
  // ---------------
//private:
public:
  static uint16_t write(uint16_t addr, const uint8_t *buf, uint16_t len);
  static uint8_t write(uint16_t addr, uint8_t data) {
    return write(addr, &data, 1);
  }
  static uint16_t read(uint16_t addr, uint8_t *buf, uint16_t len);
  static uint8_t read(uint16_t addr) {
    uint8_t data;
    read(addr, &data, 1);
    return data;
  }

#define __GP_REGISTER8(name, address)             \
  static inline void write##name(uint8_t _data) { \
    write(address, _data);                        \
  }                                               \
  static inline uint8_t read##name() {            \
    return read(address);                         \
  }
#define __GP_REGISTER16(name, address)            \
  static void write##name(uint16_t _data) {       \
    uint8_t buf[2];                               \
    buf[0] = _data >> 8;                          \
    buf[1] = _data & 0xFF;                        \
    write(address, buf, 2);                       \
  }                                               \
  static uint16_t read##name() {                  \
    uint8_t buf[2];                               \
    read(address, buf, 2);                        \
    return (buf[0] << 8) | buf[1];                \
  }
#define __GP_REGISTER_N(name, address, size)      \
  static uint16_t write##name(const uint8_t *_buff) {   \
    return write(address, _buff, size);           \
  }                                               \
  static uint16_t read##name(uint8_t *_buff) {    \
    return read(address, _buff, size);            \
  }

public:
  __GP_REGISTER8 (MR,     0x0000);    // Mode
  __GP_REGISTER_N(GAR,    0x0001, 4); // Gateway IP address
  __GP_REGISTER_N(SUBR,   0x0005, 4); // Subnet mask address
  __GP_REGISTER_N(SHAR,   0x0009, 6); // Source MAC address
  __GP_REGISTER_N(SIPR,   0x000F, 4); // Source IP address
  __GP_REGISTER8 (IR,     0x0015);    // Interrupt
  __GP_REGISTER8 (IMR,    0x0016);    // Interrupt Mask
  __GP_REGISTER16(RTR,    0x0017);    // Timeout address
  __GP_REGISTER8 (RCR,    0x0019);    // Retry count
  __GP_REGISTER8 (RMSR,   0x001A);    // Receive memory size (W5100 only)
  __GP_REGISTER8 (TMSR,   0x001B);    // Transmit memory size (W5100 only)
  __GP_REGISTER8 (PATR,   0x001C);    // Authentication type address in PPPoE mode
  __GP_REGISTER8 (PTIMER, 0x0028);    // PPP LCP Request Timer
  __GP_REGISTER8 (PMAGIC, 0x0029);    // PPP LCP Magic Number
  __GP_REGISTER_N(UIPR,   0x002A, 4); // Unreachable IP address in UDP mode (W5100 only)
  __GP_REGISTER16(UPORT,  0x002E);    // Unreachable Port address in UDP mode (W5100 only)
  __GP_REGISTER8 (VERSIONR_W5200,0x001F);   // Chip Version Register (W5200 only)
  __GP_REGISTER8 (VERSIONR_W5500,0x0039);   // Chip Version Register (W5500 only)

#undef __GP_REGISTER8
#undef __GP_REGISTER16
#undef __GP_REGISTER_N

  // W5100 Socket registers
  // ----------------------
private:
  static inline uint8_t readSn(SOCKET s, uint16_t addr) {
    return read(CH_BASE + s * CH_SIZE + addr);
  }
  static inline uint8_t writeSn(SOCKET s, uint16_t addr, uint8_t data) {
    return write(CH_BASE + s * CH_SIZE + addr, data);
  }
  static inline uint16_t readSn(SOCKET s, uint16_t addr, uint8_t *buf, uint16_t len) {
    return read(CH_BASE + s * CH_SIZE + addr, buf, len);
  }
  static inline uint16_t writeSn(SOCKET s, uint16_t addr, uint8_t *buf, uint16_t len) {
    return write(CH_BASE + s * CH_SIZE + addr, buf, len);
  }

  static uint16_t CH_BASE;
  static const uint16_t CH_SIZE = 0x0100;

#define __SOCKET_REGISTER8(name, address)                    \
  static inline void write##name(SOCKET _s, uint8_t _data) { \
    writeSn(_s, address, _data);                             \
  }                                                          \
  static inline uint8_t read##name(SOCKET _s) {              \
    return readSn(_s, address);                              \
  }
#define __SOCKET_REGISTER16(name, address)                   \
  static void write##name(SOCKET _s, uint16_t _data) {       \
    uint8_t buf[2];                                          \
    buf[0] = _data >> 8;                                     \
    buf[1] = _data & 0xFF;                                   \
    writeSn(_s, address, buf, 2);                            \
  }                                                          \
  static uint16_t read##name(SOCKET _s) {                    \
    uint8_t buf[2];                                          \
    readSn(_s, address, buf, 2);                             \
    return (buf[0] << 8) | buf[1];                           \
  }
#define __SOCKET_REGISTER_N(name, address, size)             \
  static uint16_t write##name(SOCKET _s, uint8_t *_buff) {   \
    return writeSn(_s, address, _buff, size);                \
  }                                                          \
  static uint16_t read##name(SOCKET _s, uint8_t *_buff) {    \
    return readSn(_s, address, _buff, size);                 \
  }

public:
  __SOCKET_REGISTER8(SnMR,        0x0000)        // Mode
  __SOCKET_REGISTER8(SnCR,        0x0001)        // Command
  __SOCKET_REGISTER8(SnIR,        0x0002)        // Interrupt
  __SOCKET_REGISTER8(SnSR,        0x0003)        // Status
  __SOCKET_REGISTER16(SnPORT,     0x0004)        // Source Port
  __SOCKET_REGISTER_N(SnDHAR,     0x0006, 6)     // Destination Hardw Addr
  __SOCKET_REGISTER_N(SnDIPR,     0x000C, 4)     // Destination IP Addr
  __SOCKET_REGISTER16(SnDPORT,    0x0010)        // Destination Port
  __SOCKET_REGISTER16(SnMSSR,     0x0012)        // Max Segment Size
  __SOCKET_REGISTER8(SnPROTO,     0x0014)        // Protocol in IP RAW Mode
  __SOCKET_REGISTER8(SnTOS,       0x0015)        // IP TOS
  __SOCKET_REGISTER8(SnTTL,       0x0016)        // IP TTL
  __SOCKET_REGISTER8(SnRX_SIZE,   0x001E)        // RX Memory Size (W5200 only)
  __SOCKET_REGISTER8(SnTX_SIZE,   0x001F)        // RX Memory Size (W5200 only)
  __SOCKET_REGISTER16(SnTX_FSR,   0x0020)        // TX Free Size
  __SOCKET_REGISTER16(SnTX_RD,    0x0022)        // TX Read Pointer
  __SOCKET_REGISTER16(SnTX_WR,    0x0024)        // TX Write Pointer
  __SOCKET_REGISTER16(SnRX_RSR,   0x0026)        // RX Free Size
  __SOCKET_REGISTER16(SnRX_RD,    0x0028)        // RX Read Pointer
  __SOCKET_REGISTER16(SnRX_WR,    0x002A)        // RX Write Pointer (supported?)

#undef __SOCKET_REGISTER8
#undef __SOCKET_REGISTER16
#undef __SOCKET_REGISTER_N


private:
  static uint8_t chip;
  static uint8_t ss_pin;
  static uint8_t softReset(void);
  static uint8_t isW5100(void);
  static uint8_t isW5200(void);
  static uint8_t isW5500(void);

public:
  static const int SOCKETS = MAX_SOCK_NUM;
  #if defined (W5500_MULTISOCKET)
  static uint16_t SMASK[W5500_MULTISOCKET_AMOUNT];
  static uint16_t SSIZE[W5500_MULTISOCKET_AMOUNT];
  #else
  static uint16_t SMASK;
  static uint16_t SSIZE;
  #endif
  static uint8_t  USESOCKET;
//private:
  //receive and transmit have same buffer sizes
  static uint16_t SBASE[SOCKETS]; // Tx buffer base address
  static uint16_t RBASE[SOCKETS]; // Rx buffer base address
  static bool hasOffsetAddressMapping(void) {
    if (chip == 55) return true;
    return false;
  }
  static void setSS(uint8_t pin) { ss_pin = pin; }

private:
#if defined(__AVR__)
	static volatile uint8_t *ss_pin_reg;
	static uint8_t ss_pin_mask;
	inline static void initSS() {
		ss_pin_reg = portOutputRegister(digitalPinToPort(ss_pin));
		ss_pin_mask = digitalPinToBitMask(ss_pin);
		pinMode(ss_pin, OUTPUT);
	}
	inline static void setSS() {
		*(ss_pin_reg) &= ~ss_pin_mask;
	}
	inline static void resetSS() {
		*(ss_pin_reg) |= ss_pin_mask;
	}
#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK66FX1M0__) || defined(__MK64FX512__)
	static volatile uint8_t *ss_pin_reg;
	inline static void initSS() {
		ss_pin_reg = portOutputRegister(ss_pin);
		pinMode(ss_pin, OUTPUT);
	}
	inline static void setSS() {
		*(ss_pin_reg+256) = 1;
	}
	inline static void resetSS() {
		*(ss_pin_reg+128) = 1;
	}
#elif defined(__MKL26Z64__)
	static volatile uint8_t *ss_pin_reg;
	static uint8_t ss_pin_mask;
	inline static void initSS() {
		ss_pin_reg = portOutputRegister(digitalPinToPort(ss_pin));
		ss_pin_mask = digitalPinToBitMask(ss_pin);
		pinMode(ss_pin, OUTPUT);
	}
	inline static void setSS() {
		*(ss_pin_reg+8) = ss_pin_mask;
	}
	inline static void resetSS() {
		*(ss_pin_reg+4) = ss_pin_mask;
	}
#elif defined(__SAM3X8E__) || defined(__SAM3A8C__) || defined(__SAM3A4C__)
	static volatile uint32_t *ss_pin_reg;
	static uint32_t ss_pin_mask;
	inline static void initSS() {
		ss_pin_reg = &(digitalPinToPort(ss_pin)->PIO_PER);
		ss_pin_mask = digitalPinToBitMask(ss_pin);
		pinMode(ss_pin, OUTPUT);
	}
	inline static void setSS() {
		*(ss_pin_reg+13) = ss_pin_mask;
	}
	inline static void resetSS() {
		*(ss_pin_reg+12) = ss_pin_mask;
	}
#elif defined(__PIC32MX__)
	static volatile uint32_t *ss_pin_reg;
	static uint32_t ss_pin_mask;
	inline static void initSS() {
		ss_pin_reg = portModeRegister(digitalPinToPort(ss_pin));
		ss_pin_mask = digitalPinToBitMask(ss_pin);
		pinMode(ss_pin, OUTPUT);
	}
	inline static void setSS() {
		*(ss_pin_reg+8+1) = ss_pin_mask;
	}
	inline static void resetSS() {
		*(ss_pin_reg+8+2) = ss_pin_mask;
	}

#elif defined(ARDUINO_ARCH_ESP8266)
	static volatile uint32_t *ss_pin_reg;
	static uint32_t ss_pin_mask;
	inline static void initSS() {
		ss_pin_reg = (volatile uint32_t*)GPO;
		ss_pin_mask = 1 << ss_pin;
		pinMode(ss_pin, OUTPUT);
	}
	inline static void setSS() {
		GPOC = ss_pin_mask;
	}
	inline static void resetSS() {
		GPOS = ss_pin_mask;
	}

#elif defined(__SAMD21G18A__)
	static volatile uint32_t *ss_pin_reg;
	static uint32_t ss_pin_mask;
	inline static void initSS() {
		ss_pin_reg = portModeRegister(digitalPinToPort(ss_pin));
		ss_pin_mask = digitalPinToBitMask(ss_pin);
		pinMode(ss_pin, OUTPUT);
	}
	inline static void setSS() {
		*(ss_pin_reg+5) = ss_pin_mask;
	}
	inline static void resetSS() {
		*(ss_pin_reg+6) = ss_pin_mask;
	}
#else
	inline static void initSS() {
		pinMode(ss_pin, OUTPUT);
	}
	inline static void setSS() {
		digitalWrite(ss_pin, LOW);
	}
	inline static void resetSS() {
		digitalWrite(ss_pin, HIGH);
	}
#endif
};

extern W5100Class W5100;



#endif

#ifndef UTIL_H
#define UTIL_H

#define htons(x) ( (((x)<<8)&0xFF00) | (((x)>>8)&0xFF) )
#define ntohs(x) htons(x)

#define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \
                   ((x)<< 8 & 0x00FF0000UL) | \
                   ((x)>> 8 & 0x0000FF00UL) | \
                   ((x)>>24 & 0x000000FFUL) )
#define ntohl(x) htonl(x)

#endif

w5100.cpp:
Code:
/*
* Copyright (c) 2010 by Cristian Maglie <c.maglie@bug.st>
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of either the GNU General Public License version 2
* or the GNU Lesser General Public License version 2.1, both as
* published by the Free Software Foundation.
*/

#include "w5100.h"

#if defined (__arm__) && defined (TEENSYDUINO)
#include "SPIFIFO.h"
#ifdef  HAS_SPIFIFO
#define USE_SPIFIFO
#endif
#endif


// If the core library defines a SS pin, use it as the
// default.  Otherwise, default the default to pin 10.
#if defined(PIN_SPI_SS)
#define SS_PIN_DEFAULT  PIN_SPI_SS
#elif defined(CORE_SS0_PIN)
#define SS_PIN_DEFAULT  CORE_SS0_PIN
#else
#define SS_PIN_DEFAULT  10
#endif

// W5100 controller instance
uint16_t W5100Class::SBASE[MAX_SOCK_NUM];
uint16_t W5100Class::RBASE[MAX_SOCK_NUM];
uint16_t W5100Class::CH_BASE;
#if defined (W5500_MULTISOCKET)
uint16_t W5100Class::SSIZE[W5500_MULTISOCKET_AMOUNT];
uint16_t W5100Class::SMASK[W5500_MULTISOCKET_AMOUNT];
#else
uint16_t W5100Class::SSIZE;
uint16_t W5100Class::SMASK;
#endif
uint8_t  W5100Class::USESOCKET;
uint8_t  W5100Class::chip;
uint8_t  W5100Class::ss_pin = SS_PIN_DEFAULT;
W5100Class W5100;

// pointers and bitmasks for optimized SS pin
#if defined(__AVR__)
volatile uint8_t * W5100Class::ss_pin_reg;
uint8_t W5100Class::ss_pin_mask;
#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK66FX1M0__) || defined(__MK64FX512__)
volatile uint8_t * W5100Class::ss_pin_reg;
#elif defined(__MKL26Z64__)
volatile uint8_t * W5100Class::ss_pin_reg;
uint8_t W5100Class::ss_pin_mask;
#elif defined(__SAM3X8E__) || defined(__SAM3A8C__) || defined(__SAM3A4C__)
volatile uint32_t * W5100Class::ss_pin_reg;
uint32_t W5100Class::ss_pin_mask;
#elif defined(__PIC32MX__)
volatile uint32_t * W5100Class::ss_pin_reg;
uint32_t W5100Class::ss_pin_mask;
#elif defined(ARDUINO_ARCH_ESP8266)
volatile uint32_t * W5100Class::ss_pin_reg;
uint32_t W5100Class::ss_pin_mask;
#elif defined(__SAMD21G18A__)
volatile uint32_t * W5100Class::ss_pin_reg;
uint32_t W5100Class::ss_pin_mask;
#endif


uint8_t W5100Class::init(void)
{
	uint16_t TXBUF_BASE, RXBUF_BASE;
	uint8_t i;

	// Many Ethernet shields have a CAT811 or similar reset chip
	// connected to W5100 or W5200 chips.  The W5200 will not work at
	// all, and may even drive its MISO pin, until given an active low
	// reset pulse!  The CAT811 has a 240 ms typical pulse length, and
	// a 400 ms worst case maximum pulse length.  MAX811 has a worst
	// case maximum 560 ms pulse length.  This delay is meant to wait
	// until the reset pulse is ended.  If your hardware has a shorter
	// reset time, this can be edited or removed.
	delay(560);
	//Serial.println("w5100 init");

	#ifdef USE_SPIFIFO
	SPI.begin();
	SPIFIFO.begin(ss_pin, SPI_CLOCK_24MHz);  // W5100 is 14 MHz max
	#else
	SPI.begin();
	initSS();
	resetSS();
	#endif
	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);

	// Attempt W5200 detection first, because W5200 does not properly
	// reset its SPI state when CS goes high (inactive).  Communication
	// from detecting the other chips can leave the W5200 in a state
	// where it won't recover, unless given a reset pulse.
	if (isW5200()) {
		CH_BASE = 0x4000;
		#if defined (W5500_MULTISOCKET)
			#ifdef W5500_MULTISOCKET_SIZE_BUFFER0		// different buffersizes
			SSIZE[0] = W5500_MULTISOCKET_SIZE_BUFFER0;
			SMASK[0] = SSIZE[0] - 1;
			#endif
			#ifdef W5500_MULTISOCKET_SIZE_BUFFER1
			SSIZE[1] = W5500_MULTISOCKET_SIZE_BUFFER1;
			SMASK[1] = SSIZE[1] - 1;
			#endif
			#ifdef W5500_MULTISOCKET_SIZE_BUFFER2
			SSIZE[2] = W5500_MULTISOCKET_SIZE_BUFFER2;
			SMASK[2] = SSIZE[2] - 1;
			#endif
			#ifdef W5500_MULTISOCKET_SIZE_BUFFER3
			SSIZE[3] = W5500_MULTISOCKET_SIZE_BUFFER3;
			SMASK[3] = SSIZE[3] - 1;
			#endif
			#ifdef W5500_MULTISOCKET_SIZE_BUFFER4
			SSIZE[4] = W5500_MULTISOCKET_SIZE_BUFFER4;
			SMASK[4] = SSIZE[4] - 1;
			#endif
			#ifdef W5500_MULTISOCKET_SIZE_BUFFER5
			SSIZE[5] = W5500_MULTISOCKET_SIZE_BUFFER5;
			SMASK[5] = SSIZE[5] - 1;
			#endif
			#ifdef W5500_MULTISOCKET_SIZE_BUFFER6
			SSIZE[6] = W5500_MULTISOCKET_SIZE_BUFFER6;
			SMASK[6] = SSIZE[6] - 1;
			#endif
			#ifdef W5500_MULTISOCKET_SIZE_BUFFER7
			SSIZE[7] = W5500_MULTISOCKET_SIZE_BUFFER7;
			SMASK[7] = SSIZE[7] - 1;
			#endif
		#elif defined (W5200_4K_BUFFERS)
		SSIZE = 4096;
		SMASK = 0x0FFF;
		#else
		SSIZE = 2048;    // 2K buffers
		SMASK = 0x07FF;
		#endif
		TXBUF_BASE = 0x8000;
		RXBUF_BASE = 0xC000;
		#if defined (W5500_MULTISOCKET)
		for (i = 0; i<MAX_SOCK_NUM; i++) {
			writeSnRX_SIZE(i, SSIZE[i] >> 10);
			writeSnTX_SIZE(i, SSIZE[i] >> 10);
		}
		#else
		for (i = 0; i<MAX_SOCK_NUM; i++) {
			writeSnRX_SIZE(i, SSIZE >> 10);
			writeSnTX_SIZE(i, SSIZE >> 10);
		}
		#endif

		for (; i<8; i++) {
			writeSnRX_SIZE(i, 0);
			writeSnTX_SIZE(i, 0);
		}
		// Try W5500 next.  Wiznet finally seems to have implemented
		// SPI well with this chip.  It appears to be very resilient,
		// so try it after the fragile W5200
	}
	else if (isW5500()) {
		CH_BASE = 0x1000;

		#if defined (W5500_MULTISOCKET)
			#ifdef W5500_MULTISOCKET_SIZE_BUFFER0		// different buffersizes
			SSIZE[0] = W5500_MULTISOCKET_SIZE_BUFFER0;
			SMASK[0] = SSIZE[0] - 1;
			#endif
			#ifdef W5500_MULTISOCKET_SIZE_BUFFER1
			SSIZE[1] = W5500_MULTISOCKET_SIZE_BUFFER1;
			SMASK[1] = SSIZE[1] - 1;
			#endif
			#ifdef W5500_MULTISOCKET_SIZE_BUFFER2
			SSIZE[2] = W5500_MULTISOCKET_SIZE_BUFFER2;
			SMASK[2] = SSIZE[2] - 1;
			#endif
			#ifdef W5500_MULTISOCKET_SIZE_BUFFER3
			SSIZE[3] = W5500_MULTISOCKET_SIZE_BUFFER3;
			SMASK[3] = SSIZE[3] - 1;
			#endif
			#ifdef W5500_MULTISOCKET_SIZE_BUFFER4
			SSIZE[4] = W5500_MULTISOCKET_SIZE_BUFFER4;
			SMASK[4] = SSIZE[4] - 1;
			#endif
			#ifdef W5500_MULTISOCKET_SIZE_BUFFER5
			SSIZE[5] = W5500_MULTISOCKET_SIZE_BUFFER5;
			SMASK[5] = SSIZE[5] - 1;
			#endif
			#ifdef W5500_MULTISOCKET_SIZE_BUFFER6
			SSIZE[6] = W5500_MULTISOCKET_SIZE_BUFFER6;
			SMASK[6] = SSIZE[6] - 1;
			#endif
			#ifdef W5500_MULTISOCKET_SIZE_BUFFER7
			SSIZE[7] = W5500_MULTISOCKET_SIZE_BUFFER7;
			SMASK[7] = SSIZE[7] - 1;
			#endif
		#elif defined (W5500_16K_BUFFERS)
		SSIZE = 16384;		// 16k buffers
		SMASK = SSIZE - 1;
		#elif defined (W5500_8K_BUFFERS)
		SSIZE = 8192;		// 8k buffers
		SMASK = SSIZE - 1;
		#elif defined (W5500_4K_BUFFERS)
		SSIZE = 4096;		// 4K buffers
		SMASK = SSIZE - 1;
		#else
		SSIZE = 2048;		// 2K buffers
		SMASK = SSIZE - 1;
		#endif
		TXBUF_BASE = 0x8000;
		RXBUF_BASE = 0xC000;
		#if defined (W5500_4K_BUFFERS) || defined (W5500_8K_BUFFERS) || defined (W5500_16K_BUFFERS)
		for (i = 0; i<MAX_SOCK_NUM; i++) {
			writeSnRX_SIZE(i, SSIZE >> 10);
			writeSnTX_SIZE(i, SSIZE >> 10);
		}
		for (; i<8; i++) {
			writeSnRX_SIZE(i, 0);
			writeSnTX_SIZE(i, 0);
		}
		#elif defined (W5500_MULTISOCKET)
		for (i = 0; i<MAX_SOCK_NUM; i++) {
			writeSnRX_SIZE(i, SSIZE[i] >> 10);
			writeSnTX_SIZE(i, SSIZE[i] >> 10);
		}
		for (; i<8; i++) {
			writeSnRX_SIZE(i, 0);
			writeSnTX_SIZE(i, 0);
		}
		#endif
		// Try W5100 last.  This simple chip uses fixed 4 byte frames
		// for every 8 bit access.  Terribly inefficient, but so simple
		// it recovers from "hearing" unsuccessful W5100 or W5200
		// communication.  W5100 is also the only chip without a VERSIONR
		// register for identification, so we check this last.
	}
	else if (isW5100()) {
		#if not defined (W5500_MULTISOCKET)
		CH_BASE = 0x0400;
		SSIZE = 2048;
		SMASK = 0x07FF;
		TXBUF_BASE = 0x4000;
		RXBUF_BASE = 0x6000;
		writeTMSR(0x55);
		writeRMSR(0x55);
		#endif
		// No hardware seems to be present.  Or it could be a W5200
		// that's heard other SPI communication if its chip select
		// pin wasn't high when a SD card or other SPI chip was used.
	}
	else {
		//Serial.println("no chip :-(");
		chip = 0;
		SPI.endTransaction();
		return 0; // no known chip is responding :-(
	}
	SPI.endTransaction();
	// Initialize the socket base addresses
	#if defined (W5500_MULTISOCKET)
	for (int i = 0; i<MAX_SOCK_NUM; i++) {
		SBASE[i] = TXBUF_BASE + SSIZE[i] * i;
		RBASE[i] = RXBUF_BASE + SSIZE[i] * i;
	}
	#else
	for (int i = 0; i<MAX_SOCK_NUM; i++) {
		SBASE[i] = TXBUF_BASE + SSIZE * i;
		RBASE[i] = RXBUF_BASE + SSIZE * i;
	}
	#endif
	return 1; // successful init
}

// Soft reset the Wiznet chip, by writing to its MR register reset bit
uint8_t W5100Class::softReset(void)
{
	uint16_t count = 0;

	//Serial.println("Wiznet soft reset");
	// write to reset bit
	writeMR(0x80);
	// then wait for soft reset to complete
	do {
		uint8_t mr = readMR();
		//Serial.print("mr=");
		//Serial.println(mr, HEX);
		if (mr == 0) return 1;
		delay(1);
	} while (++count < 20);
	return 0;
}

uint8_t W5100Class::isW5100(void)
{
	chip = 51;
	//Serial.println("w5100.cpp: detect W5100 chip");
	if (!softReset()) return 0;
	writeMR(0x10);
	if (readMR() != 0x10) return 0;
	writeMR(0x12);
	if (readMR() != 0x12) return 0;
	writeMR(0x00);
	if (readMR() != 0x00) return 0;
	//Serial.println("chip is W5100");
	return 1;
}

uint8_t W5100Class::isW5200(void)
{
	chip = 52;
	//Serial.println("w5100.cpp: detect W5200 chip");
	if (!softReset()) return 0;
	writeMR(0x08);
	if (readMR() != 0x08) return 0;
	writeMR(0x10);
	if (readMR() != 0x10) return 0;
	writeMR(0x00);
	if (readMR() != 0x00) return 0;
	int ver = readVERSIONR_W5200();
	//Serial.print("version=");
	//Serial.println(ver);
	if (ver != 3) return 0;
	//Serial.println("chip is W5200");
	return 1;
}

uint8_t W5100Class::isW5500(void)
{
	chip = 55;
	//Serial.println("w5100.cpp: detect W5500 chip");
	if (!softReset()) return 0;
	writeMR(0x08);
	if (readMR() != 0x08) return 0;
	writeMR(0x10);
	if (readMR() != 0x10) return 0;
	writeMR(0x00);
	if (readMR() != 0x00) return 0;
	int ver = readVERSIONR_W5500();
	//Serial.print("version=");
	//Serial.println(ver);
	if (ver != 4) return 0;
	//Serial.println("chip is W5500");
	return 1;
}


#ifdef USE_SPIFIFO
uint16_t W5100Class::write(uint16_t addr, const uint8_t *buf, uint16_t len)
{
	uint32_t i;

	if (chip == 51) {
		for (i = 0; i<len; i++) {
			SPIFIFO.write16(0xF000 | (addr >> 8), SPI_CONTINUE);
			SPIFIFO.write16((addr << 8) | buf[i]);
			addr++;
			SPIFIFO.read();
			SPIFIFO.read();
		}
	}
	else if (chip == 52) {
		SPIFIFO.clear();
		SPIFIFO.write16(addr, SPI_CONTINUE);
		SPIFIFO.write16(len | 0x8000, SPI_CONTINUE);
		for (i = 0; i<len; i++) {
			SPIFIFO.write(buf[i], ((i + 1<len) ? SPI_CONTINUE : 0));
			SPIFIFO.read();
		}
		SPIFIFO.read();
		SPIFIFO.read();
	}
	else {
		//SPIFIFO.clear();
		SPIFIFO.write16(addr, SPI_CONTINUE);
		if (addr < 0x100) {
			// common registers 00nn
			SPIFIFO.write16(0x0400 | *buf++,
				((len > 1) ? SPI_CONTINUE : 0));
		}
		else if (addr < 0x8000) {
			// socket registers  10nn, 11nn, 12nn, 13nn, etc
			SPIFIFO.write16(((addr << 5) & 0xE000) | 0x0C00 | *buf++,
				((len > 1) ? SPI_CONTINUE : 0));
		}
		else if (addr < 0xC000) {
			// transmit buffers  8000-87FF, 8800-8FFF, 9000-97FF, etc
			uint16_t block_select_bits = USESOCKET << 13;
			SPIFIFO.write16(block_select_bits | 0x1400 | *buf++, ((len > 1) ? SPI_CONTINUE : 0));
		}
		else {
			// receive buffers
			uint16_t block_select_bits = USESOCKET << 13;
			SPIFIFO.write16(block_select_bits | 0x1C00 | *buf++, ((len > 1) ? SPI_CONTINUE : 0));
		}
		len--;
		while (len >= 2) {
			len -= 2;
			SPIFIFO.write16((*buf << 8) | *(buf + 1), (len == 0) ? 0 : SPI_CONTINUE);
			buf += 2;
			SPIFIFO.read();
		}
		if (len) {
			SPIFIFO.write(*buf);
			SPIFIFO.read();
		}
		SPIFIFO.read();
		SPIFIFO.read();
	}
	return len;
}
#else
uint16_t W5100Class::write(uint16_t addr, const uint8_t *buf, uint16_t len)
{
	if (chip == 51) {
		for (uint16_t i = 0; i<len; i++) {
			setSS();
			SPI.transfer(0xF0);
			SPI.transfer(addr >> 8);
			SPI.transfer(addr & 0xFF);
			addr++;
			SPI.transfer(buf[i]);
			resetSS();
		}
	}
	else if (chip == 52) {
		setSS();
		SPI.transfer(addr >> 8);
		SPI.transfer(addr & 0xFF);
		SPI.transfer(((len >> 8) & 0x7F) | 0x80);
		SPI.transfer(len & 0xFF);
		for (uint16_t i = 0; i<len; i++) {
			SPI.transfer(buf[i]);
		}
		resetSS();
	}
	else {
		setSS();
		if (addr < 0x100) {
			// common registers 00nn
			SPI.transfer(0);
			SPI.transfer(addr & 0xFF);
			SPI.transfer(0x04);
		}
		else if (addr < 0x8000) {
			// socket registers  10nn, 11nn, 12nn, 13nn, etc
			SPI.transfer(0);
			SPI.transfer(addr & 0xFF);
			SPI.transfer(((addr >> 3) & 0xE0) | 0x0C);
		}
		else if (addr < 0xC000) {
			// transmit buffers  8000-87FF, 8800-8FFF, 9000-97FF, etc
			//  10## #nnn nnnn nnnn
			SPI.transfer(addr >> 8);
			SPI.transfer(addr & 0xFF);
			#ifdef W5500_16K_BUFFERS
			SPI.transfer(0x14);						 // 16K buffers
			#elif defined (W5500_8K_BUFFERS)
			SPI.transfer(((addr >> 8) & 0x20) | 0x14); // 8K buffers
			#elif defined (W5500_4K_BUFFERS)
			SPI.transfer(((addr >> 7) & 0x60) | 0x14); // 4K buffers
			#else
			SPI.transfer(((addr >> 6) & 0xE0) | 0x14); // 2K buffers
			#endif
		}
		else {
			// receive buffers
			SPI.transfer(addr >> 8);
			SPI.transfer(addr & 0xFF);
			#ifdef W5500_16K_BUFFERS
			SPI.transfer(0x1C);						 // 16K buffers
			#elif defined (W5500_8K_BUFFERS)
			SPI.transfer(((addr >> 8) & 0x20) | 0x1C); // 8K buffers
			#elif defined (W5500_4K_BUFFERS)
			SPI.transfer(((addr >> 7) & 0x60) | 0x1C); // 4K buffers
			#else
			SPI.transfer(((addr >> 6) & 0xE0) | 0x1C); // 2K buffers
			#endif
		}
		for (uint16_t i = 0; i<len; i++) {
			SPI.transfer(buf[i]);
		}
		resetSS();
	}
	return len;
}
#endif


#ifdef USE_SPIFIFO
uint16_t W5100Class::read(uint16_t addr, uint8_t *buf, uint16_t len)
{
	uint32_t i;
	if (chip == 51) {
		for (i = 0; i<len; i++) {
			#if 1
			SPIFIFO.write(0x0F, SPI_CONTINUE);
			SPIFIFO.write16(addr, SPI_CONTINUE);
			addr++;
			SPIFIFO.read();
			SPIFIFO.write(0);
			SPIFIFO.read();
			buf[i] = SPIFIFO.read();
			#endif
			#if 0
			// this does not work, but why?
			SPIFIFO.write16(0x0F00 | (addr >> 8), SPI_CONTINUE);
			SPIFIFO.write16(addr << 8);
			addr++;
			SPIFIFO.read();
			buf[i] = SPIFIFO.read();
			#endif
		}
	}
	else if (chip == 52) {
		// len = 1:  write header, write 1 byte, read
		// len = 2:  write header, write 2 byte, read
		// len = 3,5,7
		SPIFIFO.clear();
		SPIFIFO.write16(addr, SPI_CONTINUE);
		SPIFIFO.write16(len & 0x7FFF, SPI_CONTINUE);
		SPIFIFO.read();
		if (len == 1) {
			// read only 1 byte
			SPIFIFO.write(0);
			SPIFIFO.read();
			*buf = SPIFIFO.read();
		}
		else if (len == 2) {
			// read only 2 bytes
			SPIFIFO.write16(0);
			SPIFIFO.read();
			uint32_t val = SPIFIFO.read();
			*buf++ = val >> 8;
			*buf = val;
		}
		else if ((len & 1)) {
			// read 3 or more, odd length
			//Serial.print("W5200 read, len=");
			//Serial.println(len);
			uint32_t count = len / 2;
			SPIFIFO.write16(0, SPI_CONTINUE);
			SPIFIFO.read();
			do {
				if (count > 1) SPIFIFO.write16(0, SPI_CONTINUE);
				else SPIFIFO.write(0);
				uint32_t val = SPIFIFO.read();
				//TODO: WebClient_speedtest with READSIZE 7 is
				//dramatically faster with this Serial.print(),
				//and the 2 above, but not without both.  Why?!
				//Serial.println(val, HEX);
				*buf++ = val >> 8;
				*buf++ = val;
			} while (--count > 0);
			*buf = SPIFIFO.read();
			//Serial.println(*buf, HEX);
		}
		else {
			// read 4 or more, even length
			//Serial.print("W5200 read, len=");
			//Serial.println(len);
			uint32_t count = len / 2 - 1;
			SPIFIFO.write16(0, SPI_CONTINUE);
			SPIFIFO.read();
			do {
				SPIFIFO.write16(0, (count > 1) ? SPI_CONTINUE : 0);
				uint32_t val = SPIFIFO.read();
				*buf++ = val >> 8;
				*buf++ = val;
			} while (--count > 0);
			uint32_t val = SPIFIFO.read();
			*buf++ = val >> 8;
			*buf++ = val;
		}
	}
	else {
		//SPIFIFO.clear();
		SPIFIFO.write16(addr, SPI_CONTINUE);
		if (addr < 0x100) {
			// common registers 00nn
			SPIFIFO.write16(0,
				((len > 1) ? SPI_CONTINUE : 0));
		}
		else if (addr < 0x8000) {
			// socket registers  10nn, 11nn, 12nn, 13nn, etc
			SPIFIFO.write16(((addr << 5) & 0xE000) | 0x0800,
				((len > 1) ? SPI_CONTINUE : 0));
		}
		else if (addr < 0xC000) {
			// transmit buffers  8000-87FF, 8800-8FFF, 9000-97FF, etc
			uint16_t block_select_bits = USESOCKET << 13;
			SPIFIFO.write16(block_select_bits | 0x1000, ((len > 1) ? SPI_CONTINUE : 0));
		}
		else {
			// receive buffers
			uint16_t block_select_bits = USESOCKET << 13;
			SPIFIFO.write16(block_select_bits | 0x1800, ((len > 1) ? SPI_CONTINUE : 0));
		}
		SPIFIFO.read();
		if (len <= 1) {
			*buf++ = SPIFIFO.read();
		}
		else if (len == 2) {
			SPIFIFO.write(0);
			*buf++ = SPIFIFO.read();
			*buf++ = SPIFIFO.read();
		}
		else if (len & 1) {
			uint32_t count = len >> 1;
			SPIFIFO.write16(0, (count > 1) ? SPI_CONTINUE : 0);
			*buf++ = SPIFIFO.read();
			while (count > 1) {
				count--;
				SPIFIFO.write16(0, (count > 1) ? SPI_CONTINUE : 0);
				uint32_t val = SPIFIFO.read();
				*buf++ = val >> 8;
				*buf++ = val;
			}
			uint32_t val = SPIFIFO.read();
			*buf++ = val >> 8;
			*buf++ = val;
		}
		else {
			SPIFIFO.write16(0, SPI_CONTINUE);
			*buf++ = SPIFIFO.read();
			uint32_t count = len >> 1;
			while (count > 1) {
				count--;
				if (count > 1) {
					SPIFIFO.write16(0, SPI_CONTINUE);
				}
				else {
					SPIFIFO.write(0, 0);
				}
				uint32_t val = SPIFIFO.read();
				*buf++ = val >> 8;
				*buf++ = val;
			}
			*buf = SPIFIFO.read();
		}
	}
	

	return len;
}
#else
uint16_t W5100Class::read(uint16_t addr, uint8_t *buf, uint16_t len)
{
	if (chip == 51) {
		for (uint16_t i = 0; i<len; i++) {
			setSS();
			SPI.transfer(0x0F);
			SPI.transfer(addr >> 8);
			SPI.transfer(addr & 0xFF);
			addr++;
			buf[i] = SPI.transfer(0);
			resetSS();
		}
	}
	else if (chip == 52) {
		setSS();
		SPI.transfer(addr >> 8);
		SPI.transfer(addr & 0xFF);
		SPI.transfer((len >> 8) & 0x7F);
		SPI.transfer(len & 0xFF);
		for (uint16_t i = 0; i<len; i++) {
			buf[i] = SPI.transfer(0);
		}
		resetSS();
	}
	else {	// W5500
		setSS();
		if (addr < 0x100) {
			// common registers 00nn
			SPI.transfer(0);
			SPI.transfer(addr & 0xFF);
			SPI.transfer(0x00);
		}
		else if (addr < 0x8000) {
			// socket registers  10nn, 11nn, 12nn, 13nn, etc
			SPI.transfer(0);
			SPI.transfer(addr & 0xFF);
			SPI.transfer(((addr >> 3) & 0xE0) | 0x08);
		}
		else if (addr < 0xC000) {
			// transmit buffers  8000-87FF, 8800-8FFF, 9000-97FF, etc
			//  10## #nnn nnnn nnnn
			SPI.transfer(addr >> 8);
			SPI.transfer(addr & 0xFF);
			#ifdef W5500_16K_BUFFERS
			SPI.transfer(0x10);						 // 16K buffers (only one socket)
			#elif defined (W5500_8K_BUFFERS)
			SPI.transfer(((addr >> 8) & 0x20) | 0x10); // 8K buffers
			#elif defined (W5500_4K_BUFFERS)
			SPI.transfer(((addr >> 7) & 0x60) | 0x10); // 4K buffers
			#else
			SPI.transfer(((addr >> 6) & 0xE0) | 0x10); // 2K buffers
			#endif
		}
		else {
			// receive buffers
			SPI.transfer(addr >> 8);
			SPI.transfer(addr & 0xFF);
			#ifdef W5500_16K_BUFFERS
			SPI.transfer(0x18);						 // 16K buffers (only one socket)
			#elif defined (W5500_8K_BUFFERS)
			SPI.transfer(((addr >> 8) & 0x20) | 0x18); // 8K buffers
			#elif defined (W5500_4K_BUFFERS)
			SPI.transfer(((addr >> 7) & 0x60) | 0x18); // 4K buffers
			#else
			SPI.transfer(((addr >> 6) & 0xE0) | 0x18); // 2K buffers
			#endif
		}
		for (uint16_t i = 0; i<len; i++) {
			buf[i] = SPI.transfer(0);
		}
		resetSS();
	}
	return len;
}
#endif

void W5100Class::execCmdSn(SOCKET s, SockCMD _cmd) {
	// Send command to socket
	writeSnCR(s, _cmd);
	// Wait for command to complete
	while (readSnCR(s))
		;
}

socket.cpp:
Code:
#include "Ethernet.h"
#include "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];

	//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 < MAX_SOCK_NUM; 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 < MAX_SOCK_NUM; 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];

	//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 < MAX_SOCK_NUM; 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 < MAX_SOCK_NUM; 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) {
			state[s].RX_RSR = val;
			return val;
		}
                prev = val;
        }
#else
	uint16_t val = W5100.readSnRX_RSR(s);
	state[s].RX_RSR = val;
	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);
	//Serial.printf("read: %d\r\n", s);

	W5100.USESOCKET = s;	// tell w5100.cpp to which socket to handle

	#if defined (W5500_MULTISOCKET)
	src_mask = (uint16_t)src & W5100.SMASK[s];
	src_ptr = W5100.RBASE[s] + src_mask;

	if (W5100.hasOffsetAddressMapping() || src_mask + len <= W5100.SSIZE[s]) {
		W5100.read(src_ptr, dst, len);
	}
	else {
		size = W5100.SSIZE[s] - src_mask;
		W5100.read(src_ptr, dst, size);
		dst += size;
		W5100.read(W5100.RBASE[s], dst, len - size);
	}
	#else
	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);
	}
	#endif
	
}

// 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) {
		ret = getSnRX_RSR(s);
	}
	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;
		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\n", state[s].RX_RD);
		} else {
			state[s].RX_inc = inc;
		}
	}
	SPI.endTransaction();
	return ret;
}

uint16_t EthernetClass::socketRecvAvailable(uint8_t s)
{
	uint16_t ret = state[s].RX_RSR;
	if (ret == 0) {
		SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
		ret = getSnRX_RSR(s);
		SPI.endTransaction();
	}
	//Serial.printf("sock.recvAvailable s=%d, num=%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);

	W5100.USESOCKET = s;	// tell w5100.cpp to which socket to handle

	#if defined (W5500_MULTISOCKET)
	uint16_t ptr = state[s].RX_RD;
	W5100.read((ptr & W5100.SMASK[s]) + W5100.RBASE[s], &b, 1);
	SPI.endTransaction();
	return b;
	#else
	uint16_t ptr = state[s].RX_RD;
	W5100.read((ptr & W5100.SMASK) + W5100.RBASE[s], &b, 1);
	SPI.endTransaction();
	return b;
	#endif
}



/*****************************************/
/*    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;

	//Serial.printf("write: %d\r\n", s);
	W5100.USESOCKET = s;	// tell w5100.cpp to which socket to handle

	#if defined (W5500_MULTISOCKET)
	uint16_t offset = ptr & W5100.SMASK[s];
	uint16_t dstAddr = offset + W5100.SBASE[s];

	if (W5100.hasOffsetAddressMapping() || offset + len <= W5100.SSIZE[s]) {
		W5100.write(dstAddr, data, len);
	}
	else {
		// Wrap around circular buffer
		uint16_t size = W5100.SSIZE[s] - offset;
		W5100.write(dstAddr, data, size);
		W5100.write(W5100.SBASE[s], data + size, len - size);
	}
	#else
	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);
	}
	#endif
	
	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 defined (W5500_MULTISOCKET)
	if (len > W5100.SSIZE[s]) {
		ret = W5100.SSIZE[s]; // check size not to exceed MAX size.
	}
	else {
		ret = len;
	}
	#else
	if (len > W5100.SSIZE) {
		ret = W5100.SSIZE; // check size not to exceed MAX size.
}
	else {
		ret = len;
	}
	#endif

	// 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::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;
}

int 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 0;
	}
	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
	W5100.writeSnDIPR(s, addr);
	W5100.writeSnDPORT(s, port);
	SPI.endTransaction();
	return 1;
}

int 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 0;
		}
		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 1;
}
 
hi sstaub,

thanks for your reply!
Yeah i know, but the W5500 can have unto 8 sockets.
I saw your implementations in your library. This looks very nice! I think i will try yours out when i have time.

Another suggestion:
Also i would be a great possibility to know, whether a received packet is a broadcast, and if, which type of broadcast, and from which IP it comes. In the UDP-Class you can look for the remoteIp() but not in the Client class.
To recognize a broadcast is sometimes very important to the servers behaviour. Also then there is a need to switch on/off the possibility to receive broadcasts or not. I think the W5500 can do this by hardware, if i do not remember wrong. :)
 
How do you propose making the buffer size available to the user-level API? I only looked briefly as this code, but as nearly as I can tell the user doesn't get any way to control how much memory their connection will use, other than the first one opened (eg, for DHCP) will get the largest.
 
Hi Paul,

at the moment i only change the buffersizes only via the Ethernet.h file and the w5500.h file. I didn't do any implementation for API yet.
I am using my controller for receiving a stream via UDP and streaming it out again via hardware ports - so i build a steaming device. But as the teensy reads out the w5500 not fast enough, i need a very big buffer for the stream (25 fps), otherwise, some UDP packets will get lost.
Also i made my controller accessable via UDP which need one more socket; and i made an webinterface via SD-Card, so you can access the controller via a browser -> here you need minimum two sockets for tcp.
So i need 4 Sockets minimum.
The streaming socket will never change and also the OS-UDP socket will never change, so i have to start them first. Then the code must be like this (not very userfriendly):
Code:
// defined sockets are:
// -------------------
// 1: 8k
// 2: 2k
// 3: 2k
// 4: 2k
// 5: 2k

void osBeginFunction(){
EthernetUDP streamingUdp;
EthernetUDP osUdp;
EthernetServer webserverTcp;

// the begin functions HAVE TO be in the correct order:
streamingUdp.begin(); // --> will only get socket 0 with 8k buffer
osUdp.begin(); // --> will only get socket 1 with 2k buffer
webserverTcp.begin(); // --> will get start socket 2 with 2k buffer. multiple clients can connect from socket 2 to socket 5 (able to handle 3 clients at the same time)
}
 
at the end i would programm functions like this.
eg.:
Code:
void foo(){
	uint32_t mySocket = 2;
	EthernetUDP udp();

	udp.begin(mySocket);
	
	// or something like this:
	#define _2k		2048
	uint32_t myNeededBufferSize = _2k	// -> maybe there is a need to increase the buffer size in a time
	udp.begin(myNeededBufferSize);		// the function now has to check, if, at this moment, there is enough space for creating this buffer, if not, return an error.
}

this implementations would be much more complicated to implement than the existing library now is. But i would be a very powerfull implementation. =)

EDIT:
-----
an example, why to implement this:
You are using a streaming device which needs a lot of buffer (16k). But with a 16k buffer, there is no possibility to use TCP simultaneously.
So, if i send a command via the streaming UDP to turn on the webserver again. Then i can access the controller again via a webinterface, but then i want to have the possibility again, to use the maximum buffer size only for streaming, so i would have to close all sockets again, resize the buffer, and starting the UDP streaming socket for maximum enjoyment :)
 
You probably already saw, but if not, I released Ethernet 2.0.0 for Arduino last week (all Arduino boards, not just Teensy). Here's a writeup with the changes.

https://www.pjrc.com/arduino-ethernet-library-2-0-0/

For now, I've packed up all those boards and ethernet shields. I'm going to switch focus to other non-ethernet stuff for a while... after I get caught back up to the forum.

But next time I do work on Ethernet (perhaps months away) maybe I should merge at least some changes like turning SSIZE & SMASK into macros or inline functions which take the socket number. That could at least all the mainline code to stay closer to what you're doing. I'm personally not looking to do API changes in the library until at least 2019, but eventually stuff like non-blocking DHCP & DNS and TCP connect/disconnect will be needed. If a good API for buffer sizes is made, maybe work considering merging when those other API changes happen?
 
Hello paul,

thanks for you work on Ethernet 2.0.0.
Yes i saw it and it looks very nice, but at the moment i really need my individual buffer sizing so that i cannot use Eth. 2.0.0 at the moment.

Maybe i will post some more suggestions to this thread and hopefully also with examples why to improve or change some stuff.

Also i have a question at the moment to the Ethernet library. I didn't had time to debug it 100%, but i want to through this in here, so maybe someone knows this problem as well.
As i use the T3.5 as a minimum http server (self written) i want to disconnect clients after a timeout. Now i found out using the browser "chrome" seems always to has to be forced to be closed (--> while loop in Client.stop() --> timeout occurs and the library will force the close). If using "firefox" i don't have this problem (and if, not that often). Does someone know, whats about it?
Why i mention this problem? The timeout in Client.stop()-loop is 1 second, which, for my appliction, is a lot of time, because i need a cycle of less than 40ms. So now i could reduce the timeout or always foreing it to close - but this is not good coding just to force something knowing not the reasons. Any suggestions?
If i solve this problem i also will post it here.
 
I don't know about these browser details.

But I can tell you one of the main small API additions in 2.0.0 is a function to configure that 1 second client connect & disconnect timeout.
 
hello there,

i'd like to share another suggestion for the Ethernet library.
Since, with the W5500, it is not possible to determine a limited-broadcast nor a directed-broadcast (exept on socket 0 in MACRAW-mode), it would be nice to add the possibility in UDP-mode to turn on "BCASTB" (= "Broadcast Blocking in MACRAW and UDP mode").
To do so, the code of the old library has to be changed to:

w5100.h:
Code:
class SnMR {
public:
  static const uint8_t CLOSE  = 0x00;
  static const uint8_t TCP    = 0x01;
  static const uint8_t UDP    = 0x02;
  static const uint8_t IPRAW  = 0x03;
  static const uint8_t MACRAW = 0x04;
  static const uint8_t PPPOE  = 0x05;
  static const uint8_t ND     = 0x20;
  static const uint8_t MULTI  = 0x80;
  static const uint8_t BCASTB = 0x40; [COLOR="#FF0000"] //add this line[/COLOR]
};

Udp.h:
Code:
// enable Broadcast Blocking in MACRAW and UDP mode
  // enDis = 0: don't block packets; enDis = 1: block packets
  virtual void blockBroadcastPackets(bool enDis = 1) = 0;       [COLOR="#FF0000"]//add this function to class UPD : public Stream[/COLOR]

Ethernet.h:
Code:
class EthernetUDP : public UDP {
...
...
...
	// enable Broadcast Blocking in MACRAW and UDP mode
	// enDis = 0: don't block packets; enDis = 1: block packets
	virtual void blockBroadcastPackets( bool enDis = 1);         [COLOR="#FF0000"]// add the same function as in Udp.h[/COLOR]
};

EthernetUdp.cpp:
Code:
[COLOR="#FF0000"]// add this function:[/COLOR]
void EthernetUDP::blockBroadcastPackets(bool enDis) {
	if (sockindex >= MAX_SOCK_NUM) return;  // socket not in use

	uint8_t temp = W5100.readSnMR(sockindex);
	if (enDis) {	// enable blocking
		W5100.writeSnMR(sockindex, temp | SnMR::BCASTB);
	}
	else {	// disable blocking
		W5100.writeSnMR(sockindex, temp & (~SnMR::BCASTB));
	}
}


How To Use:
Code:
EthernetUDP udp;

void foo(){
    udp.begin(myFavoritePort);
    udp.blockBroadcastPackets();    [COLOR="#FF8C00"]// no directed nor limited broadcasts will be received[/COLOR]
    udp.blockBroadcastPackets(1);   [COLOR="#FF8C00"]// same as udp.blockBroadcastPackets();[/COLOR]
    udp.blockBroadcastPackets(0);   [COLOR="#008000"]// directed and limited broadcasts now can be received again.[/COLOR]
}
 
i'd like to share another suggestion for the Ethernet library.

Please post this on Arduino's issue tracker for the Ethernet library.

https://github.com/arduino-libraries/Ethernet/issues

My goal is to keep Teensy & Arduino's Ethernet libraries in sync. As long as they let me have commit access, the plan is to use their issue tracker for this stuff.

Just to keep expectations realistic, I'm probably not going to add Ethernet features until 2019. I spent a good chunk of July on Ethernet, and now I really need to focus on other stuff for a while. While it's theoretically possible Cristian or others with access could merge new features, that seems unlikely.
 
Please post this on Arduino's issue tracker for the Ethernet library.
I'll do.

Just to keep expectations realistic, I'm probably not going to add Ethernet features until 2019. I spent a good chunk of July on Ethernet, and now I really need to focus on other stuff for a while.
Yes i know, you said this a few posts before. I am just posting suggestions here, as it is the category of this post. I don't expect anything, just sharing ideas ;-)

Shall i share ideas and suggestions for improvement to both forums or only to the arduinos one?
 
Another suggestion:

In the Ethernet library, the byte order of the IP-Address is reversed to common use of an unsigned integer. All serializing stuff is putting the lowest byte in/out first. The class IPAddress does the opposite of this. This is quiet annoying, cause when reading in a new IP address "correct", you have to swich the byte order first, to e.g. validate it with the subnet mask.
Here the part from the code in

IPAddress.h:
Code:
...
      IPAddress(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4) {
		_address.bytes[0] = b1;
		_address.bytes[1] = b2;
		_address.bytes[2] = b3;
		_address.bytes[3] = b4;
	}
...


If you do IPAddress myAddress(192, 168, 0, 40), the result will be:
_address.bytes[0] == 192;
_address.bytes[1] == 168;
_address.bytes[2] == 0;
_address.bytes[3] == 40;

BUT for me the more logic way should be like this:
_address.bytes[0] == 40
_address.bytes[1] == 0
_address.bytes[2] == 168
_address.bytes[3] == 192

I know, usign type-cast sovles some problems, but not really all and an IP address is nothing more than and uint32_t number. So comparing and validating stuff becomes less confusing.
Example:
Transmitting IP via UDP to teensy: ... 192 168 0 40 ...
Code:
void setIp(uint8_t* ptr) {  // ptr is the pointer to the readBuffer
uint32_t **ptr32 = (uint32_t **)&ptr;
uint32_t newAddress = 0, mask;

newAddress = ptr32[0][_headerSize / 4];
mask = Ethernet.subnetMask();
if (((mask | newAddress) == mask) || ((mask | newAddress) == 0xffffffff)) { // invalid IP?
// invalid IP! make error handling stuff here
return;
...
}
Now, if i read in a IP the "wrong" way, i have to do swap the IP-Byte-Order in my UDP-Output program. I cannot just use UDP.writeArray((uint8_t *)myIp, sizeof(myIp)) (while myIp is type of uint32_t).


If i would get the data in the correct order like:
... 40 0 168 192 ...
i have to add a line or change the code like this:
Code:
void setIp(uint8_t* ptr) {  // ptr is the pointer to the readBuffer
uint32_t **ptr32 = (uint32_t **)&ptr;
uint32_t newAddress = 0, mask;

newAddress = ptr[0] * 0x1000000;  // the same as << 24
newAddress += ptr[1] * 0x10000;  // the same as newAddress |= ptr[2] << 16
newAddress += ptr[2] * 0x100;
newAddress += ptr[3];
mask = Ethernet.subnetMask();
if (((mask | newAddress) == mask) || ((mask | newAddress) == 0xffffffff)) { // invalid IP?
// invalid IP! make error handling stuff here
return;
...
}
 
a current example where the current byte order is annoing:

Code:
	newMask = ptr32[0][_headerSize / 4]; [COLOR="#FF0000"]// reading in in the correct order, but receiving e.g. "255 255 128 0" instead of "0 128 255 255"[/COLOR]
	currentIp = Ethernet.localIP();

	uint32_t a = ~newMask;
	a++;
	a = a & (~newMask);
        // validate subnet mask:
	if (a || !newMask || !(newMask + 1)) {     [COLOR="#FF0000"]// this common validating does not work because of swapped bytes in IPAddress.h. I have to swap bytes first to check or always "changing" common validations :(:([/COLOR]
		Serial.print("ERROR: invalid subnet mask (");
		Serial.print((IPAddress)newMask);
		Serial.print(")!\r\n");
		return;
	}

	if (((newMask | currentIp) == newMask) || ((newMask | currentIp) == 0xffffffff)) {
		Serial.print("ERROR: current IP doesn't match the new subnet! Please change the IP first! current IP: ");
		Serial.print((IPAddress)currentIp);
		Serial.print("\r\n");
		return;
	}
        mySubnet = (IPAddress)newMask; [COLOR="#FF0000"]// this will work if received "255 255 128 0" | this will NOT work, if received "0 128 255 255"[/COLOR]
 
Changing the IPAddress class in the core library is an issue to bring up with Arduino. It will affect far more than just the ethernet library.

But again, to keep realistic expectations, core lib changes that break backwards compatibility are almost never accepted by Arduino. In fact, getting almost any core lib change is a very long and difficult process.

I can tell you I'm not going to make Teensy's IPAddress class incompatible with Arduino's, no matter how nice the change would be. Compatibility is the overriding concern. If Arduino changes, I will of course adapt for compatibility.
 
But again, to keep realistic expectations, core lib changes that break backwards compatibility are almost never accepted by Arduino.

Then just we maybe just could add a new class like "swappedIPAddress" to the header. This will keep backwards compability to the user, but it may result in slower code.
 
No. I'm not doing *anything* more with Ethernet until 2019, except fixing serious bugs. This isn't a serious bug.

I know that's not the answer you want to hear. But hopefully a clear & honest answer is better than uncertainty or vague promises that don't happen and only become lingering disappointment.

If using a 32 bit Teensy, you might consider using __builtin_bswap32(n) to reverse the byte order. It's compact in the source code and compiles to a single instruction.
 
And just to explain a little further, I've been burned many times when I've tried to make a small "trivial" change to widely used code, without dedicating time for properly considering the effects and good testing. If you look at the blog article, you'll see I tested 15 boards with 44 shield combinations. On each of those 44 hardware cases, I ran 4 test sketches. And *that* testing was after weeks of carefully optimizing & checking performance and new APIs on several of the hardware combinations.

Someday I will make another round of substantial improvements, very likely the non-blocking APIs so many people have requested, and maybe even integration with EventResponder (or whatever event API becomes adopted by Arduino...)

But the Ethernet library is a tricky beast. Careful testing is required. I just recently poured weeks of work into this for 2.0.0, and a few solid days running all those tests (which *did* turn up problems that I fixed before the release), and all that is after many similar sessions over the years to optimize Teensy's version. 2.0.0 is going to be the stable release for quite some time. For now, I'm only going to fix serious issues. Everything else can fall to the Arduino devs (very unlikely they'll do anything with ethernet) or can sit on the issue tracker until I again dedicate solid time to ethernet and proper testing.
 
Another suggestion:

In the Ethernet library, the byte order of the IP-Address is reversed to common use of an unsigned integer. All serializing stuff is putting the lowest byte in/out first. The class IPAddress does the opposite of this. This is quiet annoying, cause when reading in a new IP address "correct", you have to swich the byte order first, to e.g. validate it with the subnet mask.

Networking uses "big-endian":
https://en.wikipedia.org/wiki/Endianness#Networking
 
Paul,
I'm grateful for the huge effort you must have expended to do all this work and testing. Thanks.

Just to be clear, for those of us who want the latest updates and fullest feature-set, would you still recommend the Teensy Ethernet library across most of the Arduino compatible processors? Or should we stick to the Ethernet 2.0 library for non-Teensy boards?
 
Use Ethernet 2.0.0.

Teensyduino 1.43 (coming soon) will have Ethernet 2.0.0, the same as used for all Arduino boards.

But if you have 1.42 installed and don't need the new features or improved speed on W5100, there's not a lot of point to replacing Teensy's former Ethernet lib with Ethernet 2.0.0. The code is very similar.

For any non-Teensy boards, definitely upgrade to Ethernet 2.0.0.
 
Back
Top