Index: uspace/srv/net/tl/tcp/conn.c
===================================================================
--- uspace/srv/net/tl/tcp/conn.c	(revision 2f0dd2ab2795cde64d540a68e8508055c277e5ad)
+++ uspace/srv/net/tl/tcp/conn.c	(revision 852052df33dad462a5b4517d1cb06ed4c12b469d)
@@ -121,4 +121,6 @@
 	fibril_condvar_initialize(&conn->cstate_cv);
 
+	conn->cstate_cb = NULL;
+
 	conn->cstate = st_listen;
 	conn->reset = false;
@@ -243,7 +245,17 @@
 	tcp_cstate_t old_state;
 
+	log_msg(LVL_DEBUG, "tcp_conn_state_set(%p)", conn);
+
 	old_state = conn->cstate;
 	conn->cstate = nstate;
 	fibril_condvar_broadcast(&conn->cstate_cv);
+
+	/* Run user callback function */
+	if (conn->cstate_cb != NULL) {
+		log_msg(LVL_DEBUG, "tcp_conn_state_set() - run user CB");
+		conn->cstate_cb(conn, conn->cstate_cb_arg);
+	} else {
+		log_msg(LVL_DEBUG, "tcp_conn_state_set() - no user CB");
+	}
 
 	assert(old_state != st_closed);
Index: uspace/srv/net/tl/tcp/sock.c
===================================================================
--- uspace/srv/net/tl/tcp/sock.c	(revision 2f0dd2ab2795cde64d540a68e8508055c277e5ad)
+++ uspace/srv/net/tl/tcp/sock.c	(revision 852052df33dad462a5b4517d1cb06ed4c12b469d)
@@ -52,4 +52,6 @@
 #define FRAGMENT_SIZE 1024
 
+#define MAX_BACKLOG 128
+
 /** Free ports pool start. */
 #define TCP_FREE_PORTS_START		1025
@@ -60,4 +62,6 @@
 static int last_used_port = TCP_FREE_PORTS_START - 1;
 static socket_ports_t gsock;
+
+static void tcp_sock_cstate_cb(tcp_conn_t *conn, void *arg);
 
 void tcp_sock_init(void)
@@ -95,4 +99,5 @@
 {
 	tcp_sockdata_t *sock;
+	socket_core_t *sock_core;
 	int sock_id;
 	int rc;
@@ -106,6 +111,10 @@
 	}
 
+	fibril_mutex_initialize(&sock->lock);
 	sock->client = client;
 	sock->laddr.ipv4 = TCP_IPV4_ANY;
+	sock->lconn = NULL;
+	sock->backlog = 0;
+	list_initialize(&sock->ready);
 
 	sock_id = SOCKET_GET_SOCKET_ID(call);
@@ -115,4 +124,8 @@
 		return;
 	}
+
+	sock_core = socket_cores_find(&client->sockets, sock_id);
+	assert(sock_core != NULL);
+	sock->sock_core = sock_core;
 
 	refresh_answer(&answer, NULL);
@@ -167,4 +180,10 @@
 	socket_core_t *sock_core;
 	tcp_sockdata_t *socket;
+	tcp_error_t trc;
+	tcp_sock_t lsocket;
+	tcp_sock_t fsocket;
+	tcp_conn_t *conn;
+	tcp_sock_lconn_t *lconn;
+	int i;
 
 	log_msg(LVL_DEBUG, "tcp_sock_listen()");
@@ -177,4 +196,7 @@
 		return;
 	}
+
+	if (backlog > MAX_BACKLOG)
+		backlog = MAX_BACKLOG;
 
 	sock_core = socket_cores_find(&client->sockets, socket_id);
@@ -187,19 +209,55 @@
 
 	/*
-	 * XXX We do not do anything and defer action to accept().
-	 * This is a slight difference in semantics, but most servers
-	 * would just listen() and immediately accept() in a loop.
-	 *
-	 * The only difference is that there is a window between
-	 * listen() and accept() or two accept()s where we refuse
-	 * connections.
+	 * Prepare @c backlog listening connections.
 	 */
