Example #1
0
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;
}
Example #2
0
/**
 * 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;
}
Example #3
0
uint16_t tcp_sndbuf1(struct tcp_pcb *pcb)
{
  return tcp_sndbuf(pcb);  // tcp_sndbuf() is a macro
}
Example #4
0
/**
 * 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;
	}
}
Example #5
0
/**
 * 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;
}
Example #6
0
/**
 * 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;
}
Example #7
0
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;
}
Example #9
0
/*-----------------------------------------------------------------------------------*/
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");
}
Example #10
0
File: rfb.c Project: jwise/netwatch
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?");
}
Example #11
0
size_t AsyncClient::space(){
    if((_pcb != NULL) && (_pcb->state == 4)){
        return tcp_sndbuf(_pcb);
    }
    return 0;
}
Example #12
0
File: tcp.c Project: jkiiski/minix
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;
}
Example #13
0
File: tcp.c Project: jkiiski/minix
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);
}