feat: 全量同步 254 个常用的 Arduino 扩展库文件
This commit is contained in:
433
arduino-libs/arduino-cli/libraries/Ethernet/src/Dhcp.cpp
Normal file
433
arduino-libs/arduino-cli/libraries/Ethernet/src/Dhcp.cpp
Normal file
@@ -0,0 +1,433 @@
|
||||
// DHCP Library v0.3 - April 25, 2009
|
||||
// Author: Jordan Terrell - blog.jordanterrell.com
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "Ethernet.h"
|
||||
#include "Dhcp.h"
|
||||
#include "utility/w5100.h"
|
||||
|
||||
int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout)
|
||||
{
|
||||
_dhcpLeaseTime=0;
|
||||
_dhcpT1=0;
|
||||
_dhcpT2=0;
|
||||
_timeout = timeout;
|
||||
_responseTimeout = responseTimeout;
|
||||
|
||||
// zero out _dhcpMacAddr
|
||||
memset(_dhcpMacAddr, 0, 6);
|
||||
reset_DHCP_lease();
|
||||
|
||||
memcpy((void*)_dhcpMacAddr, (void*)mac, 6);
|
||||
_dhcp_state = STATE_DHCP_START;
|
||||
return request_DHCP_lease();
|
||||
}
|
||||
|
||||
void DhcpClass::reset_DHCP_lease()
|
||||
{
|
||||
// zero out _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp
|
||||
memset(_dhcpLocalIp, 0, 20);
|
||||
}
|
||||
|
||||
//return:0 on error, 1 if request is sent and response is received
|
||||
int DhcpClass::request_DHCP_lease()
|
||||
{
|
||||
uint8_t messageType = 0;
|
||||
|
||||
// Pick an initial transaction ID
|
||||
_dhcpTransactionId = random(1UL, 2000UL);
|
||||
_dhcpInitialTransactionId = _dhcpTransactionId;
|
||||
|
||||
_dhcpUdpSocket.stop();
|
||||
if (_dhcpUdpSocket.begin(DHCP_CLIENT_PORT) == 0) {
|
||||
// Couldn't get a socket
|
||||
return 0;
|
||||
}
|
||||
|
||||
presend_DHCP();
|
||||
|
||||
int result = 0;
|
||||
|
||||
unsigned long startTime = millis();
|
||||
|
||||
while (_dhcp_state != STATE_DHCP_LEASED) {
|
||||
if (_dhcp_state == STATE_DHCP_START) {
|
||||
_dhcpTransactionId++;
|
||||
send_DHCP_MESSAGE(DHCP_DISCOVER, ((millis() - startTime) / 1000));
|
||||
_dhcp_state = STATE_DHCP_DISCOVER;
|
||||
} else if (_dhcp_state == STATE_DHCP_REREQUEST) {
|
||||
_dhcpTransactionId++;
|
||||
send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime)/1000));
|
||||
_dhcp_state = STATE_DHCP_REQUEST;
|
||||
} else if (_dhcp_state == STATE_DHCP_DISCOVER) {
|
||||
uint32_t respId;
|
||||
messageType = parseDHCPResponse(_responseTimeout, respId);
|
||||
if (messageType == DHCP_OFFER) {
|
||||
// We'll use the transaction ID that the offer came with,
|
||||
// rather than the one we were up to
|
||||
_dhcpTransactionId = respId;
|
||||
send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime) / 1000));
|
||||
_dhcp_state = STATE_DHCP_REQUEST;
|
||||
}
|
||||
} else if (_dhcp_state == STATE_DHCP_REQUEST) {
|
||||
uint32_t respId;
|
||||
messageType = parseDHCPResponse(_responseTimeout, respId);
|
||||
if (messageType == DHCP_ACK) {
|
||||
_dhcp_state = STATE_DHCP_LEASED;
|
||||
result = 1;
|
||||
//use default lease time if we didn't get it
|
||||
if (_dhcpLeaseTime == 0) {
|
||||
_dhcpLeaseTime = DEFAULT_LEASE;
|
||||
}
|
||||
// Calculate T1 & T2 if we didn't get it
|
||||
if (_dhcpT1 == 0) {
|
||||
// T1 should be 50% of _dhcpLeaseTime
|
||||
_dhcpT1 = _dhcpLeaseTime >> 1;
|
||||
}
|
||||
if (_dhcpT2 == 0) {
|
||||
// T2 should be 87.5% (7/8ths) of _dhcpLeaseTime
|
||||
_dhcpT2 = _dhcpLeaseTime - (_dhcpLeaseTime >> 3);
|
||||
}
|
||||
_renewInSec = _dhcpT1;
|
||||
_rebindInSec = _dhcpT2;
|
||||
} else if (messageType == DHCP_NAK) {
|
||||
_dhcp_state = STATE_DHCP_START;
|
||||
}
|
||||
}
|
||||
|
||||
if (messageType == 255) {
|
||||
messageType = 0;
|
||||
_dhcp_state = STATE_DHCP_START;
|
||||
}
|
||||
|
||||
if (result != 1 && ((millis() - startTime) > _timeout))
|
||||
break;
|
||||
}
|
||||
|
||||
// We're done with the socket now
|
||||
_dhcpUdpSocket.stop();
|
||||
_dhcpTransactionId++;
|
||||
|
||||
_lastCheckLeaseMillis = millis();
|
||||
return result;
|
||||
}
|
||||
|
||||
void DhcpClass::presend_DHCP()
|
||||
{
|
||||
}
|
||||
|
||||
void DhcpClass::send_DHCP_MESSAGE(uint8_t messageType, uint16_t secondsElapsed)
|
||||
{
|
||||
uint8_t buffer[32];
|
||||
memset(buffer, 0, 32);
|
||||
IPAddress dest_addr(255, 255, 255, 255); // Broadcast address
|
||||
|
||||
if (_dhcpUdpSocket.beginPacket(dest_addr, DHCP_SERVER_PORT) == -1) {
|
||||
//Serial.printf("DHCP transmit error\n");
|
||||
// FIXME Need to return errors
|
||||
return;
|
||||
}
|
||||
|
||||
buffer[0] = DHCP_BOOTREQUEST; // op
|
||||
buffer[1] = DHCP_HTYPE10MB; // htype
|
||||
buffer[2] = DHCP_HLENETHERNET; // hlen
|
||||
buffer[3] = DHCP_HOPS; // hops
|
||||
|
||||
// xid
|
||||
unsigned long xid = htonl(_dhcpTransactionId);
|
||||
memcpy(buffer + 4, &(xid), 4);
|
||||
|
||||
// 8, 9 - seconds elapsed
|
||||
buffer[8] = ((secondsElapsed & 0xff00) >> 8);
|
||||
buffer[9] = (secondsElapsed & 0x00ff);
|
||||
|
||||
// flags
|
||||
unsigned short flags = htons(DHCP_FLAGSBROADCAST);
|
||||
memcpy(buffer + 10, &(flags), 2);
|
||||
|
||||
// ciaddr: already zeroed
|
||||
// yiaddr: already zeroed
|
||||
// siaddr: already zeroed
|
||||
// giaddr: already zeroed
|
||||
|
||||
//put data in W5100 transmit buffer
|
||||
_dhcpUdpSocket.write(buffer, 28);
|
||||
|
||||
memset(buffer, 0, 32); // clear local buffer
|
||||
|
||||
memcpy(buffer, _dhcpMacAddr, 6); // chaddr
|
||||
|
||||
//put data in W5100 transmit buffer
|
||||
_dhcpUdpSocket.write(buffer, 16);
|
||||
|
||||
memset(buffer, 0, 32); // clear local buffer
|
||||
|
||||
// leave zeroed out for sname && file
|
||||
// put in W5100 transmit buffer x 6 (192 bytes)
|
||||
|
||||
for(int i = 0; i < 6; i++) {
|
||||
_dhcpUdpSocket.write(buffer, 32);
|
||||
}
|
||||
|
||||
// OPT - Magic Cookie
|
||||
buffer[0] = (uint8_t)((MAGIC_COOKIE >> 24)& 0xFF);
|
||||
buffer[1] = (uint8_t)((MAGIC_COOKIE >> 16)& 0xFF);
|
||||
buffer[2] = (uint8_t)((MAGIC_COOKIE >> 8)& 0xFF);
|
||||
buffer[3] = (uint8_t)(MAGIC_COOKIE& 0xFF);
|
||||
|
||||
// OPT - message type
|
||||
buffer[4] = dhcpMessageType;
|
||||
buffer[5] = 0x01;
|
||||
buffer[6] = messageType; //DHCP_REQUEST;
|
||||
|
||||
// OPT - client identifier
|
||||
buffer[7] = dhcpClientIdentifier;
|
||||
buffer[8] = 0x07;
|
||||
buffer[9] = 0x01;
|
||||
memcpy(buffer + 10, _dhcpMacAddr, 6);
|
||||
|
||||
// OPT - host name
|
||||
buffer[16] = hostName;
|
||||
buffer[17] = strlen(HOST_NAME) + 6; // length of hostname + last 3 bytes of mac address
|
||||
strcpy((char*)&(buffer[18]), HOST_NAME);
|
||||
|
||||
printByte((char*)&(buffer[24]), _dhcpMacAddr[3]);
|
||||
printByte((char*)&(buffer[26]), _dhcpMacAddr[4]);
|
||||
printByte((char*)&(buffer[28]), _dhcpMacAddr[5]);
|
||||
|
||||
//put data in W5100 transmit buffer
|
||||
_dhcpUdpSocket.write(buffer, 30);
|
||||
|
||||
if (messageType == DHCP_REQUEST) {
|
||||
buffer[0] = dhcpRequestedIPaddr;
|
||||
buffer[1] = 0x04;
|
||||
buffer[2] = _dhcpLocalIp[0];
|
||||
buffer[3] = _dhcpLocalIp[1];
|
||||
buffer[4] = _dhcpLocalIp[2];
|
||||
buffer[5] = _dhcpLocalIp[3];
|
||||
|
||||
buffer[6] = dhcpServerIdentifier;
|
||||
buffer[7] = 0x04;
|
||||
buffer[8] = _dhcpDhcpServerIp[0];
|
||||
buffer[9] = _dhcpDhcpServerIp[1];
|
||||
buffer[10] = _dhcpDhcpServerIp[2];
|
||||
buffer[11] = _dhcpDhcpServerIp[3];
|
||||
|
||||
//put data in W5100 transmit buffer
|
||||
_dhcpUdpSocket.write(buffer, 12);
|
||||
}
|
||||
|
||||
buffer[0] = dhcpParamRequest;
|
||||
buffer[1] = 0x06;
|
||||
buffer[2] = subnetMask;
|
||||
buffer[3] = routersOnSubnet;
|
||||
buffer[4] = dns;
|
||||
buffer[5] = domainName;
|
||||
buffer[6] = dhcpT1value;
|
||||
buffer[7] = dhcpT2value;
|
||||
buffer[8] = endOption;
|
||||
|
||||
//put data in W5100 transmit buffer
|
||||
_dhcpUdpSocket.write(buffer, 9);
|
||||
|
||||
_dhcpUdpSocket.endPacket();
|
||||
}
|
||||
|
||||
uint8_t DhcpClass::parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId)
|
||||
{
|
||||
uint8_t type = 0;
|
||||
uint8_t opt_len = 0;
|
||||
|
||||
unsigned long startTime = millis();
|
||||
|
||||
while (_dhcpUdpSocket.parsePacket() <= 0) {
|
||||
if ((millis() - startTime) > responseTimeout) {
|
||||
return 255;
|
||||
}
|
||||
delay(50);
|
||||
}
|
||||
// start reading in the packet
|
||||
RIP_MSG_FIXED fixedMsg;
|
||||
_dhcpUdpSocket.read((uint8_t*)&fixedMsg, sizeof(RIP_MSG_FIXED));
|
||||
|
||||
if (fixedMsg.op == DHCP_BOOTREPLY && _dhcpUdpSocket.remotePort() == DHCP_SERVER_PORT) {
|
||||
transactionId = ntohl(fixedMsg.xid);
|
||||
if (memcmp(fixedMsg.chaddr, _dhcpMacAddr, 6) != 0 ||
|
||||
(transactionId < _dhcpInitialTransactionId) ||
|
||||
(transactionId > _dhcpTransactionId)) {
|
||||
// Need to read the rest of the packet here regardless
|
||||
_dhcpUdpSocket.flush(); // FIXME
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(_dhcpLocalIp, fixedMsg.yiaddr, 4);
|
||||
|
||||
// Skip to the option part
|
||||
_dhcpUdpSocket.read((uint8_t *)NULL, 240 - (int)sizeof(RIP_MSG_FIXED));
|
||||
|
||||
while (_dhcpUdpSocket.available() > 0) {
|
||||
switch (_dhcpUdpSocket.read()) {
|
||||
case endOption :
|
||||
break;
|
||||
|
||||
case padOption :
|
||||
break;
|
||||
|
||||
case dhcpMessageType :
|
||||
opt_len = _dhcpUdpSocket.read();
|
||||
type = _dhcpUdpSocket.read();
|
||||
break;
|
||||
|
||||
case subnetMask :
|
||||
opt_len = _dhcpUdpSocket.read();
|
||||
_dhcpUdpSocket.read(_dhcpSubnetMask, 4);
|
||||
break;
|
||||
|
||||
case routersOnSubnet :
|
||||
opt_len = _dhcpUdpSocket.read();
|
||||
_dhcpUdpSocket.read(_dhcpGatewayIp, 4);
|
||||
_dhcpUdpSocket.read((uint8_t *)NULL, opt_len - 4);
|
||||
break;
|
||||
|
||||
case dns :
|
||||
opt_len = _dhcpUdpSocket.read();
|
||||
_dhcpUdpSocket.read(_dhcpDnsServerIp, 4);
|
||||
_dhcpUdpSocket.read((uint8_t *)NULL, opt_len - 4);
|
||||
break;
|
||||
|
||||
case dhcpServerIdentifier :
|
||||
opt_len = _dhcpUdpSocket.read();
|
||||
if ( IPAddress(_dhcpDhcpServerIp) == IPAddress((uint32_t)0) ||
|
||||
IPAddress(_dhcpDhcpServerIp) == _dhcpUdpSocket.remoteIP() ) {
|
||||
_dhcpUdpSocket.read(_dhcpDhcpServerIp, sizeof(_dhcpDhcpServerIp));
|
||||
} else {
|
||||
// Skip over the rest of this option
|
||||
_dhcpUdpSocket.read((uint8_t *)NULL, opt_len);
|
||||
}
|
||||
break;
|
||||
|
||||
case dhcpT1value :
|
||||
opt_len = _dhcpUdpSocket.read();
|
||||
_dhcpUdpSocket.read((uint8_t*)&_dhcpT1, sizeof(_dhcpT1));
|
||||
_dhcpT1 = ntohl(_dhcpT1);
|
||||
break;
|
||||
|
||||
case dhcpT2value :
|
||||
opt_len = _dhcpUdpSocket.read();
|
||||
_dhcpUdpSocket.read((uint8_t*)&_dhcpT2, sizeof(_dhcpT2));
|
||||
_dhcpT2 = ntohl(_dhcpT2);
|
||||
break;
|
||||
|
||||
case dhcpIPaddrLeaseTime :
|
||||
opt_len = _dhcpUdpSocket.read();
|
||||
_dhcpUdpSocket.read((uint8_t*)&_dhcpLeaseTime, sizeof(_dhcpLeaseTime));
|
||||
_dhcpLeaseTime = ntohl(_dhcpLeaseTime);
|
||||
_renewInSec = _dhcpLeaseTime;
|
||||
break;
|
||||
|
||||
default :
|
||||
opt_len = _dhcpUdpSocket.read();
|
||||
// Skip over the rest of this option
|
||||
_dhcpUdpSocket.read((uint8_t *)NULL, opt_len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Need to skip to end of the packet regardless here
|
||||
_dhcpUdpSocket.flush(); // FIXME
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
returns:
|
||||
0/DHCP_CHECK_NONE: nothing happened
|
||||
1/DHCP_CHECK_RENEW_FAIL: renew failed
|
||||
2/DHCP_CHECK_RENEW_OK: renew success
|
||||
3/DHCP_CHECK_REBIND_FAIL: rebind fail
|
||||
4/DHCP_CHECK_REBIND_OK: rebind success
|
||||
*/
|
||||
int DhcpClass::checkLease()
|
||||
{
|
||||
int rc = DHCP_CHECK_NONE;
|
||||
|
||||
unsigned long now = millis();
|
||||
unsigned long elapsed = now - _lastCheckLeaseMillis;
|
||||
|
||||
// if more then one sec passed, reduce the counters accordingly
|
||||
if (elapsed >= 1000) {
|
||||
// set the new timestamps
|
||||
_lastCheckLeaseMillis = now - (elapsed % 1000);
|
||||
elapsed = elapsed / 1000;
|
||||
|
||||
// decrease the counters by elapsed seconds
|
||||
// we assume that the cycle time (elapsed) is fairly constant
|
||||
// if the remainder is less than cycle time * 2
|
||||
// do it early instead of late
|
||||
if (_renewInSec < elapsed * 2) {
|
||||
_renewInSec = 0;
|
||||
} else {
|
||||
_renewInSec -= elapsed;
|
||||
}
|
||||
if (_rebindInSec < elapsed * 2) {
|
||||
_rebindInSec = 0;
|
||||
} else {
|
||||
_rebindInSec -= elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
// if we have a lease but should renew, do it
|
||||
if (_renewInSec == 0 &&_dhcp_state == STATE_DHCP_LEASED) {
|
||||
_dhcp_state = STATE_DHCP_REREQUEST;
|
||||
rc = 1 + request_DHCP_lease();
|
||||
}
|
||||
|
||||
// if we have a lease or is renewing but should bind, do it
|
||||
if (_rebindInSec == 0 && (_dhcp_state == STATE_DHCP_LEASED ||
|
||||
_dhcp_state == STATE_DHCP_START)) {
|
||||
// this should basically restart completely
|
||||
_dhcp_state = STATE_DHCP_START;
|
||||
reset_DHCP_lease();
|
||||
rc = 3 + request_DHCP_lease();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
IPAddress DhcpClass::getLocalIp()
|
||||
{
|
||||
return IPAddress(_dhcpLocalIp);
|
||||
}
|
||||
|
||||
IPAddress DhcpClass::getSubnetMask()
|
||||
{
|
||||
return IPAddress(_dhcpSubnetMask);
|
||||
}
|
||||
|
||||
IPAddress DhcpClass::getGatewayIp()
|
||||
{
|
||||
return IPAddress(_dhcpGatewayIp);
|
||||
}
|
||||
|
||||
IPAddress DhcpClass::getDhcpServerIp()
|
||||
{
|
||||
return IPAddress(_dhcpDhcpServerIp);
|
||||
}
|
||||
|
||||
IPAddress DhcpClass::getDnsServerIp()
|
||||
{
|
||||
return IPAddress(_dhcpDnsServerIp);
|
||||
}
|
||||
|
||||
void DhcpClass::printByte(char * buf, uint8_t n )
|
||||
{
|
||||
char *str = &buf[1];
|
||||
buf[0]='0';
|
||||
do {
|
||||
unsigned long m = n;
|
||||
n /= 16;
|
||||
char c = m - 16 * n;
|
||||
*str-- = c < 10 ? c + '0' : c + 'A' - 10;
|
||||
} while(n);
|
||||
}
|
||||
137
arduino-libs/arduino-cli/libraries/Ethernet/src/Dhcp.h
Normal file
137
arduino-libs/arduino-cli/libraries/Ethernet/src/Dhcp.h
Normal file
@@ -0,0 +1,137 @@
|
||||
// DHCP Library v0.3 - April 25, 2009
|
||||
// Author: Jordan Terrell - blog.jordanterrell.com
|
||||
|
||||
#ifndef Dhcp_h
|
||||
#define Dhcp_h
|
||||
|
||||
/* DHCP state machine. */
|
||||
#define STATE_DHCP_START 0
|
||||
#define STATE_DHCP_DISCOVER 1
|
||||
#define STATE_DHCP_REQUEST 2
|
||||
#define STATE_DHCP_LEASED 3
|
||||
#define STATE_DHCP_REREQUEST 4
|
||||
#define STATE_DHCP_RELEASE 5
|
||||
|
||||
#define DHCP_FLAGSBROADCAST 0x8000
|
||||
|
||||
/* UDP port numbers for DHCP */
|
||||
#define DHCP_SERVER_PORT 67 /* from server to client */
|
||||
#define DHCP_CLIENT_PORT 68 /* from client to server */
|
||||
|
||||
/* DHCP message OP code */
|
||||
#define DHCP_BOOTREQUEST 1
|
||||
#define DHCP_BOOTREPLY 2
|
||||
|
||||
/* DHCP message type */
|
||||
#define DHCP_DISCOVER 1
|
||||
#define DHCP_OFFER 2
|
||||
#define DHCP_REQUEST 3
|
||||
#define DHCP_DECLINE 4
|
||||
#define DHCP_ACK 5
|
||||
#define DHCP_NAK 6
|
||||
#define DHCP_RELEASE 7
|
||||
#define DHCP_INFORM 8
|
||||
|
||||
#define DHCP_HTYPE10MB 1
|
||||
#define DHCP_HTYPE100MB 2
|
||||
|
||||
#define DHCP_HLENETHERNET 6
|
||||
#define DHCP_HOPS 0
|
||||
#define DHCP_SECS 0
|
||||
|
||||
#define MAGIC_COOKIE 0x63825363
|
||||
#define MAX_DHCP_OPT 16
|
||||
|
||||
#define HOST_NAME "WIZnet"
|
||||
#define DEFAULT_LEASE (900) //default lease time in seconds
|
||||
|
||||
#define DHCP_CHECK_NONE (0)
|
||||
#define DHCP_CHECK_RENEW_FAIL (1)
|
||||
#define DHCP_CHECK_RENEW_OK (2)
|
||||
#define DHCP_CHECK_REBIND_FAIL (3)
|
||||
#define DHCP_CHECK_REBIND_OK (4)
|
||||
|
||||
enum
|
||||
{
|
||||
padOption = 0,
|
||||
subnetMask = 1,
|
||||
timerOffset = 2,
|
||||
routersOnSubnet = 3,
|
||||
/* timeServer = 4,
|
||||
nameServer = 5,*/
|
||||
dns = 6,
|
||||
/*logServer = 7,
|
||||
cookieServer = 8,
|
||||
lprServer = 9,
|
||||
impressServer = 10,
|
||||
resourceLocationServer = 11,*/
|
||||
hostName = 12,
|
||||
/*bootFileSize = 13,
|
||||
meritDumpFile = 14,*/
|
||||
domainName = 15,
|
||||
/*swapServer = 16,
|
||||
rootPath = 17,
|
||||
extentionsPath = 18,
|
||||
IPforwarding = 19,
|
||||
nonLocalSourceRouting = 20,
|
||||
policyFilter = 21,
|
||||
maxDgramReasmSize = 22,
|
||||
defaultIPTTL = 23,
|
||||
pathMTUagingTimeout = 24,
|
||||
pathMTUplateauTable = 25,
|
||||
ifMTU = 26,
|
||||
allSubnetsLocal = 27,
|
||||
broadcastAddr = 28,
|
||||
performMaskDiscovery = 29,
|
||||
maskSupplier = 30,
|
||||
performRouterDiscovery = 31,
|
||||
routerSolicitationAddr = 32,
|
||||
staticRoute = 33,
|
||||
trailerEncapsulation = 34,
|
||||
arpCacheTimeout = 35,
|
||||
ethernetEncapsulation = 36,
|
||||
tcpDefaultTTL = 37,
|
||||
tcpKeepaliveInterval = 38,
|
||||
tcpKeepaliveGarbage = 39,
|
||||
nisDomainName = 40,
|
||||
nisServers = 41,
|
||||
ntpServers = 42,
|
||||
vendorSpecificInfo = 43,
|
||||
netBIOSnameServer = 44,
|
||||
netBIOSdgramDistServer = 45,
|
||||
netBIOSnodeType = 46,
|
||||
netBIOSscope = 47,
|
||||
xFontServer = 48,
|
||||
xDisplayManager = 49,*/
|
||||
dhcpRequestedIPaddr = 50,
|
||||
dhcpIPaddrLeaseTime = 51,
|
||||
/*dhcpOptionOverload = 52,*/
|
||||
dhcpMessageType = 53,
|
||||
dhcpServerIdentifier = 54,
|
||||
dhcpParamRequest = 55,
|
||||
/*dhcpMsg = 56,
|
||||
dhcpMaxMsgSize = 57,*/
|
||||
dhcpT1value = 58,
|
||||
dhcpT2value = 59,
|
||||
/*dhcpClassIdentifier = 60,*/
|
||||
dhcpClientIdentifier = 61,
|
||||
endOption = 255
|
||||
};
|
||||
|
||||
typedef struct _RIP_MSG_FIXED
|
||||
{
|
||||
uint8_t op;
|
||||
uint8_t htype;
|
||||
uint8_t hlen;
|
||||
uint8_t hops;
|
||||
uint32_t xid;
|
||||
uint16_t secs;
|
||||
uint16_t flags;
|
||||
uint8_t ciaddr[4];
|
||||
uint8_t yiaddr[4];
|
||||
uint8_t siaddr[4];
|
||||
uint8_t giaddr[4];
|
||||
uint8_t chaddr[6];
|
||||
} RIP_MSG_FIXED;
|
||||
|
||||
#endif
|
||||
354
arduino-libs/arduino-cli/libraries/Ethernet/src/Dns.cpp
Normal file
354
arduino-libs/arduino-cli/libraries/Ethernet/src/Dns.cpp
Normal file
@@ -0,0 +1,354 @@
|
||||
// Arduino DNS client for WizNet5100-based Ethernet shield
|
||||
// (c) Copyright 2009-2010 MCQN Ltd.
|
||||
// Released under Apache License, version 2.0
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "Ethernet.h"
|
||||
#include "Dns.h"
|
||||
#include "utility/w5100.h"
|
||||
|
||||
|
||||
#define SOCKET_NONE 255
|
||||
// Various flags and header field values for a DNS message
|
||||
#define UDP_HEADER_SIZE 8
|
||||
#define DNS_HEADER_SIZE 12
|
||||
#define TTL_SIZE 4
|
||||
#define QUERY_FLAG (0)
|
||||
#define RESPONSE_FLAG (1<<15)
|
||||
#define QUERY_RESPONSE_MASK (1<<15)
|
||||
#define OPCODE_STANDARD_QUERY (0)
|
||||
#define OPCODE_INVERSE_QUERY (1<<11)
|
||||
#define OPCODE_STATUS_REQUEST (2<<11)
|
||||
#define OPCODE_MASK (15<<11)
|
||||
#define AUTHORITATIVE_FLAG (1<<10)
|
||||
#define TRUNCATION_FLAG (1<<9)
|
||||
#define RECURSION_DESIRED_FLAG (1<<8)
|
||||
#define RECURSION_AVAILABLE_FLAG (1<<7)
|
||||
#define RESP_NO_ERROR (0)
|
||||
#define RESP_FORMAT_ERROR (1)
|
||||
#define RESP_SERVER_FAILURE (2)
|
||||
#define RESP_NAME_ERROR (3)
|
||||
#define RESP_NOT_IMPLEMENTED (4)
|
||||
#define RESP_REFUSED (5)
|
||||
#define RESP_MASK (15)
|
||||
#define TYPE_A (0x0001)
|
||||
#define CLASS_IN (0x0001)
|
||||
#define LABEL_COMPRESSION_MASK (0xC0)
|
||||
// Port number that DNS servers listen on
|
||||
#define DNS_PORT 53
|
||||
|
||||
// Possible return codes from ProcessResponse
|
||||
#define SUCCESS 1
|
||||
#define TIMED_OUT -1
|
||||
#define INVALID_SERVER -2
|
||||
#define TRUNCATED -3
|
||||
#define INVALID_RESPONSE -4
|
||||
|
||||
void DNSClient::begin(const IPAddress& aDNSServer)
|
||||
{
|
||||
iDNSServer = aDNSServer;
|
||||
iRequestId = 0;
|
||||
}
|
||||
|
||||
|
||||
int DNSClient::inet_aton(const char* address, IPAddress& result)
|
||||
{
|
||||
uint16_t acc = 0; // Accumulator
|
||||
uint8_t dots = 0;
|
||||
|
||||
while (*address) {
|
||||
char c = *address++;
|
||||
if (c >= '0' && c <= '9') {
|
||||
acc = acc * 10 + (c - '0');
|
||||
if (acc > 255) {
|
||||
// Value out of [0..255] range
|
||||
return 0;
|
||||
}
|
||||
} else if (c == '.') {
|
||||
if (dots == 3) {
|
||||
// Too much dots (there must be 3 dots)
|
||||
return 0;
|
||||
}
|
||||
result[dots++] = acc;
|
||||
acc = 0;
|
||||
} else {
|
||||
// Invalid char
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (dots != 3) {
|
||||
// Too few dots (there must be 3 dots)
|
||||
return 0;
|
||||
}
|
||||
result[3] = acc;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int DNSClient::getHostByName(const char* aHostname, IPAddress& aResult, uint16_t timeout)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
// See if it's a numeric IP address
|
||||
if (inet_aton(aHostname, aResult)) {
|
||||
// It is, our work here is done
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Check we've got a valid DNS server to use
|
||||
if (iDNSServer == INADDR_NONE) {
|
||||
return INVALID_SERVER;
|
||||
}
|
||||
|
||||
// Find a socket to use
|
||||
if (iUdp.begin(1024+(millis() & 0xF)) == 1) {
|
||||
// Try up to three times
|
||||
int retries = 0;
|
||||
// while ((retries < 3) && (ret <= 0)) {
|
||||
// Send DNS request
|
||||
ret = iUdp.beginPacket(iDNSServer, DNS_PORT);
|
||||
if (ret != 0) {
|
||||
// Now output the request data
|
||||
ret = BuildRequest(aHostname);
|
||||
if (ret != 0) {
|
||||
// And finally send the request
|
||||
ret = iUdp.endPacket();
|
||||
if (ret != 0) {
|
||||
// Now wait for a response
|
||||
int wait_retries = 0;
|
||||
ret = TIMED_OUT;
|
||||
while ((wait_retries < 3) && (ret == TIMED_OUT)) {
|
||||
ret = ProcessResponse(timeout, aResult);
|
||||
wait_retries++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
retries++;
|
||||
//}
|
||||
|
||||
// We're done with the socket now
|
||||
iUdp.stop();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint16_t DNSClient::BuildRequest(const char* aName)
|
||||
{
|
||||
// Build header
|
||||
// 1 1 1 1 1 1
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | ID |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | QDCOUNT |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | ANCOUNT |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | NSCOUNT |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | ARCOUNT |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// As we only support one request at a time at present, we can simplify
|
||||
// some of this header
|
||||
iRequestId = millis(); // generate a random ID
|
||||
uint16_t twoByteBuffer;
|
||||
|
||||
// FIXME We should also check that there's enough space available to write to, rather
|
||||
// FIXME than assume there's enough space (as the code does at present)
|
||||
iUdp.write((uint8_t*)&iRequestId, sizeof(iRequestId));
|
||||
|
||||
twoByteBuffer = htons(QUERY_FLAG | OPCODE_STANDARD_QUERY | RECURSION_DESIRED_FLAG);
|
||||
iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
|
||||
|
||||
twoByteBuffer = htons(1); // One question record
|
||||
iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
|
||||
|
||||
twoByteBuffer = 0; // Zero answer records
|
||||
iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
|
||||
|
||||
iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
|
||||
// and zero additional records
|
||||
iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
|
||||
|
||||
// Build question
|
||||
const char* start =aName;
|
||||
const char* end =start;
|
||||
uint8_t len;
|
||||
// Run through the name being requested
|
||||
while (*end) {
|
||||
// Find out how long this section of the name is
|
||||
end = start;
|
||||
while (*end && (*end != '.') ) {
|
||||
end++;
|
||||
}
|
||||
|
||||
if (end-start > 0) {
|
||||
// Write out the size of this section
|
||||
len = end-start;
|
||||
iUdp.write(&len, sizeof(len));
|
||||
// And then write out the section
|
||||
iUdp.write((uint8_t*)start, end-start);
|
||||
}
|
||||
start = end+1;
|
||||
}
|
||||
|
||||
// We've got to the end of the question name, so
|
||||
// terminate it with a zero-length section
|
||||
len = 0;
|
||||
iUdp.write(&len, sizeof(len));
|
||||
// Finally the type and class of question
|
||||
twoByteBuffer = htons(TYPE_A);
|
||||
iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
|
||||
|
||||
twoByteBuffer = htons(CLASS_IN); // Internet class of question
|
||||
iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer));
|
||||
// Success! Everything buffered okay
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress)
|
||||
{
|
||||
uint32_t startTime = millis();
|
||||
|
||||
// Wait for a response packet
|
||||
while (iUdp.parsePacket() <= 0) {
|
||||
if ((millis() - startTime) > aTimeout) {
|
||||
return TIMED_OUT;
|
||||
}
|
||||
delay(50);
|
||||
}
|
||||
|
||||
// We've had a reply!
|
||||
// Read the UDP header
|
||||
//uint8_t header[DNS_HEADER_SIZE]; // Enough space to reuse for the DNS header
|
||||
union {
|
||||
uint8_t byte[DNS_HEADER_SIZE]; // Enough space to reuse for the DNS header
|
||||
uint16_t word[DNS_HEADER_SIZE/2];
|
||||
} header;
|
||||
|
||||
// Check that it's a response from the right server and the right port
|
||||
if ( (iDNSServer != iUdp.remoteIP()) || (iUdp.remotePort() != DNS_PORT) ) {
|
||||
// It's not from who we expected
|
||||
return INVALID_SERVER;
|
||||
}
|
||||
|
||||
// Read through the rest of the response
|
||||
if (iUdp.available() < DNS_HEADER_SIZE) {
|
||||
return TRUNCATED;
|
||||
}
|
||||
iUdp.read(header.byte, DNS_HEADER_SIZE);
|
||||
|
||||
uint16_t header_flags = htons(header.word[1]);
|
||||
// Check that it's a response to this request
|
||||
if ((iRequestId != (header.word[0])) ||
|
||||
((header_flags & QUERY_RESPONSE_MASK) != (uint16_t)RESPONSE_FLAG) ) {
|
||||
// Mark the entire packet as read
|
||||
iUdp.flush(); // FIXME
|
||||
return INVALID_RESPONSE;
|
||||
}
|
||||
// Check for any errors in the response (or in our request)
|
||||
// although we don't do anything to get round these
|
||||
if ( (header_flags & TRUNCATION_FLAG) || (header_flags & RESP_MASK) ) {
|
||||
// Mark the entire packet as read
|
||||
iUdp.flush(); // FIXME
|
||||
return -5; //INVALID_RESPONSE;
|
||||
}
|
||||
|
||||
// And make sure we've got (at least) one answer
|
||||
uint16_t answerCount = htons(header.word[3]);
|
||||
if (answerCount == 0) {
|
||||
// Mark the entire packet as read
|
||||
iUdp.flush(); // FIXME
|
||||
return -6; //INVALID_RESPONSE;
|
||||
}
|
||||
|
||||
// Skip over any questions
|
||||
for (uint16_t i=0; i < htons(header.word[2]); i++) {
|
||||
// Skip over the name
|
||||
uint8_t len;
|
||||
do {
|
||||
iUdp.read(&len, sizeof(len));
|
||||
if (len > 0) {
|
||||
// Don't need to actually read the data out for the string, just
|
||||
// advance ptr to beyond it
|
||||
iUdp.read((uint8_t *)NULL, (size_t)len);
|
||||
}
|
||||
} while (len != 0);
|
||||
|
||||
// Now jump over the type and class
|
||||
iUdp.read((uint8_t *)NULL, 4);
|
||||
}
|
||||
|
||||
// Now we're up to the bit we're interested in, the answer
|
||||
// There might be more than one answer (although we'll just use the first
|
||||
// type A answer) and some authority and additional resource records but
|
||||
// we're going to ignore all of them.
|
||||
|
||||
for (uint16_t i=0; i < answerCount; i++) {
|
||||
// Skip the name
|
||||
uint8_t len;
|
||||
do {
|
||||
iUdp.read(&len, sizeof(len));
|
||||
if ((len & LABEL_COMPRESSION_MASK) == 0) {
|
||||
// It's just a normal label
|
||||
if (len > 0) {
|
||||
// And it's got a length
|
||||
// Don't need to actually read the data out for the string,
|
||||
// just advance ptr to beyond it
|
||||
iUdp.read((uint8_t *)NULL, len);
|
||||
}
|
||||
} else {
|
||||
// This is a pointer to a somewhere else in the message for the
|
||||
// rest of the name. We don't care about the name, and RFC1035
|
||||
// says that a name is either a sequence of labels ended with a
|
||||
// 0 length octet or a pointer or a sequence of labels ending in
|
||||
// a pointer. Either way, when we get here we're at the end of
|
||||
// the name
|
||||
// Skip over the pointer
|
||||
iUdp.read((uint8_t *)NULL, 1); // we don't care about the byte
|
||||
// And set len so that we drop out of the name loop
|
||||
len = 0;
|
||||
}
|
||||
} while (len != 0);
|
||||
|
||||
// Check the type and class
|
||||
uint16_t answerType;
|
||||
uint16_t answerClass;
|
||||
iUdp.read((uint8_t*)&answerType, sizeof(answerType));
|
||||
iUdp.read((uint8_t*)&answerClass, sizeof(answerClass));
|
||||
|
||||
// Ignore the Time-To-Live as we don't do any caching
|
||||
iUdp.read((uint8_t *)NULL, TTL_SIZE); // don't care about the returned bytes
|
||||
|
||||
// And read out the length of this answer
|
||||
// Don't need header_flags anymore, so we can reuse it here
|
||||
iUdp.read((uint8_t*)&header_flags, sizeof(header_flags));
|
||||
|
||||
if ( (htons(answerType) == TYPE_A) && (htons(answerClass) == CLASS_IN) ) {
|
||||
if (htons(header_flags) != 4) {
|
||||
// It's a weird size
|
||||
// Mark the entire packet as read
|
||||
iUdp.flush(); // FIXME
|
||||
return -9;//INVALID_RESPONSE;
|
||||
}
|
||||
// FIXME: seeems to lock up here on ESP8266, but why??
|
||||
iUdp.read(aAddress.raw_address(), 4);
|
||||
return SUCCESS;
|
||||
} else {
|
||||
// This isn't an answer type we're after, move onto the next one
|
||||
iUdp.read((uint8_t *)NULL, htons(header_flags));
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the entire packet as read
|
||||
iUdp.flush(); // FIXME
|
||||
|
||||
// If we get here then we haven't found an answer
|
||||
return -10; //INVALID_RESPONSE;
|
||||
}
|
||||
|
||||
40
arduino-libs/arduino-cli/libraries/Ethernet/src/Dns.h
Normal file
40
arduino-libs/arduino-cli/libraries/Ethernet/src/Dns.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// Arduino DNS client for WizNet5100-based Ethernet shield
|
||||
// (c) Copyright 2009-2010 MCQN Ltd.
|
||||
// Released under Apache License, version 2.0
|
||||
|
||||
#ifndef DNSClient_h
|
||||
#define DNSClient_h
|
||||
|
||||
#include "Ethernet.h"
|
||||
|
||||
class DNSClient
|
||||
{
|
||||
public:
|
||||
void begin(const IPAddress& aDNSServer);
|
||||
|
||||
/** Convert a numeric IP address string into a four-byte IP address.
|
||||
@param aIPAddrString IP address to convert
|
||||
@param aResult IPAddress structure to store the returned IP address
|
||||
@result 1 if aIPAddrString was successfully converted to an IP address,
|
||||
else error code
|
||||
*/
|
||||
int inet_aton(const char *aIPAddrString, IPAddress& aResult);
|
||||
|
||||
/** Resolve the given hostname to an IP address.
|
||||
@param aHostname Name to be resolved
|
||||
@param aResult IPAddress structure to store the returned IP address
|
||||
@result 1 if aIPAddrString was successfully converted to an IP address,
|
||||
else error code
|
||||
*/
|
||||
int getHostByName(const char* aHostname, IPAddress& aResult, uint16_t timeout=5000);
|
||||
|
||||
protected:
|
||||
uint16_t BuildRequest(const char* aName);
|
||||
uint16_t ProcessResponse(uint16_t aTimeout, IPAddress& aAddress);
|
||||
|
||||
IPAddress iDNSServer;
|
||||
uint16_t iRequestId;
|
||||
EthernetUDP iUdp;
|
||||
};
|
||||
|
||||
#endif
|
||||
242
arduino-libs/arduino-cli/libraries/Ethernet/src/Ethernet.cpp
Normal file
242
arduino-libs/arduino-cli/libraries/Ethernet/src/Ethernet.cpp
Normal file
@@ -0,0 +1,242 @@
|
||||
/* Copyright 2018 Paul Stoffregen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
* software and associated documentation files (the "Software"), to deal in the Software
|
||||
* without restriction, including without limitation the rights to use, copy, modify,
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "Ethernet.h"
|
||||
#include "utility/w5100.h"
|
||||
#include "Dhcp.h"
|
||||
|
||||
IPAddress EthernetClass::_dnsServerAddress;
|
||||
DhcpClass* EthernetClass::_dhcp = NULL;
|
||||
|
||||
int EthernetClass::begin(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout)
|
||||
{
|
||||
static DhcpClass s_dhcp;
|
||||
_dhcp = &s_dhcp;
|
||||
|
||||
// Initialise the basic info
|
||||
if (W5100.init() == 0) return 0;
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
W5100.setMACAddress(mac);
|
||||
W5100.setIPAddress(IPAddress(0,0,0,0).raw_address());
|
||||
SPI.endTransaction();
|
||||
|
||||
// Now try to get our config info from a DHCP server
|
||||
int ret = _dhcp->beginWithDHCP(mac, timeout, responseTimeout);
|
||||
if (ret == 1) {
|
||||
// We've successfully found a DHCP server and got our configuration
|
||||
// info, so set things accordingly
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
W5100.setIPAddress(_dhcp->getLocalIp().raw_address());
|
||||
W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address());
|
||||
W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address());
|
||||
SPI.endTransaction();
|
||||
_dnsServerAddress = _dhcp->getDnsServerIp();
|
||||
socketPortRand(micros());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void EthernetClass::begin(uint8_t *mac, IPAddress ip)
|
||||
{
|
||||
// Assume the DNS server will be the machine on the same network as the local IP
|
||||
// but with last octet being '1'
|
||||
IPAddress dns = ip;
|
||||
dns[3] = 1;
|
||||
begin(mac, ip, dns);
|
||||
}
|
||||
|
||||
void EthernetClass::begin(uint8_t *mac, IPAddress ip, IPAddress dns)
|
||||
{
|
||||
// Assume the gateway will be the machine on the same network as the local IP
|
||||
// but with last octet being '1'
|
||||
IPAddress gateway = ip;
|
||||
gateway[3] = 1;
|
||||
begin(mac, ip, dns, gateway);
|
||||
}
|
||||
|
||||
void EthernetClass::begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway)
|
||||
{
|
||||
IPAddress subnet(255, 255, 255, 0);
|
||||
begin(mac, ip, dns, gateway, subnet);
|
||||
}
|
||||
|
||||
void EthernetClass::begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet)
|
||||
{
|
||||
if (W5100.init() == 0) return;
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
W5100.setMACAddress(mac);
|
||||
#if ARDUINO > 106 || TEENSYDUINO > 121
|
||||
W5100.setIPAddress(ip._address.bytes);
|
||||
W5100.setGatewayIp(gateway._address.bytes);
|
||||
W5100.setSubnetMask(subnet._address.bytes);
|
||||
#else
|
||||
W5100.setIPAddress(ip._address);
|
||||
W5100.setGatewayIp(gateway._address);
|
||||
W5100.setSubnetMask(subnet._address);
|
||||
#endif
|
||||
SPI.endTransaction();
|
||||
_dnsServerAddress = dns;
|
||||
}
|
||||
|
||||
void EthernetClass::init(uint8_t sspin)
|
||||
{
|
||||
W5100.setSS(sspin);
|
||||
}
|
||||
|
||||
EthernetLinkStatus EthernetClass::linkStatus()
|
||||
{
|
||||
switch (W5100.getLinkStatus()) {
|
||||
case UNKNOWN: return Unknown;
|
||||
case LINK_ON: return LinkON;
|
||||
case LINK_OFF: return LinkOFF;
|
||||
default: return Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
EthernetHardwareStatus EthernetClass::hardwareStatus()
|
||||
{
|
||||
switch (W5100.getChip()) {
|
||||
case 51: return EthernetW5100;
|
||||
case 52: return EthernetW5200;
|
||||
case 55: return EthernetW5500;
|
||||
default: return EthernetNoHardware;
|
||||
}
|
||||
}
|
||||
|
||||
int EthernetClass::maintain()
|
||||
{
|
||||
int rc = DHCP_CHECK_NONE;
|
||||
if (_dhcp != NULL) {
|
||||
// we have a pointer to dhcp, use it
|
||||
rc = _dhcp->checkLease();
|
||||
switch (rc) {
|
||||
case DHCP_CHECK_NONE:
|
||||
//nothing done
|
||||
break;
|
||||
case DHCP_CHECK_RENEW_OK:
|
||||
case DHCP_CHECK_REBIND_OK:
|
||||
//we might have got a new IP.
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
W5100.setIPAddress(_dhcp->getLocalIp().raw_address());
|
||||
W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address());
|
||||
W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address());
|
||||
SPI.endTransaction();
|
||||
_dnsServerAddress = _dhcp->getDnsServerIp();
|
||||
break;
|
||||
default:
|
||||
//this is actually an error, it will retry though
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void EthernetClass::MACAddress(uint8_t *mac_address)
|
||||
{
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
W5100.getMACAddress(mac_address);
|
||||
SPI.endTransaction();
|
||||
}
|
||||
|
||||
IPAddress EthernetClass::localIP()
|
||||
{
|
||||
IPAddress ret;
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
W5100.getIPAddress(ret.raw_address());
|
||||
SPI.endTransaction();
|
||||
return ret;
|
||||
}
|
||||
|
||||
IPAddress EthernetClass::subnetMask()
|
||||
{
|
||||
IPAddress ret;
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
W5100.getSubnetMask(ret.raw_address());
|
||||
SPI.endTransaction();
|
||||
return ret;
|
||||
}
|
||||
|
||||
IPAddress EthernetClass::gatewayIP()
|
||||
{
|
||||
IPAddress ret;
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
W5100.getGatewayIp(ret.raw_address());
|
||||
SPI.endTransaction();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void EthernetClass::setMACAddress(const uint8_t *mac_address)
|
||||
{
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
W5100.setMACAddress(mac_address);
|
||||
SPI.endTransaction();
|
||||
}
|
||||
|
||||
void EthernetClass::setLocalIP(const IPAddress local_ip)
|
||||
{
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
IPAddress ip = local_ip;
|
||||
W5100.setIPAddress(ip.raw_address());
|
||||
SPI.endTransaction();
|
||||
}
|
||||
|
||||
void EthernetClass::setSubnetMask(const IPAddress subnet)
|
||||
{
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
IPAddress ip = subnet;
|
||||
W5100.setSubnetMask(ip.raw_address());
|
||||
SPI.endTransaction();
|
||||
}
|
||||
|
||||
void EthernetClass::setGatewayIP(const IPAddress gateway)
|
||||
{
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
IPAddress ip = gateway;
|
||||
W5100.setGatewayIp(ip.raw_address());
|
||||
SPI.endTransaction();
|
||||
}
|
||||
|
||||
void EthernetClass::setRetransmissionTimeout(uint16_t milliseconds)
|
||||
{
|
||||
if (milliseconds > 6553) milliseconds = 6553;
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
W5100.setRetransmissionTime(milliseconds * 10);
|
||||
SPI.endTransaction();
|
||||
}
|
||||
|
||||
void EthernetClass::setRetransmissionCount(uint8_t num)
|
||||
{
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
W5100.setRetransmissionCount(num);
|
||||
SPI.endTransaction();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
EthernetClass Ethernet;
|
||||
322
arduino-libs/arduino-cli/libraries/Ethernet/src/Ethernet.h
Normal file
322
arduino-libs/arduino-cli/libraries/Ethernet/src/Ethernet.h
Normal file
@@ -0,0 +1,322 @@
|
||||
/* Copyright 2018 Paul Stoffregen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
* software and associated documentation files (the "Software"), to deal in the Software
|
||||
* without restriction, including without limitation the rights to use, copy, modify,
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef ethernet_h_
|
||||
#define ethernet_h_
|
||||
|
||||
// All symbols exposed to Arduino sketches are contained in this header file
|
||||
//
|
||||
// Older versions had much of this stuff in EthernetClient.h, EthernetServer.h,
|
||||
// and socket.h. Including headers in different order could cause trouble, so
|
||||
// these "friend" classes are now defined in the same header file. socket.h
|
||||
// was removed to avoid possible conflict with the C library header files.
|
||||
|
||||
|
||||
// Configure the maximum number of sockets to support. W5100 chips can have
|
||||
// up to 4 sockets. W5200 & W5500 can have up to 8 sockets. Several bytes
|
||||
// of RAM are used for each socket. Reducing the maximum can save RAM, but
|
||||
// you are limited to fewer simultaneous connections.
|
||||
#if defined(RAMEND) && defined(RAMSTART) && ((RAMEND - RAMSTART) <= 2048)
|
||||
#define MAX_SOCK_NUM 4
|
||||
#else
|
||||
#define MAX_SOCK_NUM 8
|
||||
#endif
|
||||
|
||||
// By default, each socket uses 2K buffers inside the Wiznet chip. If
|
||||
// MAX_SOCK_NUM is set to fewer than the chip's maximum, uncommenting
|
||||
// this will use larger buffers within the Wiznet chip. Large buffers
|
||||
// can really help with UDP protocols like Artnet. In theory larger
|
||||
// buffers should allow faster TCP over high-latency links, but this
|
||||
// does not always seem to work in practice (maybe Wiznet bugs?)
|
||||
//#define ETHERNET_LARGE_BUFFERS
|
||||
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "Client.h"
|
||||
#include "Server.h"
|
||||
#include "Udp.h"
|
||||
|
||||
enum EthernetLinkStatus {
|
||||
Unknown,
|
||||
LinkON,
|
||||
LinkOFF
|
||||
};
|
||||
|
||||
enum EthernetHardwareStatus {
|
||||
EthernetNoHardware,
|
||||
EthernetW5100,
|
||||
EthernetW5200,
|
||||
EthernetW5500
|
||||
};
|
||||
|
||||
class EthernetUDP;
|
||||
class EthernetClient;
|
||||
class EthernetServer;
|
||||
class DhcpClass;
|
||||
|
||||
class EthernetClass {
|
||||
private:
|
||||
static IPAddress _dnsServerAddress;
|
||||
static DhcpClass* _dhcp;
|
||||
public:
|
||||
// Initialise the Ethernet shield to use the provided MAC address and
|
||||
// gain the rest of the configuration through DHCP.
|
||||
// Returns 0 if the DHCP configuration failed, and 1 if it succeeded
|
||||
static int begin(uint8_t *mac, unsigned long timeout = 60000, unsigned long responseTimeout = 4000);
|
||||
static int maintain();
|
||||
static EthernetLinkStatus linkStatus();
|
||||
static EthernetHardwareStatus hardwareStatus();
|
||||
|
||||
// Manaul configuration
|
||||
static void begin(uint8_t *mac, IPAddress ip);
|
||||
static void begin(uint8_t *mac, IPAddress ip, IPAddress dns);
|
||||
static void begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway);
|
||||
static void begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet);
|
||||
static void init(uint8_t sspin = 10);
|
||||
|
||||
static void MACAddress(uint8_t *mac_address);
|
||||
static IPAddress localIP();
|
||||
static IPAddress subnetMask();
|
||||
static IPAddress gatewayIP();
|
||||
static IPAddress dnsServerIP() { return _dnsServerAddress; }
|
||||
|
||||
void setMACAddress(const uint8_t *mac_address);
|
||||
void setLocalIP(const IPAddress local_ip);
|
||||
void setSubnetMask(const IPAddress subnet);
|
||||
void setGatewayIP(const IPAddress gateway);
|
||||
void setDnsServerIP(const IPAddress dns_server) { _dnsServerAddress = dns_server; }
|
||||
void setRetransmissionTimeout(uint16_t milliseconds);
|
||||
void setRetransmissionCount(uint8_t num);
|
||||
|
||||
friend class EthernetClient;
|
||||
friend class EthernetServer;
|
||||
friend class EthernetUDP;
|
||||
private:
|
||||
// Opens a socket(TCP or UDP or IP_RAW mode)
|
||||
static uint8_t socketBegin(uint8_t protocol, uint16_t port);
|
||||
static uint8_t socketBeginMulticast(uint8_t protocol, IPAddress ip,uint16_t port);
|
||||
static uint8_t socketStatus(uint8_t s);
|
||||
// Close socket
|
||||
static void socketClose(uint8_t s);
|
||||
// Establish TCP connection (Active connection)
|
||||
static void socketConnect(uint8_t s, uint8_t * addr, uint16_t port);
|
||||
// disconnect the connection
|
||||
static void socketDisconnect(uint8_t s);
|
||||
// Establish TCP connection (Passive connection)
|
||||
static uint8_t socketListen(uint8_t s);
|
||||
// Send data (TCP)
|
||||
static uint16_t socketSend(uint8_t s, const uint8_t * buf, uint16_t len);
|
||||
static uint16_t socketSendAvailable(uint8_t s);
|
||||
// Receive data (TCP)
|
||||
static int socketRecv(uint8_t s, uint8_t * buf, int16_t len);
|
||||
static uint16_t socketRecvAvailable(uint8_t s);
|
||||
static uint8_t socketPeek(uint8_t s);
|
||||
// sets up a UDP datagram, the data for which will be provided by one
|
||||
// or more calls to bufferData and then finally sent with sendUDP.
|
||||
// return true if the datagram was successfully set up, or false if there was an error
|
||||
static bool socketStartUDP(uint8_t s, uint8_t* addr, uint16_t port);
|
||||
// copy up to len bytes of data from buf into a UDP datagram to be
|
||||
// sent later by sendUDP. Allows datagrams to be built up from a series of bufferData calls.
|
||||
// return Number of bytes successfully buffered
|
||||
static uint16_t socketBufferData(uint8_t s, uint16_t offset, const uint8_t* buf, uint16_t len);
|
||||
// Send a UDP datagram built up from a sequence of startUDP followed by one or more
|
||||
// calls to bufferData.
|
||||
// return true if the datagram was successfully sent, or false if there was an error
|
||||
static bool socketSendUDP(uint8_t s);
|
||||
// Initialize the "random" source port number
|
||||
static void socketPortRand(uint16_t n);
|
||||
};
|
||||
|
||||
extern EthernetClass Ethernet;
|
||||
|
||||
|
||||
#define UDP_TX_PACKET_MAX_SIZE 24
|
||||
|
||||
class EthernetUDP : public UDP {
|
||||
private:
|
||||
uint16_t _port; // local port to listen on
|
||||
IPAddress _remoteIP; // remote IP address for the incoming packet whilst it's being processed
|
||||
uint16_t _remotePort; // remote port for the incoming packet whilst it's being processed
|
||||
uint16_t _offset; // offset into the packet being sent
|
||||
|
||||
protected:
|
||||
uint8_t sockindex;
|
||||
uint16_t _remaining; // remaining bytes of incoming packet yet to be processed
|
||||
|
||||
public:
|
||||
EthernetUDP() : sockindex(MAX_SOCK_NUM) {} // Constructor
|
||||
virtual uint8_t begin(uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use
|
||||
virtual uint8_t beginMulticast(IPAddress, uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use
|
||||
virtual void stop(); // Finish with the UDP socket
|
||||
|
||||
// Sending UDP packets
|
||||
|
||||
// Start building up a packet to send to the remote host specific in ip and port
|
||||
// Returns 1 if successful, 0 if there was a problem with the supplied IP address or port
|
||||
virtual int beginPacket(IPAddress ip, uint16_t port);
|
||||
// Start building up a packet to send to the remote host specific in host and port
|
||||
// Returns 1 if successful, 0 if there was a problem resolving the hostname or port
|
||||
virtual int beginPacket(const char *host, uint16_t port);
|
||||
// Finish off this packet and send it
|
||||
// Returns 1 if the packet was sent successfully, 0 if there was an error
|
||||
virtual int endPacket();
|
||||
// Write a single byte into the packet
|
||||
virtual size_t write(uint8_t);
|
||||
// Write size bytes from buffer into the packet
|
||||
virtual size_t write(const uint8_t *buffer, size_t size);
|
||||
|
||||
using Print::write;
|
||||
|
||||
// Start processing the next available incoming packet
|
||||
// Returns the size of the packet in bytes, or 0 if no packets are available
|
||||
virtual int parsePacket();
|
||||
// Number of bytes remaining in the current packet
|
||||
virtual int available();
|
||||
// Read a single byte from the current packet
|
||||
virtual int read();
|
||||
// Read up to len bytes from the current packet and place them into buffer
|
||||
// Returns the number of bytes read, or 0 if none are available
|
||||
virtual int read(unsigned char* buffer, size_t len);
|
||||
// Read up to len characters from the current packet and place them into buffer
|
||||
// Returns the number of characters read, or 0 if none are available
|
||||
virtual int read(char* buffer, size_t len) { return read((unsigned char*)buffer, len); };
|
||||
// Return the next byte from the current packet without moving on to the next byte
|
||||
virtual int peek();
|
||||
virtual void flush(); // Finish reading the current packet
|
||||
|
||||
// Return the IP address of the host who sent the current incoming packet
|
||||
virtual IPAddress remoteIP() { return _remoteIP; };
|
||||
// Return the port of the host who sent the current incoming packet
|
||||
virtual uint16_t remotePort() { return _remotePort; };
|
||||
virtual uint16_t localPort() { return _port; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class EthernetClient : public Client {
|
||||
public:
|
||||
EthernetClient() : sockindex(MAX_SOCK_NUM), _timeout(1000) { }
|
||||
EthernetClient(uint8_t s) : sockindex(s), _timeout(1000) { }
|
||||
|
||||
uint8_t status();
|
||||
virtual int connect(IPAddress ip, uint16_t port);
|
||||
virtual int connect(const char *host, uint16_t port);
|
||||
virtual int availableForWrite(void);
|
||||
virtual size_t write(uint8_t);
|
||||
virtual size_t write(const uint8_t *buf, size_t size);
|
||||
virtual int available();
|
||||
virtual int read();
|
||||
virtual int read(uint8_t *buf, size_t size);
|
||||
virtual int peek();
|
||||
virtual void flush();
|
||||
virtual void stop();
|
||||
virtual uint8_t connected();
|
||||
virtual operator bool() { return sockindex < MAX_SOCK_NUM; }
|
||||
virtual bool operator==(const bool value) { return bool() == value; }
|
||||
virtual bool operator!=(const bool value) { return bool() != value; }
|
||||
virtual bool operator==(const EthernetClient&);
|
||||
virtual bool operator!=(const EthernetClient& rhs) { return !this->operator==(rhs); }
|
||||
uint8_t getSocketNumber() const { return sockindex; }
|
||||
virtual uint16_t localPort();
|
||||
virtual IPAddress remoteIP();
|
||||
virtual uint16_t remotePort();
|
||||
virtual void setConnectionTimeout(uint16_t timeout) { _timeout = timeout; }
|
||||
|
||||
friend class EthernetServer;
|
||||
|
||||
using Print::write;
|
||||
|
||||
private:
|
||||
uint8_t sockindex; // MAX_SOCK_NUM means client not in use
|
||||
uint16_t _timeout;
|
||||
};
|
||||
|
||||
|
||||
class EthernetServer : public Server {
|
||||
private:
|
||||
uint16_t _port;
|
||||
public:
|
||||
EthernetServer(uint16_t port) : _port(port) { }
|
||||
EthernetClient available();
|
||||
EthernetClient accept();
|
||||
virtual void begin();
|
||||
virtual size_t write(uint8_t);
|
||||
virtual size_t write(const uint8_t *buf, size_t size);
|
||||
virtual operator bool();
|
||||
using Print::write;
|
||||
//void statusreport();
|
||||
|
||||
// TODO: make private when socket allocation moves to EthernetClass
|
||||
static uint16_t server_port[MAX_SOCK_NUM];
|
||||
};
|
||||
|
||||
|
||||
class DhcpClass {
|
||||
private:
|
||||
uint32_t _dhcpInitialTransactionId;
|
||||
uint32_t _dhcpTransactionId;
|
||||
uint8_t _dhcpMacAddr[6];
|
||||
#ifdef __arm__
|
||||
uint8_t _dhcpLocalIp[4] __attribute__((aligned(4)));
|
||||
uint8_t _dhcpSubnetMask[4] __attribute__((aligned(4)));
|
||||
uint8_t _dhcpGatewayIp[4] __attribute__((aligned(4)));
|
||||
uint8_t _dhcpDhcpServerIp[4] __attribute__((aligned(4)));
|
||||
uint8_t _dhcpDnsServerIp[4] __attribute__((aligned(4)));
|
||||
#else
|
||||
uint8_t _dhcpLocalIp[4];
|
||||
uint8_t _dhcpSubnetMask[4];
|
||||
uint8_t _dhcpGatewayIp[4];
|
||||
uint8_t _dhcpDhcpServerIp[4];
|
||||
uint8_t _dhcpDnsServerIp[4];
|
||||
#endif
|
||||
uint32_t _dhcpLeaseTime;
|
||||
uint32_t _dhcpT1, _dhcpT2;
|
||||
uint32_t _renewInSec;
|
||||
uint32_t _rebindInSec;
|
||||
unsigned long _timeout;
|
||||
unsigned long _responseTimeout;
|
||||
unsigned long _lastCheckLeaseMillis;
|
||||
uint8_t _dhcp_state;
|
||||
EthernetUDP _dhcpUdpSocket;
|
||||
|
||||
int request_DHCP_lease();
|
||||
void reset_DHCP_lease();
|
||||
void presend_DHCP();
|
||||
void send_DHCP_MESSAGE(uint8_t, uint16_t);
|
||||
void printByte(char *, uint8_t);
|
||||
|
||||
uint8_t parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId);
|
||||
public:
|
||||
IPAddress getLocalIp();
|
||||
IPAddress getSubnetMask();
|
||||
IPAddress getGatewayIp();
|
||||
IPAddress getDhcpServerIp();
|
||||
IPAddress getDnsServerIp();
|
||||
|
||||
int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000);
|
||||
int checkLease();
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,215 @@
|
||||
/* Copyright 2018 Paul Stoffregen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
* software and associated documentation files (the "Software"), to deal in the Software
|
||||
* without restriction, including without limitation the rights to use, copy, modify,
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "Ethernet.h"
|
||||
#include "Dns.h"
|
||||
#include "utility/w5100.h"
|
||||
|
||||
int EthernetClient::connect(const char * host, uint16_t port)
|
||||
{
|
||||
DNSClient dns; // Look up the host first
|
||||
IPAddress remote_addr;
|
||||
|
||||
if (sockindex < MAX_SOCK_NUM) {
|
||||
if (Ethernet.socketStatus(sockindex) != SnSR::CLOSED) {
|
||||
Ethernet.socketDisconnect(sockindex); // TODO: should we call stop()?
|
||||
}
|
||||
sockindex = MAX_SOCK_NUM;
|
||||
}
|
||||
dns.begin(Ethernet.dnsServerIP());
|
||||
if (!dns.getHostByName(host, remote_addr)) return 0; // TODO: use _timeout
|
||||
return connect(remote_addr, port);
|
||||
}
|
||||
|
||||
int EthernetClient::connect(IPAddress ip, uint16_t port)
|
||||
{
|
||||
if (sockindex < MAX_SOCK_NUM) {
|
||||
if (Ethernet.socketStatus(sockindex) != SnSR::CLOSED) {
|
||||
Ethernet.socketDisconnect(sockindex); // TODO: should we call stop()?
|
||||
}
|
||||
sockindex = MAX_SOCK_NUM;
|
||||
}
|
||||
#if defined(ESP8266) || defined(ESP32)
|
||||
if (ip == IPAddress((uint32_t)0) || ip == IPAddress(0xFFFFFFFFul)) return 0;
|
||||
#else
|
||||
if (ip == IPAddress(0ul) || ip == IPAddress(0xFFFFFFFFul)) return 0;
|
||||
#endif
|
||||
sockindex = Ethernet.socketBegin(SnMR::TCP, 0);
|
||||
if (sockindex >= MAX_SOCK_NUM) return 0;
|
||||
Ethernet.socketConnect(sockindex, rawIPAddress(ip), port);
|
||||
uint32_t start = millis();
|
||||
while (1) {
|
||||
uint8_t stat = Ethernet.socketStatus(sockindex);
|
||||
if (stat == SnSR::ESTABLISHED) return 1;
|
||||
if (stat == SnSR::CLOSE_WAIT) return 1;
|
||||
if (stat == SnSR::CLOSED) return 0;
|
||||
if (millis() - start > _timeout) break;
|
||||
delay(1);
|
||||
}
|
||||
Ethernet.socketClose(sockindex);
|
||||
sockindex = MAX_SOCK_NUM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int EthernetClient::availableForWrite(void)
|
||||
{
|
||||
if (sockindex >= MAX_SOCK_NUM) return 0;
|
||||
return Ethernet.socketSendAvailable(sockindex);
|
||||
}
|
||||
|
||||
size_t EthernetClient::write(uint8_t b)
|
||||
{
|
||||
return write(&b, 1);
|
||||
}
|
||||
|
||||
size_t EthernetClient::write(const uint8_t *buf, size_t size)
|
||||
{
|
||||
if (sockindex >= MAX_SOCK_NUM) return 0;
|
||||
if (Ethernet.socketSend(sockindex, buf, size)) return size;
|
||||
setWriteError();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int EthernetClient::available()
|
||||
{
|
||||
if (sockindex >= MAX_SOCK_NUM) return 0;
|
||||
return Ethernet.socketRecvAvailable(sockindex);
|
||||
// TODO: do the Wiznet chips automatically retransmit TCP ACK
|
||||
// packets if they are lost by the network? Someday this should
|
||||
// be checked by a man-in-the-middle test which discards certain
|
||||
// packets. If ACKs aren't resent, we would need to check for
|
||||
// returning 0 here and after a timeout do another Sock_RECV
|
||||
// command to cause the Wiznet chip to resend the ACK packet.
|
||||
}
|
||||
|
||||
int EthernetClient::read(uint8_t *buf, size_t size)
|
||||
{
|
||||
if (sockindex >= MAX_SOCK_NUM) return 0;
|
||||
return Ethernet.socketRecv(sockindex, buf, size);
|
||||
}
|
||||
|
||||
int EthernetClient::peek()
|
||||
{
|
||||
if (sockindex >= MAX_SOCK_NUM) return -1;
|
||||
if (!available()) return -1;
|
||||
return Ethernet.socketPeek(sockindex);
|
||||
}
|
||||
|
||||
int EthernetClient::read()
|
||||
{
|
||||
uint8_t b;
|
||||
if (Ethernet.socketRecv(sockindex, &b, 1) > 0) return b;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void EthernetClient::flush()
|
||||
{
|
||||
while (sockindex < MAX_SOCK_NUM) {
|
||||
uint8_t stat = Ethernet.socketStatus(sockindex);
|
||||
if (stat != SnSR::ESTABLISHED && stat != SnSR::CLOSE_WAIT) return;
|
||||
if (Ethernet.socketSendAvailable(sockindex) >= W5100.SSIZE) return;
|
||||
}
|
||||
}
|
||||
|
||||
void EthernetClient::stop()
|
||||
{
|
||||
if (sockindex >= MAX_SOCK_NUM) return;
|
||||
|
||||
// attempt to close the connection gracefully (send a FIN to other side)
|
||||
Ethernet.socketDisconnect(sockindex);
|
||||
unsigned long start = millis();
|
||||
|
||||
// wait up to a second for the connection to close
|
||||
do {
|
||||
if (Ethernet.socketStatus(sockindex) == SnSR::CLOSED) {
|
||||
sockindex = MAX_SOCK_NUM;
|
||||
return; // exit the loop
|
||||
}
|
||||
delay(1);
|
||||
} while (millis() - start < _timeout);
|
||||
|
||||
// if it hasn't closed, close it forcefully
|
||||
Ethernet.socketClose(sockindex);
|
||||
sockindex = MAX_SOCK_NUM;
|
||||
}
|
||||
|
||||
uint8_t EthernetClient::connected()
|
||||
{
|
||||
if (sockindex >= MAX_SOCK_NUM) return 0;
|
||||
|
||||
uint8_t s = Ethernet.socketStatus(sockindex);
|
||||
return !(s == SnSR::LISTEN || s == SnSR::CLOSED || s == SnSR::FIN_WAIT ||
|
||||
(s == SnSR::CLOSE_WAIT && !available()));
|
||||
}
|
||||
|
||||
uint8_t EthernetClient::status()
|
||||
{
|
||||
if (sockindex >= MAX_SOCK_NUM) return SnSR::CLOSED;
|
||||
return Ethernet.socketStatus(sockindex);
|
||||
}
|
||||
|
||||
// the next function allows us to use the client returned by
|
||||
// EthernetServer::available() as the condition in an if-statement.
|
||||
bool EthernetClient::operator==(const EthernetClient& rhs)
|
||||
{
|
||||
if (sockindex != rhs.sockindex) return false;
|
||||
if (sockindex >= MAX_SOCK_NUM) return false;
|
||||
if (rhs.sockindex >= MAX_SOCK_NUM) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// https://github.com/per1234/EthernetMod
|
||||
// from: https://github.com/ntruchsess/Arduino-1/commit/937bce1a0bb2567f6d03b15df79525569377dabd
|
||||
uint16_t EthernetClient::localPort()
|
||||
{
|
||||
if (sockindex >= MAX_SOCK_NUM) return 0;
|
||||
uint16_t port;
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
port = W5100.readSnPORT(sockindex);
|
||||
SPI.endTransaction();
|
||||
return port;
|
||||
}
|
||||
|
||||
// https://github.com/per1234/EthernetMod
|
||||
// returns the remote IP address: http://forum.arduino.cc/index.php?topic=82416.0
|
||||
IPAddress EthernetClient::remoteIP()
|
||||
{
|
||||
if (sockindex >= MAX_SOCK_NUM) return IPAddress((uint32_t)0);
|
||||
uint8_t remoteIParray[4];
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
W5100.readSnDIPR(sockindex, remoteIParray);
|
||||
SPI.endTransaction();
|
||||
return IPAddress(remoteIParray);
|
||||
}
|
||||
|
||||
// https://github.com/per1234/EthernetMod
|
||||
// from: https://github.com/ntruchsess/Arduino-1/commit/ca37de4ba4ecbdb941f14ac1fe7dd40f3008af75
|
||||
uint16_t EthernetClient::remotePort()
|
||||
{
|
||||
if (sockindex >= MAX_SOCK_NUM) return 0;
|
||||
uint16_t port;
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
port = W5100.readSnDPORT(sockindex);
|
||||
SPI.endTransaction();
|
||||
return port;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// This file is in the public domain. No copyright is claimed.
|
||||
|
||||
#include "Ethernet.h"
|
||||
@@ -0,0 +1,179 @@
|
||||
/* Copyright 2018 Paul Stoffregen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
* software and associated documentation files (the "Software"), to deal in the Software
|
||||
* without restriction, including without limitation the rights to use, copy, modify,
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "Ethernet.h"
|
||||
#include "utility/w5100.h"
|
||||
|
||||
uint16_t EthernetServer::server_port[MAX_SOCK_NUM];
|
||||
|
||||
|
||||
void EthernetServer::begin()
|
||||
{
|
||||
uint8_t sockindex = Ethernet.socketBegin(SnMR::TCP, _port);
|
||||
if (sockindex < MAX_SOCK_NUM) {
|
||||
if (Ethernet.socketListen(sockindex)) {
|
||||
server_port[sockindex] = _port;
|
||||
} else {
|
||||
Ethernet.socketDisconnect(sockindex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EthernetClient EthernetServer::available()
|
||||
{
|
||||
bool listening = false;
|
||||
uint8_t sockindex = MAX_SOCK_NUM;
|
||||
uint8_t chip, maxindex=MAX_SOCK_NUM;
|
||||
|
||||
chip = W5100.getChip();
|
||||
if (!chip) return EthernetClient(MAX_SOCK_NUM);
|
||||
#if MAX_SOCK_NUM > 4
|
||||
if (chip == 51) maxindex = 4; // W5100 chip never supports more than 4 sockets
|
||||
#endif
|
||||
for (uint8_t i=0; i < maxindex; i++) {
|
||||
if (server_port[i] == _port) {
|
||||
uint8_t stat = Ethernet.socketStatus(i);
|
||||
if (stat == SnSR::ESTABLISHED || stat == SnSR::CLOSE_WAIT) {
|
||||
if (Ethernet.socketRecvAvailable(i) > 0) {
|
||||
sockindex = i;
|
||||
} else {
|
||||
// remote host closed connection, our end still open
|
||||
if (stat == SnSR::CLOSE_WAIT) {
|
||||
Ethernet.socketDisconnect(i);
|
||||
// status becomes LAST_ACK for short time
|
||||
}
|
||||
}
|
||||
} else if (stat == SnSR::LISTEN) {
|
||||
listening = true;
|
||||
} else if (stat == SnSR::CLOSED) {
|
||||
server_port[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!listening) begin();
|
||||
return EthernetClient(sockindex);
|
||||
}
|
||||
|
||||
EthernetClient EthernetServer::accept()
|
||||
{
|
||||
bool listening = false;
|
||||
uint8_t sockindex = MAX_SOCK_NUM;
|
||||
uint8_t chip, maxindex=MAX_SOCK_NUM;
|
||||
|
||||
chip = W5100.getChip();
|
||||
if (!chip) return EthernetClient(MAX_SOCK_NUM);
|
||||
#if MAX_SOCK_NUM > 4
|
||||
if (chip == 51) maxindex = 4; // W5100 chip never supports more than 4 sockets
|
||||
#endif
|
||||
for (uint8_t i=0; i < maxindex; i++) {
|
||||
if (server_port[i] == _port) {
|
||||
uint8_t stat = Ethernet.socketStatus(i);
|
||||
if (sockindex == MAX_SOCK_NUM &&
|
||||
(stat == SnSR::ESTABLISHED || stat == SnSR::CLOSE_WAIT)) {
|
||||
// Return the connected client even if no data received.
|
||||
// Some protocols like FTP expect the server to send the
|
||||
// first data.
|
||||
sockindex = i;
|
||||
server_port[i] = 0; // only return the client once
|
||||
} else if (stat == SnSR::LISTEN) {
|
||||
listening = true;
|
||||
} else if (stat == SnSR::CLOSED) {
|
||||
server_port[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!listening) begin();
|
||||
return EthernetClient(sockindex);
|
||||
}
|
||||
|
||||
EthernetServer::operator bool()
|
||||
{
|
||||
uint8_t maxindex=MAX_SOCK_NUM;
|
||||
#if MAX_SOCK_NUM > 4
|
||||
if (W5100.getChip() == 51) maxindex = 4; // W5100 chip never supports more than 4 sockets
|
||||
#endif
|
||||
for (uint8_t i=0; i < maxindex; i++) {
|
||||
if (server_port[i] == _port) {
|
||||
if (Ethernet.socketStatus(i) == SnSR::LISTEN) {
|
||||
return true; // server is listening for incoming clients
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void EthernetServer::statusreport()
|
||||
{
|
||||
Serial.printf("EthernetServer, port=%d\n", _port);
|
||||
for (uint8_t i=0; i < MAX_SOCK_NUM; i++) {
|
||||
uint16_t port = server_port[i];
|
||||
uint8_t stat = Ethernet.socketStatus(i);
|
||||
const char *name;
|
||||
switch (stat) {
|
||||
case 0x00: name = "CLOSED"; break;
|
||||
case 0x13: name = "INIT"; break;
|
||||
case 0x14: name = "LISTEN"; break;
|
||||
case 0x15: name = "SYNSENT"; break;
|
||||
case 0x16: name = "SYNRECV"; break;
|
||||
case 0x17: name = "ESTABLISHED"; break;
|
||||
case 0x18: name = "FIN_WAIT"; break;
|
||||
case 0x1A: name = "CLOSING"; break;
|
||||
case 0x1B: name = "TIME_WAIT"; break;
|
||||
case 0x1C: name = "CLOSE_WAIT"; break;
|
||||
case 0x1D: name = "LAST_ACK"; break;
|
||||
case 0x22: name = "UDP"; break;
|
||||
case 0x32: name = "IPRAW"; break;
|
||||
case 0x42: name = "MACRAW"; break;
|
||||
case 0x5F: name = "PPPOE"; break;
|
||||
default: name = "???";
|
||||
}
|
||||
int avail = Ethernet.socketRecvAvailable(i);
|
||||
Serial.printf(" %d: port=%d, status=%s (0x%02X), avail=%d\n",
|
||||
i, port, name, stat, avail);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t EthernetServer::write(uint8_t b)
|
||||
{
|
||||
return write(&b, 1);
|
||||
}
|
||||
|
||||
size_t EthernetServer::write(const uint8_t *buffer, size_t size)
|
||||
{
|
||||
uint8_t chip, maxindex=MAX_SOCK_NUM;
|
||||
|
||||
chip = W5100.getChip();
|
||||
if (!chip) return 0;
|
||||
#if MAX_SOCK_NUM > 4
|
||||
if (chip == 51) maxindex = 4; // W5100 chip never supports more than 4 sockets
|
||||
#endif
|
||||
available();
|
||||
for (uint8_t i=0; i < maxindex; i++) {
|
||||
if (server_port[i] == _port) {
|
||||
if (Ethernet.socketStatus(i) == SnSR::ESTABLISHED) {
|
||||
Ethernet.socketSend(i, buffer, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// This file is in the public domain. No copyright is claimed.
|
||||
|
||||
#include "Ethernet.h"
|
||||
191
arduino-libs/arduino-cli/libraries/Ethernet/src/EthernetUdp.cpp
Normal file
191
arduino-libs/arduino-cli/libraries/Ethernet/src/EthernetUdp.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Udp.cpp: Library to send/receive UDP packets with the Arduino ethernet shield.
|
||||
* This version only offers minimal wrapping of socket.cpp
|
||||
* Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/
|
||||
*
|
||||
* MIT License:
|
||||
* Copyright (c) 2008 Bjoern Hartmann
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* bjoern@cs.stanford.edu 12/30/2008
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "Ethernet.h"
|
||||
#include "Dns.h"
|
||||
#include "utility/w5100.h"
|
||||
|
||||
/* Start EthernetUDP socket, listening at local port PORT */
|
||||
uint8_t EthernetUDP::begin(uint16_t port)
|
||||
{
|
||||
if (sockindex < MAX_SOCK_NUM) Ethernet.socketClose(sockindex);
|
||||
sockindex = Ethernet.socketBegin(SnMR::UDP, port);
|
||||
if (sockindex >= MAX_SOCK_NUM) return 0;
|
||||
_port = port;
|
||||
_remaining = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* return number of bytes available in the current packet,
|
||||
will return zero if parsePacket hasn't been called yet */
|
||||
int EthernetUDP::available()
|
||||
{
|
||||
return _remaining;
|
||||
}
|
||||
|
||||
/* Release any resources being used by this EthernetUDP instance */
|
||||
void EthernetUDP::stop()
|
||||
{
|
||||
if (sockindex < MAX_SOCK_NUM) {
|
||||
Ethernet.socketClose(sockindex);
|
||||
sockindex = MAX_SOCK_NUM;
|
||||
}
|
||||
}
|
||||
|
||||
int EthernetUDP::beginPacket(const char *host, uint16_t port)
|
||||
{
|
||||
// Look up the host first
|
||||
int ret = 0;
|
||||
DNSClient dns;
|
||||
IPAddress remote_addr;
|
||||
|
||||
dns.begin(Ethernet.dnsServerIP());
|
||||
ret = dns.getHostByName(host, remote_addr);
|
||||
if (ret != 1) return ret;
|
||||
return beginPacket(remote_addr, port);
|
||||
}
|
||||
|
||||
int EthernetUDP::beginPacket(IPAddress ip, uint16_t port)
|
||||
{
|
||||
_offset = 0;
|
||||
//Serial.printf("UDP beginPacket\n");
|
||||
return Ethernet.socketStartUDP(sockindex, rawIPAddress(ip), port);
|
||||
}
|
||||
|
||||
int EthernetUDP::endPacket()
|
||||
{
|
||||
return Ethernet.socketSendUDP(sockindex);
|
||||
}
|
||||
|
||||
size_t EthernetUDP::write(uint8_t byte)
|
||||
{
|
||||
return write(&byte, 1);
|
||||
}
|
||||
|
||||
size_t EthernetUDP::write(const uint8_t *buffer, size_t size)
|
||||
{
|
||||
//Serial.printf("UDP write %d\n", size);
|
||||
uint16_t bytes_written = Ethernet.socketBufferData(sockindex, _offset, buffer, size);
|
||||
_offset += bytes_written;
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
int EthernetUDP::parsePacket()
|
||||
{
|
||||
// discard any remaining bytes in the last packet
|
||||
while (_remaining) {
|
||||
// could this fail (loop endlessly) if _remaining > 0 and recv in read fails?
|
||||
// should only occur if recv fails after telling us the data is there, lets
|
||||
// hope the w5100 always behaves :)
|
||||
read((uint8_t *)NULL, _remaining);
|
||||
}
|
||||
|
||||
if (Ethernet.socketRecvAvailable(sockindex) > 0) {
|
||||
//HACK - hand-parse the UDP packet using TCP recv method
|
||||
uint8_t tmpBuf[8];
|
||||
int ret=0;
|
||||
//read 8 header bytes and get IP and port from it
|
||||
ret = Ethernet.socketRecv(sockindex, tmpBuf, 8);
|
||||
if (ret > 0) {
|
||||
_remoteIP = tmpBuf;
|
||||
_remotePort = tmpBuf[4];
|
||||
_remotePort = (_remotePort << 8) + tmpBuf[5];
|
||||
_remaining = tmpBuf[6];
|
||||
_remaining = (_remaining << 8) + tmpBuf[7];
|
||||
|
||||
// When we get here, any remaining bytes are the data
|
||||
ret = _remaining;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
// There aren't any packets available
|
||||
return 0;
|
||||
}
|
||||
|
||||
int EthernetUDP::read()
|
||||
{
|
||||
uint8_t byte;
|
||||
|
||||
if ((_remaining > 0) && (Ethernet.socketRecv(sockindex, &byte, 1) > 0)) {
|
||||
// We read things without any problems
|
||||
_remaining--;
|
||||
return byte;
|
||||
}
|
||||
|
||||
// If we get here, there's no data available
|
||||
return -1;
|
||||
}
|
||||
|
||||
int EthernetUDP::read(unsigned char *buffer, size_t len)
|
||||
{
|
||||
if (_remaining > 0) {
|
||||
int got;
|
||||
if (_remaining <= len) {
|
||||
// data should fit in the buffer
|
||||
got = Ethernet.socketRecv(sockindex, buffer, _remaining);
|
||||
} else {
|
||||
// too much data for the buffer,
|
||||
// grab as much as will fit
|
||||
got = Ethernet.socketRecv(sockindex, buffer, len);
|
||||
}
|
||||
if (got > 0) {
|
||||
_remaining -= got;
|
||||
//Serial.printf("UDP read %d\n", got);
|
||||
return got;
|
||||
}
|
||||
}
|
||||
// If we get here, there's no data available or recv failed
|
||||
return -1;
|
||||
}
|
||||
|
||||
int EthernetUDP::peek()
|
||||
{
|
||||
// Unlike recv, peek doesn't check to see if there's any data available, so we must.
|
||||
// If the user hasn't called parsePacket yet then return nothing otherwise they
|
||||
// may get the UDP header
|
||||
if (sockindex >= MAX_SOCK_NUM || _remaining == 0) return -1;
|
||||
return Ethernet.socketPeek(sockindex);
|
||||
}
|
||||
|
||||
void EthernetUDP::flush()
|
||||
{
|
||||
// TODO: we should wait for TX buffer to be emptied
|
||||
}
|
||||
|
||||
/* Start EthernetUDP socket, listening at local port PORT */
|
||||
uint8_t EthernetUDP::beginMulticast(IPAddress ip, uint16_t port)
|
||||
{
|
||||
if (sockindex < MAX_SOCK_NUM) Ethernet.socketClose(sockindex);
|
||||
sockindex = Ethernet.socketBeginMulticast(SnMR::UDP | SnMR::MULTI, ip, port);
|
||||
if (sockindex >= MAX_SOCK_NUM) return 0;
|
||||
_port = port;
|
||||
_remaining = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Udp.cpp: Library to send/receive UDP packets with the Arduino ethernet shield.
|
||||
* This version only offers minimal wrapping of socket.cpp
|
||||
* Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/
|
||||
*
|
||||
* NOTE: UDP is fast, but has some important limitations (thanks to Warren Gray for mentioning these)
|
||||
* 1) UDP does not guarantee the order in which assembled UDP packets are received. This
|
||||
* might not happen often in practice, but in larger network topologies, a UDP
|
||||
* packet can be received out of sequence.
|
||||
* 2) UDP does not guard against lost packets - so packets *can* disappear without the sender being
|
||||
* aware of it. Again, this may not be a concern in practice on small local networks.
|
||||
* For more information, see http://www.cafeaulait.org/course/week12/35.html
|
||||
*
|
||||
* MIT License:
|
||||
* Copyright (c) 2008 Bjoern Hartmann
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* bjoern@cs.stanford.edu 12/30/2008
|
||||
*/
|
||||
|
||||
#include "Ethernet.h"
|
||||
|
||||
539
arduino-libs/arduino-cli/libraries/Ethernet/src/socket.cpp
Normal file
539
arduino-libs/arduino-cli/libraries/Ethernet/src/socket.cpp
Normal file
@@ -0,0 +1,539 @@
|
||||
/* Copyright 2018 Paul Stoffregen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
* software and associated documentation files (the "Software"), to deal in the Software
|
||||
* without restriction, including without limitation the rights to use, copy, modify,
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "Ethernet.h"
|
||||
#include "utility/w5100.h"
|
||||
|
||||
#if ARDUINO >= 156 && !defined(ARDUINO_ARCH_PIC32)
|
||||
extern void yield(void);
|
||||
#else
|
||||
#define yield()
|
||||
#endif
|
||||
|
||||
// TODO: randomize this when not using DHCP, but how?
|
||||
static uint16_t local_port = 49152; // 49152 to 65535
|
||||
|
||||
typedef struct {
|
||||
uint16_t RX_RSR; // Number of bytes received
|
||||
uint16_t RX_RD; // Address to read
|
||||
uint16_t TX_FSR; // Free space ready for transmit
|
||||
uint8_t RX_inc; // how much have we advanced RX_RD
|
||||
} socketstate_t;
|
||||
|
||||
static socketstate_t state[MAX_SOCK_NUM];
|
||||
|
||||
|
||||
static uint16_t getSnTX_FSR(uint8_t s);
|
||||
static uint16_t getSnRX_RSR(uint8_t s);
|
||||
static void write_data(uint8_t s, uint16_t offset, const uint8_t *data, uint16_t len);
|
||||
static void read_data(uint8_t s, uint16_t src, uint8_t *dst, uint16_t len);
|
||||
|
||||
|
||||
|
||||
/*****************************************/
|
||||
/* Socket management */
|
||||
/*****************************************/
|
||||
|
||||
|
||||
void EthernetClass::socketPortRand(uint16_t n)
|
||||
{
|
||||
n &= 0x3FFF;
|
||||
local_port ^= n;
|
||||
//Serial.printf("socketPortRand %d, srcport=%d\n", n, local_port);
|
||||
}
|
||||
|
||||
uint8_t EthernetClass::socketBegin(uint8_t protocol, uint16_t port)
|
||||
{
|
||||
uint8_t s, status[MAX_SOCK_NUM], chip, maxindex=MAX_SOCK_NUM;
|
||||
|
||||
// first check hardware compatibility
|
||||
chip = W5100.getChip();
|
||||
if (!chip) return MAX_SOCK_NUM; // immediate error if no hardware detected
|
||||
#if MAX_SOCK_NUM > 4
|
||||
if (chip == 51) maxindex = 4; // W5100 chip never supports more than 4 sockets
|
||||
#endif
|
||||
//Serial.printf("W5000socket begin, protocol=%d, port=%d\n", protocol, port);
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
// look at all the hardware sockets, use any that are closed (unused)
|
||||
for (s=0; s < maxindex; s++) {
|
||||
status[s] = W5100.readSnSR(s);
|
||||
if (status[s] == SnSR::CLOSED) goto makesocket;
|
||||
}
|
||||
//Serial.printf("W5000socket step2\n");
|
||||
// as a last resort, forcibly close any already closing
|
||||
for (s=0; s < maxindex; s++) {
|
||||
uint8_t stat = status[s];
|
||||
if (stat == SnSR::LAST_ACK) goto closemakesocket;
|
||||
if (stat == SnSR::TIME_WAIT) goto closemakesocket;
|
||||
if (stat == SnSR::FIN_WAIT) goto closemakesocket;
|
||||
if (stat == SnSR::CLOSING) goto closemakesocket;
|
||||
}
|
||||
#if 0
|
||||
Serial.printf("W5000socket step3\n");
|
||||
// next, use any that are effectively closed
|
||||
for (s=0; s < MAX_SOCK_NUM; s++) {
|
||||
uint8_t stat = status[s];
|
||||
// TODO: this also needs to check if no more data
|
||||
if (stat == SnSR::CLOSE_WAIT) goto closemakesocket;
|
||||
}
|
||||
#endif
|
||||
SPI.endTransaction();
|
||||
return MAX_SOCK_NUM; // all sockets are in use
|
||||
closemakesocket:
|
||||
//Serial.printf("W5000socket close\n");
|
||||
W5100.execCmdSn(s, Sock_CLOSE);
|
||||
makesocket:
|
||||
//Serial.printf("W5000socket %d\n", s);
|
||||
EthernetServer::server_port[s] = 0;
|
||||
delayMicroseconds(250); // TODO: is this needed??
|
||||
W5100.writeSnMR(s, protocol);
|
||||
W5100.writeSnIR(s, 0xFF);
|
||||
if (port > 0) {
|
||||
W5100.writeSnPORT(s, port);
|
||||
} else {
|
||||
// if don't set the source port, set local_port number.
|
||||
if (++local_port < 49152) local_port = 49152;
|
||||
W5100.writeSnPORT(s, local_port);
|
||||
}
|
||||
W5100.execCmdSn(s, Sock_OPEN);
|
||||
state[s].RX_RSR = 0;
|
||||
state[s].RX_RD = W5100.readSnRX_RD(s); // always zero?
|
||||
state[s].RX_inc = 0;
|
||||
state[s].TX_FSR = 0;
|
||||
//Serial.printf("W5000socket prot=%d, RX_RD=%d\n", W5100.readSnMR(s), state[s].RX_RD);
|
||||
SPI.endTransaction();
|
||||
return s;
|
||||
}
|
||||
|
||||
// multicast version to set fields before open thd
|
||||
uint8_t EthernetClass::socketBeginMulticast(uint8_t protocol, IPAddress ip, uint16_t port)
|
||||
{
|
||||
uint8_t s, status[MAX_SOCK_NUM], chip, maxindex=MAX_SOCK_NUM;
|
||||
|
||||
// first check hardware compatibility
|
||||
chip = W5100.getChip();
|
||||
if (!chip) return MAX_SOCK_NUM; // immediate error if no hardware detected
|
||||
#if MAX_SOCK_NUM > 4
|
||||
if (chip == 51) maxindex = 4; // W5100 chip never supports more than 4 sockets
|
||||
#endif
|
||||
//Serial.printf("W5000socket begin, protocol=%d, port=%d\n", protocol, port);
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
// look at all the hardware sockets, use any that are closed (unused)
|
||||
for (s=0; s < maxindex; s++) {
|
||||
status[s] = W5100.readSnSR(s);
|
||||
if (status[s] == SnSR::CLOSED) goto makesocket;
|
||||
}
|
||||
//Serial.printf("W5000socket step2\n");
|
||||
// as a last resort, forcibly close any already closing
|
||||
for (s=0; s < maxindex; s++) {
|
||||
uint8_t stat = status[s];
|
||||
if (stat == SnSR::LAST_ACK) goto closemakesocket;
|
||||
if (stat == SnSR::TIME_WAIT) goto closemakesocket;
|
||||
if (stat == SnSR::FIN_WAIT) goto closemakesocket;
|
||||
if (stat == SnSR::CLOSING) goto closemakesocket;
|
||||
}
|
||||
#if 0
|
||||
Serial.printf("W5000socket step3\n");
|
||||
// next, use any that are effectively closed
|
||||
for (s=0; s < MAX_SOCK_NUM; s++) {
|
||||
uint8_t stat = status[s];
|
||||
// TODO: this also needs to check if no more data
|
||||
if (stat == SnSR::CLOSE_WAIT) goto closemakesocket;
|
||||
}
|
||||
#endif
|
||||
SPI.endTransaction();
|
||||
return MAX_SOCK_NUM; // all sockets are in use
|
||||
closemakesocket:
|
||||
//Serial.printf("W5000socket close\n");
|
||||
W5100.execCmdSn(s, Sock_CLOSE);
|
||||
makesocket:
|
||||
//Serial.printf("W5000socket %d\n", s);
|
||||
EthernetServer::server_port[s] = 0;
|
||||
delayMicroseconds(250); // TODO: is this needed??
|
||||
W5100.writeSnMR(s, protocol);
|
||||
W5100.writeSnIR(s, 0xFF);
|
||||
if (port > 0) {
|
||||
W5100.writeSnPORT(s, port);
|
||||
} else {
|
||||
// if don't set the source port, set local_port number.
|
||||
if (++local_port < 49152) local_port = 49152;
|
||||
W5100.writeSnPORT(s, local_port);
|
||||
}
|
||||
// Calculate MAC address from Multicast IP Address
|
||||
byte mac[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0x00 };
|
||||
mac[3] = ip[1] & 0x7F;
|
||||
mac[4] = ip[2];
|
||||
mac[5] = ip[3];
|
||||
W5100.writeSnDIPR(s, ip.raw_address()); //239.255.0.1
|
||||
W5100.writeSnDPORT(s, port);
|
||||
W5100.writeSnDHAR(s, mac);
|
||||
W5100.execCmdSn(s, Sock_OPEN);
|
||||
state[s].RX_RSR = 0;
|
||||
state[s].RX_RD = W5100.readSnRX_RD(s); // always zero?
|
||||
state[s].RX_inc = 0;
|
||||
state[s].TX_FSR = 0;
|
||||
//Serial.printf("W5000socket prot=%d, RX_RD=%d\n", W5100.readSnMR(s), state[s].RX_RD);
|
||||
SPI.endTransaction();
|
||||
return s;
|
||||
}
|
||||
// Return the socket's status
|
||||
//
|
||||
uint8_t EthernetClass::socketStatus(uint8_t s)
|
||||
{
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
uint8_t status = W5100.readSnSR(s);
|
||||
SPI.endTransaction();
|
||||
return status;
|
||||
}
|
||||
|
||||
// Immediately close. If a TCP connection is established, the
|
||||
// remote host is left unaware we closed.
|
||||
//
|
||||
void EthernetClass::socketClose(uint8_t s)
|
||||
{
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
W5100.execCmdSn(s, Sock_CLOSE);
|
||||
SPI.endTransaction();
|
||||
}
|
||||
|
||||
|
||||
// Place the socket in listening (server) mode
|
||||
//
|
||||
uint8_t EthernetClass::socketListen(uint8_t s)
|
||||
{
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
if (W5100.readSnSR(s) != SnSR::INIT) {
|
||||
SPI.endTransaction();
|
||||
return 0;
|
||||
}
|
||||
W5100.execCmdSn(s, Sock_LISTEN);
|
||||
SPI.endTransaction();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// establish a TCP connection in Active (client) mode.
|
||||
//
|
||||
void EthernetClass::socketConnect(uint8_t s, uint8_t * addr, uint16_t port)
|
||||
{
|
||||
// set destination IP
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
W5100.writeSnDIPR(s, addr);
|
||||
W5100.writeSnDPORT(s, port);
|
||||
W5100.execCmdSn(s, Sock_CONNECT);
|
||||
SPI.endTransaction();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Gracefully disconnect a TCP connection.
|
||||
//
|
||||
void EthernetClass::socketDisconnect(uint8_t s)
|
||||
{
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
W5100.execCmdSn(s, Sock_DISCON);
|
||||
SPI.endTransaction();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************/
|
||||
/* Socket Data Receive Functions */
|
||||
/*****************************************/
|
||||
|
||||
|
||||
static uint16_t getSnRX_RSR(uint8_t s)
|
||||
{
|
||||
#if 1
|
||||
uint16_t val, prev;
|
||||
|
||||
prev = W5100.readSnRX_RSR(s);
|
||||
while (1) {
|
||||
val = W5100.readSnRX_RSR(s);
|
||||
if (val == prev) {
|
||||
return val;
|
||||
}
|
||||
prev = val;
|
||||
}
|
||||
#else
|
||||
uint16_t val = W5100.readSnRX_RSR(s);
|
||||
return val;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void read_data(uint8_t s, uint16_t src, uint8_t *dst, uint16_t len)
|
||||
{
|
||||
uint16_t size;
|
||||
uint16_t src_mask;
|
||||
uint16_t src_ptr;
|
||||
|
||||
//Serial.printf("read_data, len=%d, at:%d\n", len, src);
|
||||
src_mask = (uint16_t)src & W5100.SMASK;
|
||||
src_ptr = W5100.RBASE(s) + src_mask;
|
||||
|
||||
if (W5100.hasOffsetAddressMapping() || src_mask + len <= W5100.SSIZE) {
|
||||
W5100.read(src_ptr, dst, len);
|
||||
} else {
|
||||
size = W5100.SSIZE - src_mask;
|
||||
W5100.read(src_ptr, dst, size);
|
||||
dst += size;
|
||||
W5100.read(W5100.RBASE(s), dst, len - size);
|
||||
}
|
||||
}
|
||||
|
||||
// Receive data. Returns size, or -1 for no data, or 0 if connection closed
|
||||
//
|
||||
int EthernetClass::socketRecv(uint8_t s, uint8_t *buf, int16_t len)
|
||||
{
|
||||
// Check how much data is available
|
||||
int ret = state[s].RX_RSR;
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
if (ret < len) {
|
||||
uint16_t rsr = getSnRX_RSR(s);
|
||||
ret = rsr - state[s].RX_inc;
|
||||
state[s].RX_RSR = ret;
|
||||
//Serial.printf("Sock_RECV, RX_RSR=%d, RX_inc=%d\n", ret, state[s].RX_inc);
|
||||
}
|
||||
if (ret == 0) {
|
||||
// No data available.
|
||||
uint8_t status = W5100.readSnSR(s);
|
||||
if ( status == SnSR::LISTEN || status == SnSR::CLOSED ||
|
||||
status == SnSR::CLOSE_WAIT ) {
|
||||
// The remote end has closed its side of the connection,
|
||||
// so this is the eof state
|
||||
ret = 0;
|
||||
} else {
|
||||
// The connection is still up, but there's no data waiting to be read
|
||||
ret = -1;
|
||||
}
|
||||
} else {
|
||||
if (ret > len) ret = len; // more data available than buffer length
|
||||
uint16_t ptr = state[s].RX_RD;
|
||||
if (buf) read_data(s, ptr, buf, ret);
|
||||
ptr += ret;
|
||||
state[s].RX_RD = ptr;
|
||||
state[s].RX_RSR -= ret;
|
||||
uint16_t inc = state[s].RX_inc + ret;
|
||||
if (inc >= 250 || state[s].RX_RSR == 0) {
|
||||
state[s].RX_inc = 0;
|
||||
W5100.writeSnRX_RD(s, ptr);
|
||||
W5100.execCmdSn(s, Sock_RECV);
|
||||
//Serial.printf("Sock_RECV cmd, RX_RD=%d, RX_RSR=%d\n",
|
||||
// state[s].RX_RD, state[s].RX_RSR);
|
||||
} else {
|
||||
state[s].RX_inc = inc;
|
||||
}
|
||||
}
|
||||
SPI.endTransaction();
|
||||
//Serial.printf("socketRecv, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint16_t EthernetClass::socketRecvAvailable(uint8_t s)
|
||||
{
|
||||
uint16_t ret = state[s].RX_RSR;
|
||||
if (ret == 0) {
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
uint16_t rsr = getSnRX_RSR(s);
|
||||
SPI.endTransaction();
|
||||
ret = rsr - state[s].RX_inc;
|
||||
state[s].RX_RSR = ret;
|
||||
//Serial.printf("sockRecvAvailable s=%d, RX_RSR=%d\n", s, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// get the first byte in the receive queue (no checking)
|
||||
//
|
||||
uint8_t EthernetClass::socketPeek(uint8_t s)
|
||||
{
|
||||
uint8_t b;
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
uint16_t ptr = state[s].RX_RD;
|
||||
W5100.read((ptr & W5100.SMASK) + W5100.RBASE(s), &b, 1);
|
||||
SPI.endTransaction();
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************/
|
||||
/* Socket Data Transmit Functions */
|
||||
/*****************************************/
|
||||
|
||||
static uint16_t getSnTX_FSR(uint8_t s)
|
||||
{
|
||||
uint16_t val, prev;
|
||||
|
||||
prev = W5100.readSnTX_FSR(s);
|
||||
while (1) {
|
||||
val = W5100.readSnTX_FSR(s);
|
||||
if (val == prev) {
|
||||
state[s].TX_FSR = val;
|
||||
return val;
|
||||
}
|
||||
prev = val;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void write_data(uint8_t s, uint16_t data_offset, const uint8_t *data, uint16_t len)
|
||||
{
|
||||
uint16_t ptr = W5100.readSnTX_WR(s);
|
||||
ptr += data_offset;
|
||||
uint16_t offset = ptr & W5100.SMASK;
|
||||
uint16_t dstAddr = offset + W5100.SBASE(s);
|
||||
|
||||
if (W5100.hasOffsetAddressMapping() || offset + len <= W5100.SSIZE) {
|
||||
W5100.write(dstAddr, data, len);
|
||||
} else {
|
||||
// Wrap around circular buffer
|
||||
uint16_t size = W5100.SSIZE - offset;
|
||||
W5100.write(dstAddr, data, size);
|
||||
W5100.write(W5100.SBASE(s), data + size, len - size);
|
||||
}
|
||||
ptr += len;
|
||||
W5100.writeSnTX_WR(s, ptr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief This function used to send the data in TCP mode
|
||||
* @return 1 for success else 0.
|
||||
*/
|
||||
uint16_t EthernetClass::socketSend(uint8_t s, const uint8_t * buf, uint16_t len)
|
||||
{
|
||||
uint8_t status=0;
|
||||
uint16_t ret=0;
|
||||
uint16_t freesize=0;
|
||||
|
||||
if (len > W5100.SSIZE) {
|
||||
ret = W5100.SSIZE; // check size not to exceed MAX size.
|
||||
} else {
|
||||
ret = len;
|
||||
}
|
||||
|
||||
// if freebuf is available, start.
|
||||
do {
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
freesize = getSnTX_FSR(s);
|
||||
status = W5100.readSnSR(s);
|
||||
SPI.endTransaction();
|
||||
if ((status != SnSR::ESTABLISHED) && (status != SnSR::CLOSE_WAIT)) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
yield();
|
||||
} while (freesize < ret);
|
||||
|
||||
// copy data
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
write_data(s, 0, (uint8_t *)buf, ret);
|
||||
W5100.execCmdSn(s, Sock_SEND);
|
||||
|
||||
/* +2008.01 bj */
|
||||
while ( (W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK ) {
|
||||
/* m2008.01 [bj] : reduce code */
|
||||
if ( W5100.readSnSR(s) == SnSR::CLOSED ) {
|
||||
SPI.endTransaction();
|
||||
return 0;
|
||||
}
|
||||
SPI.endTransaction();
|
||||
yield();
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
}
|
||||
/* +2008.01 bj */
|
||||
W5100.writeSnIR(s, SnIR::SEND_OK);
|
||||
SPI.endTransaction();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint16_t EthernetClass::socketSendAvailable(uint8_t s)
|
||||
{
|
||||
uint8_t status=0;
|
||||
uint16_t freesize=0;
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
freesize = getSnTX_FSR(s);
|
||||
status = W5100.readSnSR(s);
|
||||
SPI.endTransaction();
|
||||
if ((status == SnSR::ESTABLISHED) || (status == SnSR::CLOSE_WAIT)) {
|
||||
return freesize;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t EthernetClass::socketBufferData(uint8_t s, uint16_t offset, const uint8_t* buf, uint16_t len)
|
||||
{
|
||||
//Serial.printf(" bufferData, offset=%d, len=%d\n", offset, len);
|
||||
uint16_t ret =0;
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
uint16_t txfree = getSnTX_FSR(s);
|
||||
if (len > txfree) {
|
||||
ret = txfree; // check size not to exceed MAX size.
|
||||
} else {
|
||||
ret = len;
|
||||
}
|
||||
write_data(s, offset, buf, ret);
|
||||
SPI.endTransaction();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool EthernetClass::socketStartUDP(uint8_t s, uint8_t* addr, uint16_t port)
|
||||
{
|
||||
if ( ((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00) && (addr[3] == 0x00)) ||
|
||||
((port == 0x00)) ) {
|
||||
return false;
|
||||
}
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
W5100.writeSnDIPR(s, addr);
|
||||
W5100.writeSnDPORT(s, port);
|
||||
SPI.endTransaction();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EthernetClass::socketSendUDP(uint8_t s)
|
||||
{
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
W5100.execCmdSn(s, Sock_SEND);
|
||||
|
||||
/* +2008.01 bj */
|
||||
while ( (W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK ) {
|
||||
if (W5100.readSnIR(s) & SnIR::TIMEOUT) {
|
||||
/* +2008.01 [bj]: clear interrupt */
|
||||
W5100.writeSnIR(s, (SnIR::SEND_OK|SnIR::TIMEOUT));
|
||||
SPI.endTransaction();
|
||||
//Serial.printf("sendUDP timeout\n");
|
||||
return false;
|
||||
}
|
||||
SPI.endTransaction();
|
||||
yield();
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
}
|
||||
|
||||
/* +2008.01 bj */
|
||||
W5100.writeSnIR(s, SnIR::SEND_OK);
|
||||
SPI.endTransaction();
|
||||
|
||||
//Serial.printf("sendUDP ok\n");
|
||||
/* Sent ok */
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,474 @@
|
||||
/*
|
||||
* Copyright 2018 Paul Stoffregen
|
||||
* 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 <Arduino.h>
|
||||
#include "Ethernet.h"
|
||||
#include "w5100.h"
|
||||
|
||||
|
||||
/***************************************************/
|
||||
/** Default SS pin setting **/
|
||||
/***************************************************/
|
||||
|
||||
// If variant.h or other headers specifically define the
|
||||
// default SS pin for ethernet, use it.
|
||||
#if defined(PIN_SPI_SS_ETHERNET_LIB)
|
||||
#define SS_PIN_DEFAULT PIN_SPI_SS_ETHERNET_LIB
|
||||
|
||||
// MKR boards default to pin 5 for MKR ETH
|
||||
// Pins 8-10 are MOSI/SCK/MISO on MRK, so don't use pin 10
|
||||
#elif defined(USE_ARDUINO_MKR_PIN_LAYOUT) || defined(ARDUINO_SAMD_MKRZERO) || defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_SAMD_MKRFox1200) || defined(ARDUINO_SAMD_MKRGSM1400) || defined(ARDUINO_SAMD_MKRWAN1300)
|
||||
#define SS_PIN_DEFAULT 5
|
||||
|
||||
// For boards using AVR, assume shields with SS on pin 10
|
||||
// will be used. This allows for Arduino Mega (where
|
||||
// SS is pin 53) and Arduino Leonardo (where SS is pin 17)
|
||||
// to work by default with Arduino Ethernet Shield R2 & R3.
|
||||
#elif defined(__AVR__)
|
||||
#define SS_PIN_DEFAULT 10
|
||||
|
||||
// If variant.h or other headers define these names
|
||||
// use them if none of the other cases match
|
||||
#elif defined(PIN_SPI_SS)
|
||||
#define SS_PIN_DEFAULT PIN_SPI_SS
|
||||
#elif defined(CORE_SS0_PIN)
|
||||
#define SS_PIN_DEFAULT CORE_SS0_PIN
|
||||
|
||||
// As a final fallback, use pin 10
|
||||
#else
|
||||
#define SS_PIN_DEFAULT 10
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
// W5100 controller instance
|
||||
uint8_t W5100Class::chip = 0;
|
||||
uint8_t W5100Class::CH_BASE_MSB;
|
||||
uint8_t W5100Class::ss_pin = SS_PIN_DEFAULT;
|
||||
#ifdef ETHERNET_LARGE_BUFFERS
|
||||
uint16_t W5100Class::SSIZE = 2048;
|
||||
uint16_t W5100Class::SMASK = 0x07FF;
|
||||
#endif
|
||||
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)
|
||||
{
|
||||
static bool initialized = false;
|
||||
uint8_t i;
|
||||
|
||||
if (initialized) return 1;
|
||||
|
||||
// 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");
|
||||
|
||||
SPI.begin();
|
||||
initSS();
|
||||
resetSS();
|
||||
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_MSB = 0x40;
|
||||
#ifdef ETHERNET_LARGE_BUFFERS
|
||||
#if MAX_SOCK_NUM <= 1
|
||||
SSIZE = 16384;
|
||||
#elif MAX_SOCK_NUM <= 2
|
||||
SSIZE = 8192;
|
||||
#elif MAX_SOCK_NUM <= 4
|
||||
SSIZE = 4096;
|
||||
#else
|
||||
SSIZE = 2048;
|
||||
#endif
|
||||
SMASK = SSIZE - 1;
|
||||
#endif
|
||||
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);
|
||||
}
|
||||
// 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_MSB = 0x10;
|
||||
#ifdef ETHERNET_LARGE_BUFFERS
|
||||
#if MAX_SOCK_NUM <= 1
|
||||
SSIZE = 16384;
|
||||
#elif MAX_SOCK_NUM <= 2
|
||||
SSIZE = 8192;
|
||||
#elif MAX_SOCK_NUM <= 4
|
||||
SSIZE = 4096;
|
||||
#else
|
||||
SSIZE = 2048;
|
||||
#endif
|
||||
SMASK = SSIZE - 1;
|
||||
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);
|
||||
}
|
||||
#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()) {
|
||||
CH_BASE_MSB = 0x04;
|
||||
#ifdef ETHERNET_LARGE_BUFFERS
|
||||
#if MAX_SOCK_NUM <= 1
|
||||
SSIZE = 8192;
|
||||
writeTMSR(0x03);
|
||||
writeRMSR(0x03);
|
||||
#elif MAX_SOCK_NUM <= 2
|
||||
SSIZE = 4096;
|
||||
writeTMSR(0x0A);
|
||||
writeRMSR(0x0A);
|
||||
#else
|
||||
SSIZE = 2048;
|
||||
writeTMSR(0x55);
|
||||
writeRMSR(0x55);
|
||||
#endif
|
||||
SMASK = SSIZE - 1;
|
||||
#else
|
||||
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();
|
||||
initialized = true;
|
||||
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;
|
||||
}
|
||||
|
||||
W5100Linkstatus W5100Class::getLinkStatus()
|
||||
{
|
||||
uint8_t phystatus;
|
||||
|
||||
if (!init()) return UNKNOWN;
|
||||
switch (chip) {
|
||||
case 52:
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
phystatus = readPSTATUS_W5200();
|
||||
SPI.endTransaction();
|
||||
if (phystatus & 0x20) return LINK_ON;
|
||||
return LINK_OFF;
|
||||
case 55:
|
||||
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
|
||||
phystatus = readPHYCFGR_W5500();
|
||||
SPI.endTransaction();
|
||||
if (phystatus & 0x01) return LINK_ON;
|
||||
return LINK_OFF;
|
||||
default:
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t W5100Class::write(uint16_t addr, const uint8_t *buf, uint16_t len)
|
||||
{
|
||||
uint8_t cmd[8];
|
||||
|
||||
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();
|
||||
cmd[0] = addr >> 8;
|
||||
cmd[1] = addr & 0xFF;
|
||||
cmd[2] = ((len >> 8) & 0x7F) | 0x80;
|
||||
cmd[3] = len & 0xFF;
|
||||
SPI.transfer(cmd, 4);
|
||||
#ifdef SPI_HAS_TRANSFER_BUF
|
||||
SPI.transfer(buf, NULL, len);
|
||||
#else
|
||||
// TODO: copy 8 bytes at a time to cmd[] and block transfer
|
||||
for (uint16_t i=0; i < len; i++) {
|
||||
SPI.transfer(buf[i]);
|
||||
}
|
||||
#endif
|
||||
resetSS();
|
||||
} else { // chip == 55
|
||||
setSS();
|
||||
if (addr < 0x100) {
|
||||
// common registers 00nn
|
||||
cmd[0] = 0;
|
||||
cmd[1] = addr & 0xFF;
|
||||
cmd[2] = 0x04;
|
||||
} else if (addr < 0x8000) {
|
||||
// socket registers 10nn, 11nn, 12nn, 13nn, etc
|
||||
cmd[0] = 0;
|
||||
cmd[1] = addr & 0xFF;
|
||||
cmd[2] = ((addr >> 3) & 0xE0) | 0x0C;
|
||||
} else if (addr < 0xC000) {
|
||||
// transmit buffers 8000-87FF, 8800-8FFF, 9000-97FF, etc
|
||||
// 10## #nnn nnnn nnnn
|
||||
cmd[0] = addr >> 8;
|
||||
cmd[1] = addr & 0xFF;
|
||||
#if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1
|
||||
cmd[2] = 0x14; // 16K buffers
|
||||
#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2
|
||||
cmd[2] = ((addr >> 8) & 0x20) | 0x14; // 8K buffers
|
||||
#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4
|
||||
cmd[2] = ((addr >> 7) & 0x60) | 0x14; // 4K buffers
|
||||
#else
|
||||
cmd[2] = ((addr >> 6) & 0xE0) | 0x14; // 2K buffers
|
||||
#endif
|
||||
} else {
|
||||
// receive buffers
|
||||
cmd[0] = addr >> 8;
|
||||
cmd[1] = addr & 0xFF;
|
||||
#if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1
|
||||
cmd[2] = 0x1C; // 16K buffers
|
||||
#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2
|
||||
cmd[2] = ((addr >> 8) & 0x20) | 0x1C; // 8K buffers
|
||||
#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4
|
||||
cmd[2] = ((addr >> 7) & 0x60) | 0x1C; // 4K buffers
|
||||
#else
|
||||
cmd[2] = ((addr >> 6) & 0xE0) | 0x1C; // 2K buffers
|
||||
#endif
|
||||
}
|
||||
if (len <= 5) {
|
||||
for (uint8_t i=0; i < len; i++) {
|
||||
cmd[i + 3] = buf[i];
|
||||
}
|
||||
SPI.transfer(cmd, len + 3);
|
||||
} else {
|
||||
SPI.transfer(cmd, 3);
|
||||
#ifdef SPI_HAS_TRANSFER_BUF
|
||||
SPI.transfer(buf, NULL, len);
|
||||
#else
|
||||
// TODO: copy 8 bytes at a time to cmd[] and block transfer
|
||||
for (uint16_t i=0; i < len; i++) {
|
||||
SPI.transfer(buf[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
resetSS();
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
uint16_t W5100Class::read(uint16_t addr, uint8_t *buf, uint16_t len)
|
||||
{
|
||||
uint8_t cmd[4];
|
||||
|
||||
if (chip == 51) {
|
||||
for (uint16_t i=0; i < len; i++) {
|
||||
setSS();
|
||||
#if 1
|
||||
SPI.transfer(0x0F);
|
||||
SPI.transfer(addr >> 8);
|
||||
SPI.transfer(addr & 0xFF);
|
||||
addr++;
|
||||
buf[i] = SPI.transfer(0);
|
||||
#else
|
||||
cmd[0] = 0x0F;
|
||||
cmd[1] = addr >> 8;
|
||||
cmd[2] = addr & 0xFF;
|
||||
cmd[3] = 0;
|
||||
SPI.transfer(cmd, 4); // TODO: why doesn't this work?
|
||||
buf[i] = cmd[3];
|
||||
addr++;
|
||||
#endif
|
||||
resetSS();
|
||||
}
|
||||
} else if (chip == 52) {
|
||||
setSS();
|
||||
cmd[0] = addr >> 8;
|
||||
cmd[1] = addr & 0xFF;
|
||||
cmd[2] = (len >> 8) & 0x7F;
|
||||
cmd[3] = len & 0xFF;
|
||||
SPI.transfer(cmd, 4);
|
||||
memset(buf, 0, len);
|
||||
SPI.transfer(buf, len);
|
||||
resetSS();
|
||||
} else { // chip == 55
|
||||
setSS();
|
||||
if (addr < 0x100) {
|
||||
// common registers 00nn
|
||||
cmd[0] = 0;
|
||||
cmd[1] = addr & 0xFF;
|
||||
cmd[2] = 0x00;
|
||||
} else if (addr < 0x8000) {
|
||||
// socket registers 10nn, 11nn, 12nn, 13nn, etc
|
||||
cmd[0] = 0;
|
||||
cmd[1] = addr & 0xFF;
|
||||
cmd[2] = ((addr >> 3) & 0xE0) | 0x08;
|
||||
} else if (addr < 0xC000) {
|
||||
// transmit buffers 8000-87FF, 8800-8FFF, 9000-97FF, etc
|
||||
// 10## #nnn nnnn nnnn
|
||||
cmd[0] = addr >> 8;
|
||||
cmd[1] = addr & 0xFF;
|
||||
#if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1
|
||||
cmd[2] = 0x10; // 16K buffers
|
||||
#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2
|
||||
cmd[2] = ((addr >> 8) & 0x20) | 0x10; // 8K buffers
|
||||
#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4
|
||||
cmd[2] = ((addr >> 7) & 0x60) | 0x10; // 4K buffers
|
||||
#else
|
||||
cmd[2] = ((addr >> 6) & 0xE0) | 0x10; // 2K buffers
|
||||
#endif
|
||||
} else {
|
||||
// receive buffers
|
||||
cmd[0] = addr >> 8;
|
||||
cmd[1] = addr & 0xFF;
|
||||
#if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1
|
||||
cmd[2] = 0x18; // 16K buffers
|
||||
#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2
|
||||
cmd[2] = ((addr >> 8) & 0x20) | 0x18; // 8K buffers
|
||||
#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4
|
||||
cmd[2] = ((addr >> 7) & 0x60) | 0x18; // 4K buffers
|
||||
#else
|
||||
cmd[2] = ((addr >> 6) & 0xE0) | 0x18; // 2K buffers
|
||||
#endif
|
||||
}
|
||||
SPI.transfer(cmd, 3);
|
||||
memset(buf, 0, len);
|
||||
SPI.transfer(buf, len);
|
||||
resetSS();
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
void W5100Class::execCmdSn(SOCKET s, SockCMD _cmd)
|
||||
{
|
||||
// Send command to socket
|
||||
writeSnCR(s, _cmd);
|
||||
// Wait for command to complete
|
||||
while (readSnCR(s)) ;
|
||||
}
|
||||
466
arduino-libs/arduino-cli/libraries/Ethernet/src/utility/w5100.h
Normal file
466
arduino-libs/arduino-cli/libraries/Ethernet/src/utility/w5100.h
Normal file
@@ -0,0 +1,466 @@
|
||||
/*
|
||||
* Copyright 2018 Paul Stoffregen
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// w5100.h contains private W5x00 hardware "driver" level definitions
|
||||
// which are not meant to be exposed to other libraries or Arduino users
|
||||
|
||||
#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.
|
||||
// Higher SPI clock only results in faster transfer to hosts on a LAN
|
||||
// or with very low packet latency. With ordinary internet latency,
|
||||
// the TCP window size & packet loss determine your overall speed.
|
||||
//#define SPI_ETHERNET_SETTINGS SPISettings(30000000, MSBFIRST, SPI_MODE0)
|
||||
|
||||
|
||||
// Require Ethernet.h, because we need MAX_SOCK_NUM
|
||||
#ifndef ethernet_h_
|
||||
#error "Ethernet.h must be included before w5100.h"
|
||||
#endif
|
||||
|
||||
|
||||
// Arduino 101's SPI can not run faster than 8 MHz.
|
||||
#if defined(ARDUINO_ARCH_ARC32)
|
||||
#undef SPI_ETHERNET_SETTINGS
|
||||
#define SPI_ETHERNET_SETTINGS SPISettings(8000000, MSBFIRST, SPI_MODE0)
|
||||
#endif
|
||||
|
||||
// Arduino Zero can't use W5100-based shields faster than 8 MHz
|
||||
// https://github.com/arduino-libraries/Ethernet/issues/37#issuecomment-408036848
|
||||
// W5500 does seem to work at 12 MHz. Delete this if only using W5500
|
||||
#if defined(__SAMD21G18A__)
|
||||
#undef SPI_ETHERNET_SETTINGS
|
||||
#define SPI_ETHERNET_SETTINGS SPISettings(8000000, MSBFIRST, SPI_MODE0)
|
||||
#endif
|
||||
|
||||
|
||||
typedef uint8_t SOCKET;
|
||||
|
||||
class SnMR {
|
||||
public:
|
||||
static const uint8_t CLOSE = 0x00;
|
||||
static const uint8_t TCP = 0x21;
|
||||
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;
|
||||
};
|
||||
|
||||
enum W5100Linkstatus {
|
||||
UNKNOWN,
|
||||
LINK_ON,
|
||||
LINK_OFF
|
||||
};
|
||||
|
||||
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); \
|
||||
}
|
||||
static W5100Linkstatus getLinkStatus();
|
||||
|
||||
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)
|
||||
__GP_REGISTER8 (PSTATUS_W5200, 0x0035); // PHY Status
|
||||
__GP_REGISTER8 (PHYCFGR_W5500, 0x002E); // PHY Configuration register, default: 10111xxx
|
||||
|
||||
|
||||
#undef __GP_REGISTER8
|
||||
#undef __GP_REGISTER16
|
||||
#undef __GP_REGISTER_N
|
||||
|
||||
// W5100 Socket registers
|
||||
// ----------------------
|
||||
private:
|
||||
static uint16_t CH_BASE(void) {
|
||||
//if (chip == 55) return 0x1000;
|
||||
//if (chip == 52) return 0x4000;
|
||||
//return 0x0400;
|
||||
return CH_BASE_MSB << 8;
|
||||
}
|
||||
static uint8_t CH_BASE_MSB; // 1 redundant byte, saves ~80 bytes code on AVR
|
||||
static const uint16_t CH_SIZE = 0x0100;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#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 uint8_t getChip(void) { return chip; }
|
||||
#ifdef ETHERNET_LARGE_BUFFERS
|
||||
static uint16_t SSIZE;
|
||||
static uint16_t SMASK;
|
||||
#else
|
||||
static const uint16_t SSIZE = 2048;
|
||||
static const uint16_t SMASK = 0x07FF;
|
||||
#endif
|
||||
static uint16_t SBASE(uint8_t socknum) {
|
||||
if (chip == 51) {
|
||||
return socknum * SSIZE + 0x4000;
|
||||
} else {
|
||||
return socknum * SSIZE + 0x8000;
|
||||
}
|
||||
}
|
||||
static uint16_t RBASE(uint8_t socknum) {
|
||||
if (chip == 51) {
|
||||
return socknum * SSIZE + 0x6000;
|
||||
} else {
|
||||
return socknum * SSIZE + 0xC000;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user