From e58f4c567a0b5953be9f69d2b29bf7fbd104daee Mon Sep 17 00:00:00 2001 From: goldsimon Date: Fri, 29 Jan 2010 22:09:31 +0000 Subject: [PATCH] Add non-blocking support for connect (partly from patch #6860) plus many cleanups in socket & netconn API --- CHANGELOG | 5 + src/api/api_lib.c | 4 +- src/api/api_msg.c | 227 +++++++++++++++++++++++-------------- src/api/err.c | 22 ++-- src/api/sockets.c | 38 +++---- src/include/lwip/api.h | 11 +- src/include/lwip/err.h | 24 ++-- src/include/lwip/sockets.h | 4 - 8 files changed, 203 insertions(+), 132 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a50cfa26..63d11980 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -19,6 +19,11 @@ HISTORY ++ New features: + 2010-01-29: Simon Goldschmidt (patch by Laura Garrett) + * api.h, sockets.h, err.h, api_lib.c, api_msg.c, sockets.c, err.c: + Add non-blocking support for connect (partly from patch #6860), + plus many cleanups in socket & netconn API. + 2010-01-27: Simon Goldschmidt * opt.h, tcp.h, init.c, api_msg.c: Added TCP_SNDQUEUELOWAT corresponding to TCP_SNDLOWAT and added tcp_sndqueuelen() - this fixes bug #28605 diff --git a/src/api/api_lib.c b/src/api/api_lib.c index 3e46be12..0050e046 100644 --- a/src/api/api_lib.c +++ b/src/api/api_lib.c @@ -111,7 +111,6 @@ netconn_delete(struct netconn *conn) msg.msg.conn = conn; tcpip_apimsg(&msg); - conn->pcb.tcp = NULL; netconn_free(conn); /* don't care for return value of do_delconn since it only calls void functions */ @@ -493,6 +492,9 @@ netconn_write(struct netconn *conn, const void *dataptr, size_t size, u8_t apifl LWIP_ERROR("netconn_write: invalid conn", (conn != NULL), return ERR_ARG;); LWIP_ERROR("netconn_write: invalid conn->type", (conn->type == NETCONN_TCP), return ERR_VAL;); + if (size == 0) { + return ERR_OK; + } msg.function = do_write; msg.msg.conn = conn; diff --git a/src/api/api_msg.c b/src/api/api_msg.c index 85ffa78a..da1abc23 100644 --- a/src/api/api_msg.c +++ b/src/api/api_msg.c @@ -98,7 +98,8 @@ recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p, } } - if(q != NULL) { + if (q != NULL) { + u16_t len; buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); if (buf == NULL) { pbuf_free(q); @@ -110,13 +111,14 @@ recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p, buf->addr = &(((struct ip_hdr*)(q->payload))->src); buf->port = pcb->protocol; + len = q->tot_len; if (sys_mbox_trypost(conn->recvmbox, buf) != ERR_OK) { netbuf_delete(buf); return 0; } else { - SYS_ARCH_INC(conn->recv_avail, q->tot_len); + SYS_ARCH_INC(conn->recv_avail, len); /* Register event with callback */ - API_EVENT(conn, NETCONN_EVT_RCVPLUS, q->tot_len); + API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); } } } @@ -138,6 +140,7 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, { struct netbuf *buf; struct netconn *conn; + u16_t len; #if LWIP_SO_RCVBUF int recv_avail; #endif /* LWIP_SO_RCVBUF */ @@ -179,13 +182,14 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, #endif /* LWIP_NETBUF_RECVINFO */ } + len = p->tot_len; if (sys_mbox_trypost(conn->recvmbox, buf) != ERR_OK) { netbuf_delete(buf); return; } else { - SYS_ARCH_INC(conn->recv_avail, p->tot_len); + SYS_ARCH_INC(conn->recv_avail, len); /* Register event with callback */ - API_EVENT(conn, NETCONN_EVT_RCVPLUS, p->tot_len); + API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); } } #endif /* LWIP_UDP */ @@ -220,20 +224,24 @@ recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) } return ERR_OK; } + /* Unlike for UDP or RAW pcbs, don't check for available space + using recv_avail since that could break the connection + (data is already ACKed) */ /* don't overwrite fatal errors! */ NETCONN_SET_SAFE_ERR(conn, err); if (p != NULL) { len = p->tot_len; - SYS_ARCH_INC(conn->recv_avail, len); } else { len = 0; } if (sys_mbox_trypost(conn->recvmbox, p) != ERR_OK) { + /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */ return ERR_MEM; } else { + SYS_ARCH_INC(conn->recv_avail, len); /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); } @@ -265,6 +273,7 @@ poll_tcp(void *arg, struct tcp_pcb *pcb) } else if (conn->state == NETCONN_CLOSE) { do_close_internal(conn); } + /* @todo: implement connect timeout here? */ return ERR_OK; } @@ -285,7 +294,6 @@ sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len) LWIP_ASSERT("conn != NULL", (conn != NULL)); if (conn->state == NETCONN_WRITE) { - LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL); do_writemore(conn); } else if (conn->state == NETCONN_CLOSE) { do_close_internal(conn); @@ -327,7 +335,7 @@ err_tcp(void *arg, err_t err) conn->last_err = err; SYS_ARCH_UNPROTECT(lev); - /* API_EVENT might call tcp_tmr, so reset conn->state now */ + /* reset conn->state now before waking up other threads */ old_state = conn->state; conn->state = NETCONN_NONE; @@ -347,13 +355,17 @@ err_tcp(void *arg, err_t err) (old_state == NETCONN_CONNECT)) { /* calling do_writemore/do_close_internal is not necessary since the pcb has already been deleted! */ + int was_nonblocking_connect = conn->in_non_blocking_connect; + conn->in_non_blocking_connect = 0; - /* set error return code */ - LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); - conn->current_msg->err = err; - conn->current_msg = NULL; - /* wake up the waiting task */ - sys_sem_signal(conn->op_completed); + if (!was_nonblocking_connect) { + /* set error return code */ + LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); + conn->current_msg->err = err; + conn->current_msg = NULL; + /* wake up the waiting task */ + sys_sem_signal(conn->op_completed); + } } else { LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL); } @@ -415,7 +427,7 @@ accept_function(void *arg, struct tcp_pcb *newpcb, err_t err) newconn->last_err = err; if (sys_mbox_trypost(conn->acceptmbox, newconn) != ERR_OK) { - /* When returning != ERR_OK, the connection is aborted in tcp_process(), + /* When returning != ERR_OK, the pcb is aborted in tcp_process(), so do nothing here! */ newconn->pcb.tcp = NULL; /* no need to drain since we know the recvmbox is empty. */ @@ -591,6 +603,8 @@ netconn_alloc(enum netconn_type t, netconn_callback callback) #if LWIP_SO_RCVBUF conn->recv_bufsize = RECV_BUFSIZE_DEFAULT; #endif /* LWIP_SO_RCVBUF */ + conn->non_blocking = 0; + conn->in_non_blocking_connect = 0; return conn; } @@ -707,8 +721,8 @@ do_close_internal(struct netconn *conn) conn->state = NETCONN_NONE; /* Set back some callback pointers as conn is going away */ conn->pcb.tcp = NULL; - /* Trigger select() in socket layer. This send should something else so the - errorfd is set, not the read and write fd! */ + /* @todo: this lets select make the socket readable and writable, + which is wrong! errfd instead? */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); /* wake up the application task */ @@ -736,45 +750,52 @@ do_close_internal(struct netconn *conn) void do_delconn(struct api_msg_msg *msg) { - /* Drain and delete mboxes */ - netconn_drain(msg->conn); + /* @todo TCP: abort running write/connect? */ + if ((msg->conn->state != NETCONN_NONE) && (msg->conn->state != NETCONN_LISTEN)) { + /* this only happens for TCP netconns */ + LWIP_ASSERT("msg->conn->type == NETCONN_TCP", msg->conn->type == NETCONN_TCP); + msg->err = ERR_INPROGRESS; + } else { + /* Drain and delete mboxes */ + netconn_drain(msg->conn); - if (msg->conn->pcb.tcp != NULL) { + if (msg->conn->pcb.tcp != NULL) { - switch (NETCONNTYPE_GROUP(msg->conn->type)) { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { #if LWIP_RAW - case NETCONN_RAW: - raw_remove(msg->conn->pcb.raw); - break; + case NETCONN_RAW: + raw_remove(msg->conn->pcb.raw); + break; #endif /* LWIP_RAW */ #if LWIP_UDP - case NETCONN_UDP: - msg->conn->pcb.udp->recv_arg = NULL; - udp_remove(msg->conn->pcb.udp); - break; + case NETCONN_UDP: + msg->conn->pcb.udp->recv_arg = NULL; + udp_remove(msg->conn->pcb.udp); + break; #endif /* LWIP_UDP */ #if LWIP_TCP - case NETCONN_TCP: - LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && - msg->conn->write_offset == 0); - msg->conn->state = NETCONN_CLOSE; - msg->conn->current_msg = msg; - do_close_internal(msg->conn); - /* API_EVENT is called inside do_close_internal, before releasing - the application thread, so we can return at this point! */ - return; + case NETCONN_TCP: + LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && + msg->conn->write_offset == 0); + msg->conn->state = NETCONN_CLOSE; + msg->conn->current_msg = msg; + do_close_internal(msg->conn); + /* API_EVENT is called inside do_close_internal, before releasing + the application thread, so we can return at this point! */ + return; #endif /* LWIP_TCP */ - default: - break; + default: + break; + } + msg->conn->pcb.tcp = NULL; } + /* tcp netconns don't come here! */ + + /* @todo: this lets select make the socket readable and writable, + which is wrong! errfd instead? */ + API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0); + API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0); } - /* tcp netconns don't come here! */ - - /* Trigger select() in socket layer. This send should something else so the - errorfd is set, not the read and write fd! */ - API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0); - API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0); - if (msg->conn->op_completed != SYS_SEM_NULL) { sys_sem_signal(msg->conn->op_completed); } @@ -830,6 +851,7 @@ static err_t do_connected(void *arg, struct tcp_pcb *pcb, err_t err) { struct netconn *conn; + int was_blocking; LWIP_UNUSED_ARG(pcb); @@ -840,15 +862,32 @@ do_connected(void *arg, struct tcp_pcb *pcb, err_t err) } LWIP_ASSERT("conn->state == NETCONN_CONNECT", conn->state == NETCONN_CONNECT); - LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); + LWIP_ASSERT("(conn->current_msg != NULL) || conn->in_non_blocking_connect", + (conn->current_msg != NULL) || conn->in_non_blocking_connect); - conn->current_msg->err = err; + if (conn->current_msg != NULL) { + conn->current_msg->err = err; + } if ((conn->type == NETCONN_TCP) && (err == ERR_OK)) { setup_tcp(conn); } + was_blocking = !conn->in_non_blocking_connect; + conn->in_non_blocking_connect = 0; conn->current_msg = NULL; conn->state = NETCONN_NONE; - sys_sem_signal(conn->op_completed); + if (!was_blocking) { + SYS_ARCH_DECL_PROTECT(lev); + SYS_ARCH_PROTECT(lev); + if (conn->last_err == ERR_INPROGRESS) { + conn->last_err = ERR_OK; + } + SYS_ARCH_UNPROTECT(lev); + } + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + + if (was_blocking) { + sys_sem_signal(conn->op_completed); + } return ERR_OK; } #endif /* LWIP_TCP */ @@ -866,44 +905,48 @@ do_connect(struct api_msg_msg *msg) if (msg->conn->pcb.tcp == NULL) { /* This may happen when calling netconn_connect() a second time */ msg->err = ERR_CLSD; - sys_sem_signal(msg->conn->op_completed); - return; - } - - switch (NETCONNTYPE_GROUP(msg->conn->type)) { + } else { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { #if LWIP_RAW case NETCONN_RAW: msg->err = raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr); - sys_sem_signal(msg->conn->op_completed); break; #endif /* LWIP_RAW */ #if LWIP_UDP case NETCONN_UDP: msg->err = udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port); - sys_sem_signal(msg->conn->op_completed); break; #endif /* LWIP_UDP */ #if LWIP_TCP case NETCONN_TCP: - setup_tcp(msg->conn); - msg->err = tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port, - do_connected); - /* sys_sem_signal() is called from do_connected (or err_tcp()), - * when the connection is established! */ - if (msg->err == ERR_OK) { - msg->conn->state = NETCONN_CONNECT; - msg->conn->current_msg = msg; + /* Prevent connect while doing any other action. */ + if (msg->conn->state != NETCONN_NONE) { + msg->err = ERR_ISCONN; } else { - /* tcp_connect failed, so do_connected will not be called: return now */ - sys_sem_signal(msg->conn->op_completed); + setup_tcp(msg->conn); + msg->err = tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, + msg->msg.bc.port, do_connected); + if (msg->err == ERR_OK) { + msg->conn->state = NETCONN_CONNECT; + msg->conn->in_non_blocking_connect = msg->conn->non_blocking; + if (msg->conn->non_blocking) { + msg->err = ERR_INPROGRESS; + } else { + msg->conn->current_msg = msg; + /* sys_sem_signal() is called from do_connected (or err_tcp()), + * when the connection is established! */ + return; + } + } } break; #endif /* LWIP_TCP */ default: - LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; - sys_sem_signal(msg->conn->op_completed); }while(0)); + LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0)); break; + } } + sys_sem_signal(msg->conn->op_completed); } /** @@ -944,13 +987,14 @@ do_listen(struct api_msg_msg *msg) msg->err = ERR_CONN; if (msg->conn->pcb.tcp != NULL) { if (msg->conn->type == NETCONN_TCP) { - if (msg->conn->pcb.tcp->state == CLOSED) { + if (msg->conn->state == NETCONN_NONE) { #if TCP_LISTEN_BACKLOG struct tcp_pcb* lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog); #else /* TCP_LISTEN_BACKLOG */ struct tcp_pcb* lpcb = tcp_listen(msg->conn->pcb.tcp); #endif /* TCP_LISTEN_BACKLOG */ if (lpcb == NULL) { + /* in this case, the old pcb is still allocated */ msg->err = ERR_MEM; } else { /* delete the recvmbox and allocate the acceptmbox */ @@ -970,6 +1014,10 @@ do_listen(struct api_msg_msg *msg) msg->conn->pcb.tcp = lpcb; tcp_arg(msg->conn->pcb.tcp, msg->conn); tcp_accept(msg->conn->pcb.tcp, accept_function); + } else { + /* since the old pcb is already deallocated, free lpcb now */ + tcp_close(lpcb); + msg->conn->pcb.tcp = NULL; } } } @@ -1072,6 +1120,9 @@ do_writemore(struct netconn *conn) LWIP_ASSERT("conn != NULL", conn != NULL); LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE)); LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); + LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL); + LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len", + conn->write_offset < conn->current_msg->msg.w.len); dataptr = (u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset; diff = conn->current_msg->msg.w.len - conn->write_offset; @@ -1094,29 +1145,31 @@ do_writemore(struct netconn *conn) err = tcp_write(conn->pcb.tcp, dataptr, len, conn->current_msg->msg.w.apiflags); LWIP_ASSERT("do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len)); + + /* if OK or memory error, check available space */ + if (((err == ERR_OK) || (err == ERR_MEM)) && + ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) || + (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT))) { + /* The queued byte- or pbuf-count exceeds the configured low-water limit, + let select mark this pcb as non-writable. */ + API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); + } + if (err == ERR_OK) { conn->write_offset += len; if (conn->write_offset == conn->current_msg->msg.w.len) { /* everything was written */ write_finished = 1; conn->write_offset = 0; - /* API_EVENT might call tcp_tmr, so reset conn->state now */ - conn->state = NETCONN_NONE; - } - err = tcp_output_nagle(conn->pcb.tcp); - /* If the queued byte- or pbuf-count exceeds the configured low-water limit, - let select mark this pcb as non-writable. */ - if ((err == ERR_OK) && (tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) || - (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) { - API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); } + tcp_output_nagle(conn->pcb.tcp); } else if (err == ERR_MEM) { /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called we do NOT return to the application thread, since ERR_MEM is only a temporary error! */ /* tcp_enqueue returned ERR_MEM, try tcp_output anyway */ - err = tcp_output(conn->pcb.tcp); + tcp_output(conn->pcb.tcp); #if LWIP_TCPIP_CORE_LOCKING conn->write_delayed = 1; @@ -1162,11 +1215,14 @@ do_write(struct api_msg_msg *msg) } else { if (msg->conn->type == NETCONN_TCP) { #if LWIP_TCP - if (msg->conn->pcb.tcp != NULL) { + if (msg->conn->state != NETCONN_NONE) { + msg->err = ERR_INPROGRESS; + } else if (msg->conn->pcb.tcp != NULL) { msg->conn->state = NETCONN_WRITE; /* set all the variables used by do_writemore */ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && msg->conn->write_offset == 0); + LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0); msg->conn->current_msg = msg; msg->conn->write_offset = 0; #if LWIP_TCPIP_CORE_LOCKING @@ -1258,7 +1314,13 @@ void do_close(struct api_msg_msg *msg) { #if LWIP_TCP - if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) { + /* @todo: abort running write/connect? */ + if ((msg->conn->state != NETCONN_NONE) && (msg->conn->state != NETCONN_LISTEN)) { + /* this only happens for TCP netconns */ + LWIP_ASSERT("msg->conn->type == NETCONN_TCP", msg->conn->type == NETCONN_TCP); + msg->err = ERR_INPROGRESS; + } else if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) { + /* Drain and delete mboxes */ netconn_drain(msg->conn); LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && msg->conn->write_offset == 0); @@ -1266,12 +1328,13 @@ do_close(struct api_msg_msg *msg) msg->conn->current_msg = msg; do_close_internal(msg->conn); /* for tcp netconns, do_close_internal ACKs the message */ + return; } else #endif /* LWIP_TCP */ { msg->err = ERR_VAL; - sys_sem_signal(msg->conn->op_completed); } + sys_sem_signal(msg->conn->op_completed); } #if LWIP_IGMP diff --git a/src/api/err.c b/src/api/err.c index a90cb98c..6701c61e 100644 --- a/src/api/err.c +++ b/src/api/err.c @@ -44,18 +44,18 @@ static const char *err_strerr[] = { "Ok.", /* ERR_OK 0 */ "Out of memory error.", /* ERR_MEM -1 */ "Buffer error.", /* ERR_BUF -2 */ - "Timeout.", /* ERR_TIMEOUT -3 */ + "Timeout.", /* ERR_TIMEOUT -3 */ "Routing problem.", /* ERR_RTE -4 */ - "Connection aborted.", /* ERR_ABRT -5 */ - "Connection reset.", /* ERR_RST -6 */ - "Connection closed.", /* ERR_CLSD -7 */ - "Not connected.", /* ERR_CONN -8 */ - "Illegal value.", /* ERR_VAL -9 */ - "Illegal argument.", /* ERR_ARG -10 */ - "Address in use.", /* ERR_USE -11 */ - "Low-level netif error.", /* ERR_IF -12 */ - "Already connected.", /* ERR_ISCONN -13 */ - "Operation in progress." /* ERR_INPROGRESS -14 */ + "Operation in progress.", /* ERR_INPROGRESS -5 */ + "Illegal value.", /* ERR_VAL -6 */ + "Connection aborted.", /* ERR_ABRT -7 */ + "Connection reset.", /* ERR_RST -8 */ + "Connection closed.", /* ERR_CLSD -9 */ + "Not connected.", /* ERR_CONN -10 */ + "Illegal argument.", /* ERR_ARG -11 */ + "Address in use.", /* ERR_USE -12 */ + "Low-level netif error.", /* ERR_IF -13 */ + "Already connected.", /* ERR_ISCONN -14 */ }; /** diff --git a/src/api/sockets.c b/src/api/sockets.c index f1818bab..e32a294f 100644 --- a/src/api/sockets.c +++ b/src/api/sockets.c @@ -67,11 +67,9 @@ struct lwip_socket { /** number of times data was received, set by event_callback(), tested by the receive and select functions */ s16_t rcvevent; - /** number of times data was received, set by event_callback(), + /** number of times data was ACKed (free send buffer), set by event_callback(), tested by select */ u16_t sendevent; - /** socket flags (currently, only used for O_NONBLOCK) */ - u16_t flags; /** last error that occurred on this socket */ int err; }; @@ -130,16 +128,16 @@ static const int err_to_errno_table[] = { ENOBUFS, /* ERR_BUF -2 Buffer error. */ ETIMEDOUT, /* ERR_TIMEOUT -3 Timeout */ EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */ - ECONNABORTED, /* ERR_ABRT -5 Connection aborted. */ - ECONNRESET, /* ERR_RST -6 Connection reset. */ - ESHUTDOWN, /* ERR_CLSD -7 Connection closed. */ - ENOTCONN, /* ERR_CONN -8 Not connected. */ - EINVAL, /* ERR_VAL -9 Illegal value. */ - EIO, /* ERR_ARG -10 Illegal argument. */ - EADDRINUSE, /* ERR_USE -11 Address in use. */ - -1, /* ERR_IF -12 Low-level netif error */ - -1, /* ERR_ISCONN -13 Already connected. */ - EINPROGRESS /* ERR_INPROGRESS -14 Operation in progress */ + EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */ + EINVAL, /* ERR_VAL -6 Illegal value. */ + ECONNABORTED, /* ERR_ABRT -7 Connection aborted. */ + ECONNRESET, /* ERR_RST -8 Connection reset. */ + ESHUTDOWN, /* ERR_CLSD -9 Connection closed. */ + ENOTCONN, /* ERR_CONN -10 Not connected. */ + EIO, /* ERR_ARG -11 Illegal argument. */ + EADDRINUSE, /* ERR_USE -12 Address in use. */ + -1, /* ERR_IF -13 Low-level netif error */ + -1, /* ERR_ISCONN -14 Already connected. */ }; #define ERR_TO_ERRNO_TABLE_SIZE \ @@ -227,8 +225,8 @@ alloc_socket(struct netconn *newconn) sockets[i].lastdata = NULL; sockets[i].lastoffset = 0; sockets[i].rcvevent = 0; - sockets[i].sendevent = 1; /* TCP send buf is empty */ - sockets[i].flags = 0; + /* TCP sendbuf is empty, but not connected yet, so not yet writable */ + sockets[i].sendevent = (newconn->type == NETCONN_TCP ? 0 : 1); sockets[i].err = 0; sys_sem_signal(socksem); return i; @@ -260,7 +258,7 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) if (!sock) return -1; - if ((sock->flags & O_NONBLOCK) && (sock->rcvevent <= 0)) { + if ((sock->conn->non_blocking) && (sock->rcvevent <= 0)) { LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s)); sock_set_errno(sock, EWOULDBLOCK); return -1; @@ -494,7 +492,7 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags, buf = sock->lastdata; } else { /* If this is non-blocking call, then check first */ - if (((flags & MSG_DONTWAIT) || (sock->flags & O_NONBLOCK)) && + if (((flags & MSG_DONTWAIT) || (sock->conn->non_blocking)) && (sock->rcvevent <= 0)) { if (off > 0) { /* already received data, return that */ @@ -1958,10 +1956,10 @@ lwip_ioctl(int s, long cmd, void *argp) case FIONBIO: if (argp && *(u32_t*)argp) - sock->flags |= O_NONBLOCK; + sock->conn->non_blocking = 1; else - sock->flags &= ~O_NONBLOCK; - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, !!(sock->flags & O_NONBLOCK))); + sock->conn->non_blocking = 0; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, sock->conn->non_blocking)); sock_set_errno(sock, 0); return 0; diff --git a/src/include/lwip/api.h b/src/include/lwip/api.h index 6aa900db..0fcc51d2 100644 --- a/src/include/lwip/api.h +++ b/src/include/lwip/api.h @@ -61,6 +61,7 @@ extern "C" { #define NETCONNTYPE_GROUP(t) (t&0xF0) #define NETCONNTYPE_DATAGRAM(t) (t&0xE0) +/** Protocol family and type of the netconn */ enum netconn_type { NETCONN_INVALID = 0, /* NETCONN_TCP Group */ @@ -73,6 +74,8 @@ enum netconn_type { NETCONN_RAW = 0x40 }; +/** Current state of the netconn. Non-TCP netconns are always + * in state NETCONN_NONE! */ enum netconn_state { NETCONN_NONE, NETCONN_WRITE, @@ -81,6 +84,7 @@ enum netconn_state { NETCONN_CLOSE }; +/** Use to inform the callback function about changes */ enum netconn_evt { NETCONN_EVT_RCVPLUS, NETCONN_EVT_RCVMINUS, @@ -89,6 +93,7 @@ enum netconn_evt { }; #if LWIP_IGMP +/** Used for netconn_join_leave_group() */ enum netconn_igmp { NETCONN_JOIN, NETCONN_LEAVE @@ -152,10 +157,14 @@ struct netconn { #if LWIP_TCPIP_CORE_LOCKING /** TCP: when data passed to netconn_write doesn't fit into the send buffer, this temporarily stores whether to wake up the original application task - if data couldn't be sent in the first try. */ + if data couldn't be sent in the first try. @todo: combine in 'flags' */ u8_t write_delayed; #endif /* LWIP_TCPIP_CORE_LOCKING */ #endif /* LWIP_TCP */ + /** Should this netconn avoid blocking? @todo: combine in 'flags' */ + u8_t non_blocking; + /** Was the last connect action a non-blocking one? @todo: combine in 'flags' */ + u8_t in_non_blocking_connect; /** A callback function that is informed about events for this netconn */ netconn_callback callback; }; diff --git a/src/include/lwip/err.h b/src/include/lwip/err.h index 69676445..35a2e659 100644 --- a/src/include/lwip/err.h +++ b/src/include/lwip/err.h @@ -54,24 +54,22 @@ typedef LWIP_ERR_T err_t; #define ERR_BUF -2 /* Buffer error. */ #define ERR_TIMEOUT -3 /* Timeout. */ #define ERR_RTE -4 /* Routing problem. */ +#define ERR_INPROGRESS -5 /* Operation in progress */ +#define ERR_VAL -6 /* Illegal value. */ -#define ERR_IS_FATAL(e) ((e) < ERR_RTE) +#define ERR_IS_FATAL(e) ((e) < ERR_VAL) -#define ERR_ABRT -5 /* Connection aborted. */ -#define ERR_RST -6 /* Connection reset. */ -#define ERR_CLSD -7 /* Connection closed. */ -#define ERR_CONN -8 /* Not connected. */ +#define ERR_ABRT -7 /* Connection aborted. */ +#define ERR_RST -8 /* Connection reset. */ +#define ERR_CLSD -9 /* Connection closed. */ +#define ERR_CONN -10 /* Not connected. */ -#define ERR_VAL -9 /* Illegal value. */ +#define ERR_ARG -11 /* Illegal argument. */ -#define ERR_ARG -10 /* Illegal argument. */ +#define ERR_USE -12 /* Address in use. */ -#define ERR_USE -11 /* Address in use. */ - -#define ERR_IF -12 /* Low-level netif error */ -#define ERR_ISCONN -13 /* Already connected. */ - -#define ERR_INPROGRESS -14 /* Operation in progress */ +#define ERR_IF -13 /* Low-level netif error */ +#define ERR_ISCONN -14 /* Already connected. */ #ifdef LWIP_DEBUG diff --git a/src/include/lwip/sockets.h b/src/include/lwip/sockets.h index 7b52e151..3aa3a9b9 100644 --- a/src/include/lwip/sockets.h +++ b/src/include/lwip/sockets.h @@ -262,10 +262,6 @@ typedef struct ip_mreq { #define SIOCATMARK _IOR('s', 7, unsigned long) /* at oob mark? */ #endif -/* Socket flags: */ -#ifndef O_NONBLOCK -#define O_NONBLOCK 04000U -#endif /* FD_SET used for lwip_select */ #ifndef FD_SET