Add non-blocking support for connect (partly from patch #6860) plus many cleanups in socket & netconn API

This commit is contained in:
goldsimon 2010-01-29 22:09:31 +00:00
parent 1dd8300e69
commit e58f4c567a
8 changed files with 203 additions and 132 deletions

View File

@ -19,6 +19,11 @@ HISTORY
++ New features: ++ 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 2010-01-27: Simon Goldschmidt
* opt.h, tcp.h, init.c, api_msg.c: Added TCP_SNDQUEUELOWAT corresponding * opt.h, tcp.h, init.c, api_msg.c: Added TCP_SNDQUEUELOWAT corresponding
to TCP_SNDLOWAT and added tcp_sndqueuelen() - this fixes bug #28605 to TCP_SNDLOWAT and added tcp_sndqueuelen() - this fixes bug #28605

View File

@ -111,7 +111,6 @@ netconn_delete(struct netconn *conn)
msg.msg.conn = conn; msg.msg.conn = conn;
tcpip_apimsg(&msg); tcpip_apimsg(&msg);
conn->pcb.tcp = NULL;
netconn_free(conn); netconn_free(conn);
/* don't care for return value of do_delconn since it only calls void functions */ /* 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", (conn != NULL), return ERR_ARG;);
LWIP_ERROR("netconn_write: invalid conn->type", (conn->type == NETCONN_TCP), return ERR_VAL;); 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.function = do_write;
msg.msg.conn = conn; msg.msg.conn = conn;

View File

@ -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); buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
if (buf == NULL) { if (buf == NULL) {
pbuf_free(q); 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->addr = &(((struct ip_hdr*)(q->payload))->src);
buf->port = pcb->protocol; buf->port = pcb->protocol;
len = q->tot_len;
if (sys_mbox_trypost(conn->recvmbox, buf) != ERR_OK) { if (sys_mbox_trypost(conn->recvmbox, buf) != ERR_OK) {
netbuf_delete(buf); netbuf_delete(buf);
return 0; return 0;
} else { } else {
SYS_ARCH_INC(conn->recv_avail, q->tot_len); SYS_ARCH_INC(conn->recv_avail, len);
/* Register event with callback */ /* 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 netbuf *buf;
struct netconn *conn; struct netconn *conn;
u16_t len;
#if LWIP_SO_RCVBUF #if LWIP_SO_RCVBUF
int recv_avail; int recv_avail;
#endif /* LWIP_SO_RCVBUF */ #endif /* LWIP_SO_RCVBUF */
@ -179,13 +182,14 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
#endif /* LWIP_NETBUF_RECVINFO */ #endif /* LWIP_NETBUF_RECVINFO */
} }
len = p->tot_len;
if (sys_mbox_trypost(conn->recvmbox, buf) != ERR_OK) { if (sys_mbox_trypost(conn->recvmbox, buf) != ERR_OK) {
netbuf_delete(buf); netbuf_delete(buf);
return; return;
} else { } else {
SYS_ARCH_INC(conn->recv_avail, p->tot_len); SYS_ARCH_INC(conn->recv_avail, len);
/* Register event with callback */ /* Register event with callback */
API_EVENT(conn, NETCONN_EVT_RCVPLUS, p->tot_len); API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
} }
} }
#endif /* LWIP_UDP */ #endif /* LWIP_UDP */
@ -220,20 +224,24 @@ recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
} }
return ERR_OK; 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! */ /* don't overwrite fatal errors! */
NETCONN_SET_SAFE_ERR(conn, err); NETCONN_SET_SAFE_ERR(conn, err);
if (p != NULL) { if (p != NULL) {
len = p->tot_len; len = p->tot_len;
SYS_ARCH_INC(conn->recv_avail, len);
} else { } else {
len = 0; len = 0;
} }
if (sys_mbox_trypost(conn->recvmbox, p) != ERR_OK) { 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; return ERR_MEM;
} else { } else {
SYS_ARCH_INC(conn->recv_avail, len);
/* Register event with callback */ /* Register event with callback */
API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); 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) { } else if (conn->state == NETCONN_CLOSE) {
do_close_internal(conn); do_close_internal(conn);
} }
/* @todo: implement connect timeout here? */
return ERR_OK; return ERR_OK;
} }
@ -285,7 +294,6 @@ sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
LWIP_ASSERT("conn != NULL", (conn != NULL)); LWIP_ASSERT("conn != NULL", (conn != NULL));
if (conn->state == NETCONN_WRITE) { if (conn->state == NETCONN_WRITE) {
LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
do_writemore(conn); do_writemore(conn);
} else if (conn->state == NETCONN_CLOSE) { } else if (conn->state == NETCONN_CLOSE) {
do_close_internal(conn); do_close_internal(conn);
@ -327,7 +335,7 @@ err_tcp(void *arg, err_t err)
conn->last_err = err; conn->last_err = err;
SYS_ARCH_UNPROTECT(lev); 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; old_state = conn->state;
conn->state = NETCONN_NONE; conn->state = NETCONN_NONE;
@ -347,13 +355,17 @@ err_tcp(void *arg, err_t err)
(old_state == NETCONN_CONNECT)) { (old_state == NETCONN_CONNECT)) {
/* calling do_writemore/do_close_internal is not necessary /* calling do_writemore/do_close_internal is not necessary
since the pcb has already been deleted! */ 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 */ if (!was_nonblocking_connect) {
LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); /* set error return code */
conn->current_msg->err = err; LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
conn->current_msg = NULL; conn->current_msg->err = err;
/* wake up the waiting task */ conn->current_msg = NULL;
sys_sem_signal(conn->op_completed); /* wake up the waiting task */
sys_sem_signal(conn->op_completed);
}
} else { } else {
LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL); 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; newconn->last_err = err;
if (sys_mbox_trypost(conn->acceptmbox, newconn) != ERR_OK) { 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! */ so do nothing here! */
newconn->pcb.tcp = NULL; newconn->pcb.tcp = NULL;
/* no need to drain since we know the recvmbox is empty. */ /* 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 #if LWIP_SO_RCVBUF
conn->recv_bufsize = RECV_BUFSIZE_DEFAULT; conn->recv_bufsize = RECV_BUFSIZE_DEFAULT;
#endif /* LWIP_SO_RCVBUF */ #endif /* LWIP_SO_RCVBUF */
conn->non_blocking = 0;
conn->in_non_blocking_connect = 0;
return conn; return conn;
} }
@ -707,8 +721,8 @@ do_close_internal(struct netconn *conn)
conn->state = NETCONN_NONE; conn->state = NETCONN_NONE;
/* Set back some callback pointers as conn is going away */ /* Set back some callback pointers as conn is going away */
conn->pcb.tcp = NULL; conn->pcb.tcp = NULL;
/* Trigger select() in socket layer. This send should something else so the /* @todo: this lets select make the socket readable and writable,
errorfd is set, not the read and write fd! */ which is wrong! errfd instead? */
API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
/* wake up the application task */ /* wake up the application task */
@ -736,45 +750,52 @@ do_close_internal(struct netconn *conn)
void void
do_delconn(struct api_msg_msg *msg) do_delconn(struct api_msg_msg *msg)
{ {
/* Drain and delete mboxes */ /* @todo TCP: abort running write/connect? */
netconn_drain(msg->conn); 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 #if LWIP_RAW
case NETCONN_RAW: case NETCONN_RAW:
raw_remove(msg->conn->pcb.raw); raw_remove(msg->conn->pcb.raw);
break; break;
#endif /* LWIP_RAW */ #endif /* LWIP_RAW */
#if LWIP_UDP #if LWIP_UDP
case NETCONN_UDP: case NETCONN_UDP:
msg->conn->pcb.udp->recv_arg = NULL; msg->conn->pcb.udp->recv_arg = NULL;
udp_remove(msg->conn->pcb.udp); udp_remove(msg->conn->pcb.udp);
break; break;
#endif /* LWIP_UDP */ #endif /* LWIP_UDP */
#if LWIP_TCP #if LWIP_TCP
case NETCONN_TCP: case NETCONN_TCP:
LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
msg->conn->write_offset == 0); msg->conn->write_offset == 0);
msg->conn->state = NETCONN_CLOSE; msg->conn->state = NETCONN_CLOSE;
msg->conn->current_msg = msg; msg->conn->current_msg = msg;
do_close_internal(msg->conn); do_close_internal(msg->conn);
/* API_EVENT is called inside do_close_internal, before releasing /* API_EVENT is called inside do_close_internal, before releasing
the application thread, so we can return at this point! */ the application thread, so we can return at this point! */
return; return;
#endif /* LWIP_TCP */ #endif /* LWIP_TCP */
default: default:
break; 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) { if (msg->conn->op_completed != SYS_SEM_NULL) {
sys_sem_signal(msg->conn->op_completed); 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) do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
{ {
struct netconn *conn; struct netconn *conn;
int was_blocking;
LWIP_UNUSED_ARG(pcb); 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->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)) { if ((conn->type == NETCONN_TCP) && (err == ERR_OK)) {
setup_tcp(conn); setup_tcp(conn);
} }
was_blocking = !conn->in_non_blocking_connect;
conn->in_non_blocking_connect = 0;
conn->current_msg = NULL; conn->current_msg = NULL;
conn->state = NETCONN_NONE; 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; return ERR_OK;
} }
#endif /* LWIP_TCP */ #endif /* LWIP_TCP */
@ -866,44 +905,48 @@ do_connect(struct api_msg_msg *msg)
if (msg->conn->pcb.tcp == NULL) { if (msg->conn->pcb.tcp == NULL) {
/* This may happen when calling netconn_connect() a second time */ /* This may happen when calling netconn_connect() a second time */
msg->err = ERR_CLSD; msg->err = ERR_CLSD;
sys_sem_signal(msg->conn->op_completed); } else {
return; switch (NETCONNTYPE_GROUP(msg->conn->type)) {
}
switch (NETCONNTYPE_GROUP(msg->conn->type)) {
#if LWIP_RAW #if LWIP_RAW
case NETCONN_RAW: case NETCONN_RAW:
msg->err = raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr); msg->err = raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr);
sys_sem_signal(msg->conn->op_completed);
break; break;
#endif /* LWIP_RAW */ #endif /* LWIP_RAW */
#if LWIP_UDP #if LWIP_UDP
case NETCONN_UDP: case NETCONN_UDP:
msg->err = udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port); msg->err = udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port);
sys_sem_signal(msg->conn->op_completed);
break; break;
#endif /* LWIP_UDP */ #endif /* LWIP_UDP */
#if LWIP_TCP #if LWIP_TCP
case NETCONN_TCP: case NETCONN_TCP:
setup_tcp(msg->conn); /* Prevent connect while doing any other action. */
msg->err = tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port, if (msg->conn->state != NETCONN_NONE) {
do_connected); msg->err = ERR_ISCONN;
/* 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;
} else { } else {
/* tcp_connect failed, so do_connected will not be called: return now */ setup_tcp(msg->conn);
sys_sem_signal(msg->conn->op_completed); 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; break;
#endif /* LWIP_TCP */ #endif /* LWIP_TCP */
default: default:
LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0));
sys_sem_signal(msg->conn->op_completed); }while(0));
break; break;
}
} }
sys_sem_signal(msg->conn->op_completed);
} }
/** /**
@ -944,13 +987,14 @@ do_listen(struct api_msg_msg *msg)
msg->err = ERR_CONN; msg->err = ERR_CONN;
if (msg->conn->pcb.tcp != NULL) { if (msg->conn->pcb.tcp != NULL) {
if (msg->conn->type == NETCONN_TCP) { if (msg->conn->type == NETCONN_TCP) {
if (msg->conn->pcb.tcp->state == CLOSED) { if (msg->conn->state == NETCONN_NONE) {
#if TCP_LISTEN_BACKLOG #if TCP_LISTEN_BACKLOG
struct tcp_pcb* lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog); struct tcp_pcb* lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog);
#else /* TCP_LISTEN_BACKLOG */ #else /* TCP_LISTEN_BACKLOG */
struct tcp_pcb* lpcb = tcp_listen(msg->conn->pcb.tcp); struct tcp_pcb* lpcb = tcp_listen(msg->conn->pcb.tcp);
#endif /* TCP_LISTEN_BACKLOG */ #endif /* TCP_LISTEN_BACKLOG */
if (lpcb == NULL) { if (lpcb == NULL) {
/* in this case, the old pcb is still allocated */
msg->err = ERR_MEM; msg->err = ERR_MEM;
} else { } else {
/* delete the recvmbox and allocate the acceptmbox */ /* delete the recvmbox and allocate the acceptmbox */
@ -970,6 +1014,10 @@ do_listen(struct api_msg_msg *msg)
msg->conn->pcb.tcp = lpcb; msg->conn->pcb.tcp = lpcb;
tcp_arg(msg->conn->pcb.tcp, msg->conn); tcp_arg(msg->conn->pcb.tcp, msg->conn);
tcp_accept(msg->conn->pcb.tcp, accept_function); 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 != NULL", conn != NULL);
LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE)); LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE));
LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); 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; dataptr = (u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset;
diff = conn->current_msg->msg.w.len - 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); 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)); 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) { if (err == ERR_OK) {
conn->write_offset += len; conn->write_offset += len;
if (conn->write_offset == conn->current_msg->msg.w.len) { if (conn->write_offset == conn->current_msg->msg.w.len) {
/* everything was written */ /* everything was written */
write_finished = 1; write_finished = 1;
conn->write_offset = 0; 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) { } else if (err == ERR_MEM) {
/* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called /* 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 we do NOT return to the application thread, since ERR_MEM is
only a temporary error! */ only a temporary error! */
/* tcp_enqueue returned ERR_MEM, try tcp_output anyway */ /* 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 #if LWIP_TCPIP_CORE_LOCKING
conn->write_delayed = 1; conn->write_delayed = 1;
@ -1162,11 +1215,14 @@ do_write(struct api_msg_msg *msg)
} else { } else {
if (msg->conn->type == NETCONN_TCP) { if (msg->conn->type == NETCONN_TCP) {
#if LWIP_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; msg->conn->state = NETCONN_WRITE;
/* set all the variables used by do_writemore */ /* set all the variables used by do_writemore */
LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
msg->conn->write_offset == 0); msg->conn->write_offset == 0);
LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0);
msg->conn->current_msg = msg; msg->conn->current_msg = msg;
msg->conn->write_offset = 0; msg->conn->write_offset = 0;
#if LWIP_TCPIP_CORE_LOCKING #if LWIP_TCPIP_CORE_LOCKING
@ -1258,7 +1314,13 @@ void
do_close(struct api_msg_msg *msg) do_close(struct api_msg_msg *msg)
{ {
#if LWIP_TCP #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); netconn_drain(msg->conn);
LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
msg->conn->write_offset == 0); msg->conn->write_offset == 0);
@ -1266,12 +1328,13 @@ do_close(struct api_msg_msg *msg)
msg->conn->current_msg = msg; msg->conn->current_msg = msg;
do_close_internal(msg->conn); do_close_internal(msg->conn);
/* for tcp netconns, do_close_internal ACKs the message */ /* for tcp netconns, do_close_internal ACKs the message */
return;
} else } else
#endif /* LWIP_TCP */ #endif /* LWIP_TCP */
{ {
msg->err = ERR_VAL; msg->err = ERR_VAL;
sys_sem_signal(msg->conn->op_completed);
} }
sys_sem_signal(msg->conn->op_completed);
} }
#if LWIP_IGMP #if LWIP_IGMP

