mbed OS5
Fork of UIPEthernet by
Diff: UIPClient.cpp
- Revision:
- 0:5350a66d5279
- Child:
- 2:049ce85163c5
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UIPClient.cpp Mon Sep 15 11:12:30 2014 +0000 @@ -0,0 +1,627 @@ +/* + 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; + } +}