Index: uspace/lib/c/generic/async.c
===================================================================
--- uspace/lib/c/generic/async.c	(revision 57dea627b9006b8560cadda6c4bba014adcbdc35)
+++ uspace/lib/c/generic/async.c	(revision d9e68d07a1ce78ab2bebb77398cc506402a01544)
@@ -123,4 +123,7 @@
 	list_t exch_list;
 	
+	/** Session interface */
+	iface_t iface;
+	
 	/** Exchange management style */
 	exch_mgmt_t mgmt;
@@ -245,4 +248,35 @@
 	void *data;
 } connection_t;
+
+/** Interface data */
+typedef struct {
+	ht_link_t link;
+	
+	/** Interface ID */
+	iface_t iface;
+	
+	/** Futex protecting the hash table */
+	futex_t futex;
+	
+	/** Interface ports */
+	hash_table_t port_hash_table;
+	
+	/** Next available port ID */
+	port_id_t port_id_avail;
+} interface_t;
+
+/* Port data */
+typedef struct {
+	ht_link_t link;
+	
+	/** Port ID */
+	port_id_t id;
+	
+	/** Port connection handler */
+	async_port_handler_t handler;
+	
+	/** Client data */
+	void *data;
+} port_t;
 
 /* Notification data */
@@ -355,4 +389,108 @@
 static void *fallback_port_data = NULL;
 
+static hash_table_t interface_hash_table;
+
+static size_t interface_key_hash(void *key)
+{
+	iface_t iface = *(iface_t *) key;
+	return iface;
+}
+
+static size_t interface_hash(const ht_link_t *item)
+{
+	interface_t *interface = hash_table_get_inst(item, interface_t, link);
+	return interface_key_hash(&interface->iface);
+}
+
+static bool interface_key_equal(void *key, const ht_link_t *item)
+{
+	iface_t iface = *(iface_t *) key;
+	interface_t *interface = hash_table_get_inst(item, interface_t, link);
+	return iface == interface->iface;
+}
+
+/** Operations for the port hash table. */
+static hash_table_ops_t interface_hash_table_ops = {
+	.hash = interface_hash,
+	.key_hash = interface_key_hash,
+	.key_equal = interface_key_equal,
+	.equal = NULL,
+	.remove_callback = NULL
+};
+
+static size_t port_key_hash(void *key)
+{
+	port_id_t port_id = *(port_id_t *) key;
+	return port_id;
+}
+
+static size_t port_hash(const ht_link_t *item)
+{
+	port_t *port = hash_table_get_inst(item, port_t, link);
+	return port_key_hash(&port->id);
+}
+
+static bool port_key_equal(void *key, const ht_link_t *item)
+{
+	port_id_t port_id = *(port_id_t *) key;
+	port_t *port = hash_table_get_inst(item, port_t, link);
+	return port_id == port->id;
+}
+
+/** Operations for the port hash table. */
+static hash_table_ops_t port_hash_table_ops = {
+	.hash = port_hash,
+	.key_hash = port_key_hash,
+	.key_equal = port_key_equal,
+	.equal = NULL,
+	.remove_callback = NULL
+};
+
+static interface_t *async_new_interface(iface_t iface)
+{
+	interface_t *interface =
+	    (interface_t *) malloc(sizeof(interface_t));
+	if (!interface)
+		return NULL;
+	
+	bool ret = hash_table_create(&interface->port_hash_table, 0, 0,
+	    &port_hash_table_ops);
+	if (!ret) {
+		free(interface);
+		return NULL;
+	}
+	
+	interface->iface = iface;
+	futex_initialize(&interface->futex, 1);
+	interface->port_id_avail = 0;
+	
+	hash_table_insert(&interface_hash_table, &interface->link);
+	
+	return interface;
+}
+
+static port_t *async_new_port(interface_t *interface,
+    async_port_handler_t handler, void *data)
+{
+	port_t *port = (port_t *) malloc(sizeof(port_t));
+	if (!port)
+		return NULL;
+	
+	futex_down(&interface->futex);
+	
+	port_id_t id = interface->port_id_avail;
+	interface->port_id_avail++;
+	
+	port->id = id;
+	port->handler = handler;
+	port->data = data;
+	
+	hash_table_insert(&interface->port_hash_table, &port->link);
+	
+	futex_up(&interface->futex);
+	
+	return port;
+}
+
 static size_t notification_handler_stksz = FIBRIL_DFLT_STK_SIZE;
 
