Index: uspace/lib/block/libblock.c
===================================================================
--- uspace/lib/block/libblock.c	(revision c3fe001b5f8a022da41bcea1f44c55176d97ab95)
+++ uspace/lib/block/libblock.c	(revision 4802dd7d8d17cb02ec8f79547ffde538bb8c6736)
@@ -40,5 +40,4 @@
 #include "../../srv/vfs/vfs.h"
 #include <ipc/loc.h>
-#include <ipc/bd.h>
 #include <ipc/services.h>
 #include <errno.h>
@@ -47,4 +46,5 @@
 #include <as.h>
 #include <assert.h>
+#include <bd.h>
 #include <fibril_synch.h>
 #include <adt/list.h>
@@ -80,7 +80,5 @@
 	service_id_t service_id;
 	async_sess_t *sess;
-	fibril_mutex_t comm_area_lock;
-	void *comm_area;
-	size_t comm_size;
+	bd_t *bd;
 	void *bb_buf;
 	aoff64_t bb_addr;
@@ -89,8 +87,6 @@
 } devcon_t;
 
-static int read_blocks(devcon_t *, aoff64_t, size_t);
-static int write_blocks(devcon_t *, aoff64_t, size_t);
-static int get_block_size(async_sess_t *, size_t *);
-static int get_num_blocks(async_sess_t *, aoff64_t *);
+static int read_blocks(devcon_t *, aoff64_t, size_t, void *, size_t);
+static int write_blocks(devcon_t *, aoff64_t, size_t, void *, size_t);
 static aoff64_t ba_ltop(devcon_t *, aoff64_t);
 
