
DHCP Client for WIZ820io(W5200)
Dependencies: EthernetNetIf mbed
Revision 0:db4242c89949, committed 2012-04-10
- Comitter:
- va009039
- Date:
- Tue Apr 10 03:38:22 2012 +0000
- Commit message:
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DHCPClient.cpp Tue Apr 10 03:38:22 2012 +0000 @@ -0,0 +1,100 @@ +// DHCPClient.cpp 2012/4/10 +// DHCP Client for WIZ820io(W5200) +#include "mbed.h" +#include "MyEthernetNetIf.h" +#include "MyUDPSocket.h" +#include "DHCPClient.h" +#ifdef DEBUG +#include "Utils.h" +#endif //DEBUG + +static DHCPClient* _g_dhcp; +static void _DHCP_OnEvent(UDPSocketEvent e) +{ + _g_dhcp->callback(e); +} + +int DHCPClient::offer_ack(uint8_t buf[], int size) { + memcpy(yiaddr, &buf[DHCP_OFFSET_YIADDR], 4); + uint8_t *p; + int msg_type = -1; + p = buf + DHCP_OFFSET_OPTIONS; + while(*p != 0xff && p < (uint8_t*)&buf[DHCP_MAX_PACKET_SIZE]) { + int code = *p++; + if (code == 0x00) { + continue; + } + int len = *p++; +#ifdef DHCP_VERBOSE + char codeStr[24]; + snprintf(codeStr, sizeof(codeStr), "DHCP option: %d", code); + printfBytes(codeStr, p, len); +#endif //DHCP_VERBOSE + if (code == 53) { + msg_type = *p; + } else if (code == 1) { + memcpy(netmask, p, 4); // Subnet mask address + } else if (code == 3) { + memcpy(gateway, p, 4); // Gateway IP address + } else if (code == 6) {// DNS + memcpy(dnsaddr, p, 4); + } + p += len; + } + return msg_type; +} + +void DHCPClient::callback(UDPSocketEvent e) +{ + PRINT_FUNC(); + uint8_t buf[DHCP_MAX_PACKET_SIZE]; + Host host; + int len = p_udp->recvfrom((char*)buf, sizeof(buf), &host); + if (!verify(buf, len)) { + return; + } + int r = offer_ack(buf, len); + if (r == 2) { // OFFER + request(buf, 300); + Host server(IpAddr(255,255,255,255), 67); // DHCP broadcast + p_udp->sendto((char*)buf, 300, &server); + } else if (r == 5) { // ACK + exit_flag = true; + } +} + +int DHCPClient::setup(int timeout_ms) +{ + Timer t; + t.start(); + p_udp = new MyUDPSocket; + p_udp->setOnEvent(_DHCP_OnEvent); + Host local(IpAddr(0,0,0,0), 68); + Host server(IpAddr(255,255,255,255), 67); // DHCP broadcast + p_udp->bind(local); + uint8_t buf[300]; + discover(buf, sizeof(buf)); + p_udp->sendto((char*)buf, sizeof(buf), &server); + exit_flag = false; + int err = 0; + while(1) { + p_udp->poll(); + if (exit_flag) { + break; + } + if (t.read_ms() > timeout_ms) { + err = -1; + break; + } +#ifdef DEBUG + wait_ms(100); +#endif //DEBUG + } + delete p_udp; + return err; +} + +DHCPClient::DHCPClient() +{ + _g_dhcp = this; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DHCPClient.h Tue Apr 10 03:38:22 2012 +0000 @@ -0,0 +1,86 @@ +// DHCPClient.h 2012/4/10 +#ifndef DHCPCLIENT_H +#define DHCPCLIENT_H +#include "UDPSocket.h" + +//#define DHCP_VERBOSE + +#define DHCP_OFFSET_OP 0 +#define DHCP_OFFSET_XID 4 +#define DHCP_OFFSET_YIADDR 16 +#define DHCP_OFFSET_CHADDR 28 +#define DHCP_OFFSET_MAGIC_COOKIE 236 +#define DHCP_OFFSET_OPTIONS 240 +#define DHCP_MAX_PACKET_SIZE 600 + +extern void _DHCP_OnEvent(UDPSocketEvent e); // DHCPClient.cpp + +class DHCPClient { + int discover(uint8_t buf[], int size) { + memset(buf, 0x00, size); + const uint8_t headers[] = {0x01,0x01,0x06,0x00, + 0x12,0x34,0x56,0x78}; // xid + memcpy(buf, headers, sizeof(headers)); + int t = clock(); + xid[0] = t<<24; + xid[1] = t<<16; + xid[2] = t<<8; + xid[3] = t; + memcpy(buf+DHCP_OFFSET_XID, xid, 4); + W5100.getMACAddress(buf+DHCP_OFFSET_CHADDR); + const uint8_t options[] = {0x63,0x82,0x53,0x63, // magic cookie + 0x35,0x01,0x01, // DHCP DISCOVER + 0xff}; + memcpy(buf+DHCP_OFFSET_MAGIC_COOKIE, options, sizeof(options)); + //printHex((u8*)buf, 236); + uint8_t ip[4] = {0,0,0,0}; + W5100.setIPAddress(ip); + return DHCP_OFFSET_MAGIC_COOKIE + sizeof(options); + } + + int request(uint8_t buf[], int size) { + memset(buf, 0x00, size); + const uint8_t headers[] = {0x01,0x01,0x06,0x00, + 0x12,0x34,0x56,0x78}; // xid + memcpy(buf, headers, sizeof(headers)); + memcpy(buf+DHCP_OFFSET_XID, xid, 4); + memcpy(buf+DHCP_OFFSET_YIADDR, yiaddr, 4); + W5100.getMACAddress(buf+DHCP_OFFSET_CHADDR); + const uint8_t options[] = {0x63,0x82,0x53,0x63, // magic cookie + 0x35,0x01,0x03, // DHCP REQUEST + 0x32,0x04,0x00,0x00,0x00,0x00, // request IP + 0xff}; + memcpy(buf+DHCP_OFFSET_MAGIC_COOKIE, options, sizeof(options)); + memcpy(buf+DHCP_OFFSET_MAGIC_COOKIE+9, yiaddr, 4); + return DHCP_OFFSET_MAGIC_COOKIE + sizeof(options); + } + + int offer_ack(uint8_t buf[], int size); + + bool verify(uint8_t buf[], int len) { + if (len < DHCP_OFFSET_OPTIONS) { + return false; + } + if (buf[DHCP_OFFSET_OP] != 0x02) { + return false; + } + if (memcmp(buf+DHCP_OFFSET_XID, xid, 4) != 0) { + return false; + } + return true; + } +public: + void callback(UDPSocketEvent e); + int setup(int timeout_ms = 15000); + DHCPClient(); + uint8_t chaddr[6]; // MAC + uint8_t yiaddr[4]; // IP + uint8_t dnsaddr[4]; // DNS + uint8_t gateway[4]; + uint8_t netmask[4]; +private: + MyUDPSocket* p_udp; + uint8_t xid[4]; + bool exit_flag; +}; +#endif //DHCPCLIENT_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/EthernetNetIf.lib Tue Apr 10 03:38:22 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/donatien/code/EthernetNetIf/#bc7df6da7589
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyEthernetNetIf.cpp Tue Apr 10 03:38:22 2012 +0000 @@ -0,0 +1,84 @@ +// MyEthernetNetIf.h 2012/4/10 +// EthernetNetIf for WIZ820io(W5200) +#include "mbed.h" +#include "MyEthernetNetIf.h" +#include "DHCPClient.h" +#include "w5100.h" + +extern W5100Class W5100; // w5100.cpp + +bool wait_linkup(int timeout = 3000) { + Timer link_t; + link_t.start(); + while(link_t.read_ms() < timeout) { + if (0x20 & W5100.readPHYSTATUS()) { + return true; + } + wait_ms(50); + } + return false; +} + +void MyEthernetNetIf::hardware_setup(){ + W5100.hardware_reset(); + W5100.init(); + wait_linkup(); +} + +MyEthernetNetIf::MyEthernetNetIf(IpAddr ip, IpAddr netmask, IpAddr gateway, IpAddr dns) { + m_ip = ip; + m_netmask = netmask; + m_gateway = gateway; + m_dns = dns; + m_useDhcp = false; +} + +MyEthernetNetIf:: MyEthernetNetIf() { + m_useDhcp = true; +} + +EthernetErr MyEthernetNetIf::setup(int timeout_ms) +{ + hardware_setup(); + uint8_t mac[6] = {0x00,0x00,0x5e,0x00,0x01,0x01}; + W5100.setMACAddress(mac); + printf("HW Addr is : %02x:%02x:%02x:%02x:%02x:%02x.\n", mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]); + uint8_t u[4]; + u[0] = m_ip[0]; + u[1] = m_ip[1]; + u[2] = m_ip[2]; + u[3] = m_ip[3]; + W5100.setIPAddress(u); + u[0] = m_netmask[0]; + u[1] = m_netmask[1]; + u[2] = m_netmask[2]; + u[3] = m_netmask[3]; + W5100.setSubnetMask(u); + u[0] = m_gateway[0]; + u[1] = m_gateway[1]; + u[2] = m_gateway[2]; + u[3] = m_gateway[3]; + W5100.setGatewayIp(u); + if (! m_useDhcp) { + return ETH_OK; + } + printf("DHCP Started, waiting for IP...\n"); + DHCPClient* dhcp; + EthernetErr err; + dhcp = new DHCPClient; + int r = dhcp->setup(timeout_ms); + if (r == (-1)) { + printf("Timeout.\n"); + err = ETH_TIMEOUT; + } else { + W5100.writeSIPR(dhcp->yiaddr); + W5100.writeSUBR(dhcp->netmask); + W5100.writeGAR(dhcp->gateway); + m_ip = IpAddr(dhcp->yiaddr[0],dhcp->yiaddr[1],dhcp->yiaddr[2],dhcp->yiaddr[3]); + m_dns = IpAddr(dhcp->dnsaddr[0],dhcp->dnsaddr[1],dhcp->dnsaddr[2],dhcp->dnsaddr[3]); + printf("Connected, IP: %d.%d.%d.%d\n", dhcp->yiaddr[0], dhcp->yiaddr[1], dhcp->yiaddr[2], dhcp->yiaddr[3]); + err = ETH_OK; + } + delete dhcp; + return err; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyEthernetNetIf.h Tue Apr 10 03:38:22 2012 +0000 @@ -0,0 +1,24 @@ +// MyEthernetNetIf.h 2012/4/10 +#ifndef MYETHERNETNETIF_H +#define MYETHERNETNETIF_H +#include "w5100.h" +#include "EthernetNetIf.h" +#include "MyUDPSocket.h" + +class MyEthernetNetIf { + void hardware_setup(); +public: + MyEthernetNetIf(IpAddr ip, IpAddr netmask, IpAddr gateway, IpAddr dns); + MyEthernetNetIf(); + IpAddr getDns() { return m_dns; } + EthernetErr setup(int timeout_ms = 15000); + IpAddr getIp() {return m_ip; } ; + IpAddr getDNS() {return m_dns; }; +private: + IpAddr m_ip; + IpAddr m_netmask; + IpAddr m_gateway; + IpAddr m_dns; + bool m_useDhcp; +}; +#endif // MYETHERNETNETIF_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyUDPSocket.h Tue Apr 10 03:38:22 2012 +0000 @@ -0,0 +1,125 @@ +#ifndef MYUDPSOCKET_H +#define MYUDPSOCKET_H +#include "UDPSocket.h" + +#ifdef DEBUG +#define PRINT_FUNC() printf("%d:%s\n", __LINE__,__PRETTY_FUNCTION__) +#else //DEBUG +#define PRINT_FUNC() +#endif //DEBUG + +class MyUDPSocket { + int _socket; + void (*m_pCb)(UDPSocketEvent); +public: + MyUDPSocket() { + _socket = 7; + W5100.writeSnMR(_socket, SnMR::UDP); // set UDP mode + } + + UDPSocketErr bind(const Host& me) { + if (! me.getIp().isNull()) { + uint8_t ip[4]; + ip[0] = me.getIp()[0]; + ip[1] = me.getIp()[1]; + ip[2] = me.getIp()[2]; + ip[3] = me.getIp()[3]; + W5100.setIPAddress(ip); + } + int port = me.getPort(); + if (port) { + W5100.writeSnPORT(_socket, port); + } + W5100.execCmdSn( _socket, Sock_OPEN); // set OPEN command + return UDPSOCKET_OK; + } + + int /*if < 0 : UDPSocketErr*/ sendto(const char* buf, int len, Host* pHost) { +#ifdef DEBUG + PRINT_FUNC(); + printHex((u8*)buf, len); +#endif //DEBUG + uint8_t ip[4]; + ip[0] = pHost->getIp()[0]; + ip[1] = pHost->getIp()[1]; + ip[2] = pHost->getIp()[2]; + ip[3] = pHost->getIp()[3]; + int port = pHost->getPort(); + W5100.writeSnDIPR(_socket, ip); + W5100.writeSnDPORT(_socket, port); + W5100.send_data_processing(_socket, (uint8_t*)buf, len); + W5100.execCmdSn(_socket, Sock_SEND); +#ifdef DEBUG + W5100.getIPAddress(ip); + printf("SIPR: %d.%d.%d.%d Sn_PORT:%d\n", ip[0], ip[1], ip[2], ip[3], W5100.readSnPORT(_socket)); + W5100.readSnDIPR(_socket, ip); + printf("Sn_DIPR: %d.%d.%d.%d Sn_DPORT:%d\n", ip[0], ip[1], ip[2], ip[3], W5100.readSnDPORT(_socket)); +#endif //DEBUG + return len; + } + + int /*if < 0 : UDPSocketErr*/ recvfrom(char* buf, int len, Host* pHost) { + int size = W5100.getRXReceivedSize(_socket); + if (size < 8) { + return -1; + } + uint8_t info[8]; + W5100.recv_data_processing(_socket, info, 8); + W5100.execCmdSn(_socket, Sock_RECV); + pHost->setIp(IpAddr(info[0],info[1],info[2],info[3])); + pHost->setPort(info[4]<<8|info[5]); + size -= 8; + if (size > len) { + size = len; + } + W5100.recv_data_processing(_socket, (uint8_t*)buf, size); + W5100.execCmdSn(_socket, Sock_RECV); +#ifdef DEBUG + printfBytes("UDP PACKET-INFO", (u8*)info, 8); + printHex((u8*)buf, size); +#endif //DEBUG + return size; + } + + UDPSocketErr close() + { + W5100.execCmdSn(_socket, Sock_CLOSE); + return UDPSOCKET_OK; + } + + void setOnEvent(void (*pMethod)(UDPSocketEvent)) + { + m_pCb = pMethod; + } + + void resetOnEvent() { + m_pCb = NULL; + } + + void poll() + { + PRINT_FUNC(); +#ifdef DEBUG + printf("socket:%d SnMR:%02x SnIR:%02x SnSR:%02x\n", _socket, + W5100.readSnMR(_socket), W5100.readSnIR(_socket), W5100.readSnSR(_socket)); + uint8_t ip[4]; + W5100.readSnDIPR(_socket, ip); + printf("Sn_DIPR: %d.%d.%d.%d Sn_DPORT: %d\n", ip[0], ip[1], ip[2], ip[3], W5100.readSnDPORT(_socket)); + uint8_t mac[6]; + W5100.readSnDHAR(_socket, mac); + printf("Sn_DHAR: %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + printf("Sn_RX_RSR:%d, Sn_RX_RD:%d, Sn_RX_WR:%d\n", + W5100.readSnRX_RSR(_socket), W5100.readSnRX_RD(_socket), W5100.readSnRX_WR(_socket)); + printf("Sn_TX_FSR:%d, Sn_TX_RD:%d, Sn_TX_WR:%d\n", + W5100.readSnTX_FSR(_socket), W5100.readSnTX_RD(_socket), W5100.readSnTX_WR(_socket)); +#endif //DEBUG + if (m_pCb == NULL) { + return; + } + if (W5100.getRXReceivedSize(_socket) > 0) { + m_pCb(UDPSOCKET_READABLE); + return; + } + } +}; +#endif // MYUDPSOCKET_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Tue Apr 10 03:38:22 2012 +0000 @@ -0,0 +1,16 @@ +#include "mbed.h" +#include "MyEthernetNetIf.h" + +#define WIZ820IO + +#ifdef WIZ820IO +MyEthernetNetIf eth; +#else +EthernetNetIf eth; +#endif + +int main() { + printf("setup...\n"); + int r = eth.setup(); + printf("%d\n", r); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Tue Apr 10 03:38:22 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/737756e0b479
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/w5100.cpp Tue Apr 10 03:38:22 2012 +0000 @@ -0,0 +1,271 @@ +/* + * 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 <stdio.h> +#include <string.h> +#include "w5100.h" +#ifndef MBED +#include <avr/interrupt.h> +#endif //MBED + +// W5100 controller instance +W5100Class W5100; + +#define TX_RX_MAX_BUF_SIZE 2048 +#define TX_BUF 0x1100 +#define RX_BUF (TX_BUF + TX_RX_MAX_BUF_SIZE) + +#ifdef W5200 +#define TXBUF_BASE 0x8000 +#define RXBUF_BASE 0xC000 +#else +#define TXBUF_BASE 0x4000 +#define RXBUF_BASE 0x6000 +#endif + +#ifdef MBED +SPI spi(p11, p12, p13); // mosi, miso, sclk +DigitalOut _cs(p14); +DigitalOut _reset(p15); +inline void delay(int n) { wait_ms(n); } +inline static void initSS(){ _cs = 1; } +inline static void setSS() { _cs = 0; } +inline static void resetSS() { _cs = 1; } + +void W5100Class::hardware_reset() { + _reset = 1; + _reset = 0; + wait_us(2); + _reset = 1; + wait_ms(150); +} +#endif //MBED + +void W5100Class::init(void) +{ + initSS(); + writeMR(1<<RST); + +#ifdef W5200 + for (int i=0; i<MAX_SOCK_NUM; i++) { + write((0x4000 + i * 0x100 + 0x001F), 2); + write((0x4000 + i * 0x100 + 0x001E), 2); + } +#else + writeTMSR(0x55); + writeRMSR(0x55); +#endif + + for (int i=0; i<MAX_SOCK_NUM; i++) { + SBASE[i] = TXBUF_BASE + SSIZE * i; + RBASE[i] = RXBUF_BASE + RSIZE * i; + } +} + +uint16_t W5100Class::getTXFreeSize(SOCKET s) +{ + uint16_t val=0, val1=0; + do { + val1 = readSnTX_FSR(s); + if (val1 != 0) + val = readSnTX_FSR(s); + } + while (val != val1); + return val; +} + +uint16_t W5100Class::getRXReceivedSize(SOCKET s) +{ + uint16_t val=0,val1=0; + do { + val1 = readSnRX_RSR(s); + if (val1 != 0) + val = readSnRX_RSR(s); + } + while (val != val1); + return val; +} + + +void W5100Class::send_data_processing(SOCKET s, const uint8_t *data, uint16_t len) +{ + // This is same as having no offset in a call to send_data_processing_offset + send_data_processing_offset(s, 0, data, len); +} + +void W5100Class::send_data_processing_offset(SOCKET s, uint16_t data_offset, const uint8_t *data, uint16_t len) +{ + uint16_t ptr = readSnTX_WR(s); + ptr += data_offset; + uint16_t offset = ptr & SMASK; + uint16_t dstAddr = offset + SBASE[s]; + + if (offset + len > SSIZE) + { + // Wrap around circular buffer + uint16_t size = SSIZE - offset; + write(dstAddr, data, size); + write(SBASE[s], data + size, len - size); + } + else { + write(dstAddr, data, len); + } + + ptr += len; + writeSnTX_WR(s, ptr); +} + + +void W5100Class::recv_data_processing(SOCKET s, uint8_t *data, uint16_t len, uint8_t peek) +{ + uint16_t ptr; + ptr = readSnRX_RD(s); + read_data(s, (uint8_t *)ptr, data, len); + if (!peek) + { + ptr += len; + writeSnRX_RD(s, ptr); + } +} + +void W5100Class::read_data(SOCKET s, volatile uint8_t *src, volatile uint8_t *dst, uint16_t len) +{ + uint16_t size; + uint16_t src_mask; + uint16_t src_ptr; + +#ifdef MBED + src_mask = (int)src & RMASK; +#else + src_mask = (uint16_t)src & RMASK; +#endif //MBED + src_ptr = RBASE[s] + src_mask; + + if( (src_mask + len) > RSIZE ) + { + size = RSIZE - src_mask; + read(src_ptr, (uint8_t *)dst, size); + dst += size; + read(RBASE[s], (uint8_t *) dst, len - size); + } + else + read(src_ptr, (uint8_t *) dst, len); +} + + +uint8_t W5100Class::write(uint16_t _addr, uint8_t _data) +{ + setSS(); + +#ifdef W5200 + spi.write(_addr >> 8); + spi.write(_addr & 0xFF); + spi.write(0x80); + spi.write(0x01); +#else + spi.write(0xF0); + spi.write(_addr >> 8); + spi.write(_addr & 0xFF); +#endif + spi.write(_data); + resetSS(); + return 1; +} + +uint16_t W5100Class::write(uint16_t _addr, const uint8_t *_buf, uint16_t _len) +{ + +#ifdef W5200 + setSS(); + spi.write(_addr >> 8); + spi.write(_addr & 0xFF); + spi.write((0x80 | ((_len & 0x7F00) >> 8))); + spi.write(_len & 0x00FF); + + for (uint16_t i=0; i<_len; i++) + { + spi.write(_buf[i]); + + } + resetSS(); +#else + + for (uint16_t i=0; i<_len; i++) + { + setSS(); + spi.write(0xF0); + spi.write(_addr >> 8); + spi.write(_addr & 0xFF); + _addr++; + spi.write(_buf[i]); + resetSS(); + } +#endif + + return _len; +} + +uint8_t W5100Class::read(uint16_t _addr) +{ + setSS(); +#ifdef W5200 + spi.write(_addr >> 8); + spi.write(_addr & 0xFF); + spi.write(0x00); + spi.write(0x01); +#else + spi.write(0x0F); + spi.write(_addr >> 8); + spi.write(_addr & 0xFF); +#endif + + uint8_t _data = spi.write(0); + resetSS(); + return _data; +} + +uint16_t W5100Class::read(uint16_t _addr, uint8_t *_buf, uint16_t _len) +{ +#ifdef W5200 + setSS(); + spi.write(_addr >> 8); + spi.write(_addr & 0xFF); + spi.write((0x00 | ((_len & 0x7F00) >> 8))); + spi.write(_len & 0x00FF); + + for (uint16_t i=0; i<_len; i++) + { + _buf[i] = spi.write(0); + + } + resetSS(); + +#else + + for (uint16_t i=0; i<_len; i++) + { + setSS(); + spi.write(0x0F); + spi.write(_addr >> 8); + spi.write(_addr & 0xFF); + _addr++; + _buf[i] = spi.write(0); + resetSS(); + } +#endif + return _len; +} + +void W5100Class::execCmdSn(SOCKET s, SockCMD _cmd) { + // Send command to socket + writeSnCR(s, _cmd); + // Wait for command to complete + while (readSnCR(s)) + ; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/w5100.h Tue Apr 10 03:38:22 2012 +0000 @@ -0,0 +1,435 @@ +/* + * 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 +#define MBED +#ifdef MBED +#include "mbed.h" +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +#else //MBED +#include <avr/pgmspace.h> +#include <SPI.h> +#endif //MBED +#define W5200 + +#ifdef W5200 +#define MAX_SOCK_NUM 8 +#else +#define MAX_SOCK_NUM 4 +#endif + + +typedef uint8_t SOCKET; + +#ifndef W5200 +#define IDM_OR 0x8000 +#define IDM_AR0 0x8001 +#define IDM_AR1 0x8002 +#define IDM_DR 0x8003 +#endif + +/* +class MR { +public: + static const uint8_t RST = 0x80; + static const uint8_t PB = 0x10; + static const uint8_t PPPOE = 0x08; + static const uint8_t LB = 0x04; + static const uint8_t AI = 0x02; + static const uint8_t IND = 0x01; +}; +*/ +/* +class IR { +public: + static const uint8_t CONFLICT = 0x80; + static const uint8_t UNREACH = 0x40; + static const uint8_t PPPoE = 0x20; + static const uint8_t SOCK0 = 0x01; + static const uint8_t SOCK1 = 0x02; + static const uint8_t SOCK2 = 0x04; + static const uint8_t SOCK3 = 0x08; + static inline uint8_t SOCK(SOCKET ch) { return (0x01 << ch); }; +}; +*/ + +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 SnCmd { +public: + static const uint8_t OPEN = 0x01; + static const uint8_t LISTEN = 0x02; + static const uint8_t CONNECT = 0x04; + static const uint8_t DISCON = 0x08; + static const uint8_t CLOSE = 0x10; + static const uint8_t SEND = 0x20; + static const uint8_t SEND_MAC = 0x21; + static const uint8_t SEND_KEEP = 0x22; + static const uint8_t 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: +#ifdef MBED + void hardware_reset(); +#endif //MBED + void init(); + + /** + * @brief This function is being used for copy the data form Receive buffer of the chip to application buffer. + * + * It calculate the actual physical address where one has to read + * the data from Receive buffer. Here also take care of the condition while it exceed + * the Rx memory uper-bound of socket. + */ + void read_data(SOCKET s, volatile uint8_t * src, volatile uint8_t * dst, uint16_t len); + + /** + * @brief This function is being called by send() and sendto() function also. + * + * This function read the Tx write pointer register and after copy the data in buffer update the Tx write pointer + * register. User should read upper byte first and lower byte later to get proper value. + */ + void send_data_processing(SOCKET s, const uint8_t *data, uint16_t len); + /** + * @brief A copy of send_data_processing that uses the provided ptr for the + * write offset. Only needed for the "streaming" UDP API, where + * a single UDP packet is built up over a number of calls to + * send_data_processing_ptr, because TX_WR doesn't seem to get updated + * correctly in those scenarios + * @param ptr value to use in place of TX_WR. If 0, then the value is read + * in from TX_WR + * @return New value for ptr, to be used in the next call + */ +// FIXME Update documentation + void send_data_processing_offset(SOCKET s, uint16_t data_offset, const uint8_t *data, uint16_t len); + + /** + * @brief This function is being called by recv() also. + * + * This function read the Rx read pointer register + * and after copy the data from receive buffer update the Rx write pointer register. + * User should read upper byte first and lower byte later to get proper value. + */ + void recv_data_processing(SOCKET s, uint8_t *data, uint16_t len, uint8_t peek = 0); + + inline void setGatewayIp(uint8_t *_addr); + inline void getGatewayIp(uint8_t *_addr); + + inline void setSubnetMask(uint8_t *_addr); + inline void getSubnetMask(uint8_t *_addr); + + inline void setMACAddress(uint8_t * addr); + inline void getMACAddress(uint8_t * addr); + + inline void setIPAddress(uint8_t * addr); + inline void getIPAddress(uint8_t * addr); + + inline void setRetransmissionTime(uint16_t timeout); + inline void setRetransmissionCount(uint8_t _retry); + + void execCmdSn(SOCKET s, SockCMD _cmd); + + uint16_t getTXFreeSize(SOCKET s); + uint16_t getRXReceivedSize(SOCKET s); + + + // W5100 Registers + // --------------- +private: + static uint8_t write(uint16_t _addr, uint8_t _data); + static uint16_t write(uint16_t addr, const uint8_t *buf, uint16_t len); + static uint8_t read(uint16_t addr); + static uint16_t read(uint16_t addr, uint8_t *buf, uint16_t len); + +#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) { \ + write(address, _data >> 8); \ + write(address+1, _data & 0xFF); \ + } \ + static uint16_t read##name() { \ + uint16_t res = read(address); \ + res = (res << 8) + read(address + 1); \ + return res; \ + } +#define __GP_REGISTER_N(name, address, size) \ + static uint16_t write##name(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 + + #ifndef W5200 + __GP_REGISTER8 (RMSR, 0x001A); // Receive memory size + __GP_REGISTER8 (TMSR, 0x001B); // Transmit memory size + #endif + __GP_REGISTER8 (PATR, 0x001C); // Authentication type address in PPPoE mode + + __GP_REGISTER8 (VERSIONR,0x001f); // Chip version + + __GP_REGISTER8 (PTIMER, 0x0028); // PPP LCP Request Timer + __GP_REGISTER8 (PMAGIC, 0x0029); // PPP LCP Magic Number + #ifndef W5200 + __GP_REGISTER_N(UIPR, 0x002A, 4); // Unreachable IP address in UDP mode + __GP_REGISTER16(UPORT, 0x002E); // Unreachable Port address in UDP mode +#endif + __GP_REGISTER8 (PHYSTATUS,0x0035); // PHY Status + +#undef __GP_REGISTER8 +#undef __GP_REGISTER16 +#undef __GP_REGISTER_N + + // W5100 Socket registers + // ---------------------- +private: + static inline uint8_t readSn(SOCKET _s, uint16_t _addr); + static inline uint8_t writeSn(SOCKET _s, uint16_t _addr, uint8_t _data); + static inline uint16_t readSn(SOCKET _s, uint16_t _addr, uint8_t *_buf, uint16_t len); + static inline uint16_t writeSn(SOCKET _s, uint16_t _addr, uint8_t *_buf, uint16_t len); + +#ifdef W5200 + static const uint16_t CH_BASE = 0x4000; +#else + static const uint16_t CH_BASE = 0x0400; +#endif + + 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) { \ + writeSn(_s, address, _data >> 8); \ + writeSn(_s, address+1, _data & 0xFF); \ + } \ + static uint16_t read##name(SOCKET _s) { \ + uint16_t res = readSn(_s, address); \ + res = (res << 8) + readSn(_s, address + 1); \ + return res; \ + } +#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_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 const uint8_t RST = 7; // Reset BIT + +#ifdef W5200 + static const int SOCKETS = 8; +#else + static const int SOCKETS = 4; +#endif + + static const uint16_t SMASK = 0x07FF; // Tx buffer MASK + static const uint16_t RMASK = 0x07FF; // Rx buffer MASK +public: + static const uint16_t SSIZE = 2048; // Max Tx buffer size +private: + static const uint16_t RSIZE = 2048; // Max Rx buffer size + uint16_t SBASE[SOCKETS]; // Tx buffer base address + uint16_t RBASE[SOCKETS]; // Rx buffer base address + +private: +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + inline static void initSS() { DDRB |= _BV(4); }; + inline static void setSS() { PORTB &= ~_BV(4); }; + inline static void resetSS() { PORTB |= _BV(4); }; +#elif defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB162__) + inline static void initSS() { DDRB |= _BV(0); }; + inline static void setSS() { PORTB &= ~_BV(0); }; + inline static void resetSS() { PORTB |= _BV(0); }; +#else +#ifndef MBED + inline static void initSS() { DDRB |= _BV(2); }; + inline static void setSS() { PORTB &= ~_BV(2); }; + inline static void resetSS() { PORTB |= _BV(2); }; +#endif //MBED +#endif + +}; + +extern W5100Class W5100; + +uint8_t W5100Class::readSn(SOCKET _s, uint16_t _addr) { + return read(CH_BASE + _s * CH_SIZE + _addr); +} + +uint8_t W5100Class::writeSn(SOCKET _s, uint16_t _addr, uint8_t _data) { + return write(CH_BASE + _s * CH_SIZE + _addr, _data); +} + +uint16_t W5100Class::readSn(SOCKET _s, uint16_t _addr, uint8_t *_buf, uint16_t _len) { + return read(CH_BASE + _s * CH_SIZE + _addr, _buf, _len); +} + +uint16_t W5100Class::writeSn(SOCKET _s, uint16_t _addr, uint8_t *_buf, uint16_t _len) { + return write(CH_BASE + _s * CH_SIZE + _addr, _buf, _len); +} + +void W5100Class::getGatewayIp(uint8_t *_addr) { + readGAR(_addr); +} + +void W5100Class::setGatewayIp(uint8_t *_addr) { + writeGAR(_addr); +} + +void W5100Class::getSubnetMask(uint8_t *_addr) { + readSUBR(_addr); +} + +void W5100Class::setSubnetMask(uint8_t *_addr) { + writeSUBR(_addr); +} + +void W5100Class::getMACAddress(uint8_t *_addr) { + readSHAR(_addr); +} + +void W5100Class::setMACAddress(uint8_t *_addr) { + writeSHAR(_addr); +} + +void W5100Class::getIPAddress(uint8_t *_addr) { + readSIPR(_addr); +} + +void W5100Class::setIPAddress(uint8_t *_addr) { + writeSIPR(_addr); +} + +void W5100Class::setRetransmissionTime(uint16_t _timeout) { + writeRTR(_timeout); +} + +void W5100Class::setRetransmissionCount(uint8_t _retry) { + writeRCR(_retry); +} + +#endif