@@ -380,4 +518,38 @@
  */
 static FIBRIL_CONDVAR_INITIALIZE(avail_phone_cv);
+
+int async_create_port(iface_t iface, async_port_handler_t handler,
+    void *data, port_id_t *port_id)
+{
+	if ((iface & IFACE_MOD_MASK) == IFACE_MOD_CALLBACK)
+		return EINVAL;
+	
+	interface_t *interface;
+	
+	futex_down(&async_futex);
+	
+	ht_link_t *link = hash_table_find(&interface_hash_table, &iface);
+	if (link)
+		interface = hash_table_get_inst(link, interface_t, link);
+	else
+		interface = async_new_interface(iface);
+	
+	if (!interface) {
+		futex_up(&async_futex);
+		return ENOMEM;
+	}
+	
+	port_t *port = async_new_port(interface, handler, data);
+	if (!port) {
+		futex_up(&async_futex);
+		return ENOMEM;
+	}
+	
+	*port_id = port->id;
+	
+	futex_up(&async_futex);
+	
+	return EOK;
+}
 
 void async_set_fallback_port_handler(async_port_handler_t handler, void *data)
@@ -959,4 +1131,25 @@
 }
 
+static port_t *async_find_port(iface_t iface, port_id_t port_id)
+{
+	port_t *port = NULL;
+	
+	futex_down(&async_futex);
+	
+	ht_link_t *link = hash_table_find(&interface_hash_table, &iface);
+	if (link) {
+		interface_t *interface =
+		    hash_table_get_inst(link, interface_t, link);
+		
+		link = hash_table_find(&interface->port_hash_table, &port_id);
+		if (link)
+			port = hash_table_get_inst(link, port_t, link);
+	}
+	
+	futex_up(&async_futex);
+	
+	return port;
+}
+
 /** Wrapper for client connection fibril.
  *
@@ -1120,7 +1313,28 @@
 	}
 	
-	switch (IPC_GET_IMETHOD(*call)) {
-	case IPC_M_CLONE_ESTABLISH:
-	case IPC_M_CONNECT_ME_TO:
+	/* New connection */
+	if (IPC_GET_IMETHOD(*call) == IPC_M_CONNECT_ME_TO) {
+		iface_t iface = (iface_t) IPC_GET_ARG1(*call);
+		sysarg_t in_phone_hash = IPC_GET_ARG5(*call);
+		
+		async_notification_handler_t handler = fallback_port_handler;
+		void *data = fallback_port_data;
+		
+		// TODO: Currently ignores all ports but the first one
+		port_t *port = async_find_port(iface, 0);
+		if (port) {
+			handler = port->handler;
+			data = port->data;
+		}
+		
+		async_new_connection(call->in_task_id, in_phone_hash, callid,
+		    call, handler, data);
+		return;
+	}
+	
+	/* Cloned connection */
+	if (IPC_GET_IMETHOD(*call) == IPC_M_CLONE_ESTABLISH) {
+		// TODO: Currently ignores ports altogether
+		
 		/* Open new connection with fibril, etc. */
 		async_new_connection(call->in_task_id, IPC_GET_ARG5(*call),
@@ -1286,4 +1500,8 @@
 void __async_init(void)
 {
+	if (!hash_table_create(&interface_hash_table, 0, 0,
+	    &interface_hash_table_ops))
+		abort();
+	
 	if (!hash_table_create(&client_hash_table, 0, 0, &client_hash_table_ops))
 		abort();
@@ -1300,4 +1518,5 @@
 		abort();
 	
+	session_ns->iface = 0;
 	session_ns->mgmt = EXCHANGE_ATOMIC;
 	session_ns->phone = PHONE_NS;
@@ -1905,4 +2124,5 @@
 	}
 	
+	sess->iface = 0;
 	sess->mgmt = mgmt;
 	sess->phone = phone;
@@ -1981,4 +2201,5 @@
 	}
 	
+	sess->iface = 0;
 	sess->mgmt = mgmt;
 	sess->phone = phone;
@@ -2052,4 +2273,5 @@
 	}
 	
+	sess->iface = 0;
 	sess->mgmt = mgmt;
 	sess->phone = phone;
@@ -2068,4 +2290,55 @@
 }
 
