mbed OS5
Fork of UIPEthernet by
UIPClient.cpp
- Committer:
- hudakz
- Date:
- 2014-09-15
- Revision:
- 0:5350a66d5279
- Child:
- 2:049ce85163c5
File content as of revision 0:5350a66d5279:
/* UIPClient.cpp - Arduino implementation of a uIP wrapper class. Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de> All rights reserved. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ extern "C" { #include "uip-conf.h" #include "uip.h" #include "uip_arp.h" #include "string.h" } #include "UIPEthernet.h" #include "UIPClient.h" #include "Dns.h" #ifdef UIPETHERNET_DEBUG_CLIENT #include "HardwareSerial.h" #endif #define UIP_TCP_PHYH_LEN UIP_LLH_LEN + UIP_IPTCPH_LEN uip_userdata_closed_t* UIPClient:: closed_conns[UIP_CONNS]; /** * @brief * @note * @param * @retval */ UIPClient::UIPClient(void) : _uip_conn(NULL), data(NULL) { UIPEthernet.set_uip_callback(&UIPClient::uip_callback); } /** * @brief * @note * @param * @retval */ UIPClient::UIPClient(struct uip_conn* conn) : _uip_conn(conn), data(conn ? (uip_userdata_t*)conn->appstate.user : NULL) { } /** * @brief * @note * @param * @retval */ UIPClient::UIPClient(uip_userdata_closed_t* conn_data) : _uip_conn(NULL), data((uip_userdata_t*)conn_data) { } /** * @brief * @note * @param * @retval */ int UIPClient::connect(IPAddress ip, uint16_t port) { uip_ipaddr_t ipaddr; uip_ip_addr(ipaddr, ip); _uip_conn = uip_connect(&ipaddr, htons(port)); if(_uip_conn) { while((_uip_conn->tcpstateflags & UIP_TS_MASK) != UIP_CLOSED) { UIPEthernet.tick(); if((_uip_conn->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED) { #ifdef UIPETHERNET_DEBUG_CLIENT Serial.print("connected, state: "); Serial.print(((uip_userdata_t*)_uip_conn->appstate.user)->state); Serial.print(", first packet in: "); Serial.println(((uip_userdata_t*)_uip_conn->appstate.user)->packets_in[0]); #endif return 1; } } } return 0; } /** * @brief * @note * @param * @retval */ int UIPClient::connect(const char* host, uint16_t port) { // Look up the host first int ret = 0; #if UIP_UDP DNSClient dns; IPAddress remote_addr; dns.begin(UIPEthernet.dnsServerIP()); ret = dns.getHostByName(host, remote_addr); if(ret == 1) { return connect(remote_addr, port); } #endif return ret; } /** * @brief * @note * @param * @retval */ void UIPClient::stop(void) { if(data) { _flushBlocks(&data->packets_in[0]); if(data->state & UIP_CLIENT_CLOSED) { uip_userdata_closed_t ** cc = &UIPClient::closed_conns[0]; for(uint8_t i = 0; i < UIP_CONNS; i++) { if(*cc == (void*)data) { *cc = NULL; break; } cc++; } free(data); } else { data->state |= UIP_CLIENT_CLOSE; } data = NULL; } _uip_conn = NULL; UIPEthernet.tick(); } /** * @brief * @note * @param * @retval */ uint8_t UIPClient::connected(void) { return((_uip_conn && (_uip_conn->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED) || available() > 0) ? 1 : 0; } /** * @brief * @note * @param * @retval */ bool UIPClient::operator==(const UIPClient& rhs) { return _uip_conn && rhs._uip_conn && _uip_conn == rhs._uip_conn; } /** * @brief * @note * @param * @retval */ UIPClient::operator bool(void) { UIPEthernet.tick(); return(data || (_uip_conn && (data = (uip_userdata_t*)_uip_conn->appstate.user))) && (!(data->state & UIP_CLIENT_CLOSED) || data->packets_in[0] != NOBLOCK); } /** * @brief * @note * @param * @retval */ size_t UIPClient::write(uint8_t c) { return _write(_uip_conn, &c, 1); } /** * @brief * @note * @param * @retval */ size_t UIPClient::write(const uint8_t* buf, size_t size) { return _write(_uip_conn, buf, size); } /** * @brief * @note * @param * @retval */ size_t UIPClient::_write(struct uip_conn* conn, const uint8_t* buf, size_t size) { uip_userdata_t* u; int remain = size; uint16_t written; #if UIP_ATTEMPTS_ON_WRITE > 0 uint16_t attempts = UIP_ATTEMPTS_ON_WRITE; #endif repeat : UIPEthernet.tick(); if(conn && (u = (uip_userdata_t*)conn->appstate.user) && !(u->state & (UIP_CLIENT_CLOSE | UIP_CLIENT_CLOSED))) { memhandle* p = _currentBlock(&u->packets_out[0]); if(*p == NOBLOCK) { newpacket: *p = UIPEthernet.network.allocBlock(UIP_SOCKET_DATALEN); if(*p == NOBLOCK) { #if UIP_ATTEMPTS_ON_WRITE > 0 if((--attempts) > 0) #endif #if UIP_ATTEMPTS_ON_WRITE != 0 goto repeat; #endif goto ready; } u->out_pos = 0; } #ifdef UIPETHERNET_DEBUG_CLIENT Serial.print("UIPClient.write: writePacket("); Serial.print(*p); Serial.print(") pos: "); Serial.print(u->out_pos); Serial.print(", buf["); Serial.print(size - remain); Serial.print("-"); Serial.print(remain); Serial.print("]: '"); Serial.write((uint8_t*)buf + size - remain, remain); Serial.println("'"); #endif written = UIPEthernet.network.writePacket(*p, u->out_pos, (uint8_t*)buf + size - remain, remain); remain -= written; u->out_pos += written; if(remain > 0) { if(p == &u->packets_out[UIP_SOCKET_NUMPACKETS - 1]) { #if UIP_ATTEMPTS_ON_WRITE > 0 if((--attempts) > 0) #endif #if UIP_ATTEMPTS_ON_WRITE != 0 goto repeat; #endif goto ready; } p++; goto newpacket; } ready: return size - remain; } return -1; } /** * @brief * @note * @param * @retval */ int UIPClient::available(void) { if(*this) return _available(data); return -1; } /** * @brief * @note * @param * @retval */ int UIPClient::_available(uip_userdata_t* u) { memhandle* p = &u->packets_in[0]; if(*p == NOBLOCK) return 0; int len = 0; for(memhandle * end = p + UIP_SOCKET_NUMPACKETS; p < end; p++) { if(*p == NOBLOCK) break; len += UIPEthernet.network.blockSize(*p); } return len; } /** * @brief * @note * @param * @retval */ int UIPClient::read(uint8_t* buf, size_t size) { if(*this) { int remain = size; memhandle* p = &data->packets_in[0]; if(*p == NOBLOCK) return 0; int read; do { read = UIPEthernet.network.readPacket(*p, 0, buf + size - remain, remain); if(read == UIPEthernet.network.blockSize(*p)) { remain -= read; _eatBlock(p); if(_uip_conn && uip_stopped(_uip_conn) && !(data->state & (UIP_CLIENT_CLOSE | UIP_CLIENT_CLOSED))) data->state |= UIP_CLIENT_RESTART; if(*p == NOBLOCK) return size - remain; } else { UIPEthernet.network.resizeBlock(*p, read); break; } } while(remain > 0); return size; } return -1; } /** * @brief * @note * @param * @retval */ int UIPClient::read(void) { uint8_t c; if(read(&c, 1) < 0) return -1; return c; } /** * @brief * @note * @param * @retval */ int UIPClient::peek(void) { if(*this) { memhandle p = data->packets_in[0]; if(p != NOBLOCK) { uint8_t c; UIPEthernet.network.readPacket(p, 0, &c, 1); return c; } } return -1; } /** * @brief * @note * @param * @retval */ void UIPClient::flush(void) { if(*this) { _flushBlocks(&data->packets_in[0]); } } /** * @brief * @note * @param * @retval */ void UIPClient::uip_callback(uip_tcp_appstate_t* s) { uip_userdata_t* u = (uip_userdata_t*)s->user; if(!u && uip_connected()) { #ifdef UIPETHERNET_DEBUG_CLIENT Serial.println("UIPClient uip_connected"); #endif // We want to store some data in our connections, so allocate some space // for it. The connection_data struct is defined in a separate .h file, // due to the way the Arduino IDE works. (typedefs come after function // definitions.) u = (uip_userdata_t*)malloc(sizeof(uip_userdata_t)); if(u) { memset(u, 0, sizeof(uip_userdata_t)); s->user = u; } } if(u) { if(uip_newdata()) { #ifdef UIPETHERNET_DEBUG_CLIENT Serial.print("UIPClient uip_newdata, uip_len:"); Serial.println(uip_len); #endif if(uip_len && !(u->state & (UIP_CLIENT_CLOSE | UIP_CLIENT_CLOSED))) { memhandle newPacket = UIPEthernet.network.allocBlock(uip_len); if(newPacket != NOBLOCK) { memhandle* p = _currentBlock(&u->packets_in[0]); //if it's not the first packet if(*p != NOBLOCK) { uint8_t slot = p - &u->packets_in[0]; if(slot < UIP_SOCKET_NUMPACKETS - 1) p++; //if this is the last slot stop this connection if(slot >= UIP_SOCKET_NUMPACKETS - 2) { uip_stop(); //if there's no free slot left omit loosing this packet and (again) stop this connection if(slot == UIP_SOCKET_NUMPACKETS - 1) goto reject_newdata; } } UIPEthernet.network.copyPacket ( newPacket, 0, UIPEthernet.in_packet, ((uint8_t*)uip_appdata) - uip_buf, uip_len ); *p = newPacket; goto finish_newdata; } reject_newdata: UIPEthernet.packetstate &= ~UIPETHERNET_FREEPACKET; uip_stop(); } } finish_newdata: if(u->state & UIP_CLIENT_RESTART) { u->state &= ~UIP_CLIENT_RESTART; uip_restart(); } // If the connection has been closed, save received but unread data. if(uip_closed() || uip_timedout()) { #ifdef UIPETHERNET_DEBUG_CLIENT Serial.println("UIPClient uip_closed"); #endif // drop outgoing packets not sent yet: _flushBlocks(&u->packets_out[0]); if(u->packets_in[0] != NOBLOCK) { uip_userdata_closed_t ** closed_conn_data = &UIPClient::closed_conns[0]; for(uip_socket_ptr i = 0; i < UIP_CONNS; i++) { if(!*closed_conn_data) { *closed_conn_data = (uip_userdata_closed_t*)u; (*closed_conn_data)->lport = uip_conn->lport; break; } closed_conn_data++; } } u->state |= UIP_CLIENT_CLOSED; // disassociate appdata. s->user = NULL; goto nodata; } if(uip_acked()) { #ifdef UIPETHERNET_DEBUG_CLIENT Serial.println("UIPClient uip_acked"); #endif _eatBlock(&u->packets_out[0]); } if(uip_poll() || uip_rexmit()) { #ifdef UIPETHERNET_DEBUG_CLIENT Serial.println("UIPClient uip_poll"); #endif memhandle p = u->packets_out[0]; if(p != NOBLOCK) { if(u->packets_out[1] == NOBLOCK) { uip_len = u->out_pos; if(uip_len > 0) { UIPEthernet.network.resizeBlock(p, 0, uip_len); } } else uip_len = UIPEthernet.network.blockSize(p); if(uip_len > 0) { UIPEthernet.uip_hdrlen = ((uint8_t*)uip_appdata) - uip_buf; UIPEthernet.uip_packet = UIPEthernet.network.allocBlock(UIPEthernet.uip_hdrlen + uip_len); if(UIPEthernet.uip_packet != NOBLOCK) { UIPEthernet.network.copyPacket(UIPEthernet.uip_packet, UIPEthernet.uip_hdrlen, p, 0, uip_len); UIPEthernet.packetstate |= UIPETHERNET_SENDPACKET; uip_send(uip_appdata, uip_len); } return; } } } // don't close connection unless all outgoing packets are sent if(u->state & UIP_CLIENT_CLOSE) { #ifdef UIPETHERNET_DEBUG_CLIENT Serial.print("UIPClient state UIP_CLIENT_CLOSE"); #endif if(u->packets_out[0] == NOBLOCK) { #ifdef UIPETHERNET_DEBUG_CLIENT Serial.print("UIPClient state UIP_CLIENT_CLOSE -> free userdata"); #endif free(u); s->user = NULL; uip_close(); } else uip_stop(); } } nodata: UIPEthernet.uip_packet = NOBLOCK; uip_len = 0; } /** * @brief * @note * @param * @retval */ memhandle* UIPClient::_currentBlock(memhandle* block) { for(memhandle * end = block + UIP_SOCKET_NUMPACKETS - 1; block < end; block++) if(*(block + 1) == NOBLOCK) break; return block; } /** * @brief * @note * @param * @retval */ void UIPClient::_eatBlock(memhandle* block) { #ifdef UIPETHERNET_DEBUG_CLIENT memhandle* start = block; Serial.print("eatblock("); Serial.print(*block); Serial.print("): "); for(uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS; i++) { Serial.print(start[i]); Serial.print(" "); } Serial.print("-> "); #endif memhandle* end = block + (UIP_SOCKET_NUMPACKETS - 1); UIPEthernet.network.freeBlock(*block); while(block < end) *block = *((block++) + 1); *end = NOBLOCK; #ifdef UIPETHERNET_DEBUG_CLIENT for(uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS; i++) { Serial.print(start[i]); Serial.print(" "); } Serial.println(); #endif } /** * @brief * @note * @param * @retval */ void UIPClient::_flushBlocks(memhandle* block) { for(memhandle * end = block + UIP_SOCKET_NUMPACKETS; block < end; block++) { if(*block != NOBLOCK) { UIPEthernet.network.freeBlock(*block); *block = NOBLOCK; } else break; } }