int net_send(spdid_t spdid, net_connection_t nc, void *data, int sz) { struct intern_connection *ic; u16_t tid = cos_get_thd_id(); int ret = sz; // if (!cos_argreg_buff_intern(data, sz)) return -EFAULT; if (!net_conn_valid(nc)) return -EINVAL; if (sz > MAX_SEND) return -EMSGSIZE; // NET_LOCK_TAKE(); ic = net_conn_get_internal(nc); if (NULL == ic) { ret = -EINVAL; goto err; } if (tid != ic->tid) { /* printc("tid %d ic->tid %d\n", tid, ic->tid); */ // Jiguo::::CRA case a different thread reads, so no return /* ret = -EPERM; */ /* goto err; */ } switch (ic->conn_type) { case UDP: { struct udp_pcb *up; struct pbuf *p; /* There's no blocking in the UDP case, so this is simple */ up = ic->conn.up; p = pbuf_alloc(PBUF_TRANSPORT, sz, PBUF_ROM); if (NULL == p) { ret = -ENOMEM; goto err; } p->payload = data; if (ERR_OK != udp_send(up, p)) { pbuf_free(p); /* IP/port must not be set */ ret = -ENOTCONN; goto err; } pbuf_free(p); break; } case TCP: { struct tcp_pcb *tp; #define TCP_SEND_COPY #ifdef TCP_SEND_COPY void *d; struct packet_queue *pq; #endif tp = ic->conn.tp; if (tcp_sndbuf(tp) < sz) { ret = 0; break; } #ifdef TCP_SEND_COPY pq = malloc(sizeof(struct packet_queue) + sz); if (unlikely(NULL == pq)) { ret = -ENOMEM; goto err; } #ifdef TEST_TIMING pq->ts_start = timing_record(APP_PROC, ic->ts_start); #endif pq->headers = NULL; d = net_packet_data(pq); memcpy(d, data, sz); if (ERR_OK != (ret = tcp_write(tp, d, sz, 0))) { #else if (ERR_OK != (ret = tcp_write(tp, data, sz, TCP_WRITE_FLAG_COPY))) { #endif free(pq); printc("tcp_write returned %d (sz %d, tcp_sndbuf %d, ERR_MEM: %d)", ret, sz, tcp_sndbuf(tp), ERR_MEM); BUG(); } /* No implementation of nagle's algorithm yet. Send * out the packet immediately if possible. */ if (ERR_OK != (ret = tcp_output(tp))) { printc("tcp_output returned %d, ERR_MEM: %d", ret, ERR_MEM); BUG(); } ret = sz; break; } case TCP_CLOSED: ret = -EPIPE; break; default: BUG(); } err: // NET_LOCK_RELEASE(); return ret; } /************************ LWIP integration: **************************/ struct ip_addr ip, mask, gw; struct netif cos_if; static void cos_net_interrupt(char *packet, int sz) { void *d; int len; struct pbuf *p; struct ip_hdr *ih; struct packet_queue *pq; #ifdef TEST_TIMING unsigned long long ts; #endif // printc(">>> %d\n", net_lock.lock_id); NET_LOCK_TAKE(); // printc("<<< %d\n", net_lock.lock_id); assert(packet); ih = (struct ip_hdr*)packet; if (unlikely(4 != IPH_V(ih))) goto done; len = ntohs(IPH_LEN(ih)); if (unlikely(len != sz || len > MTU)) { printc("len %d != %d or > %d", len, sz, MTU); goto done; } p = pbuf_alloc(PBUF_IP, len, PBUF_ROM); if (unlikely(!p)) { prints("OOM in interrupt: allocation of pbuf failed.\n"); goto done; } /* For now, we're going to do an additional copy. Currently, * packets should be small, so this shouldn't hurt that badly. * This is done because 1) we are freeing the packet * elsewhere, 2) we want to malloc some (small) packets to * save space and free up the ring buffers, 3) it is difficult * to know in (1) which deallocation method (free or return to * ring buff) to use */ pq = malloc(len + sizeof(struct packet_queue)); if (unlikely(NULL == pq)) { printc("OOM in interrupt: allocation of packet data (%d bytes) failed.\n", len); pbuf_free(p); goto done; } pq->headers = d = net_packet_data(pq); #ifdef TEST_TIMING #ifdef TCP_SEND_COPY ts = pq->ts_start = timing_timestamp(); #endif #endif memcpy(d, packet, len); p->payload = p->alloc_track = d; /* hand off packet ownership here... */ if (ERR_OK != cos_if.input(p, &cos_if)) { prints("net: failure in IP input."); pbuf_free(p); goto done; } #ifdef TEST_TIMING timing_record(UPCALL_PROC, ts); #endif done: NET_LOCK_RELEASE(); return; }
/** * See if more data needs to be written from a previous call to netconn_write. * Called initially from do_write. If the first call can't send all data * (because of low memory or empty send-buffer), this function is called again * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the * blocking application thread (waiting in netconn_write) is released. * * @param conn netconn (that is currently in state NETCONN_WRITE) to process * @return ERR_OK * ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished */ static err_t do_writemore(struct netconn *conn) { err_t err; void *dataptr; u16_t len, available; u8_t write_finished = 0; size_t diff; u8_t dontblock = netconn_is_nonblocking(conn) || (conn->current_msg->msg.w.apiflags & NETCONN_DONTBLOCK); u8_t apiflags = conn->current_msg->msg.w.apiflags; 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); #if LWIP_SO_SNDTIMEO if ((conn->send_timeout != 0) && ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) { write_finished = 1; if (conn->write_offset == 0) { /* nothing has been written */ err = ERR_WOULDBLOCK; conn->current_msg->msg.w.len = 0; } else { /* partial write */ err = ERR_OK; conn->current_msg->msg.w.len = conn->write_offset; } } else #endif /* LWIP_SO_SNDTIMEO */ { dataptr = (u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset; diff = conn->current_msg->msg.w.len - conn->write_offset; if (diff > 0xffffUL) { /* max_u16_t */ len = 0xffff; #if LWIP_TCPIP_CORE_LOCKING conn->flags |= NETCONN_FLAG_WRITE_DELAYED; #endif apiflags |= TCP_WRITE_FLAG_MORE; } else { len = (u16_t)diff; } available = tcp_sndbuf(conn->pcb.tcp); if (available < len) { /* don't try to write more than sendbuf */ len = available; if (dontblock){ if (!len) { err = ERR_WOULDBLOCK; goto err_mem; } } else { #if LWIP_TCPIP_CORE_LOCKING conn->flags |= NETCONN_FLAG_WRITE_DELAYED; #endif apiflags |= TCP_WRITE_FLAG_MORE; } } LWIP_ASSERT("do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len)); err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags); /* if OK or memory error, check available space */ if ((err == ERR_OK) || (err == ERR_MEM)) { err_mem: if (dontblock && (len < conn->current_msg->msg.w.len)) { /* non-blocking write did not write everything: mark the pcb non-writable and let poll_tcp check writable space to mark the pcb writable again */ API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE; } else if ((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) || dontblock) { /* return sent length */ conn->current_msg->msg.w.len = conn->write_offset; /* everything was written */ write_finished = 1; conn->write_offset = 0; } tcp_output(conn->pcb.tcp); } else if ((err == ERR_MEM) && !dontblock) { /* 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_write returned ERR_MEM, try tcp_output anyway */ tcp_output(conn->pcb.tcp); #if LWIP_TCPIP_CORE_LOCKING conn->flags |= NETCONN_FLAG_WRITE_DELAYED; #endif } else { /* On errors != ERR_MEM, we don't try writing any more but return the error to the application thread. */ write_finished = 1; conn->current_msg->msg.w.len = 0; } } if (write_finished) { /* everything was written: set back connection state and back to application task */ conn->current_msg->err = err; conn->current_msg = NULL; conn->state = NETCONN_NONE; #if LWIP_TCPIP_CORE_LOCKING if ((conn->flags & NETCONN_FLAG_WRITE_DELAYED) != 0) #endif { sys_sem_signal(&conn->op_completed); } } #if LWIP_TCPIP_CORE_LOCKING else return ERR_MEM; #endif return ERR_OK; }
uint16_t tcp_sndbuf1(struct tcp_pcb *pcb) { return tcp_sndbuf(pcb); // tcp_sndbuf() is a macro }
/** * Receive callback function for UDP netconns. * Posts the packet to conn->recvmbox or deletes it on memory error. * * @see udp.h (struct udp_pcb.recv) for parameters */ static void recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) { struct netbuf *buf; struct netconn *conn; u16_t len; #if LWIP_SO_RCVBUF int recv_avail; #endif /* LWIP_SO_RCVBUF */ LWIP_UNUSED_ARG(pcb); /* only used for asserts... */ LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL); LWIP_ASSERT("recv_udp must have an argument", arg != NULL); conn = (struct netconn *)arg; LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb); #if LWIP_SO_RCVBUF SYS_ARCH_GET(conn->recv_avail, recv_avail); if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox) || ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) { #else /* LWIP_SO_RCVBUF */ if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox)) { #endif /* LWIP_SO_RCVBUF */ pbuf_free(p); return; } buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); if (buf == NULL) { pbuf_free(p); return; } else { buf->p = p; buf->ptr = p; ip_addr_set(&buf->addr, addr); buf->port = port; #if LWIP_NETBUF_RECVINFO { const struct ip_hdr* iphdr = ip_current_header(); /* get the UDP header - always in the first pbuf, ensured by udp_input */ const struct udp_hdr* udphdr = (void*)(((char*)iphdr) + IPH_LEN(iphdr)); #if LWIP_CHECKSUM_ON_COPY buf->flags = NETBUF_FLAG_DESTADDR; #endif /* LWIP_CHECKSUM_ON_COPY */ ip_addr_set(&buf->toaddr, ip_current_dest_addr()); buf->toport_chksum = udphdr->dest; } #endif /* LWIP_NETBUF_RECVINFO */ } len = p->tot_len; if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) { netbuf_delete(buf); return; } else { #if LWIP_SO_RCVBUF SYS_ARCH_INC(conn->recv_avail, len); #endif /* LWIP_SO_RCVBUF */ /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); } } #endif /* LWIP_UDP */ #if LWIP_TCP /** * Receive callback function for TCP netconns. * Posts the packet to conn->recvmbox, but doesn't delete it on errors. * * @see tcp.h (struct tcp_pcb.recv) for parameters and return value */ static err_t recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { struct netconn *conn; u16_t len; LWIP_UNUSED_ARG(pcb); LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL); LWIP_ASSERT("recv_tcp must have an argument", arg != NULL); conn = (struct netconn *)arg; LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb); if (conn == NULL) { return ERR_VAL; } if (!sys_mbox_valid(&conn->recvmbox)) { /* recvmbox already deleted */ if (p != NULL) { tcp_recved(pcb, p->tot_len); pbuf_free(p); } 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; } 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 { #if LWIP_SO_RCVBUF SYS_ARCH_INC(conn->recv_avail, len); #endif /* LWIP_SO_RCVBUF */ /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); } return ERR_OK; } /** * Poll callback function for TCP netconns. * Wakes up an application thread that waits for a connection to close * or data to be sent. The application thread then takes the * appropriate action to go on. * * Signals the conn->sem. * netconn_close waits for conn->sem if closing failed. * * @see tcp.h (struct tcp_pcb.poll) for parameters and return value */ static err_t poll_tcp(void *arg, struct tcp_pcb *pcb) { struct netconn *conn = (struct netconn *)arg; LWIP_UNUSED_ARG(pcb); LWIP_ASSERT("conn != NULL", (conn != NULL)); if (conn->state == NETCONN_WRITE) { do_writemore(conn); } else if (conn->state == NETCONN_CLOSE) { do_close_internal(conn); } /* @todo: implement connect timeout here? */ /* Did a nonblocking write fail before? Then check available write-space. */ if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) { /* If the queued byte- or pbuf-count drops below the configured low-water limit, let select mark this pcb as writable again. */ if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) && (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) { conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE; API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); } } return ERR_OK; } /** * Sent callback function for TCP netconns. * Signals the conn->sem and calls API_EVENT. * netconn_write waits for conn->sem if send buffer is low. * * @see tcp.h (struct tcp_pcb.sent) for parameters and return value */ static err_t sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len) { struct netconn *conn = (struct netconn *)arg; LWIP_UNUSED_ARG(pcb); LWIP_ASSERT("conn != NULL", (conn != NULL)); if (conn->state == NETCONN_WRITE) { do_writemore(conn); } else if (conn->state == NETCONN_CLOSE) { do_close_internal(conn); } if (conn) { /* If the queued byte- or pbuf-count drops below the configured low-water limit, let select mark this pcb as writable again. */ if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) && (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) { conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE; API_EVENT(conn, NETCONN_EVT_SENDPLUS, len); } } return ERR_OK; } /** * Error callback function for TCP netconns. * Signals conn->sem, posts to all conn mboxes and calls API_EVENT. * The application thread has then to decide what to do. * * @see tcp.h (struct tcp_pcb.err) for parameters */ static void err_tcp(void *arg, err_t err) { struct netconn *conn; enum netconn_state old_state; SYS_ARCH_DECL_PROTECT(lev); conn = (struct netconn *)arg; LWIP_ASSERT("conn != NULL", (conn != NULL)); conn->pcb.tcp = NULL; /* no check since this is always fatal! */ SYS_ARCH_PROTECT(lev); conn->last_err = err; SYS_ARCH_UNPROTECT(lev); /* reset conn->state now before waking up other threads */ old_state = conn->state; conn->state = NETCONN_NONE; /* Notify the user layer about a connection error. Used to signal select. */ API_EVENT(conn, NETCONN_EVT_ERROR, 0); /* Try to release selects pending on 'read' or 'write', too. They will get an error if they actually try to read or write. */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); /* pass NULL-message to recvmbox to wake up pending recv */ if (sys_mbox_valid(&conn->recvmbox)) { /* use trypost to prevent deadlock */ sys_mbox_trypost(&conn->recvmbox, NULL); } /* pass NULL-message to acceptmbox to wake up pending accept */ if (sys_mbox_valid(&conn->acceptmbox)) { /* use trypost to preven deadlock */ sys_mbox_trypost(&conn->acceptmbox, NULL); } if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) || (old_state == NETCONN_CONNECT)) { /* calling do_writemore/do_close_internal is not necessary since the pcb has already been deleted! */ int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn); SET_NONBLOCKING_CONNECT(conn, 0); 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); } } /** * Setup a tcp_pcb with the correct callback function pointers * and their arguments. * * @param conn the TCP netconn to setup */ static void setup_tcp(struct netconn *conn) { struct tcp_pcb *pcb; pcb = conn->pcb.tcp; tcp_arg(pcb, conn); tcp_recv(pcb, recv_tcp); tcp_sent(pcb, sent_tcp); tcp_poll(pcb, poll_tcp, 4); tcp_err(pcb, err_tcp); } /** * Accept callback function for TCP netconns. * Allocates a new netconn and posts that to conn->acceptmbox. * * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value */ static err_t accept_function(void *arg, struct tcp_pcb *newpcb, err_t err) { struct netconn *newconn; struct netconn *conn = (struct netconn *)arg; LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->tate: %s\n", tcp_debug_state_str(newpcb->state))); if (!sys_mbox_valid(&conn->acceptmbox)) { LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: acceptmbox already deleted\n")); return ERR_VAL; } /* We have to set the callback here even though * the new socket is unknown. conn->socket is marked as -1. */ newconn = netconn_alloc(conn->type, conn->callback); if (newconn == NULL) { return ERR_MEM; } newconn->pcb.tcp = newpcb; setup_tcp(newconn); /* no protection: when creating the pcb, the netconn is not yet known to the application thread */ newconn->last_err = err; if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) { /* 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. */ sys_mbox_free(&newconn->recvmbox); sys_mbox_set_invalid(&newconn->recvmbox); netconn_free(newconn); return ERR_MEM; } else { /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); } return ERR_OK; } #endif /* LWIP_TCP */ /** * Create a new pcb of a specific type. * Called from do_newconn(). * * @param msg the api_msg_msg describing the connection type * @return msg->conn->err, but the return value is currently ignored */ static void pcb_new(struct api_msg_msg *msg) { LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL); /* Allocate a PCB for this connection */ switch(NETCONNTYPE_GROUP(msg->conn->type)) { #if LWIP_RAW case NETCONN_RAW: msg->conn->pcb.raw = raw_new(msg->msg.n.proto); if(msg->conn->pcb.raw == NULL) { msg->err = ERR_MEM; break; } raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn); break; #endif /* LWIP_RAW */ #if LWIP_UDP case NETCONN_UDP: msg->conn->pcb.udp = udp_new(); if(msg->conn->pcb.udp == NULL) { msg->err = ERR_MEM; break; } #if LWIP_UDPLITE if (msg->conn->type==NETCONN_UDPLITE) { udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE); } #endif /* LWIP_UDPLITE */ if (msg->conn->type==NETCONN_UDPNOCHKSUM) { udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM); } udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); break; #endif /* LWIP_UDP */ #if LWIP_TCP case NETCONN_TCP: msg->conn->pcb.tcp = tcp_new(); if(msg->conn->pcb.tcp == NULL) { msg->err = ERR_MEM; break; } setup_tcp(msg->conn); break; #endif /* LWIP_TCP */ default: /* Unsupported netconn type, e.g. protocol disabled */ msg->err = ERR_VAL; break; } }
/** * Receive callback function for RAW netconns. * Doesn't 'eat' the packet, only references it and sends it to * conn->recvmbox * * @see raw.h (struct raw_pcb.recv) for parameters and return value */ static u8_t recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p, struct ip_addr *addr) { struct pbuf *q; struct netbuf *buf; struct netconn *conn; #if LWIP_SO_RCVBUF int recv_avail; #endif /* LWIP_SO_RCVBUF */ LWIP_UNUSED_ARG(addr); conn = arg; #if LWIP_SO_RCVBUF SYS_ARCH_GET(conn->recv_avail, recv_avail); if ((conn != NULL) && (conn->recvmbox != SYS_MBOX_NULL) && ((recv_avail + (int)(p->tot_len)) <= conn->recv_bufsize)) { #else /* LWIP_SO_RCVBUF */ if ((conn != NULL) && (conn->recvmbox != SYS_MBOX_NULL)) { #endif /* LWIP_SO_RCVBUF */ /* copy the whole packet into new pbufs */ q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); if(q != NULL) { if (pbuf_copy(q, p) != ERR_OK) { pbuf_free(q); q = NULL; } } if(q != NULL) { buf = memp_malloc(MEMP_NETBUF); if (buf == NULL) { pbuf_free(q); return 0; } buf->p = q; buf->ptr = q; buf->addr = &(((struct ip_hdr*)(q->payload))->src); buf->port = pcb->protocol; if (sys_mbox_trypost(conn->recvmbox, buf) != ERR_OK) { netbuf_delete(buf); return 0; } else { SYS_ARCH_INC(conn->recv_avail, q->tot_len); /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, q->tot_len); } } } return 0; /* do not eat the packet */ } #endif /* LWIP_RAW*/ #if LWIP_UDP /** * Receive callback function for UDP netconns. * Posts the packet to conn->recvmbox or deletes it on memory error. * * @see udp.h (struct udp_pcb.recv) for parameters */ static void recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port) { struct netbuf *buf; struct netconn *conn; #if LWIP_SO_RCVBUF int recv_avail; #endif /* LWIP_SO_RCVBUF */ LWIP_UNUSED_ARG(pcb); /* only used for asserts... */ LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL); LWIP_ASSERT("recv_udp must have an argument", arg != NULL); conn = arg; LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb); #if LWIP_SO_RCVBUF SYS_ARCH_GET(conn->recv_avail, recv_avail); if ((conn == NULL) || (conn->recvmbox == SYS_MBOX_NULL) || ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) { #else /* LWIP_SO_RCVBUF */ if ((conn == NULL) || (conn->recvmbox == SYS_MBOX_NULL)) { #endif /* LWIP_SO_RCVBUF */ pbuf_free(p); return; } buf = memp_malloc(MEMP_NETBUF); if (buf == NULL) { pbuf_free(p); return; } else { buf->p = p; buf->ptr = p; buf->addr = addr; buf->port = port; #if LWIP_NETBUF_RECVINFO { const struct ip_hdr* iphdr = ip_current_header(); /* get the UDP header - always in the first pbuf, ensured by udp_input */ const struct udp_hdr* udphdr = (void*)(((char*)iphdr) + IPH_LEN(iphdr)); buf->toaddr = (struct ip_addr*)&iphdr->dest; buf->toport = udphdr->dest; } #endif /* LWIP_NETBUF_RECVINFO */ } if (sys_mbox_trypost(conn->recvmbox, buf) != ERR_OK) { netbuf_delete(buf); return; } else { SYS_ARCH_INC(conn->recv_avail, p->tot_len); /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, p->tot_len); } } #endif /* LWIP_UDP */ #if LWIP_TCP /** * Receive callback function for TCP netconns. * Posts the packet to conn->recvmbox, but doesn't delete it on errors. * * @see tcp.h (struct tcp_pcb.recv) for parameters and return value */ static err_t recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { struct netconn *conn; u16_t len; LWIP_UNUSED_ARG(pcb); LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL); LWIP_ASSERT("recv_tcp must have an argument", arg != NULL); conn = arg; LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb); if ((conn == NULL) || (conn->recvmbox == SYS_MBOX_NULL)) { return ERR_VAL; } conn->err = 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) { return ERR_MEM; } else { /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); } return ERR_OK; } /** * Poll callback function for TCP netconns. * Wakes up an application thread that waits for a connection to close * or data to be sent. The application thread then takes the * appropriate action to go on. * * Signals the conn->sem. * netconn_close waits for conn->sem if closing failed. * * @see tcp.h (struct tcp_pcb.poll) for parameters and return value */ static err_t poll_tcp(void *arg, struct tcp_pcb *pcb) { struct netconn *conn = arg; LWIP_UNUSED_ARG(pcb); LWIP_ASSERT("conn != NULL", (conn != NULL)); if (conn->state == NETCONN_WRITE) { do_writemore(conn); } else if (conn->state == NETCONN_CLOSE) { do_close_internal(conn); } return ERR_OK; } /** * Sent callback function for TCP netconns. * Signals the conn->sem and calls API_EVENT. * netconn_write waits for conn->sem if send buffer is low. * * @see tcp.h (struct tcp_pcb.sent) for parameters and return value */ static err_t sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len) { struct netconn *conn = arg; LWIP_UNUSED_ARG(pcb); 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); } if (conn) { if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT)) { API_EVENT(conn, NETCONN_EVT_SENDPLUS, len); } } return ERR_OK; } /** * Error callback function for TCP netconns. * Signals conn->sem, posts to all conn mboxes and calls API_EVENT. * The application thread has then to decide what to do. * * @see tcp.h (struct tcp_pcb.err) for parameters */ static void err_tcp(void *arg, err_t err) { struct netconn *conn; conn = arg; LWIP_ASSERT("conn != NULL", (conn != NULL)); conn->pcb.tcp = NULL; conn->err = err; if (conn->recvmbox != SYS_MBOX_NULL) { /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); sys_mbox_post(conn->recvmbox, NULL); } if (conn->op_completed != SYS_SEM_NULL && conn->state == NETCONN_CONNECT) { conn->state = NETCONN_NONE; sys_sem_signal(conn->op_completed); } if (conn->acceptmbox != SYS_MBOX_NULL) { /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); sys_mbox_post(conn->acceptmbox, NULL); } if ((conn->state == NETCONN_WRITE) || (conn->state == NETCONN_CLOSE)) { /* calling do_writemore/do_close_internal is not necessary since the pcb has already been deleted! */ conn->state = NETCONN_NONE; /* wake up the waiting task */ sys_sem_signal(conn->op_completed); } } /** * Setup a tcp_pcb with the correct callback function pointers * and their arguments. * * @param conn the TCP netconn to setup */ static void setup_tcp(struct netconn *conn) { struct tcp_pcb *pcb; pcb = conn->pcb.tcp; tcp_arg(pcb, conn); tcp_recv(pcb, recv_tcp); tcp_sent(pcb, sent_tcp); tcp_poll(pcb, poll_tcp, 4); tcp_err(pcb, err_tcp); } /** * Accept callback function for TCP netconns. * Allocates a new netconn and posts that to conn->acceptmbox. * * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value */ static err_t accept_function(void *arg, struct tcp_pcb *newpcb, err_t err) { struct netconn *newconn; struct netconn *conn; #if API_MSG_DEBUG #if TCP_DEBUG tcp_debug_print_state(newpcb->state); #endif /* TCP_DEBUG */ #endif /* API_MSG_DEBUG */ conn = (struct netconn *)arg; LWIP_ERROR("accept_function: invalid conn->acceptmbox", conn->acceptmbox != SYS_MBOX_NULL, return ERR_VAL;); /* We have to set the callback here even though * the new socket is unknown. conn->socket is marked as -1. */ newconn = netconn_alloc(conn->type, conn->callback); if (newconn == NULL) { return ERR_MEM; } newconn->pcb.tcp = newpcb; setup_tcp(newconn); newconn->err = err; if (sys_mbox_trypost(conn->acceptmbox, newconn) != ERR_OK) { /* When returning != ERR_OK, the connection is aborted in tcp_process(), so do nothing here! */ newconn->pcb.tcp = NULL; netconn_free(newconn); return ERR_MEM; } else { /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); } return ERR_OK; } #endif /* LWIP_TCP */ /** * Create a new pcb of a specific type. * Called from do_newconn(). * * @param msg the api_msg_msg describing the connection type * @return msg->conn->err, but the return value is currently ignored */ static err_t pcb_new(struct api_msg_msg *msg) { msg->conn->err = ERR_OK; LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL); /* Allocate a PCB for this connection */ switch(NETCONNTYPE_GROUP(msg->conn->type)) { #if LWIP_RAW case NETCONN_RAW: msg->conn->pcb.raw = raw_new(msg->msg.n.proto); if(msg->conn->pcb.raw == NULL) { msg->conn->err = ERR_MEM; break; } raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn); break; #endif /* LWIP_RAW */ #if LWIP_UDP case NETCONN_UDP: msg->conn->pcb.udp = udp_new(); if(msg->conn->pcb.udp == NULL) { msg->conn->err = ERR_MEM; break; } #if LWIP_UDPLITE if (msg->conn->type==NETCONN_UDPLITE) { udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE); } #endif /* LWIP_UDPLITE */ if (msg->conn->type==NETCONN_UDPNOCHKSUM) { udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM); } udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); break; #endif /* LWIP_UDP */ #if LWIP_TCP case NETCONN_TCP: msg->conn->pcb.tcp = tcp_new(); if(msg->conn->pcb.tcp == NULL) { msg->conn->err = ERR_MEM; break; } setup_tcp(msg->conn); break; #endif /* LWIP_TCP */ default: /* Unsupported netconn type, e.g. protocol disabled */ msg->conn->err = ERR_VAL; break; } return msg->conn->err; }
/** * See if more data needs to be written from a previous call to netconn_write. * Called initially from do_write. If the first call can't send all data * (because of low memory or empty send-buffer), this function is called again * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the * blocking application thread (waiting in netconn_write) is released. * * @param conn netconn (that is currently in state NETCONN_WRITE) to process * @return ERR_OK * ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished */ static err_t do_writemore(struct netconn *conn) { err_t err; void *dataptr; u16_t len, available; u8_t write_finished = 0; size_t diff; LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE)); dataptr = (u8_t*)conn->write_msg->msg.w.dataptr + conn->write_offset; diff = conn->write_msg->msg.w.len - conn->write_offset; if (diff > 0xffffUL) { /* max_u16_t */ len = 0xffff; #if LWIP_TCPIP_CORE_LOCKING conn->write_delayed = 1; #endif } else { len = (u16_t)diff; } available = tcp_sndbuf(conn->pcb.tcp); if (available < len) { /* don't try to write more than sendbuf */ len = available; #if LWIP_TCPIP_CORE_LOCKING conn->write_delayed = 1; #endif } err = tcp_write(conn->pcb.tcp, dataptr, len, conn->write_msg->msg.w.apiflags); LWIP_ASSERT("do_writemore: invalid length!", ((conn->write_offset + len) <= conn->write_msg->msg.w.len)); if (err == ERR_OK) { conn->write_offset += len; if (conn->write_offset == conn->write_msg->msg.w.len) { /* everything was written */ write_finished = 1; conn->write_msg = NULL; 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); conn->err = err; if ((err == ERR_OK) && (tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT)) { API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); } } 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); #if LWIP_TCPIP_CORE_LOCKING conn->write_delayed = 1; #endif } else { /* On errors != ERR_MEM, we don't try writing any more but return the error to the application thread. */ conn->err = err; write_finished = 1; } if (write_finished) { /* everything was written: set back connection state and back to application task */ conn->state = NETCONN_NONE; #if LWIP_TCPIP_CORE_LOCKING if (conn->write_delayed != 0) #endif { sys_sem_signal(conn->op_completed); } } #if LWIP_TCPIP_CORE_LOCKING else return ERR_MEM; #endif return ERR_OK; }
err_t netconn_write(struct netconn *conn, void *dataptr, u16_t size, u8_t copy) { struct stack *stack = conn->stack; struct api_msg msg; u16_t len; if (conn == NULL) { return ERR_VAL; } if (conn->err != ERR_OK) { return conn->err; } if (conn->sem == SYS_SEM_NULL) { conn->sem = sys_sem_new(0); if (conn->sem == SYS_SEM_NULL) { return ERR_MEM; } } msg.type = API_MSG_WRITE; msg.msg.conn = conn; conn->state = NETCONN_WRITE; while (conn->err == ERR_OK && size > 0) { msg.msg.msg.w.dataptr = dataptr; msg.msg.msg.w.copy = copy; if (conn->type == NETCONN_TCP) { int avail; while ((avail=tcp_sndbuf(conn->pcb.tcp)) == 0) { sys_sem_wait(conn->sem); if (conn->err != ERR_OK) { goto ret; } } if (size > avail) { /* We cannot send more than one send buffer's worth of data at a time. */ len = avail; } else { len = size; } } else { len = size; } LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_write: writing %d bytes (%d)\n", len, copy)); msg.msg.msg.w.len = len; api_msg_post(stack, &msg); sys_mbox_fetch(conn->mbox, NULL); if (conn->err == ERR_OK) { dataptr = (void *)((char *)dataptr + len); size -= len; } else if (conn->err == ERR_MEM) { conn->err = ERR_OK; sys_sem_wait(conn->sem); } else { goto ret; } } ret: conn->state = NETCONN_NONE; // X1004 /* if (conn->sem != SYS_SEM_NULL) { sys_sem_free(conn->sem); conn->sem = SYS_SEM_NULL; } // */ return conn->err; }
err_t netconn_write(struct netconn *conn, void *dataptr, u16_t size, u8_t copy) { struct api_msg *msg; u16_t len; if (conn == NULL) { return ERR_VAL; } if (conn->err != ERR_OK) { return conn->err; } if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) { return (conn->err = ERR_MEM); } msg->type = API_MSG_WRITE; msg->msg.conn = conn; conn->state = NETCONN_WRITE; while (conn->err == ERR_OK && size > 0) { msg->msg.msg.w.dataptr = dataptr; msg->msg.msg.w.copy = copy; if (conn->type == NETCONN_TCP) { if (tcp_sndbuf(conn->pcb.tcp) == 0) { sys_sem_wait(conn->sem); if (conn->err != ERR_OK) { goto ret; } } if (size > tcp_sndbuf(conn->pcb.tcp)) { /* We cannot send more than one send buffer's worth of data at a time. */ len = tcp_sndbuf(conn->pcb.tcp); } else { len = size; } } else { len = size; } LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_write: writing %d bytes (%d)\n", len, copy)); msg->msg.msg.w.len = len; api_msg_post(msg); sys_mbox_fetch(conn->mbox, NULL); if (conn->err == ERR_OK) { dataptr = (void *)((u8_t *)dataptr + len); size -= len; } else if (conn->err == ERR_MEM) { conn->err = ERR_OK; sys_sem_wait(conn->sem); } else { goto ret; } } ret: memp_free(MEMP_API_MSG, msg); conn->state = NETCONN_NONE; return conn->err; }
/*-----------------------------------------------------------------------------------*/ static void send_data(struct tcp_pcb *pcb, struct http_state *hs) { #ifdef INCLUDE_HTTPD_SSI_PARAMS char c; char param_name[30]; int i = 0; hs->ssi_params = NULL; #endif err_t err; u16_t len; u8_t data_to_send = false; #ifdef DYNAMIC_HTTP_HEADERS u16_t hdrlen, sendlen; /* If we were passed a NULL state structure pointer, ignore the call. */ if(!hs) { return; } /* Assume no error until we find otherwise */ err = ERR_OK; /* Do we have any more header data to send for this file? */ if(hs->hdr_index < NUM_FILE_HDR_STRINGS) { /* How much data can we send? */ len = tcp_sndbuf(pcb); sendlen = len; while(len && (hs->hdr_index < NUM_FILE_HDR_STRINGS) && sendlen) { /* How much do we have to send from the current header? */ hdrlen = strlen(hs->hdrs[hs->hdr_index]); /* How much of this can we send? */ sendlen = (len < (hdrlen - hs->hdr_pos)) ? len : (hdrlen - hs->hdr_pos); /* Send this amount of data or as much as we can given memory * constraints. */ do { err = tcp_write(pcb, (const void *)(hs->hdrs[hs->hdr_index] + hs->hdr_pos), sendlen, 0); if (err == ERR_MEM) { sendlen /= 2; } else if (err == ERR_OK) { /* Remember that we added some more data to be transmitted. */ data_to_send = true; } }while ((err == ERR_MEM) && sendlen); /* Fix up the header position for the next time round. */ hs->hdr_pos += sendlen; len -= sendlen; /* Have we finished sending this string? */ if(hs->hdr_pos == hdrlen) { /* Yes - move on to the next one */ hs->hdr_index++; hs->hdr_pos = 0; } } /* If we get here and there are still header bytes to send, we send * the header information we just wrote immediately. If there are no * more headers to send, but we do have file data to send, drop through * to try to send some file data too. */ if((hs->hdr_index < NUM_FILE_HDR_STRINGS) || !hs->file) { DEBUG_PRINT("tcp_output\n"); tcp_output(pcb); return; } } #else /* Assume no error until we find otherwise */ err = ERR_OK; #endif /* Have we run out of file data to send? If so, we need to read the next * block from the file. */ if (hs->left == 0) { int count; /* Do we already have a send buffer allocated? */ if (hs->buf) { /* Yes - get the length of the buffer */ count = hs->buf_len; } else { /* We don't have a send buffer so allocate one up to 2mss bytes long. */ count = 2 * pcb->mss; do { hs->buf = mem_malloc(count); if (hs->buf) { hs->buf_len = count; break; } count = count / 2; } while (count > 100); /* Did we get a send buffer? If not, return immediately. */ if (hs->buf == NULL) { DEBUG_PRINT ("No buff\n"); return; } } /* Do we have a valid file handle? */ if (hs->handle == NULL) { // // No - close the connection. // close_conn(pcb, hs); return; } /* Read a block of data from the file. */ DEBUG_PRINT ("Trying to read %d bytes.\n", count); count = fs_read(hs->handle, hs->buf, count); if (count < 0) { /* We reached the end of the file so this request is done */ DEBUG_PRINT ("End of file.\n"); fs_close(hs->handle); hs->handle = NULL; close_conn(pcb, hs); return; } /* Set up to send the block of data we just read */ DEBUG_PRINT ("Read %d bytes.\n", count); hs->left = count; hs->file = hs->buf; #ifdef INCLUDE_HTTPD_SSI hs->parse_left = count; hs->parsed = hs->buf; #endif } #ifdef INCLUDE_HTTPD_SSI if (!hs->tag_check) { #endif /* We are not processing an SHTML file so no tag checking is necessary. * Just send the data as we received it from the file. */ /* We cannot send more data than space available in the send buffer. */ if (tcp_sndbuf(pcb) < hs->left) { len = tcp_sndbuf(pcb); } else { len = hs->left; LWIP_ASSERT("hs->left did not fit into u16_t!", (len == hs->left)); } if (len > (2 * pcb->mss)) { len = 2 * pcb->mss; } do { DEBUG_PRINT ("Sending %d bytes\n", len); /* If the data is being read from a buffer in RAM, we need to copy it * into the PCB. If it's in flash, however, we can avoid the copy since * the data is obviously not going to be overwritten during the life * of the connection. */ err = tcp_write(pcb, hs->file, len, (hs->file < (char *) 0x20000000) ? 0 : 1); if (err == ERR_MEM) { len /= 2; } } while (err == ERR_MEM && len > 1); if (err == ERR_OK) { data_to_send = true; hs->file += len; hs->left -= len; } #ifdef INCLUDE_HTTPD_SSI } else { /* We are processing an SHTML file so need to scan for tags and replace * them with insert strings. We need to be careful here since a tag may * straddle the boundary of two blocks read from the file and we may also * have to split the insert string between two tcp_write operations. */ /* How much data could we send? */ len = tcp_sndbuf(pcb); /* Do we have remaining data to send before parsing more? */ if (hs->parsed > hs->file) { /* We cannot send more data than space available in the send buffer. */ if (tcp_sndbuf(pcb) < (hs->parsed - hs->file)) { len = tcp_sndbuf(pcb); } else { len = (hs->parsed - hs->file); LWIP_ASSERT("Data size did not fit into u16_t!", (hs->parsed - hs->file)); } if (len > (2 * pcb->mss)) { len = 2 * pcb->mss; } do { DEBUG_PRINT ("Sending %d bytes\n", len); err = tcp_write(pcb, hs->file, len, 0); if (err == ERR_MEM) { len /= 2; } } while (err == ERR_MEM && len > 1); if (err == ERR_OK) { data_to_send = true; hs->file += len; hs->left -= len; } // // If the send buffer is full, return now. // if (tcp_sndbuf(pcb) == 0) { if (data_to_send) { tcp_output(pcb); DEBUG_PRINT ("Output\n"); } return; } } DEBUG_PRINT ("State %d, %d left\n", hs->tag_state, hs->parse_left); /* We have sent all the data that was already parsed so continue parsing * the buffer contents looking for SSI tags. */ while ((hs->parse_left) && (err == ERR_OK)) { switch (hs->tag_state) { case TAG_NONE: /* We are not currently processing an SSI tag so scan for the * start of the lead-in marker. */ if (*hs->parsed == g_pcTagLeadIn[0]) { /* We found what could be the lead-in for a new tag so change * state appropriately. */ hs->tag_state = TAG_LEADIN; hs->tag_index = 1; } /* Move on to the next character in the buffer */ hs->parse_left--; hs->parsed++; break; case TAG_LEADIN: /* We are processing the lead-in marker, looking for the start of * the tag name. */ /* Have we reached the end of the leadin? */ if (hs->tag_index == LEN_TAG_LEAD_IN) { hs->tag_index = 0; hs->tag_state = TAG_FOUND; } else { /* Have we found the next character we expect for the tag leadin? */ if (*hs->parsed == g_pcTagLeadIn[hs->tag_index]) { /* Yes - move to the next one unless we have found the complete * leadin, in which case we start looking for the tag itself */ hs->tag_index++; } else { /* We found an unexpected character so this is not a tag. Move * back to idle state. */ hs->tag_state = TAG_NONE; } /* Move on to the next character in the buffer */ hs->parse_left--; hs->parsed++; } break; case TAG_FOUND: /* We are reading the tag name, looking for the start of the * lead-out marker and removing any whitespace found. */ /* Remove leading whitespace between the tag leading and the first * tag name character. */ // if((hs->tag_index == 0) && ((*hs->parsed == ' ') || // (*hs->parsed == '\t') || (*hs->parsed == '\n') || // (*hs->parsed == '\r'))) // { /* Move on to the next character in the buffer */ // hs->parse_left--; // hs->parsed++; // break; // } /* ignore whitespaces, looking param characters */ if ((*hs->parsed == ' ') || (*hs->parsed == '\t') || (*hs->parsed == '\n') || (*hs->parsed == '\r')) { /* Move on to the next character in the buffer */ hs->parse_left--; hs->parsed++; #if DEBUG_SSI_PARAMS printf("HTTPD - SWITCH : TAG_FOUND: found space, next char: %c\n", *(hs->parsed)); #endif #if INCLUDE_HTTPD_SSI_PARAMS if (*(hs->parsed) != g_pcTagLeadOut[0] && *(hs->parsed) != ' ') hs->tag_state = TAG_PARAM; #endif break; } /* Have we found the end of the tag name? This is signalled by * us finding the first leadout character */ if ((*hs->parsed == g_pcTagLeadOut[0])) { if (hs->tag_index == 0) { /* We read a zero length tag so ignore it. */ hs->tag_state = TAG_NONE; } else { /* We read a non-empty tag so go ahead and look for the * leadout string. */ hs->tag_state = TAG_LEADOUT; hs->tag_name_len = hs->tag_index; hs->tag_name[hs->tag_index] = '\0'; if (*hs->parsed == g_pcTagLeadOut[0]) { hs->tag_index = 1; } else { hs->tag_index = 0; } } } else { /* This character is part of the tag name so save it */ if (hs->tag_index < MAX_TAG_NAME_LEN) { hs->tag_name[hs->tag_index++] = *hs->parsed; } else { /* The tag was too long so ignore it. */ hs->tag_state = TAG_NONE; } } /* Move on to the next character in the buffer */ hs->parse_left--; hs->parsed++; break; /* we are looking for parameters */ #if INCLUDE_HTTPD_SSI_PARAMS case TAG_PARAM: /* Move on to the next character in the buffer */ /* Have we found the end of the tag name? This is signalled by * us finding the first leadout character */ /* Have we found the end of the tag name? This is signalled by * us finding the first leadout character */ if ((*hs->parsed == g_pcTagLeadOut[0])) { if (hs->tag_index == 0) { /* We read a zero length tag so ignore it. */ hs->tag_state = TAG_NONE; } else { param_name[i] = '\0'; #if DEBUG_SSI_PARAMS printf("SSI param: %s\n", param_name); #endif if (strlen(param_name) > 0) { SSIParamAdd(&(hs->ssi_params), param_name); } /* We read a non-empty tag so go ahead and look for the * leadout string. */ hs->tag_state = TAG_LEADOUT; hs->tag_name_len = hs->tag_index; hs->tag_name[hs->tag_index] = '\0'; if (*hs->parsed == g_pcTagLeadOut[0]) { hs->tag_index = 1; } else { hs->tag_index = 0; } } } else { if (*hs->parsed != 0) { c = *hs->parsed; if (c == ' ') { #if DEBUG_SSI_PARAMS printf("Add from space SSI param : %s\n", param_name); #endif param_name[i] = '\0'; if (strlen(param_name) > 0) { SSIParamAdd(&(hs->ssi_params), param_name); } // delete parameter, ready for new param_name[0] = 0; i = 0; } else { param_name[i] = *hs->parsed; i++; } } } /* Move on to the next character in the buffer */ hs->parse_left--; hs->parsed++; break; #endif /* * We are looking for the end of the lead-out marker. */ case TAG_LEADOUT: /* Remove leading whitespace between the tag leading and the first * tag leadout character. */ if ((hs->tag_index == 0) && ((*hs->parsed == ' ') || (*hs->parsed == '\t') || (*hs->parsed == '\n') || (*hs->parsed == '\r'))) { /* Move on to the next character in the buffer */ hs->parse_left--; hs->parsed++; break; } /* Have we found the next character we expect for the tag leadout? */ if (*hs->parsed == g_pcTagLeadOut[hs->tag_index]) { /* Yes - move to the next one unless we have found the complete * leadout, in which case we need to call the client to process * the tag. */ /* Move on to the next character in the buffer */ hs->parse_left--; hs->parsed++; if (hs->tag_index == (LEN_TAG_LEAD_OUT - 1)) { /* Call the client to ask for the insert string for the * tag we just found. */ get_tag_insert(hs); /* Next time through, we are going to be sending data * immediately, either the end of the block we start * sending here or the insert string. */ hs->tag_index = 0; hs->tag_state = TAG_SENDING; hs->tag_end = hs->parsed; /* If there is any unsent data in the buffer prior to the * tag, we need to send it now. */ if (hs->tag_end > hs->file) { /* How much of the data can we send? */ if (len > hs->tag_end - hs->file) { len = hs->tag_end - hs->file; } do { DEBUG_PRINT ("Sending %d bytes\n", len); err = tcp_write(pcb, hs->file, len, 0); if (err == ERR_MEM) { len /= 2; } } while (err == ERR_MEM && (len > 1)); if (err == ERR_OK) { data_to_send = true; hs->file += len; hs->left -= len; } } } else { hs->tag_index++; } } else { /* We found an unexpected character so this is not a tag. Move * back to idle state. */ hs->parse_left--; hs->parsed++; hs->tag_state = TAG_NONE; } break; /* * We have found a valid tag and are in the process of sending * data as a result of that discovery. We send either remaining data * from the file prior to the insert point or the insert string itself. */ case TAG_SENDING: /* Do we have any remaining file data to send from the buffer prior * to the tag? */ if (hs->tag_end > hs->file) { /* How much of the data can we send? */ if (len > hs->tag_end - hs->file) { len = hs->tag_end - hs->file; } do { DEBUG_PRINT ("Sending %d bytes\n", len); err = tcp_write(pcb, hs->file, len, 0); if (err == ERR_MEM) { len /= 2; } } while (err == ERR_MEM && (len > 1)); if (err == ERR_OK) { data_to_send = true; hs->file += len; hs->left -= len; } } else { /* Do we still have insert data left to send? */ if (hs->tag_index < hs->tag_insert_len) { /* We are sending the insert string itself. How much of the * insert can we send? */ if (len > (hs->tag_insert_len - hs->tag_index)) { len = (hs->tag_insert_len - hs->tag_index); } do { DEBUG_PRINT ("Sending %d bytes\n", len); /* * Note that we set the copy flag here since we only have a * single tag insert buffer per connection. If we don't do * this, insert corruption can occur if more than one insert * is processed before we call tcp_output. */ err = tcp_write(pcb, &(hs->tag_insert[hs->tag_index]), len, 1); if (err == ERR_MEM) { len /= 2; } } while (err == ERR_MEM && (len > 1)); if (err == ERR_OK) { data_to_send = true; hs->tag_index += len; return; } } else { /* We have sent all the insert data so go back to looking for * a new tag. */ DEBUG_PRINT ("Everything sent.\n"); hs->tag_index = 0; hs->tag_state = TAG_NONE; } } } } /* * If we drop out of the end of the for loop, this implies we must have * file data to send so send it now. In TAG_SENDING state, we've already * handled this so skip the send if that's the case. */ if ((hs->tag_state != TAG_SENDING) && (hs->parsed > hs->file)) { /* We cannot send more data than space available in the send buffer. */ if (tcp_sndbuf(pcb) < (hs->parsed - hs->file)) { len = tcp_sndbuf(pcb); } else { len = (hs->parsed - hs->file); LWIP_ASSERT("Data size did not fit into u16_t!", (hs->parsed - hs->file)); } if (len > (2 * pcb->mss)) { len = 2 * pcb->mss; } do { DEBUG_PRINT ("Sending %d bytes\n", len); err = tcp_write(pcb, hs->file, len, 0); if (err == ERR_MEM) { len /= 2; } } while (err == ERR_MEM && len > 1); if (err == ERR_OK) { data_to_send = true; hs->file += len; hs->left -= len; } } } #endif /* INCLUDE_HTTPD_SSI */ /* If we wrote anything to be sent, go ahead and send it now. */ if (data_to_send) { DEBUG_PRINT ("tcp_output\n"); tcp_output(pcb); } DEBUG_PRINT ("send_data end.\n"); }
static void send_fsm(struct tcp_pcb *pcb, struct rfb_state *state) { struct update_header hdr; int bytes_left; int totaldim; err_t err; while(1) { switch (state->send_state) { case SST_IDLE: /* Nothing to do */ if (state->update_requested) { outputf("RFB send: update requested"); state->update_requested = 0; state->chunk_actually_sent = 0; state->send_state = SST_HEADER; } else { return; } /* FALL THROUGH to SST_HEADER */ case SST_HEADER: /* Calculate the width and height for this chunk, remembering * that if SCREEN_CHUNKS_[XY] do not evenly divide the width and * height, we may need to have shorter chunks at the edge of * the screen. */ state->chunk_width = ceildiv(fb->curmode.xres, SCREEN_CHUNKS_X); state->chunk_xpos = state->chunk_width * state->chunk_xnum; totaldim = state->chunk_width * (state->chunk_xnum + 1); if (totaldim > fb->curmode.xres) { state->chunk_width -= (totaldim - fb->curmode.xres); } state->chunk_height = ceildiv(fb->curmode.yres, SCREEN_CHUNKS_Y); state->chunk_ypos = state->chunk_height * state->chunk_ynum; totaldim = state->chunk_height * (state->chunk_ynum + 1); if (totaldim > fb->curmode.yres) { state->chunk_height -= (totaldim - fb->curmode.yres); } /* Do we _actually_ need to send this chunk? */ if (fb->checksum_rect) { state->chunk_checksum = fb->checksum_rect(state->chunk_xpos, state->chunk_ypos, state->chunk_width, state->chunk_height); if (state->chunk_checksum == state->checksums[state->chunk_xnum][state->chunk_ynum]) { if (advance_chunk(state)) return; continue; } /* Checksum gets set in data block, AFTER the data has been sent. */ } state->chunk_actually_sent = 1; /* Send a header */ hdr.msgtype = 0; state->chunk_bytes_sent = 0; hdr.nrects = htons(1); hdr.xpos = htons(state->chunk_xpos); hdr.ypos = htons(state->chunk_ypos); hdr.width = htons(state->chunk_width); hdr.height= htons(state->chunk_height); hdr.enctype = htonl(0); err = tcp_write(pcb, &hdr, sizeof(hdr), TCP_WRITE_FLAG_COPY); if (err != ERR_OK) { if (err != ERR_MEM) outputf("RFB: header send error %d", err); /* Try again later. */ return; } state->send_state = SST_DATA; /* Snag the data. */ fb->copy_pixels(state->blockbuf, state->chunk_xpos, state->chunk_ypos, state->chunk_width, state->chunk_height); /* FALL THROUGH to SST_DATA */ case SST_DATA: bytes_left = 4 * state->chunk_width * state->chunk_height - state->chunk_bytes_sent; if (bytes_left == 0) { state->send_state = SST_HEADER; state->checksums[state->chunk_xnum][state->chunk_ynum] = state->chunk_checksum; if (advance_chunk(state)) return; break; } /* That's enough. */ if (bytes_left > 1400) { bytes_left = 1400; } err = tcp_write(pcb, state->blockbuf + state->chunk_bytes_sent, bytes_left, TCP_WRITE_FLAG_COPY); if (err == ERR_OK) { state->chunk_bytes_sent += bytes_left; } else { if (err != ERR_MEM) outputf("RFB: send error %d", err); return; } if (tcp_sndbuf(pcb) == 0) { return; } } } if (tcp_output(pcb) != ERR_OK) outputf("RFB: tcp_output bailed in send_fsm?"); }
size_t AsyncClient::space(){ if((_pcb != NULL) && (_pcb->state == 4)){ return tcp_sndbuf(_pcb); } return 0; }
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 void tcp_op_write(struct socket * sock, message * m, __unused int blk) { int ret; struct wbuf * wbuf; unsigned snd_buf_len, usr_buf_len; u8_t flgs = 0; if (!sock->pcb) { sock_reply(sock, ENOTCONN); return; } usr_buf_len = m->COUNT; debug_tcp_print("socket num %ld data size %d", get_sock_num(sock), usr_buf_len); /* * Let at most one buffer grow beyond TCP_BUF_SIZE. This is to minimize * small writes from userspace if only a few bytes were sent before */ if (sock->buf_size >= TCP_BUF_SIZE) { /* FIXME do not block for now */ debug_tcp_print("WARNING : tcp buffers too large, cannot allocate more"); sock_reply(sock, ENOMEM); return; } /* * Never let the allocated buffers grow more than to 2xTCP_BUF_SIZE and * never copy more than space available */ usr_buf_len = (usr_buf_len > TCP_BUF_SIZE ? TCP_BUF_SIZE : usr_buf_len); wbuf = wbuf_add(sock, usr_buf_len); debug_tcp_print("new wbuf for %d bytes", wbuf->len); if (!wbuf) { debug_tcp_print("cannot allocate new buffer of %d bytes", usr_buf_len); sock_reply(sock, ENOMEM); } if ((ret = copy_from_user(m->m_source, wbuf->data, usr_buf_len, (cp_grant_id_t) m->IO_GRANT, 0)) != OK) { sock_reply(sock, ret); return; } wbuf->written = 0; wbuf->rem_len = usr_buf_len; /* * If a writing operation is already in progress, we just enqueue the * data and quit. */ if (sock->flags & SOCK_FLG_OP_WRITING) { struct wbuf_chain * wc = (struct wbuf_chain *)sock->buf; /* * We are adding a buffer with unsent data. If we don't have any other * unsent data, set the pointer to this buffer. */ if (wc->unsent == NULL) { wc->unsent = wbuf; debug_tcp_print("unsent %p remains %d\n", wbuf, wbuf->rem_len); } debug_tcp_print("returns %d\n", usr_buf_len); sock_reply(sock, usr_buf_len); /* * We cannot accept new operations (write). We set the flag * after sending reply not to revive only. We could deadlock. */ if (sock->buf_size >= TCP_BUF_SIZE) sock->flags |= SOCK_FLG_OP_PENDING; return; } /* * Start sending data if the operation is not in progress yet. The * current buffer is the nly one we have, we cannot send more. */ snd_buf_len = tcp_sndbuf((struct tcp_pcb *)sock->pcb); debug_tcp_print("tcp can accept %d bytes", snd_buf_len); wbuf->unacked = (snd_buf_len < wbuf->rem_len ? snd_buf_len : wbuf->rem_len); wbuf->rem_len -= wbuf->unacked; if (wbuf->rem_len) { flgs = TCP_WRITE_FLAG_MORE; /* * Remember that this buffer has some data which we didn't pass * to tcp yet. */ ((struct wbuf_chain *)sock->buf)->unsent = wbuf; debug_tcp_print("unsent %p remains %d\n", wbuf, wbuf->rem_len); } ret = tcp_write((struct tcp_pcb *)sock->pcb, wbuf->data, wbuf->unacked, flgs); tcp_output((struct tcp_pcb *)sock->pcb); debug_tcp_print("%d bytes to tcp", wbuf->unacked); if (ret == ERR_OK) { /* * Operation is being processed, no need to remember the message * in this case, we are going to reply immediatly */ debug_tcp_print("returns %d\n", usr_buf_len); sock_reply(sock, usr_buf_len); sock->flags |= SOCK_FLG_OP_WRITING; if (sock->buf_size >= TCP_BUF_SIZE) sock->flags |= SOCK_FLG_OP_PENDING; } else sock_reply(sock, EIO); }