/*
		Ethernet shield test program
    Copyright (C) 2018  Andreas Mieke
    Copyright (C) 2010  Arduino LLC

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

#ifndef __W5100_H
#define __W5100_H

#include "stm32f10x.h"                  // Device header
#include "stm32f10x_rcc.h"              // Keil::Device:StdPeriph Drivers:RCC
#include "stm32f10x_gpio.h"             // Keil::Device:StdPeriph Drivers:GPIO
#include "stm32f10x_spi.h"              // Keil::Device:StdPeriph Drivers:SPI

#define MAX_SOCK_NUM 4
typedef uint8_t SOCKET;

enum {
	ETH_MR_RST		= 0x80,
	ETH_MR_PB			= 0x10,
	ETH_MR_PPPoE	=	0x08,
	ETH_MR_AI			= 0x02,
	ETH_MR_IND		= 0x01
};

enum {
	ETH_IR_CONFLICT		= 0x80,
	ETH_IR_UNREACH		= 0x40,
	ETH_IR_PPPoE			= 0x20,
	ETH_IR_S3_INT			= 0x08,
	ETH_IR_S2_INT			= 0x04,
	ETH_IR_S1_INT			= 0x02,
	ETH_IR_S0_INT			= 0x01
};

enum {
	ETH_IMR_IR7		= 0x80,
	ETH_IMR_IR6		= 0x40,
	ETH_IMR_IR5		= 0x20,
	ETH_IMR_IR3		= 0x08,
	ETH_IMR_IR2		= 0x04,
	ETH_IMR_IR1		= 0x02,
	ETH_IMR_IR0		= 0x01
};

enum {
	ETH_SMR_MULTI		= 0x80,
	ETH_SMR_ND			= 0x20,
	ETH_SMR_CLOSED	= 0x00,
	ETH_SMR_TCP			= 0x01,
	ETH_SMR_UDP			= 0x02,
	ETH_SMR_IPRAW		= 0x03,
	ETH_SMR_MACRAW	= 0x04,
	ETH_SMR_PPPoE		= 0x05
};

enum {
	ETH_SCR_OPEN			= 0x01,
	ETH_SCR_LISTEN		= 0x02,
	ETH_SCR_CONNECT		= 0x04,
	ETH_SCR_DISCON		= 0x08,
	ETH_SCR_CLOSE			= 0x10,
	ETH_SCR_SEND			= 0x20,
	ETH_SCR_SEND_MAC	= 0x21,
	ETH_SCR_SEND_KEEP	= 0x22,
	ETH_SCR_RECV			= 0x40
};

enum {
	ETH_SIR_SEND_OK		= 0x10,
	ETH_SIR_TIMEOUT		= 0x08,
	ETH_SIR_RECV			= 0x04,
	ETH_SIR_DISCON		= 0x02,
	ETH_SIR_CON				= 0x01
};

enum {
	ETH_SSR_CLOSED				= 0x00,
	ETH_SSR_INIT					= 0x12, // Actually 13, Cortex doesn't seem to register the LSB
	ETH_SSR_LISTEN				= 0x14,
	ETH_SSR_SYNSENT				= 0x15,
	ETH_SSR_SYNRECV				= 0x16,
	ETH_SSR_ESTABLISHED		= 0x17,
	ETH_SSR_FIN_WAIT			= 0x18,
	ETH_SSR_CLOSING				= 0x1A,
	ETH_SSR_TIME_WAIT			= 0x1B,
	ETH_SSR_CLOSE_WAIT		= 0x1C,
	ETH_SSR_LAST_ACK			= 0x1D,
	ETH_SSR_UDP						= 0x22,
	ETH_SSR_IPRAW					= 0x32,
	ETH_SSR_MACRAW				= 0x42,
	ETH_SSR_PPPoE					= 0x5F
};

enum {
	ETH_SPROTO_IP   = 0,
  ETH_SPROTO_ICMP = 1,
  ETH_SPROTO_IGMP = 2,
  ETH_SPROTO_GGP  = 3,
  ETH_SPROTO_TCP  = 6,
  ETH_SPROTO_PUP  = 12,
  ETH_SPROTO_UDP  = 17,
  ETH_SPROTO_IDP  = 22,
  ETH_SPROTO_ND   = 77,
  ETH_SPROTO_RAW  = 255
};

void ETH_init(void);
void ETH_read_data(SOCKET s, volatile uint16_t src, volatile uint8_t * dst, uint16_t len);
void ETH_send_data_processing(SOCKET s, const uint8_t *data, uint16_t len);
void ETH_send_data_processing_offset(SOCKET s, uint16_t data_offset, const uint8_t *data, uint16_t len);
void ETH_recv_data_processing(SOCKET s, uint8_t *data, uint16_t len, uint8_t peek);

inline void ETH_set_gateway_IP(uint8_t *addr);
inline void ETH_get_gateway_IP(uint8_t *addr);

inline void ETH_set_subnet_mask(uint8_t *addr);
inline void ETH_get_subnet_mask(uint8_t *addr);

inline void ETH_set_mac(uint8_t * addr);
inline void ETH_get_mac(uint8_t * addr);

inline void ETH_set_IP(uint8_t * addr);
inline void ETH_get_IP(uint8_t * addr);

inline void ETH_set_retransmission_time(uint16_t timeout);
inline void ETH_set_retransmission_count(uint8_t retry);

inline void ETH_exec_socket_cmd(SOCKET s, uint8_t cmd);
  
uint16_t ETH_get_TX_free_size(SOCKET s);
uint16_t ETH_get_RX_received_size(SOCKET s);

uint8_t ETH_write_8(uint16_t addr, uint8_t data);
uint16_t ETH_write_n(uint16_t addr, const uint8_t *buf, uint16_t len);

uint8_t ETH_read_8(uint16_t addr);
uint16_t ETH_read_n(uint16_t addr, uint8_t *buf, uint16_t len);

#define __GP_REGISTER8(name, address)										\
  static inline void ETH_write##name(uint8_t _data) {		\
    ETH_write_8(address, _data);												\
  }																											\
  static inline uint8_t ETH_read##name() {							\
    return ETH_read_8(address);													\
  }
#define __GP_REGISTER16(name, address)									\
  static void ETH_write##name(uint16_t _data) {					\
    ETH_write_8(address,   _data >> 8);									\
    ETH_write_8(address+1, _data & 0xFF);								\
  }																											\
  static uint16_t ETH_read##name() {										\
    uint16_t res = ETH_read_8(address);									\
    res = (res << 8) + ETH_read_8(address + 1);					\
    return res;																					\
  }
#define __GP_REGISTER_N(name, address, size)						\
  static uint16_t ETH_write##name(uint8_t *_buff) {			\
    return ETH_write_n(address, _buff, size);						\
  }																											\
  static uint16_t ETH_read##name(uint8_t *_buff) {			\
    return ETH_read_n(address, _buff, size);						\
  }

__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
__GP_REGISTER8 (TMSR,   0x001B);    // Transmit memory size
__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
__GP_REGISTER16(UPORT,  0x002E);    // Unreachable Port address in UDP mode

#undef __GP_REGISTER8
#undef __GP_REGISTER16
#undef __GP_REGISTER_N

static inline uint8_t ETH_sock_read_8(SOCKET _s, uint16_t _addr);
static inline uint8_t ETH_sock_write_8(SOCKET _s, uint16_t _addr, uint8_t _data);
static inline uint16_t ETH_sock_read_n(SOCKET _s, uint16_t _addr, uint8_t *_buf, uint16_t len);
static inline uint16_t ETH_sock_write_n(SOCKET _s, uint16_t _addr, uint8_t *_buf, uint16_t len);

static const uint16_t ETH_CH_BASE = 0x0400;
static const uint16_t ETH_CH_SIZE = 0x0100;

#define __SOCKET_REGISTER8(name, address)														\
  static inline void ETH_write##name(SOCKET _s, uint8_t _data) {		\
    ETH_sock_write_8(_s, address, _data);														\
  }																																	\
  static inline uint8_t ETH_read##name(SOCKET _s) {									\
    return ETH_sock_read_8(_s, address);														\
  }
#define __SOCKET_REGISTER16(name, address)													\
  static void ETH_write##name(SOCKET _s, uint16_t _data) {					\
    ETH_sock_write_8(_s, address,   _data >> 8);										\
    ETH_sock_write_8(_s, address+1, _data & 0xFF);									\
  }																																	\
  static uint16_t ETH_read##name(SOCKET _s) {												\
    uint16_t res = ETH_sock_read_8(_s, address);										\
    uint16_t res2 = ETH_sock_read_8(_s,address + 1);								\
    res = res << 8;																									\
    res2 = res2 & 0xFF;																							\
    res = res | res2;																								\
    return res;																											\
  }
#define __SOCKET_REGISTER_N(name, address, size)										\
  static uint16_t ETH_write##name(SOCKET _s, uint8_t *_buff) {			\
    return ETH_sock_write_n(_s, address, _buff, size);							\
  }																																	\
  static uint16_t ETH_read##name(SOCKET _s, uint8_t *_buff) {				\
    return ETH_sock_read_n(_s, address, _buff, size);								\
  }

__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_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

static const int ETH_SOCKETS = 4;
static const uint16_t ETH_SMASK = 0x07FF; // Tx buffer MASK
static const uint16_t ETH_RMASK = 0x07FF; // Rx buffer MASK
static const uint16_t ETH_SSIZE = 2048; // Max Tx buffer size
static const uint16_t ETH_RSIZE = 2048; // Max Rx buffer size

extern uint16_t ETH_SBASE[ETH_SOCKETS]; // Tx buffer base address
extern uint16_t ETH_RBASE[ETH_SOCKETS]; // Rx buffer base address

uint8_t ETH_sock_read_8(SOCKET s, uint16_t addr)
{
	return ETH_read_8(ETH_CH_BASE + s * ETH_CH_SIZE + addr);
}

uint8_t ETH_sock_write_8(SOCKET s, uint16_t addr, uint8_t data)
{
	return ETH_write_8(ETH_CH_BASE + s * ETH_CH_SIZE + addr, data);
}

uint16_t ETH_sock_read_n(SOCKET s, uint16_t addr, uint8_t *buf, uint16_t len)
{
	return ETH_read_n(ETH_CH_BASE + s * ETH_CH_SIZE + addr, buf, len);
}

uint16_t ETH_sock_write_n(SOCKET s, uint16_t addr, uint8_t *buf, uint16_t len)
{
	return ETH_write_n(ETH_CH_BASE + s * ETH_CH_SIZE + addr, buf, len);
}

void ETH_set_gateway_IP(uint8_t *addr) { ETH_writeGAR(addr); }
void ETH_get_gateway_IP(uint8_t *addr) { ETH_readGAR(addr); }

void ETH_set_subnet_mask(uint8_t *addr) { ETH_writeSUBR(addr); }
void ETH_get_subnet_mask(uint8_t *addr) { ETH_readSUBR(addr); }

void ETH_set_mac(uint8_t * addr) { ETH_writeSHAR(addr); }
void ETH_get_mac(uint8_t * addr) { ETH_readSHAR(addr); }

void ETH_set_IP(uint8_t * addr) { ETH_writeSIPR(addr); }
void ETH_get_IP(uint8_t * addr) { ETH_readSIPR(addr); }

void ETH_set_retransmission_time(uint16_t timeout) { ETH_writeRTR(timeout); }
void ETH_set_retransmission_count(uint8_t retry) { ETH_writeRCR(retry); }

#endif