View File

@ -44,18 +44,18 @@ static const char *err_strerr[] = {
"Ok.", /* ERR_OK 0 */ "Ok.", /* ERR_OK 0 */
"Out of memory error.", /* ERR_MEM -1 */ "Out of memory error.", /* ERR_MEM -1 */
"Buffer error.", /* ERR_BUF -2 */ "Buffer error.", /* ERR_BUF -2 */
"Timeout.", /* ERR_TIMEOUT -3 */ "Timeout.", /* ERR_TIMEOUT -3 */
"Routing problem.", /* ERR_RTE -4 */ "Routing problem.", /* ERR_RTE -4 */
"Connection aborted.", /* ERR_ABRT -5 */ "Operation in progress.", /* ERR_INPROGRESS -5 */
"Connection reset.", /* ERR_RST -6 */ "Illegal value.", /* ERR_VAL -6 */
"Connection closed.", /* ERR_CLSD -7 */ "Connection aborted.", /* ERR_ABRT -7 */
"Not connected.", /* ERR_CONN -8 */ "Connection reset.", /* ERR_RST -8 */
"Illegal value.", /* ERR_VAL -9 */ "Connection closed.", /* ERR_CLSD -9 */
"Illegal argument.", /* ERR_ARG -10 */ "Not connected.", /* ERR_CONN -10 */
"Address in use.", /* ERR_USE -11 */ "Illegal argument.", /* ERR_ARG -11 */
"Low-level netif error.", /* ERR_IF -12 */ "Address in use.", /* ERR_USE -12 */
"Already connected.", /* ERR_ISCONN -13 */ "Low-level netif error.", /* ERR_IF -13 */
"Operation in progress." /* ERR_INPROGRESS -14 */ "Already connected.", /* ERR_ISCONN -14 */
}; };
/** /**

View File

@ -67,11 +67,9 @@ struct lwip_socket {
/** number of times data was received, set by event_callback(), /** number of times data was received, set by event_callback(),
tested by the receive and select functions */ tested by the receive and select functions */
s16_t rcvevent; 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 */ tested by select */
u16_t sendevent; u16_t sendevent;
/** socket flags (currently, only used for O_NONBLOCK) */
u16_t flags;
/** last error that occurred on this socket */ /** last error that occurred on this socket */
int err; int err;
}; };
@ -130,16 +128,16 @@ static const int err_to_errno_table[] = {
ENOBUFS, /* ERR_BUF -2 Buffer error. */ ENOBUFS, /* ERR_BUF -2 Buffer error. */
ETIMEDOUT, /* ERR_TIMEOUT -3 Timeout */ ETIMEDOUT, /* ERR_TIMEOUT -3 Timeout */
EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */ EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */
ECONNABORTED, /* ERR_ABRT -5 Connection aborted. */ EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */
ECONNRESET, /* ERR_RST -6 Connection reset. */ EINVAL, /* ERR_VAL -6 Illegal value. */
ESHUTDOWN, /* ERR_CLSD -7 Connection closed. */ ECONNABORTED, /* ERR_ABRT -7 Connection aborted. */
ENOTCONN, /* ERR_CONN -8 Not connected. */ ECONNRESET, /* ERR_RST -8 Connection reset. */
EINVAL, /* ERR_VAL -9 Illegal value. */ ESHUTDOWN, /* ERR_CLSD -9 Connection closed. */
EIO, /* ERR_ARG -10 Illegal argument. */ ENOTCONN, /* ERR_CONN -10 Not connected. */
EADDRINUSE, /* ERR_USE -11 Address in use. */ EIO, /* ERR_ARG -11 Illegal argument. */
-1, /* ERR_IF -12 Low-level netif error */ EADDRINUSE, /* ERR_USE -12 Address in use. */
-1, /* ERR_ISCONN -13 Already connected. */ -1, /* ERR_IF -13 Low-level netif error */
EINPROGRESS /* ERR_INPROGRESS -14 Operation in progress */ -1, /* ERR_ISCONN -14 Already connected. */
}; };
#define ERR_TO_ERRNO_TABLE_SIZE \ #define ERR_TO_ERRNO_TABLE_SIZE \
@ -227,8 +225,8 @@ alloc_socket(struct netconn *newconn)
sockets[i].lastdata = NULL; sockets[i].lastdata = NULL;
sockets[i].lastoffset = 0; sockets[i].lastoffset = 0;
sockets[i].rcvevent = 0; sockets[i].rcvevent = 0;
sockets[i].sendevent = 1; /* TCP send buf is empty */ /* TCP sendbuf is empty, but not connected yet, so not yet writable */
sockets[i].flags = 0; sockets[i].sendevent = (newconn->type == NETCONN_TCP ? 0 : 1);
sockets[i].err = 0; sockets[i].err = 0;
sys_sem_signal(socksem); sys_sem_signal(socksem);
return i; return i;
@ -260,7 +258,7 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
if (!sock) if (!sock)
return -1; 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)); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s));
sock_set_errno(sock, EWOULDBLOCK); sock_set_errno(sock, EWOULDBLOCK);
return -1; return -1;
@ -494,7 +492,7 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags,
buf = sock->lastdata; buf = sock->lastdata;
} else { } else {
/* If this is non-blocking call, then check first */ /* 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)) { (sock->rcvevent <= 0)) {
if (off > 0) { if (off > 0) {
/* already received data, return that */ /* already received data, return that */
@ -1958,10 +1956,10 @@ lwip_ioctl(int s, long cmd, void *argp)
case FIONBIO: case FIONBIO:
if (argp && *(u32_t*)argp) if (argp && *(u32_t*)argp)
sock->flags |= O_NONBLOCK; sock->conn->non_blocking = 1;
else else
sock->flags &= ~O_NONBLOCK; sock->conn->non_blocking = 0;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, !!(sock->flags & O_NONBLOCK))); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, sock->conn->non_blocking));
sock_set_errno(sock, 0); sock_set_errno(sock, 0);
return 0; return 0;

