static err_t tcp_accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err) { struct socket * sock = (struct socket *) arg; debug_tcp_print("socket num %ld", get_sock_num(sock)); assert(err == ERR_OK && newpcb); assert(sock->flags & SOCK_FLG_OP_LISTENING); if (sock->flags & SOCK_FLG_OP_PENDING) { int ret; ret = tcp_do_accept(sock, &sock->mess, newpcb); sock_revive(sock, ret); sock->flags &= ~SOCK_FLG_OP_PENDING; if (ret == OK) { return ERR_OK; } /* in case of an error fall through */ } /* If we cannot accept rightaway we enqueue the connection for later */ debug_tcp_print("Enqueue connection sock %ld pcb %p\n", get_sock_num(sock), newpcb); if (sock_enqueue_data(sock, newpcb, 1) != OK) { tcp_abort(newpcb); return ERR_ABRT; } if (sock_select_read_set(sock)) sock_select_notify(sock); return ERR_OK; }
static err_t tcp_connected_callback(void *arg, struct tcp_pcb *tpcb, __unused err_t err) { struct socket * sock = (struct socket *) arg; debug_tcp_print("socket num %ld err %d", get_sock_num(sock), err); if (sock->pcb == NULL) { if (sock_select_set(sock)) sock_select_notify(sock); return ERR_OK; } assert((struct tcp_pcb *)sock->pcb == tpcb); tcp_sent(tpcb, tcp_sent_callback); tcp_recv(tpcb, tcp_recv_callback); sock_revive(sock, OK); sock->flags &= ~(SOCK_FLG_OP_PENDING | SOCK_FLG_OP_CONNECTING); /* revive does the sock_select_notify() for us */ return ERR_OK; }
static void tcp_error_callback(void *arg, err_t err) { int perr; struct socket * sock = (struct socket *) arg; debug_tcp_print("socket num %ld err %d", get_sock_num(sock), err); switch (err) { case ERR_RST: perr = ECONNREFUSED; break; case ERR_CLSD: perr = EPIPE; break; case ERR_CONN: perr = ENOTCONN; break; default: perr = EIO; } if (sock->flags & SOCK_FLG_OP_PENDING) { sock_revive(sock, perr); sock->flags &= ~SOCK_FLG_OP_PENDING; } else if (sock_select_set(sock)) sock_select_notify(sock); /* * When error callback is called the tcb either does not exist anymore * or is going to be deallocated soon after. We must not use the pcb * anymore */ sock->pcb = NULL; }
static void udp_recv_callback(void *arg, struct udp_pcb *pcb, struct pbuf *pbuf, ip_addr_t *addr, u16_t port) { struct socket * sock = (struct socket *) arg; struct udp_recv_data * data; debug_udp_print("socket num : %ld addr : %x port : %d\n", get_sock_num(sock), (unsigned int) addr->addr, port); if (sock->flags & SOCK_FLG_OP_PENDING) { /* we are resuming a suspended operation */ int ret; ret = udp_do_receive(sock, &sock->req, pcb, pbuf, addr, port); send_req_reply(&sock->req, ret); sock->flags &= ~SOCK_FLG_OP_PENDING; if (ret > 0) { pbuf_free(pbuf); return; } } /* Do not enqueue more data than allowed */ if (sock->recv_data_size > UDP_BUF_SIZE) { pbuf_free(pbuf); return; } /* * nobody is waiting for the data or an error occured above, we enqueue * the packet */ if (!(data = udp_recv_alloc())) { pbuf_free(pbuf); return; } data->ip = *addr; data->port = port; data->pbuf = pbuf; if (sock_enqueue_data(sock, data, data->pbuf->tot_len) != OK) { udp_recv_free(data); return; } /* * We don't need to notify when somebody is already waiting, reviving * read operation will do the trick for us. But we must announce new * data available here. */ if (sock_select_read_set(sock)) sock_select_notify(sock); }
static err_t tcp_sent_callback(void *arg, struct tcp_pcb *tpcb, u16_t len) { struct socket * sock = (struct socket *) arg; struct wbuf * wbuf; struct wbuf_chain * wc = (struct wbuf_chain *) sock->buf; unsigned snd_buf_len; int ret; debug_tcp_print("socket num %ld", get_sock_num(sock)); /* an error might have had happen */ if (sock->pcb == NULL) { if (sock_select_set(sock)) sock_select_notify(sock); return ERR_OK; } assert((struct tcp_pcb *)sock->pcb == tpcb); /* operation must have been canceled, do not send any other data */ if (!sock->flags & SOCK_FLG_OP_PENDING) return ERR_OK; wbuf = wbuf_ack_sent(sock, len); if (wbuf == NULL) { debug_tcp_print("all data acked, nothing more to send"); sock->flags &= ~SOCK_FLG_OP_WRITING; if (!(sock->flags & SOCK_FLG_OP_READING)) sock->flags &= ~SOCK_FLG_OP_PENDING; /* no reviving, we must notify. Write and read possible */ if (sock_select_rw_set(sock)) sock_select_notify(sock); return ERR_OK; } /* we have just freed some space, write will be accepted */ if (sock->buf_size < TCP_BUF_SIZE && sock_select_rw_set(sock)) { if (!(sock->flags & SOCK_FLG_OP_READING)) { sock->flags &= ~SOCK_FLG_OP_PENDING; sock_select_notify(sock); } } /* * Check if there is some space for new data, there should be, we just * got a confirmation that some data reached the other end of the * connection */ snd_buf_len = tcp_sndbuf(tpcb); assert(snd_buf_len > 0); debug_tcp_print("tcp can accept %d bytes", snd_buf_len); if (!wc->unsent) { debug_tcp_print("nothing to send"); return ERR_OK; } wbuf = wc->unsent; while (wbuf) { unsigned towrite; u8_t flgs = 0; towrite = (snd_buf_len < wbuf->rem_len ? snd_buf_len : wbuf->rem_len); wbuf->rem_len -= towrite; debug_tcp_print("data to send, sending %d", towrite); if (wbuf->rem_len || wbuf->next) flgs = TCP_WRITE_FLAG_MORE; ret = tcp_write(tpcb, wbuf->data + wbuf->written + wbuf->unacked, towrite, flgs); debug_tcp_print("%d bytes to tcp", towrite); /* tcp_output() is called once we return from this callback */ if (ret != ERR_OK) { debug_print("tcp_write() failed (%d), written %d" , ret, wbuf->written); sock->flags &= ~(SOCK_FLG_OP_PENDING | SOCK_FLG_OP_WRITING); /* no reviving, we must notify. Write and read possible */ if (sock_select_rw_set(sock)) sock_select_notify(sock); return ERR_OK; } wbuf->unacked += towrite; snd_buf_len -= towrite; debug_tcp_print("tcp still accepts %d bytes\n", snd_buf_len); if (snd_buf_len) { assert(wbuf->rem_len == 0); wbuf = wbuf->next; wc->unsent = wbuf; if (wbuf) debug_tcp_print("unsent %p remains %d\n", wbuf, wbuf->rem_len); else { debug_tcp_print("nothing to send"); } } else break; } return ERR_OK; }
static err_t tcp_recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *pbuf, err_t err) { int ret, enqueued = 0; struct socket * sock = (struct socket *) arg; debug_tcp_print("socket num %ld", get_sock_num(sock)); if (sock->pcb == NULL) { if (sock_select_set(sock)) sock_select_notify(sock); return ERR_OK; } assert((struct tcp_pcb *) sock->pcb == tpcb); if (err != ERR_OK) return ERR_OK; if (!pbuf) { debug_tcp_print("tcp stream closed on the remote side"); // sock->flags |= SOCK_FLG_CLOSED; /* wake up the reader and report EOF */ if (sock->flags & SOCK_FLG_OP_PENDING && sock->flags & SOCK_FLG_OP_READING && !(sock->flags & SOCK_FLG_OP_REVIVING)) { sock_revive(sock, 0); sock->flags &= ~(SOCK_FLG_OP_PENDING | SOCK_FLG_OP_READING); } #if 0 /* if there are any undelivered data, drop them */ sock_dequeue_data_all(sock, tcp_recv_free); tcp_abandon(tpcb, 0); sock->pcb = NULL; #endif return ERR_OK; } /* * FIXME we always enqueue the data first. If the head is empty and read * operation is pending we could try to deliver immeditaly without * enqueueing */ if (enqueue_rcv_data(sock, pbuf) == ERR_OK) enqueued = 1; /* * Deliver data if there is a pending read operation, otherwise notify * select if the socket is being monitored */ if (sock->flags & SOCK_FLG_OP_PENDING) { if (sock->flags & SOCK_FLG_OP_READING) { ret = read_from_tcp(sock, &sock->mess); debug_tcp_print("read op finished"); sock_revive(sock, ret); sock->flags &= ~(SOCK_FLG_OP_PENDING | SOCK_FLG_OP_READING); } } else if (!(sock->flags & SOCK_FLG_OP_WRITING) && sock_select_rw_set(sock)) sock_select_notify(sock); /* perhaps we have deliverd some data to user, try to enqueue again */ if (!enqueued) { return enqueue_rcv_data(sock, pbuf); } else return ERR_OK; }