HelenOS sources
This source file includes following definitions.
- tcp_uc_open
- tcp_uc_send
- tcp_uc_receive
- tcp_uc_close
- tcp_uc_abort
- tcp_uc_status
- tcp_uc_delete
- tcp_uc_set_cb
- tcp_uc_get_userptr
- tcp_as_segment_arrived
- tcp_to_user
#include <errno.h>
#include <fibril_synch.h>
#include <io/log.h>
#include <macros.h>
#include <mem.h>
#include "conn.h"
#include "tcp_type.h"
#include "tqueue.h"
#include "ucall.h"
tcp_error_t tcp_uc_open(inet_ep2_t *epp, acpass_t acpass,
tcp_open_flags_t oflags, tcp_conn_t **conn)
{
tcp_conn_t *nconn;
errno_t rc;
log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_open(%p, %s, %s, %p)",
epp, acpass == ap_active ? "active" : "passive",
oflags == tcp_open_nonblock ? "nonblock" : "none", conn);
nconn = tcp_conn_new(epp);
rc = tcp_conn_add(nconn);
if (rc != EOK) {
tcp_conn_delete(nconn);
return TCP_EEXISTS;
}
tcp_conn_lock(nconn);
if (acpass == ap_active) {
tcp_conn_sync(nconn);
}
if (oflags == tcp_open_nonblock) {
tcp_conn_unlock(nconn);
log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_open -> %p", nconn);
*conn = nconn;
return TCP_EOK;
}
log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_open: Wait for connection.");
while (nconn->cstate == st_listen ||
nconn->cstate == st_syn_sent ||
nconn->cstate == st_syn_received) {
fibril_condvar_wait(&nconn->cstate_cv, &nconn->lock);
}
if (nconn->cstate != st_established) {
log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_open: Connection was reset.");
assert(nconn->cstate == st_closed);
tcp_conn_unlock(nconn);
tcp_conn_delete(nconn);
return TCP_ERESET;
}
tcp_conn_unlock(nconn);
log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_open: Connection was established.");
*conn = nconn;
log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_open -> %p", nconn);
return TCP_EOK;
}
tcp_error_t tcp_uc_send(tcp_conn_t *conn, void *data, size_t size,
xflags_t flags)
{
size_t buf_free;
size_t xfer_size;
log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_uc_send()", conn->name);
tcp_conn_lock(conn);
if (conn->cstate == st_closed) {
tcp_conn_unlock(conn);
return TCP_ENOTEXIST;
}
if (conn->cstate == st_listen) {
tcp_conn_sync(conn);
}
if (conn->snd_buf_fin) {
tcp_conn_unlock(conn);
return TCP_ECLOSING;
}
while (size > 0) {
buf_free = conn->snd_buf_size - conn->snd_buf_used;
while (buf_free == 0 && !conn->reset) {
log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: buf_free == 0, waiting.",
conn->name);
fibril_condvar_wait(&conn->snd_buf_cv, &conn->lock);
buf_free = conn->snd_buf_size - conn->snd_buf_used;
}
if (conn->reset) {
tcp_conn_unlock(conn);
return TCP_ERESET;
}
xfer_size = min(size, buf_free);
memcpy(conn->snd_buf + conn->snd_buf_used, data, xfer_size);
data += xfer_size;
conn->snd_buf_used += xfer_size;
size -= xfer_size;
tcp_tqueue_new_data(conn);
}
tcp_tqueue_new_data(conn);
tcp_conn_unlock(conn);
return TCP_EOK;
}
tcp_error_t tcp_uc_receive(tcp_conn_t *conn, void *buf, size_t size,
size_t *rcvd, xflags_t *xflags)
{
size_t xfer_size;
log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_uc_receive()", conn->name);
tcp_conn_lock(conn);
if (conn->cstate == st_closed) {
tcp_conn_unlock(conn);
return TCP_ENOTEXIST;
}
while (conn->rcv_buf_used == 0 && !conn->rcv_buf_fin && !conn->reset) {
tcp_conn_unlock(conn);
return TCP_EAGAIN;
log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_receive() - wait for data");
fibril_condvar_wait(&conn->rcv_buf_cv, &conn->lock);
}
if (conn->rcv_buf_used == 0) {
*rcvd = 0;
*xflags = 0;
if (conn->rcv_buf_fin) {
tcp_conn_unlock(conn);
return TCP_ECLOSING;
} else {
assert(conn->reset);
tcp_conn_unlock(conn);
return TCP_ERESET;
}
}
xfer_size = min(size, conn->rcv_buf_used);
memcpy(buf, conn->rcv_buf, xfer_size);
*rcvd = xfer_size;
memmove(conn->rcv_buf, conn->rcv_buf + xfer_size, conn->rcv_buf_used -
xfer_size);
conn->rcv_buf_used -= xfer_size;
conn->rcv_wnd += xfer_size;
*xflags = 0;
tcp_tqueue_ctrl_seg(conn, CTL_ACK);
log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_uc_receive() - returning %zu bytes",
conn->name, xfer_size);
tcp_conn_unlock(conn);
return TCP_EOK;
}
tcp_error_t tcp_uc_close(tcp_conn_t *conn)
{
log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_uc_close(%p)", conn->name,
conn);
tcp_conn_lock(conn);
if (conn->cstate == st_closed) {
log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_close - ENOTEXIST");
tcp_conn_unlock(conn);
return TCP_ENOTEXIST;
}
if (conn->cstate == st_listen || conn->cstate == st_syn_sent) {
log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_close - listen/syn_sent");
tcp_conn_reset(conn);
tcp_conn_unlock(conn);
return TCP_EOK;
}
if (conn->snd_buf_fin) {
log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_close - ECLOSING");
tcp_conn_unlock(conn);
return TCP_ECLOSING;
}
log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_close - set snd_buf_fin");
conn->snd_buf_fin = true;
tcp_tqueue_new_data(conn);
tcp_conn_unlock(conn);
return TCP_EOK;
}
void tcp_uc_abort(tcp_conn_t *conn)
{
log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_abort()");
tcp_conn_lock(conn);
tcp_conn_reset(conn);
tcp_conn_unlock(conn);
}
void tcp_uc_status(tcp_conn_t *conn, tcp_conn_status_t *cstatus)
{
log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_status()");
cstatus->cstate = conn->cstate;
}
void tcp_uc_delete(tcp_conn_t *conn)
{
log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_delete()");
tcp_conn_delete(conn);
}
void tcp_uc_set_cb(tcp_conn_t *conn, tcp_cb_t *cb, void *arg)
{
log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_set_cb(%p, %p, %p)",
conn, cb, arg);
conn->cb = cb;
conn->cb_arg = arg;
}
void *tcp_uc_get_userptr(tcp_conn_t *conn)
{
return conn->cb_arg;
}
void tcp_as_segment_arrived(inet_ep2_t *epp, tcp_segment_t *seg)
{
tcp_conn_t *conn;
log_msg(LOG_DEFAULT, LVL_DEBUG,
"tcp_as_segment_arrived(f:(%u), l:(%u))",
epp->remote.port, epp->local.port);
conn = tcp_conn_find_ref(epp);
if (conn == NULL) {
log_msg(LOG_DEFAULT, LVL_WARN, "No connection found.");
tcp_unexpected_segment(epp, seg);
return;
}
tcp_conn_segment_arrived(conn, epp, seg);
tcp_conn_delref(conn);
}
void tcp_to_user(void)
{
log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_to_user()");
}
HelenOS homepage, sources at GitHub