@@ -112,10 +108,7 @@
 
 static int devcon_add(service_id_t service_id, async_sess_t *sess,
-    size_t bsize, void *comm_area, size_t comm_size)
+    size_t bsize, bd_t *bd)
 {
 	devcon_t *devcon;
-	
-	if (comm_size < bsize)
-		return EINVAL;
 	
 	devcon = malloc(sizeof(devcon_t));
@@ -126,7 +119,5 @@
 	devcon->service_id = service_id;
 	devcon->sess = sess;
-	fibril_mutex_initialize(&devcon->comm_area_lock);
-	devcon->comm_area = comm_area;
-	devcon->comm_size = comm_size;
+	devcon->bd = bd;
 	devcon->bb_buf = NULL;
 	devcon->bb_addr = 0;
@@ -158,23 +149,14 @@
     size_t comm_size)
 {
-	void *comm_area = mmap(NULL, comm_size, PROTO_READ | PROTO_WRITE,
-	    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
-	if (!comm_area)
-		return ENOMEM;
-	
+	bd_t *bd;
+
 	async_sess_t *sess = loc_service_connect(mgmt, service_id,
 	    IPC_FLAG_BLOCKING);
 	if (!sess) {
-		munmap(comm_area, comm_size);
 		return ENOENT;
 	}
 	
-	async_exch_t *exch = async_exchange_begin(sess);
-	int rc = async_share_out_start(exch, comm_area,
-	    AS_AREA_READ | AS_AREA_WRITE);
-	async_exchange_end(exch);
-	
+	int rc = bd_open(sess, &bd);
 	if (rc != EOK) {
-		munmap(comm_area, comm_size);
 		async_hangup(sess);
 		return rc;
@@ -182,15 +164,14 @@
 	
 	size_t bsize;
-	rc = get_block_size(sess, &bsize);
-	
+	rc = bd_get_block_size(bd, &bsize);
 	if (rc != EOK) {
-		munmap(comm_area, comm_size);
+		bd_close(bd);
 		async_hangup(sess);
 		return rc;
 	}
 	
-	rc = devcon_add(service_id, sess, bsize, comm_area, comm_size);
+	rc = devcon_add(service_id, sess, bsize, bd);
 	if (rc != EOK) {
-		munmap(comm_area, comm_size);
+		bd_close(bd);
 		async_hangup(sess);
 		return rc;
@@ -213,5 +194,5 @@
 		free(devcon->bb_buf);
 	
-	munmap(devcon->comm_area, devcon->comm_size);
+	bd_close(devcon->bd);
 	async_hangup(devcon->sess);
 	
@@ -233,13 +214,9 @@
 		return ENOMEM;
 
-	fibril_mutex_lock(&devcon->comm_area_lock);
-	rc = read_blocks(devcon, 0, 1);
+	rc = read_blocks(devcon, 0, 1, bb_buf, devcon->pblock_size);
 	if (rc != EOK) {
-		fibril_mutex_unlock(&devcon->comm_area_lock);
 	    	free(bb_buf);
 		return rc;
 	}
-	memcpy(bb_buf, devcon->comm_area, devcon->pblock_size);
-	fibril_mutex_unlock(&devcon->comm_area_lock);
 
 	devcon->bb_buf = bb_buf;
@@ -338,6 +315,6 @@
 		list_remove(&b->free_link);
 		if (b->dirty) {
-			memcpy(devcon->comm_area, b->data, b->size);
-			rc = write_blocks(devcon, b->pba, cache->blocks_cluster);
+			rc = write_blocks(devcon, b->pba, cache->blocks_cluster,
+			    b->data, b->size);
 			if (rc != EOK)
 				return rc;
@@ -481,9 +458,6 @@
 				list_append(&b->free_link, &cache->free_list);
 				fibril_mutex_unlock(&cache->lock);
-				fibril_mutex_lock(&devcon->comm_area_lock);
-				memcpy(devcon->comm_area, b->data, b->size);
 				rc = write_blocks(devcon, b->pba,
-				    cache->blocks_cluster);
-				fibril_mutex_unlock(&devcon->comm_area_lock);
+				    cache->blocks_cluster, b->data, b->size);
 				if (rc != EOK) {
 					/*
@@ -555,8 +529,6 @@
 			 * the new contents from the device.
 			 */
-			fibril_mutex_lock(&devcon->comm_area_lock);
-			rc = read_blocks(devcon, b->pba, cache->blocks_cluster);
-			memcpy(b->data, devcon->comm_area, cache->lblock_size);
-			fibril_mutex_unlock(&devcon->comm_area_lock);
+			rc = read_blocks(devcon, b->pba, cache->blocks_cluster,
+			    b->data, cache->lblock_size);
 			if (rc != EOK) 
 				b->toxic = true;
@@ -616,8 +588,6 @@
 	if (block->dirty && (block->refcnt == 1) &&
 	    (blocks_cached > CACHE_HI_WATERMARK || mode != CACHE_MODE_WB)) {
-		fibril_mutex_lock(&devcon->comm_area_lock);
-		memcpy(devcon->comm_area, block->data, block->size);
-		rc = write_blocks(devcon, block->pba, cache->blocks_cluster);
-		fibril_mutex_unlock(&devcon->comm_area_lock);
+		rc = write_blocks(devcon, block->pba, cache->blocks_cluster,
+		    block->data, block->size);
 		block->dirty = false;
 	}
@@ -688,4 +658,5 @@
  *
  * @param service_id	Service ID of the block device.
+ * @param buf		Buffer for holding one block
  * @param bufpos	Pointer to the first unread valid offset within the
  * 			communication buffer.
@@ -699,6 +670,6 @@
  * @return		EOK on success or a negative return code on failure.
  */
-int block_seqread(service_id_t service_id, size_t *bufpos, size_t *buflen,
-    aoff64_t *pos, void *dst, size_t size)
+int block_seqread(service_id_t service_id, void *buf, size_t *bufpos,
+    size_t *buflen, aoff64_t *pos, void *dst, size_t size)
 {
 	size_t offset = 0;
@@ -711,5 +682,4 @@
 	block_size = devcon->pblock_size;
 	
-	fibril_mutex_lock(&devcon->comm_area_lock);
 	while (left > 0) {
 		size_t rd;
@@ -725,5 +695,5 @@
 			 * destination buffer.
 			 */
-			memcpy(dst + offset, devcon->comm_area + *bufpos, rd);
+			memcpy(dst + offset, buf + *bufpos, rd);
 			offset += rd;
 			*bufpos += rd;
@@ -736,7 +706,7 @@
 			int rc;
 
-			rc = read_blocks(devcon, *pos / block_size, 1);
+			rc = read_blocks(devcon, *pos / block_size, 1, buf,
+			    devcon->pblock_size);
 			if (rc != EOK) {
-				fibril_mutex_unlock(&devcon->comm_area_lock);
 				return rc;
 			}
@@ -746,5 +716,4 @@
 		}
 	}
-	fibril_mutex_unlock(&devcon->comm_area_lock);
 	
 	return EOK;
@@ -763,18 +732,9 @@
 {
 	devcon_t *devcon;
-	int rc;
 
 	devcon = devcon_search(service_id);
 	assert(devcon);
-	
-	fibril_mutex_lock(&devcon->comm_area_lock);
-
-	rc = read_blocks(devcon, ba, cnt);
-	if (rc == EOK)
-		memcpy(buf, devcon->comm_area, devcon->pblock_size * cnt);
-
-	fibril_mutex_unlock(&devcon->comm_area_lock);
-
-	return rc;
+
+	return read_blocks(devcon, ba, cnt, buf, devcon->pblock_size * cnt);
 }
 
@@ -792,17 +752,9 @@
 {
 	devcon_t *devcon;
-	int rc;
 
 	devcon = devcon_search(service_id);
 	assert(devcon);
-	
-	fibril_mutex_lock(&devcon->comm_area_lock);
-
-	memcpy(devcon->comm_area, data, devcon->pblock_size * cnt);
-	rc = write_blocks(devcon, ba, cnt);
-
-	fibril_mutex_unlock(&devcon->comm_area_lock);
-
-	return rc;
+
+	return write_blocks(devcon, ba, cnt, (void *)data, devcon->pblock_size * cnt);
 }
 
@@ -820,6 +772,6 @@
 	devcon = devcon_search(service_id);
 	assert(devcon);
-	
-	return get_block_size(devcon->sess, bsize);
+
+	return bd_get_block_size(devcon->bd, bsize);
 }
 
@@ -836,5 +788,5 @@
 	assert(devcon);
 	
-	return get_num_blocks(devcon->sess, nblocks);
+	return bd_get_num_blocks(devcon->bd, nblocks);
 }
 
@@ -887,5 +839,5 @@
 	memcpy(data, buffer + offset, bytes);
 	free(buffer);
-	
+
 	return EOK;
 }
@@ -903,25 +855,18 @@
 {
 	devcon_t *devcon = devcon_search(service_id);
-	assert(devcon);
-	
 	toc_block_t *toc = NULL;
-	
-	fibril_mutex_lock(&devcon->comm_area_lock);
-	
-	async_exch_t *exch = async_exchange_begin(devcon->sess);
-	int rc = async_req_1_0(exch, BD_READ_TOC, session);
-	async_exchange_end(exch);
-	
-	if (rc == EOK) {
-		toc = (toc_block_t *) malloc(sizeof(toc_block_t));
-		if (toc != NULL) {
-			memset(toc, 0, sizeof(toc_block_t));
-			memcpy(toc, devcon->comm_area,
-			    min(devcon->pblock_size, sizeof(toc_block_t)));
-		}
-	}
-	
-	
-	fibril_mutex_unlock(&devcon->comm_area_lock);
+	int rc;
+	
+	assert(devcon);
+	
+	toc = (toc_block_t *) malloc(sizeof(toc_block_t));
+	if (toc == NULL)
+		return NULL;
+	
+	rc = bd_read_toc(devcon->bd, session, toc, sizeof(toc_block_t));
+	if (rc != EOK) {
+		free(toc);
+		return NULL;
+	}
 	
 	return toc;
@@ -937,13 +882,10 @@
  * @return		EOK on success or negative error code on failure.
  */
-static int read_blocks(devcon_t *devcon, aoff64_t ba, size_t cnt)
-{
-	assert(devcon);
-	
-	async_exch_t *exch = async_exchange_begin(devcon->sess);
-	int rc = async_req_3_0(exch, BD_READ_BLOCKS, LOWER32(ba),
-	    UPPER32(ba), cnt);
-	async_exchange_end(exch);
-	
+static int read_blocks(devcon_t *devcon, aoff64_t ba, size_t cnt, void *buf,
+    size_t size)
+{
+	assert(devcon);
+	
+	int rc = bd_read_blocks(devcon->bd, ba, cnt, buf, size);
 	if (rc != EOK) {
 		printf("Error %d reading %zu blocks starting at block %" PRIuOFF64
@@ -967,13 +909,10 @@
  * @return		EOK on success or negative error code on failure.
  */
-static int write_blocks(devcon_t *devcon, aoff64_t ba, size_t cnt)
-{
-	assert(devcon);
-	
-	async_exch_t *exch = async_exchange_begin(devcon->sess);
-	int rc = async_req_3_0(exch, BD_WRITE_BLOCKS, LOWER32(ba),
-	    UPPER32(ba), cnt);
-	async_exchange_end(exch);
-	
+static int write_blocks(devcon_t *devcon, aoff64_t ba, size_t cnt, void *data,
+    size_t size)
+{
+	assert(devcon);
+	
+	int rc = bd_write_blocks(devcon->bd, ba, cnt, data, size);
 	if (rc != EOK) {
 		printf("Error %d writing %zu blocks starting at block %" PRIuOFF64
@@ -987,35 +926,4 @@
 }
 
-/** Get block size used by the device. */
-static int get_block_size(async_sess_t *sess, size_t *bsize)
-{
-	sysarg_t bs;
-	
-	async_exch_t *exch = async_exchange_begin(sess);
-	int rc = async_req_0_1(exch, BD_GET_BLOCK_SIZE, &bs);
-	async_exchange_end(exch);
-	
-	if (rc == EOK)
-		*bsize = (size_t) bs;
-	
-	return rc;
-}
-
-/** Get total number of blocks on block device. */
-static int get_num_blocks(async_sess_t *sess, aoff64_t *nblocks)
-{
-	sysarg_t nb_l;
-	sysarg_t nb_h;
-	
-	async_exch_t *exch = async_exchange_begin(sess);
-	int rc = async_req_0_2(exch, BD_GET_NUM_BLOCKS, &nb_l, &nb_h);
-	async_exchange_end(exch);
-	
-	if (rc == EOK)
-		*nblocks = (aoff64_t) MERGE_LOUP32(nb_l, nb_h);
-	
-	return rc;
-}
-
 /** Convert logical block address to physical block address. */
 static aoff64_t ba_ltop(devcon_t *devcon, aoff64_t lba)
Index: uspace/lib/block/libblock.h
===================================================================
--- uspace/lib/block/libblock.h	(revision c3fe001b5f8a022da41bcea1f44c55176d97ab95)
+++ uspace/lib/block/libblock.h	(revision 4802dd7d8d17cb02ec8f79547ffde538bb8c6736)
@@ -122,6 +122,6 @@
 extern int block_put(block_t *);
 
-extern int block_seqread(service_id_t, size_t *, size_t *, aoff64_t *, void *,
-    size_t);
+extern int block_seqread(service_id_t, void *, size_t *, size_t *, aoff64_t *,
+    void *, size_t);
 
 extern int block_get_bsize(service_id_t, size_t *);
Index: uspace/lib/c/Makefile
===================================================================
--- uspace/lib/c/Makefile	(revision c3fe001b5f8a022da41bcea1f44c55176d97ab95)
+++ uspace/lib/c/Makefile	(revision 4802dd7d8d17cb02ec8f79547ffde538bb8c6736)
@@ -62,4 +62,6 @@
 	generic/ddi.c \
 	generic/as.c \
+	generic/bd.c \
+	generic/bd_srv.c \
 	generic/cap.c \
 	generic/cfg.c \
Index: uspace/lib/c/generic/bd.c
===================================================================
--- uspace/lib/c/generic/bd.c	(revision 4802dd7d8d17cb02ec8f79547ffde538bb8c6736)
+++ uspace/lib/c/generic/bd.c	(revision 4802dd7d8d17cb02ec8f79547ffde538bb8c6736)
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2012 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/**
+ * @file
+ * @brief IP link client stub
+ */
+
+#include <async.h>
+#include <assert.h>
+#include <bd.h>
+#include <errno.h>
+#include <ipc/bd.h>
+#include <ipc/services.h>
+#include <loc.h>
+#include <macros.h>
+#include <stdlib.h>
+
+static void bd_cb_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg);
+
+int bd_open(async_sess_t *sess, bd_t **rbd)
+{
+	bd_t *bd = calloc(1, sizeof(bd_t));
+	if (bd == NULL)
+		return ENOMEM;
+	
+	bd->sess = sess;
+	
+	async_exch_t *exch = async_exchange_begin(sess);
+	
+	int rc = async_connect_to_me(exch, 0, 0, 0, bd_cb_conn, bd);
+	async_exchange_end(exch);
+	
+	if (rc != EOK)
+		goto error;
+	
+	*rbd = bd;
+	return EOK;
+	
+error:
+	if (bd != NULL)
+		free(bd);
+	
+	return rc;
+}
+
+void bd_close(bd_t *bd)
+{
+	/* XXX Synchronize with bd_cb_conn */
+	free(bd);
+}
+
+int bd_read_blocks(bd_t *bd, aoff64_t ba, size_t cnt, void *data, size_t size)
+{
+	async_exch_t *exch = async_exchange_begin(bd->sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_3(exch, BD_READ_BLOCKS, LOWER32(ba),
+	    UPPER32(ba), cnt, &answer);
+	int rc = async_data_read_start(exch, data, size);
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	sysarg_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK)
+		return retval;
+
+	return EOK;
+}
+
+int bd_read_toc(bd_t *bd, uint8_t session, void *buf, size_t size)
+{
+	async_exch_t *exch = async_exchange_begin(bd->sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, BD_READ_TOC, session, &answer);
+	int rc = async_data_read_start(exch, buf, size);
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	sysarg_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK)
+		return retval;
+
+	return EOK;
+}
+
+int bd_write_blocks(bd_t *bd, aoff64_t ba, size_t cnt, const void *data,
+    size_t size)
+{
+	async_exch_t *exch = async_exchange_begin(bd->sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_3(exch, BD_WRITE_BLOCKS, LOWER32(ba),
+	    UPPER32(ba), cnt, &answer);
+	int rc = async_data_write_start(exch, data, size);
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	sysarg_t retval;
+	async_wait_for(req, &retval);
+	if (retval != EOK)
+		return retval;
+
+	return EOK;
+}
+
+int bd_get_block_size(bd_t *bd, size_t *rbsize)
+{
+	sysarg_t bsize;
+	async_exch_t *exch = async_exchange_begin(bd->sess);
+
+	int rc = async_req_0_1(exch, BD_GET_BLOCK_SIZE, &bsize);
+	async_exchange_end(exch);
+
+	if (rc != EOK)
+		return rc;
+
+	*rbsize = bsize;
+	return EOK;
+}
+
+int bd_get_num_blocks(bd_t *bd, aoff64_t *rnb)
+{
+	sysarg_t nb_l;
+	sysarg_t nb_h;
+	async_exch_t *exch = async_exchange_begin(bd->sess);
+
+	int rc = async_req_0_2(exch, BD_GET_NUM_BLOCKS, &nb_l, &nb_h);
+	async_exchange_end(exch);
+
+	if (rc != EOK)
+		return rc;
+
+	*rnb = (aoff64_t) MERGE_LOUP32(nb_l, nb_h);
+	return EOK;
+}
+
+static void bd_cb_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	bd_t *bd = (bd_t *)arg;
+
+	(void)bd;
+
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+
+		if (!IPC_GET_IMETHOD(call)) {
+			/* TODO: Handle hangup */
+			return;
+		}
+
+		switch (IPC_GET_IMETHOD(call)) {
+		default:
+			async_answer_0(callid, ENOTSUP);
+		}
+	}
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/bd_srv.c
===================================================================
--- uspace/lib/c/generic/bd_srv.c	(revision 4802dd7d8d17cb02ec8f79547ffde538bb8c6736)
+++ uspace/lib/c/generic/bd_srv.c	(revision 4802dd7d8d17cb02ec8f79547ffde538bb8c6736)
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2012 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/**
+ * @file
+ * @brief Block device server stub
+ */
+#include <errno.h>
+#include <ipc/bd.h>
+#include <macros.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <bd_srv.h>
+
+static void bd_read_blocks_srv(bd_srv_t *srv, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	aoff64_t ba;
+	size_t cnt;
+	void *buf;
+	size_t size;
+	int rc;
+	ipc_callid_t rcallid;
+
+	ba = MERGE_LOUP32(IPC_GET_ARG1(*call), IPC_GET_ARG2(*call));
+	cnt = IPC_GET_ARG3(*call);
+
+	if (!async_data_read_receive(&rcallid, &size)) {
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+
+	buf = malloc(size);
+	if (buf == NULL) {
+		async_answer_0(rcallid, ENOMEM);
+		async_answer_0(callid, ENOMEM);
+		return;
+	}
+
+	if (srv->ops->read_blocks == NULL) {
+		async_answer_0(rcallid, ENOTSUP);
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	rc = srv->ops->read_blocks(srv, ba, cnt, buf, size);
+	if (rc != EOK) {
+		async_answer_0(rcallid, ENOMEM);
+		async_answer_0(callid, ENOMEM);
+		return;
+	}
+
+	async_data_read_finalize(rcallid, buf, size);
+
+	free(buf);
+	async_answer_0(callid, EOK);
+}
+
+static void bd_read_toc_srv(bd_srv_t *srv, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	uint8_t session;
+	void *buf;
+	size_t size;
+	int rc;
+	ipc_callid_t rcallid;
+
+	session = IPC_GET_ARG1(*call);
+
+	if (!async_data_read_receive(&rcallid, &size)) {
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+
+	buf = malloc(size);
+	if (buf == NULL) {
+		async_answer_0(rcallid, ENOMEM);
+		async_answer_0(callid, ENOMEM);
+		return;
+	}
+
+	if (srv->ops->read_toc == NULL) {
+		async_answer_0(rcallid, ENOTSUP);
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	rc = srv->ops->read_toc(srv, session, buf, size);
+	if (rc != EOK) {
+		async_answer_0(rcallid, ENOMEM);
+		async_answer_0(callid, ENOMEM);
+		return;
+	}
+
+	async_data_read_finalize(rcallid, buf, size);
+
+	free(buf);
+	async_answer_0(callid, EOK);
+}
+
+static void bd_write_blocks_srv(bd_srv_t *srv, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	aoff64_t ba;
+	size_t cnt;
+	void *data;
+	size_t size;
+	int rc;
+
+	ba = MERGE_LOUP32(IPC_GET_ARG1(*call), IPC_GET_ARG2(*call));
+	cnt = IPC_GET_ARG3(*call);
+
+	rc = async_data_write_accept(&data, false, 0, 0, 0, &size);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	if (srv->ops->write_blocks == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	rc = srv->ops->write_blocks(srv, ba, cnt, data, size);
+	free(data);
+	async_answer_0(callid, rc);
+}
+
+static void bd_get_block_size_srv(bd_srv_t *srv, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	int rc;
+	size_t block_size;
+
+	if (srv->ops->get_block_size == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	rc = srv->ops->get_block_size(srv, &block_size);
+	async_answer_1(callid, rc, block_size);
+}
+
+static void bd_get_num_blocks_srv(bd_srv_t *srv, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	int rc;
+	aoff64_t num_blocks;
+
+	if (srv->ops->get_num_blocks == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	rc = srv->ops->get_num_blocks(srv, &num_blocks);
+	async_answer_2(callid, rc, LOWER32(num_blocks), UPPER32(num_blocks));
+}
+
+void bd_srv_init(bd_srv_t *srv)
+{
+	fibril_mutex_initialize(&srv->lock);
+	srv->connected = false;
+	srv->ops = NULL;
+	srv->arg = NULL;
+	srv->client_sess = NULL;
+}
+
+int bd_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	bd_srv_t *srv = (bd_srv_t *)arg;
+	int rc;
+
+	fibril_mutex_lock(&srv->lock);
+	if (srv->connected) {
+		fibril_mutex_unlock(&srv->lock);
+		async_answer_0(iid, EBUSY);
+		return EBUSY;
+	}
+
+	srv->connected = true;
+	fibril_mutex_unlock(&srv->lock);
+
+	/* Accept the connection */
+	async_answer_0(iid, EOK);
+
+	async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
+	if (sess == NULL)
+		return ENOMEM;
+
+	srv->client_sess = sess;
+
+	rc = srv->ops->open(srv);
+	if (rc != EOK)
+		return rc;
+
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		sysarg_t method = IPC_GET_IMETHOD(call);
+
+		if (!method) {
+			/* The other side has hung up */
+			fibril_mutex_lock(&srv->lock);
+			srv->connected = false;
+			fibril_mutex_unlock(&srv->lock);
+			async_answer_0(callid, EOK);
+			break;
+		}
+
+		switch (method) {
+		case BD_READ_BLOCKS:
+			bd_read_blocks_srv(srv, callid, &call);
+			break;
+		case BD_READ_TOC:
+			bd_read_toc_srv(srv, callid, &call);
+			break;
+		case BD_WRITE_BLOCKS:
+			bd_write_blocks_srv(srv, callid, &call);
+			break;
+		case BD_GET_BLOCK_SIZE:
+			bd_get_block_size_srv(srv, callid, &call);
+			break;
+		case BD_GET_NUM_BLOCKS:
+			bd_get_num_blocks_srv(srv, callid, &call);
+			break;
+		default:
+			async_answer_0(callid, EINVAL);
+		}
+	}
+
+	return srv->ops->close(srv);
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/iplink_srv.c
===================================================================
--- uspace/lib/c/generic/iplink_srv.c	(revision c3fe001b5f8a022da41bcea1f44c55176d97ab95)
+++ uspace/lib/c/generic/iplink_srv.c	(revision 4802dd7d8d17cb02ec8f79547ffde538bb8c6736)
@@ -139,4 +139,7 @@
 		if (!method) {
 			/* The other side has hung up */
+		    	fibril_mutex_lock(&srv->lock);
+			srv->connected = false;
+		    	fibril_mutex_unlock(&srv->lock);
 			async_answer_0(callid, EOK);
 			break;
Index: uspace/lib/c/include/bd.h
===================================================================
--- uspace/lib/c/include/bd.h	(revision 4802dd7d8d17cb02ec8f79547ffde538bb8c6736)
+++ uspace/lib/c/include/bd.h	(revision 4802dd7d8d17cb02ec8f79547ffde538bb8c6736)
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2012 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_BD_H_
+#define LIBC_BD_H_
+
+#include <async.h>
+#include <sys/types.h>
+
+typedef struct {
+	async_sess_t *sess;
+} bd_t;
+
+extern int bd_open(async_sess_t *, bd_t **);
+extern void bd_close(bd_t *);
+extern int bd_read_blocks(bd_t *, aoff64_t, size_t, void *, size_t);
+extern int bd_read_toc(bd_t *, uint8_t, void *, size_t);
+extern int bd_write_blocks(bd_t *, aoff64_t, size_t, const void *, size_t);
+extern int bd_get_block_size(bd_t *, size_t *);
+extern int bd_get_num_blocks(bd_t *, aoff64_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/bd_srv.h
===================================================================
--- uspace/lib/c/include/bd_srv.h	(revision 4802dd7d8d17cb02ec8f79547ffde538bb8c6736)
+++ uspace/lib/c/include/bd_srv.h	(revision 4802dd7d8d17cb02ec8f79547ffde538bb8c6736)
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2012 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_BD_SRV_H_
+#define LIBC_BD_SRV_H_
+
+#include <async.h>
+#include <fibril_synch.h>
+#include <bool.h>
+#include <sys/types.h>
+
+typedef struct bd_ops bd_ops_t;
+
+typedef struct {
+	fibril_mutex_t lock;
+	bool connected;
+	bd_ops_t *ops;
+	void *arg;
+	async_sess_t *client_sess;
+} bd_srv_t;
+
+typedef struct bd_ops {
+	int (*open)(bd_srv_t *);
+	int (*close)(bd_srv_t *);
+	int (*read_blocks)(bd_srv_t *, aoff64_t, size_t, void *, size_t);
+	int (*read_toc)(bd_srv_t *, uint8_t, void *, size_t);
+	int (*write_blocks)(bd_srv_t *, aoff64_t, size_t, const void *, size_t);
+	int (*get_block_size)(bd_srv_t *, size_t *);
+	int (*get_num_blocks)(bd_srv_t *, aoff64_t *);
+} bd_ops_t;
+
+extern void bd_srv_init(bd_srv_t *);
+
+extern int bd_conn(ipc_callid_t, ipc_call_t *, void *);
+
+#endif
+
+/** @}
+ */