+/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
+ *
+ * Ask through phone for a new connection to some service and block until
+ * success.
+ *
+ * @param exch  Exchange for sending the message.
+ * @param iface Connection interface.
+ * @param arg2  User defined argument.
+ * @param arg3  User defined argument.
+ *
+ * @return New session on success or NULL on error.
+ *
+ */
+async_sess_t *async_connect_me_to_blocking_iface(async_exch_t *exch, iface_t iface,
+    sysarg_t arg2, sysarg_t arg3)
+{
+	if (exch == NULL) {
+		errno = ENOENT;
+		return NULL;
+	}
+	
+	async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
+	if (sess == NULL) {
+		errno = ENOMEM;
+		return NULL;
+	}
+	
+	int phone = async_connect_me_to_internal(exch->phone, iface, arg2,
+	    arg3, IPC_FLAG_BLOCKING);
+	if (phone < 0) {
+		errno = phone;
+		free(sess);
+		return NULL;
+	}
+	
+	sess->iface = iface;
+	sess->phone = phone;
+	sess->arg1 = iface;
+	sess->arg2 = arg2;
+	sess->arg3 = arg3;
+	
+	fibril_mutex_initialize(&sess->remote_state_mtx);
+	sess->remote_state_data = NULL;
+	
+	list_initialize(&sess->exch_list);
+	fibril_mutex_initialize(&sess->mutex);
+	atomic_set(&sess->refcnt, 0);
+	
+	return sess;
+}
+
 /** Connect to a task specified by id.
  *
@@ -2086,4 +2359,5 @@
 	}
 	
+	sess->iface = 0;
 	sess->mgmt = EXCHANGE_ATOMIC;
 	sess->phone = phone;
@@ -2163,5 +2437,9 @@
 		return NULL;
 	
-	async_exch_t *exch;
+	exch_mgmt_t mgmt = sess->mgmt;
+	if (sess->iface != 0)
+		mgmt = sess->iface & IFACE_EXCHANGE_MASK;
+	
+	async_exch_t *exch = NULL;
 	
 	fibril_mutex_lock(&async_sess_mutex);
@@ -2182,6 +2460,6 @@
 		 */
 		
-		if ((sess->mgmt == EXCHANGE_ATOMIC) ||
-		    (sess->mgmt == EXCHANGE_SERIALIZE)) {
+		if ((mgmt == EXCHANGE_ATOMIC) ||
+		    (mgmt == EXCHANGE_SERIALIZE)) {
 			exch = (async_exch_t *) malloc(sizeof(async_exch_t));
 			if (exch != NULL) {
@@ -2191,12 +2469,11 @@
 				exch->phone = sess->phone;
 			}
-		} else {  /* EXCHANGE_PARALLEL */
+		} else if (mgmt == EXCHANGE_PARALLEL) {
+			int phone;
+			
+		retry:
 			/*
 			 * Make a one-time attempt to connect a new data phone.
 			 */
-			
-			int phone;
-			
-retry:
 			phone = async_connect_me_to_internal(sess->phone, sess->arg1,
 			    sess->arg2, sess->arg3, 0);
@@ -2240,5 +2517,5 @@
 		atomic_inc(&sess->refcnt);
 		
-		if (sess->mgmt == EXCHANGE_SERIALIZE)
+		if (mgmt == EXCHANGE_SERIALIZE)
 			fibril_mutex_lock(&sess->mutex);
 	}
@@ -2260,7 +2537,11 @@
 	assert(sess != NULL);
 	
+	exch_mgmt_t mgmt = sess->mgmt;
+	if (sess->iface != 0)
+		mgmt = sess->iface & IFACE_EXCHANGE_MASK;
+	
 	atomic_dec(&sess->refcnt);
 	
-	if (sess->mgmt == EXCHANGE_SERIALIZE)
+	if (mgmt == EXCHANGE_SERIALIZE)
 		fibril_mutex_unlock(&sess->mutex);
 	
@@ -2818,4 +3099,5 @@
 	}
 	
+	sess->iface = 0;
 	sess->mgmt = mgmt;
 	sess->phone = phone;
@@ -2867,4 +3149,5 @@
 	}
 	
+	sess->iface = 0;
 	sess->mgmt = mgmt;
 	sess->phone = phone;
@@ -2912,4 +3195,5 @@
 		return NULL;
 	
+	sess->iface = 0;
 	sess->mgmt = mgmt;
 	sess->phone = phone;