View File

@ -61,6 +61,7 @@ extern "C" {
#define NETCONNTYPE_GROUP(t) (t&0xF0) #define NETCONNTYPE_GROUP(t) (t&0xF0)
#define NETCONNTYPE_DATAGRAM(t) (t&0xE0) #define NETCONNTYPE_DATAGRAM(t) (t&0xE0)
/** Protocol family and type of the netconn */
enum netconn_type { enum netconn_type {
NETCONN_INVALID = 0, NETCONN_INVALID = 0,
/* NETCONN_TCP Group */ /* NETCONN_TCP Group */
@ -73,6 +74,8 @@ enum netconn_type {
NETCONN_RAW = 0x40 NETCONN_RAW = 0x40
}; };
/** Current state of the netconn. Non-TCP netconns are always
* in state NETCONN_NONE! */
enum netconn_state { enum netconn_state {
NETCONN_NONE, NETCONN_NONE,
NETCONN_WRITE, NETCONN_WRITE,
@ -81,6 +84,7 @@ enum netconn_state {
NETCONN_CLOSE NETCONN_CLOSE
}; };
/** Use to inform the callback function about changes */
enum netconn_evt { enum netconn_evt {
NETCONN_EVT_RCVPLUS, NETCONN_EVT_RCVPLUS,
NETCONN_EVT_RCVMINUS, NETCONN_EVT_RCVMINUS,
@ -89,6 +93,7 @@ enum netconn_evt {
}; };
#if LWIP_IGMP #if LWIP_IGMP
/** Used for netconn_join_leave_group() */
enum netconn_igmp { enum netconn_igmp {
NETCONN_JOIN, NETCONN_JOIN,
NETCONN_LEAVE NETCONN_LEAVE
@ -152,10 +157,14 @@ struct netconn {
#if LWIP_TCPIP_CORE_LOCKING #if LWIP_TCPIP_CORE_LOCKING
/** TCP: when data passed to netconn_write doesn't fit into the send buffer, /** 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 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; u8_t write_delayed;
#endif /* LWIP_TCPIP_CORE_LOCKING */ #endif /* LWIP_TCPIP_CORE_LOCKING */
#endif /* LWIP_TCP */ #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 */ /** A callback function that is informed about events for this netconn */
netconn_callback callback; netconn_callback callback;
}; };

View File

@ -54,24 +54,22 @@ typedef LWIP_ERR_T err_t;
#define ERR_BUF -2 /* Buffer error. */ #define ERR_BUF -2 /* Buffer error. */
#define ERR_TIMEOUT -3 /* Timeout. */ #define ERR_TIMEOUT -3 /* Timeout. */
#define ERR_RTE -4 /* Routing problem. */ #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_ABRT -7 /* Connection aborted. */
#define ERR_RST -6 /* Connection reset. */ #define ERR_RST -8 /* Connection reset. */
#define ERR_CLSD -7 /* Connection closed. */ #define ERR_CLSD -9 /* Connection closed. */
#define ERR_CONN -8 /* Not connected. */ #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 -13 /* Low-level netif error */
#define ERR_ISCONN -14 /* Already connected. */
#define ERR_IF -12 /* Low-level netif error */
#define ERR_ISCONN -13 /* Already connected. */
#define ERR_INPROGRESS -14 /* Operation in progress */
#ifdef LWIP_DEBUG #ifdef LWIP_DEBUG

View File

@ -262,10 +262,6 @@ typedef struct ip_mreq {
#define SIOCATMARK _IOR('s', 7, unsigned long) /* at oob mark? */ #define SIOCATMARK _IOR('s', 7, unsigned long) /* at oob mark? */
#endif #endif
/* Socket flags: */
#ifndef O_NONBLOCK
#define O_NONBLOCK 04000U
#endif
/* FD_SET used for lwip_select */ /* FD_SET used for lwip_select */
#ifndef FD_SET #ifndef FD_SET