-	(void)backlog;
-	(void)socket;
-
+	fibril_mutex_lock(&socket->lock);
+
+	socket->backlog = backlog;
+	socket->lconn = calloc(sizeof(tcp_conn_t *), backlog);
+	if (socket->lconn == NULL) {
+		fibril_mutex_unlock(&socket->lock);
+		async_answer_0(callid, ENOMEM);
+		return;
+	}
+
+	log_msg(LVL_DEBUG, " - open connections");
+
+	lsocket.addr.ipv4 = TCP_IPV4_ANY;
+	lsocket.port = sock_core->port;
+	fsocket.addr.ipv4 = TCP_IPV4_ANY;
+	fsocket.port = TCP_PORT_ANY;
+
+	for (i = 0; i < backlog; i++) {
+
+		lconn = calloc(sizeof(tcp_sock_lconn_t), 1);
+		if (lconn == NULL) {
+			/* XXX Clean up */
+			fibril_mutex_unlock(&socket->lock);
+			async_answer_0(callid, ENOMEM);
+			return;
+		}
+
+		trc = tcp_uc_open(&lsocket, &fsocket, ap_passive,
+		    tcp_open_nonblock, &conn);
+		if (conn == NULL) {
+			/* XXX Clean up */
+			fibril_mutex_unlock(&socket->lock);
+			async_answer_0(callid, ENOMEM);
+			return;
+		}
+
+		tcp_uc_set_cstate_cb(conn, tcp_sock_cstate_cb, lconn);
+
+		assert(trc == TCP_EOK);
+		conn->name = (char *)"S";
+
+		lconn->conn = conn;
+		lconn->socket = socket;
+		link_initialize(&lconn->ready_list);
+		socket->lconn[i] = lconn;
+	}
+
+	fibril_mutex_unlock(&socket->lock);
 	async_answer_0(callid, EOK);
-	log_msg(LVL_DEBUG, "tcp_sock_listen(): notify data\n");
-	/* Push one accept notification to client's queue */
-	tcp_sock_notify_aconn(sock_core);
 }
 
@@ -248,4 +306,6 @@
 	}
 
+	fibril_mutex_lock(&socket->lock);
+
 	if (socket->laddr.ipv4 == TCP_IPV4_ANY) {
 		/* Find route to determine local IP address. */
@@ -254,4 +314,5 @@
 		    (void **)&phdr, &phdr_len);
 		if (rc != EOK) {
+			fibril_mutex_unlock(&socket->lock);
 			async_answer_0(callid, rc);
 			log_msg(LVL_DEBUG, "tcp_transmit_connect: Failed to find route.");
@@ -269,8 +330,10 @@
 	fsocket.port = uint16_t_be2host(addr->sin_port);
 
-	trc = tcp_uc_open(&lsocket, &fsocket, ap_active, &socket->conn);
+	trc = tcp_uc_open(&lsocket, &fsocket, ap_active, 0, &socket->conn);
 
 	if (socket->conn != NULL)
 		socket->conn->name = (char *)"C";
+
+	fibril_mutex_unlock(&socket->lock);
 
 	switch (trc) {
@@ -305,4 +368,6 @@
 	tcp_sock_t fsocket;
 	tcp_conn_t *conn;
+	tcp_conn_t *rconn;
+	tcp_sock_lconn_t *lconn;
 	int rc;
 
@@ -319,12 +384,27 @@
 
 	socket = (tcp_sockdata_t *)sock_core->specific_data;
+	fibril_mutex_lock(&socket->lock);
 
 	log_msg(LVL_DEBUG, " - verify socket->conn");
 	if (socket->conn != NULL) {
+		fibril_mutex_unlock(&socket->lock);
 		async_answer_0(callid, EINVAL);
 		return;
 	}
 
-	log_msg(LVL_DEBUG, " - open connection");
+	if (list_empty(&socket->ready)) {
+		fibril_mutex_unlock(&socket->lock);
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+
+	lconn = list_get_instance(list_first(&socket->ready),
+	    tcp_sock_lconn_t, ready_list);
+	list_remove(&lconn->ready_list);
+
+	conn = lconn->conn;
+	tcp_uc_set_cstate_cb(conn, NULL, NULL);
+
+	/* Replenish listening connection */
 
 	lsocket.addr.ipv4 = TCP_IPV4_ANY;
@@ -333,34 +413,31 @@
 	fsocket.port = TCP_PORT_ANY;
 
-	trc = tcp_uc_open(&lsocket, &fsocket, ap_passive, &conn);
-	if (conn != NULL)
-		conn->name = (char *)"S";
-
-	log_msg(LVL_DEBUG, " - decode TCP return code");
-
-	switch (trc) {
-	case TCP_EOK:
-		rc = EOK;
-		break;
-	case TCP_ERESET:
-		rc = ECONNABORTED;
-		break;
-	default:
-		assert(false);
-	}
-
-	log_msg(LVL_DEBUG, " - check TCP return code");
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		return;
-	}
+	trc = tcp_uc_open(&lsocket, &fsocket, ap_passive, tcp_open_nonblock,
+	    &rconn);
+	if (rconn == NULL) {
+		/* XXX Clean up */
+		fibril_mutex_unlock(&socket->lock);
+		async_answer_0(callid, ENOMEM);
+		return;
+	}
+
+	tcp_uc_set_cstate_cb(rconn, tcp_sock_cstate_cb, lconn);
+
+	assert(trc == TCP_EOK);
+	rconn->name = (char *)"S";
+
+	lconn->conn = rconn;
+
+	/* Allocate socket for accepted connection */
 
 	log_msg(LVL_DEBUG, "tcp_sock_accept(): allocate asocket\n");
 	asocket = calloc(sizeof(tcp_sockdata_t), 1);
 	if (asocket == NULL) {
+		fibril_mutex_unlock(&socket->lock);
 		async_answer_0(callid, ENOMEM);
 		return;
 	}
 
+	fibril_mutex_initialize(&asocket->lock);
 	asocket->client = client;
 	asocket->conn = conn;
@@ -369,4 +446,5 @@
 	rc = socket_create(&client->sockets, client->sess, asocket, &asock_id);
 	if (rc != EOK) {
+		fibril_mutex_unlock(&socket->lock);
 		async_answer_0(callid, rc);
 		return;
@@ -385,10 +463,8 @@
 	answer_call(callid, asock_core->socket_id, &answer, 3);
 
-	/* Push one accept notification to client's queue */
-	tcp_sock_notify_aconn(sock_core);
-
 	/* Push one fragment notification to client's queue */
+	log_msg(LVL_DEBUG, "tcp_sock_accept(): notify data\n");
 	tcp_sock_notify_data(asock_core);
-	log_msg(LVL_DEBUG, "tcp_sock_accept(): notify aconn\n");
+	fibril_mutex_unlock(&socket->lock);
 }
 
@@ -419,5 +495,8 @@
 
 	socket = (tcp_sockdata_t *)sock_core->specific_data;
+	fibril_mutex_lock(&socket->lock);
+
 	if (socket->conn == NULL) {
+		fibril_mutex_unlock(&socket->lock);
 		async_answer_0(callid, ENOTCONN);
 		return;
@@ -426,4 +505,5 @@
 	for (index = 0; index < fragments; index++) {
 		if (!async_data_write_receive(&wcallid, &length)) {
+			fibril_mutex_unlock(&socket->lock);
 			async_answer_0(callid, EINVAL);
 			return;
@@ -435,4 +515,5 @@
 		rc = async_data_write_finalize(wcallid, buffer, length);
 		if (rc != EOK) {
+			fibril_mutex_unlock(&socket->lock);
 			async_answer_0(callid, rc);
 			return;
@@ -459,4 +540,5 @@
 
 		if (rc != EOK) {
+			fibril_mutex_unlock(&socket->lock);
 			async_answer_0(callid, rc);
 			return;
@@ -467,4 +549,5 @@
 	SOCKET_SET_DATA_FRAGMENT_SIZE(answer, FRAGMENT_SIZE);
 	answer_call(callid, EOK, &answer, 2);
+	fibril_mutex_unlock(&socket->lock);
 }
 
@@ -504,5 +587,8 @@
 
 	socket = (tcp_sockdata_t *)sock_core->specific_data;
+	fibril_mutex_lock(&socket->lock);
+
 	if (socket->conn == NULL) {
+		fibril_mutex_unlock(&socket->lock);
 		async_answer_0(callid, ENOTCONN);
 		return;
@@ -532,4 +618,5 @@
 	log_msg(LVL_DEBUG, "**** tcp_uc_receive -> %d", rc);
 	if (rc != EOK) {
+		fibril_mutex_unlock(&socket->lock);
 		async_answer_0(callid, rc);
 		return;
@@ -545,4 +632,5 @@
 		log_msg(LVL_DEBUG, "addr read receive");
 		if (!async_data_read_receive(&rcallid, &addr_length)) {
+			fibril_mutex_unlock(&socket->lock);
 			async_answer_0(callid, EINVAL);
 			return;
@@ -555,4 +643,5 @@
 		rc = async_data_read_finalize(rcallid, &addr, addr_length);
 		if (rc != EOK) {
+			fibril_mutex_unlock(&socket->lock);
 			async_answer_0(callid, EINVAL);
 			return;
@@ -562,4 +651,5 @@
 	log_msg(LVL_DEBUG, "data read receive");
 	if (!async_data_read_receive(&rcallid, &length)) {
+		fibril_mutex_unlock(&socket->lock);
 		async_answer_0(callid, EINVAL);
 		return;
@@ -580,4 +670,5 @@
 	/* Push one fragment notification to client's queue */
 	tcp_sock_notify_data(sock_core);
+	fibril_mutex_unlock(&socket->lock);
 }
 
@@ -603,8 +694,10 @@
 
 	socket = (tcp_sockdata_t *)sock_core->specific_data;
+	fibril_mutex_lock(&socket->lock);
 
 	if (socket->conn != NULL) {
 		trc = tcp_uc_close(socket->conn);
 		if (trc != TCP_EOK && trc != TCP_ENOTEXIST) {
+			fibril_mutex_unlock(&socket->lock);
 			async_answer_0(callid, EBADF);
 			return;
@@ -623,8 +716,10 @@
 	    tcp_free_sock_data);
 	if (rc != EOK) {
+		fibril_mutex_unlock(&socket->lock);
 		async_answer_0(callid, rc);
 		return;
 	}
 
+	fibril_mutex_unlock(&socket->lock);
 	async_answer_0(callid, EOK);
 }
@@ -640,4 +735,31 @@
 	log_msg(LVL_DEBUG, "tcp_sock_setsockopt()");
 	async_answer_0(callid, ENOTSUP);
+}
+
+/** Called when connection state changes. */
+static void tcp_sock_cstate_cb(tcp_conn_t *conn, void *arg)
+{
+	tcp_conn_status_t cstatus;
+	tcp_sock_lconn_t *lconn = (tcp_sock_lconn_t *)arg;
+	tcp_sockdata_t *socket = lconn->socket;
+
+	log_msg(LVL_DEBUG, "tcp_sock_cstate_cb()");
+	fibril_mutex_lock(&socket->lock);
+	assert(conn == lconn->conn);
+
+	tcp_uc_status(conn, &cstatus);
+	if (cstatus.cstate != st_established) {
+		fibril_mutex_unlock(&socket->lock);
+		return;
+	}
+
+	assert_link_not_used(&lconn->ready_list);
+	list_append(&lconn->ready_list, &socket->ready);
+
+	log_msg(LVL_DEBUG, "tcp_sock_cstate_cb(): notify accept");
+
+	/* Push one accept notification to client's queue */
+	tcp_sock_notify_aconn(socket->sock_core);
+	fibril_mutex_unlock(&socket->lock);
 }
 
Index: uspace/srv/net/tl/tcp/tcp_type.h
===================================================================
--- uspace/srv/net/tl/tcp/tcp_type.h	(revision 2f0dd2ab2795cde64d540a68e8508055c277e5ad)
+++ uspace/srv/net/tl/tcp/tcp_type.h	(revision 852052df33dad462a5b4517d1cb06ed4c12b469d)
@@ -152,7 +152,22 @@
 } acpass_t;
 
-typedef struct tcp_conn {
+typedef enum {
+	tcp_open_nonblock = 1
+} tcp_open_flags_t;
+
+typedef struct tcp_conn tcp_conn_t;
+
+/** Connection state change callback function */
+typedef void (*tcp_cstate_cb_t)(tcp_conn_t *, void *);
+
+/** Connection */
+struct tcp_conn {
 	char *name;
 	link_t link;
+
+	/** Connection state change callback function */
+	tcp_cstate_cb_t cstate_cb;
+	/** Argument to @c cstate_cb */
+	void *cstate_cb_arg;
 
 	/** Connection identification (local and foreign socket) */
@@ -233,8 +248,10 @@
 	/** Initial receive sequence number */
 	uint32_t irs;
-} tcp_conn_t;
-
-typedef struct {
-	unsigned dummy;
+};
+
+/** Data returned by Status user call */
+typedef struct {
+	/** Connection state */
+	tcp_cstate_t cstate;
 } tcp_conn_status_t;
 
@@ -314,5 +331,9 @@
 } tcp_client_t;
 
-typedef struct {
+typedef struct tcp_sockdata {
+	/** Lock */
+	fibril_mutex_t lock;
+	/** Socket core */
+	socket_core_t *sock_core;
 	/** Client */
 	tcp_client_t *client;
@@ -321,5 +342,19 @@
 	/** Local address */
 	netaddr_t laddr;
+	/** Backlog size */
+	int backlog;
+	/** Array of listening connections, @c backlog elements */
+	struct tcp_sock_lconn **lconn;
+	/** List of connections (from lconn) that are ready to be accepted */
+	list_t ready;
 } tcp_sockdata_t;
+
+typedef struct tcp_sock_lconn {
+	tcp_conn_t *conn;
+	tcp_sockdata_t *socket;
+	int index;
+	link_t ready_list;
+} tcp_sock_lconn_t;
+
 
 #endif
Index: uspace/srv/net/tl/tcp/test.c
===================================================================
--- uspace/srv/net/tl/tcp/test.c	(revision 2f0dd2ab2795cde64d540a68e8508055c277e5ad)
+++ uspace/srv/net/tl/tcp/test.c	(revision 852052df33dad462a5b4517d1cb06ed4c12b469d)
@@ -62,5 +62,5 @@
 	fsock.addr.ipv4 = 0x7f000001;
 	printf("S: User open...\n");
-	tcp_uc_open(&lsock, &fsock, ap_passive, &conn);
+	tcp_uc_open(&lsock, &fsock, ap_passive, 0, &conn);
 	conn->name = (char *) "S";
 
@@ -102,5 +102,5 @@
 	async_usleep(1000*1000*3);
 	printf("C: User open...\n");
-	tcp_uc_open(&lsock, &fsock, ap_active, &conn);
+	tcp_uc_open(&lsock, &fsock, ap_active, 0, &conn);
 	conn->name = (char *) "C";
 
Index: uspace/srv/net/tl/tcp/ucall.c
===================================================================
--- uspace/srv/net/tl/tcp/ucall.c	(revision 2f0dd2ab2795cde64d540a68e8508055c277e5ad)
+++ uspace/srv/net/tl/tcp/ucall.c	(revision 852052df33dad462a5b4517d1cb06ed4c12b469d)
@@ -53,4 +53,5 @@
  * @param fsock		Foreign socket
  * @param acpass	Active/passive
+ * @param oflags	Open flags
  * @param conn		Connection
  *
@@ -65,11 +66,11 @@
  */
 tcp_error_t tcp_uc_open(tcp_sock_t *lsock, tcp_sock_t *fsock, acpass_t acpass,
-    tcp_conn_t **conn)
+    tcp_open_flags_t oflags, tcp_conn_t **conn)
 {
 	tcp_conn_t *nconn;
 
-	log_msg(LVL_DEBUG, "tcp_uc_open(%p, %p, %s, %p)",
+	log_msg(LVL_DEBUG, "tcp_uc_open(%p, %p, %s, %s, %p)",
 	    lsock, fsock, acpass == ap_active ? "active" : "passive",
-	    conn);
+	    oflags == tcp_open_nonblock ? "nonblock" : "none", conn);
 
 	nconn = tcp_conn_new(lsock, fsock);
@@ -79,4 +80,9 @@
 		/* Synchronize (initiate) connection */
 		tcp_conn_sync(nconn);
+	}
+
+	if (oflags == tcp_open_nonblock) {
+		*conn = nconn;
+		return TCP_EOK;
 	}
 
@@ -101,4 +107,5 @@
 
 	*conn = nconn;
+	log_msg(LVL_DEBUG, "tcp_uc_open -> %p", nconn);
 	return TCP_EOK;
 }
@@ -258,4 +265,5 @@
 {
 	log_msg(LVL_DEBUG, "tcp_uc_status()");
+	cstatus->cstate = conn->cstate;
 }
 
@@ -270,4 +278,13 @@
 	log_msg(LVL_DEBUG, "tcp_uc_delete()");
 	tcp_conn_delete(conn);
+}
+
+void tcp_uc_set_cstate_cb(tcp_conn_t *conn, tcp_cstate_cb_t cb, void *arg)
+{
+	log_msg(LVL_DEBUG, "tcp_uc_set_ctate_cb(%p, %p, %p)",
+	    conn, cb, arg);
+
+	conn->cstate_cb = cb;
+	conn->cstate_cb_arg = arg;
 }
 
Index: uspace/srv/net/tl/tcp/ucall.h
===================================================================
--- uspace/srv/net/tl/tcp/ucall.h	(revision 2f0dd2ab2795cde64d540a68e8508055c277e5ad)
+++ uspace/srv/net/tl/tcp/ucall.h	(revision 852052df33dad462a5b4517d1cb06ed4c12b469d)
@@ -42,5 +42,6 @@
  * User calls
  */
-extern tcp_error_t tcp_uc_open(tcp_sock_t *, tcp_sock_t *, acpass_t, tcp_conn_t **);
+extern tcp_error_t tcp_uc_open(tcp_sock_t *, tcp_sock_t *, acpass_t,
+    tcp_open_flags_t, tcp_conn_t **);
 extern tcp_error_t tcp_uc_send(tcp_conn_t *, void *, size_t, xflags_t);
 extern tcp_error_t tcp_uc_receive(tcp_conn_t *, void *, size_t, size_t *, xflags_t *);
@@ -49,4 +50,5 @@
 extern void tcp_uc_status(tcp_conn_t *, tcp_conn_status_t *);
 extern void tcp_uc_delete(tcp_conn_t *);
+extern void tcp_uc_set_cstate_cb(tcp_conn_t *, tcp_cstate_cb_t, void *);
 
 /*
