diff --git a/repos/os/include/block/driver.h b/repos/os/include/block/driver.h
index 83dd12eca5..9b4533a3a4 100644
--- a/repos/os/include/block/driver.h
+++ b/repos/os/include/block/driver.h
@@ -24,13 +24,30 @@
 #include <block_session/rpc_object.h>
 
 namespace Block {
+	class Driver_session_base;
 	class Driver_session;
 	class Driver;
 	struct Driver_factory;
 };
 
 
-class Block::Driver_session : public Block::Session_rpc_object
+struct Block::Driver_session_base
+{
+	/**
+	 * Acknowledges a packet processed by the driver to the client
+	 *
+	 * \param packet   the packet to acknowledge
+	 * \param success  indicated whether the processing was successful
+	 *
+	 * \throw Ack_congestion
+	 */
+	virtual void ack_packet(Packet_descriptor &packet,
+	                        bool success) = 0;
+};
+
+
+class Block::Driver_session : public Driver_session_base,
+                              public Block::Session_rpc_object
 {
 	public:
 
@@ -44,17 +61,6 @@ class Block::Driver_session : public Block::Session_rpc_object
 		Driver_session(Genode::Dataspace_capability tx_ds,
 		               Genode::Rpc_entrypoint &ep)
 		: Session_rpc_object(tx_ds, ep) { }
-
-		/**
-		 * Acknowledges a packet processed by the driver to the client
-		 *
-		 * \param packet   the packet to acknowledge
-		 * \param success  indicated whether the processing was successful
-		 *
-		 * \throw Ack_congestion
-		 */
-		virtual void ack_packet(Packet_descriptor &packet,
-		                        bool success) = 0;
 };
 
 
@@ -65,7 +71,7 @@ class Block::Driver
 {
 	private:
 
-		Driver_session *_session = nullptr;
+		Driver_session_base *_session = nullptr;
 
 	public:
 
@@ -214,7 +220,7 @@ class Block::Driver
 		 *
 		 * Session might get used to acknowledge requests.
 		 */
-		void session(Driver_session *session) {
+		void session(Driver_session_base *session) {
 			if (!(_session = session)) session_invalidated(); }
 
 		/**
diff --git a/repos/os/run/sd_card_bench.run b/repos/os/run/sd_card_bench.run
index b8c3019aba..9ac3108ade 100644
--- a/repos/os/run/sd_card_bench.run
+++ b/repos/os/run/sd_card_bench.run
@@ -2,13 +2,13 @@
 # Check for platform support
 #
 
-if {[expr ![have_spec exynos5] && \
-          ![have_spec omap4] && \
-          ![have_spec imx6] && \
-          ![have_spec imx53] && \
-          ![have_spec pl180] && \
-          ![have_spec rpi]]} \
-{
+      if {[have_spec pl180]}   { set buffer_size_kib [expr 12 * 1024]
+} elseif {[have_spec imx6]}    { set buffer_size_kib [expr      1024]
+} elseif {[have_spec imx53]}   { set buffer_size_kib [expr      1024]
+} elseif {[have_spec rpi]}     { set buffer_size_kib [expr  4 * 1024]
+} elseif {[have_spec omap4]}   { set buffer_size_kib [expr  4 * 1024]
+} elseif {[have_spec exynos5]} { set buffer_size_kib [expr      1024]
+} else   {
 	puts "\n Run script is not supported on this platform. \n";
 	exit 0;
 }
@@ -29,6 +29,7 @@ append_platform_drv_build_components
 build $build_components
 create_boot_directory
 
+
 #
 # Install the config
 #
@@ -51,6 +52,7 @@ append config {
 
 append_platform_drv_config
 
+
 append config {
 	<start name="timer">
 		<resource name="RAM" quantum="1M"/>
@@ -58,7 +60,11 @@ append config {
 	</start>
 	<start name="sd_card_bench">
 		<resource name="RAM" quantum="16M"/>
-		<provides><service name="Block"/></provides>
+		<provides><service name="Block"/></provides>}
+
+append config "<config buffer_size_kib=\"$buffer_size_kib\"/>"
+
+append config {
 	</start>
 </config>}
 
diff --git a/repos/os/src/drivers/sd_card/spec/exynos5/driver.h b/repos/os/src/drivers/sd_card/spec/exynos5/driver.h
index d29d556b59..3edfe5dfdf 100644
--- a/repos/os/src/drivers/sd_card/spec/exynos5/driver.h
+++ b/repos/os/src/drivers/sd_card/spec/exynos5/driver.h
@@ -19,7 +19,6 @@
 #include <os/attached_io_mem_dataspace.h>
 #include <base/log.h>
 #include <timer_session/connection.h>
-#include <block/component.h>
 #include <os/server.h>
 #include <regulator_session/connection.h>
 
@@ -32,7 +31,8 @@ namespace Block {
 }
 
 
-class Block::Sdhci_driver : public Block::Driver
+class Block::Sdhci_driver : public Block::Driver,
+                            public Sd_ack_handler
 {
 	private:
 
@@ -81,7 +81,7 @@ class Block::Sdhci_driver : public Block::Driver
 			_clock_regulator(env),
 			_mmio(MSH_BASE, MSH_SIZE),
 			_controller(env.ep(), (addr_t)_mmio.local_addr<void>(),
-			            _delayer, true),
+			            _delayer, *this, true),
 			_use_dma(true)
 		{
 			Sd_card::Card_info const card_info = _controller.card_info();
@@ -90,6 +90,9 @@ class Block::Sdhci_driver : public Block::Driver
 			Genode::log("capacity: ", card_info.capacity_mb(), " MiB");
 		}
 
+		void handle_ack(Block::Packet_descriptor pkt, bool success) {
+			ack_packet(pkt, success); }
+
 
 		/*****************************
 		 ** Block::Driver interface **
@@ -110,14 +113,12 @@ class Block::Sdhci_driver : public Block::Driver
 			return o;
 		}
 
-		void read_dma(Block::sector_t   block_number,
-		              Genode::size_t    block_count,
-		              Genode::addr_t    phys,
+		void read_dma(Block::sector_t    block_number,
+		              Genode::size_t     block_count,
+		              Genode::addr_t     phys,
 		              Packet_descriptor &packet)
 		{
-			if (!_controller.read_blocks_dma(block_number, block_count, phys))
-				throw Io_error();
-			ack_packet(packet);
+			_controller.read_blocks_dma(block_number, block_count, phys, packet);
 		}
 
 		void write_dma(Block::sector_t    block_number,
@@ -125,9 +126,7 @@ class Block::Sdhci_driver : public Block::Driver
 		               Genode::addr_t     phys,
 		               Packet_descriptor &packet)
 		{
-			if (!_controller.write_blocks_dma(block_number, block_count, phys))
-				throw Io_error();
-			ack_packet(packet);
+			_controller.write_blocks_dma(block_number, block_count, phys, packet);
 		}
 
 		bool dma_enabled() { return _use_dma; }
diff --git a/repos/os/src/drivers/sd_card/spec/exynos5/dwmmc.h b/repos/os/src/drivers/sd_card/spec/exynos5/dwmmc.h
index 3acb7bbd17..d6d16b6ce8 100644
--- a/repos/os/src/drivers/sd_card/spec/exynos5/dwmmc.h
+++ b/repos/os/src/drivers/sd_card/spec/exynos5/dwmmc.h
@@ -18,6 +18,7 @@
 #include <irq_session/connection.h>
 #include <os/attached_ram_dataspace.h>
 #include <util/mmio.h>
+#include <block/component.h>
 
 #include <sd_card.h>
 
@@ -268,6 +269,11 @@ struct Dwmmc : Genode::Mmio
 };
 
 
+struct Sd_ack_handler {
+	virtual void handle_ack(Block::Packet_descriptor pkt, bool success) = 0;
+};
+
+
 struct Exynos5_msh_controller : private Dwmmc, Sd_card::Host_controller
 {
 	private:
@@ -325,9 +331,16 @@ struct Exynos5_msh_controller : private Dwmmc, Sd_card::Host_controller
 		Delayer           &_delayer;
 		Sd_card::Card_info _card_info;
 
-		Genode::Irq_connection  _irq;
-		Genode::Signal_receiver _irq_rec;
-		Genode::Signal_context  _irq_ctx;
+		struct Block_transfer
+		{
+			Block::Packet_descriptor packet;
+			bool                     pending = false;
+
+		} _block_transfer;
+
+		Sd_ack_handler                                 &_ack_handler;
+		Genode::Signal_handler<Exynos5_msh_controller>  _irq_handler;
+		Genode::Irq_connection                          _irq;
 
 		Sd_card::Card_info _init()
 		{
@@ -473,51 +486,42 @@ struct Exynos5_msh_controller : private Dwmmc, Sd_card::Host_controller
 			return true;
 		}
 
-		void _wait_for_irq()
+		void _handle_irq()
 		{
-			/*
-			 * Acknowledge the IRQ first to implicitly activate
-			 * receiving of further IRQ signals on the first usage
-			 * of this method.
-			 */
 			_irq.ack_irq();
-			_irq_rec.wait_for_signal();
-		}
 
-		bool _wait_for_transfer_complete()
-		{
-			while (1) {
-				_wait_for_irq();
+			if (!_block_transfer.pending) {
+				return; }
 
-				if (read<Rintsts::Data_transfer_over>()) {
-					write<Rintsts>(~0U);
-					return true;
-				}
-
-				if (read<Rintsts::Response_error>()) {
-					Genode::error("Response error");
-					return false;
-				}
-
-				if (read<Rintsts::Data_read_timeout>()) {
-					Genode::error("Data read timeout");
-					return false;
-				}
-
-				if (read<Rintsts::Data_crc_error>()) {
-					Genode::error("CRC error");
-					return false;
+			bool success = false;
+			if (read<Rintsts::Response_error>()) {
+				Genode::error("Response error");
+			}
+			if (read<Rintsts::Data_read_timeout>()) {
+				Genode::error("Data read timeout");
+			}
+			if (read<Rintsts::Data_crc_error>()) {
+				Genode::error("CRC error");
+			}
+			if (read<Rintsts::Data_transfer_over>()) {
+				write<Rintsts>(~0U);
+				if (!_issue_command(Sd_card::Stop_transmission())) {
+					Genode::error("unable to stop transmission");
+				} else {
+					success = true;
 				}
 			}
+			_block_transfer.pending = false;
+			_ack_handler.handle_ack(_block_transfer.packet, success);
 		}
 
-
 	public:
 
 		enum { IRQ_NUMBER = Genode::Board_base::SDMMC0_IRQ };
 
 		Exynos5_msh_controller(Server::Entrypoint &ep,
 		                       Genode::addr_t const mmio_base, Delayer &delayer,
+		                       Sd_ack_handler &ack_handler,
 		                       bool use_dma)
 		: Dwmmc(mmio_base),
 			_idmac_desc_ds(Genode::env()->ram_session(),
@@ -525,13 +529,15 @@ struct Exynos5_msh_controller : private Dwmmc, Sd_card::Host_controller
 			               Genode::UNCACHED),
 			_idmac_desc(_idmac_desc_ds.local_addr<Idmac_desc>()),
 			_idmac_desc_phys(Genode::Dataspace_client(_idmac_desc_ds.cap()).phys_addr()),
-			_delayer(delayer), _card_info(_init()), _irq(IRQ_NUMBER)
+			_delayer(delayer), _card_info(_init()),
+			_ack_handler(ack_handler),
+			_irq_handler(ep, *this, &Exynos5_msh_controller::_handle_irq),
+			_irq(IRQ_NUMBER)
 		{
-			_irq.sigh(_irq_rec.manage(&_irq_ctx));
+			_irq.sigh(_irq_handler);
+			_irq.ack_irq();
 		}
 
-		~Exynos5_msh_controller() { _irq_rec.dissolve(&_irq_ctx); }
-
 		bool _issue_command(Sd_card::Command_base const &command)
 		{
 			if (verbose)
@@ -661,49 +667,44 @@ struct Exynos5_msh_controller : private Dwmmc, Sd_card::Host_controller
 			return _card_info;
 		}
 
-		bool read_blocks_dma(Block::sector_t block_number, size_t block_count,
-		                     Genode::addr_t buffer_phys)
+		void read_blocks_dma(Block::sector_t block_number, size_t block_count,
+		                     Genode::addr_t buffer_phys, Block::Packet_descriptor pkt)
 		{
+			if (_block_transfer.pending) {
+				throw Block::Driver::Request_congestion(); }
+
 			if (!_setup_idmac_descriptor_table(block_count, buffer_phys))
-				return false;
+				throw Block::Driver::Io_error();
+
+			_block_transfer.packet  = pkt;
+			_block_transfer.pending = true;
 
 			if (!_issue_command(Sd_card::Read_multiple_block(block_number))) {
 				Genode::error("Read_multiple_block failed, Status: ",
 				              Genode::Hex(read<Status>()));
-				return false;
+				throw Block::Driver::Io_error();
 			}
-
-			bool complete = _wait_for_transfer_complete();
-
-			if (!_issue_command(Sd_card::Stop_transmission())) {
-				Genode::error("unable to stop transmission");
-				return false;
-			}
-
-			return complete;
 		}
 
-		bool write_blocks_dma(Block::sector_t block_number, size_t block_count,
-		                      Genode::addr_t buffer_phys)
+		void write_blocks_dma(Block::sector_t block_number, size_t block_count,
+		                      Genode::addr_t buffer_phys, Block::Packet_descriptor pkt)
 		{
+			if (_block_transfer.pending) {
+				throw Block::Driver::Request_congestion(); }
+
 			if (!_setup_idmac_descriptor_table(block_count, buffer_phys))
-				return false;
+				throw Block::Driver::Io_error();
+
+			_block_transfer.packet  = pkt;
+			_block_transfer.pending = true;
 
 			if (!_issue_command(Sd_card::Write_multiple_block(block_number))) {
 				Genode::error("Read_multiple_block failed, Status: ",
 				              Genode::Hex(read<Status>()));
-				return false;
+				throw Block::Driver::Io_error();
 			}
-
-			bool complete = _wait_for_transfer_complete();
-
-			if (!_issue_command(Sd_card::Stop_transmission())) {
-				Genode::error("unable to stop transmission");
-				return false;
-			}
-
-			return complete;
 		}
 };
 
+
 #endif /* _DRIVERS__SD_CARD__SPEC__EXYNOS5__DWMMC_H_ */
diff --git a/repos/os/src/drivers/sd_card/spec/imx/driver.h b/repos/os/src/drivers/sd_card/spec/imx/driver.h
index 743dc87eb5..7d64b68a67 100644
--- a/repos/os/src/drivers/sd_card/spec/imx/driver.h
+++ b/repos/os/src/drivers/sd_card/spec/imx/driver.h
@@ -14,123 +14,7 @@
 #ifndef _DRIVER_H_
 #define _DRIVER_H_
 
-/* Genode includes */
-#include <util/mmio.h>
-#include <os/attached_io_mem_dataspace.h>
-#include <base/log.h>
-#include <timer_session/connection.h>
-#include <block/component.h>
-#include <drivers/board_base.h>
-#include <os/server.h>
-
 /* local includes */
 #include <sdhc.h>
 
-namespace Block {
-	using namespace Genode;
-	class Sdhci_driver;
-}
-
-
-class Block::Sdhci_driver : public Block::Driver
-{
-	private:
-
-		struct Timer_delayer : Timer::Connection, Mmio::Delayer
-		{
-			/**
-			 * Implementation of 'Delayer' interface
-			 */
-			void usleep(unsigned us) { Timer::Connection::usleep(us); }
-		} _delayer;
-
-		Attached_io_mem_dataspace _sdhc_mmio;
-		Sdhc                      _controller;
-
-		bool const _use_dma;
-
-	public:
-
-		Sdhci_driver(Env &)
-		:
-			_sdhc_mmio(Board_base::SDHC_MMIO_BASE, Board_base::SDHC_MMIO_SIZE),
-			_controller((addr_t)_sdhc_mmio.local_addr<void>(),
-			            Board_base::SDHC_IRQ, _delayer, true),
-			_use_dma(true)
-		{
-			Sd_card::Card_info const card_info = _controller.card_info();
-
-			log("SD card detected");
-			log("capacity: ", card_info.capacity_mb(), " MiB");
-		}
-
-
-		/*****************************
-		 ** Block::Driver interface **
-		 *****************************/
-
-		Genode::size_t block_size() { return 512; }
-
-		virtual Block::sector_t block_count()
-		{
-			return _controller.card_info().capacity_mb() * 1024 * 2;
-		}
-
-		Block::Session::Operations ops()
-		{
-			Block::Session::Operations o;
-			o.set_operation(Block::Packet_descriptor::READ);
-			o.set_operation(Block::Packet_descriptor::WRITE);
-			return o;
-		}
-
-		void read(Block::sector_t    block_number,
-		          Genode::size_t     block_count,
-		          char              *out_buffer,
-		          Packet_descriptor &packet)
-		{
-			if (!_controller.read_blocks(block_number, block_count, out_buffer))
-				throw Io_error();
-			ack_packet(packet);
-		}
-
-		void write(Block::sector_t    block_number,
-		           Genode::size_t     block_count,
-		           char const        *buffer,
-		           Packet_descriptor &packet)
-		{
-			if (!_controller.write_blocks(block_number, block_count, buffer))
-				throw Io_error();
-			ack_packet(packet);
-		}
-
-		void read_dma(Block::sector_t    block_number,
-		              Genode::size_t     block_count,
-		              Genode::addr_t     phys,
-		              Packet_descriptor &packet)
-		{
-			if (!_controller.read_blocks_dma(block_number, block_count, phys))
-				throw Io_error();
-			ack_packet(packet);
-		}
-
-		void write_dma(Block::sector_t    block_number,
-		               Genode::size_t     block_count,
-		               Genode::addr_t     phys,
-		               Packet_descriptor &packet)
-		{
-			if (!_controller.write_blocks_dma(block_number, block_count, phys))
-				throw Io_error();
-			ack_packet(packet);
-		}
-
-		bool dma_enabled() { return _use_dma; }
-
-		Genode::Ram_dataspace_capability alloc_dma_buffer(Genode::size_t size) {
-			return Genode::env()->ram_session()->alloc(size, UNCACHED); }
-
-		void free_dma_buffer(Genode::Ram_dataspace_capability c) {
-			return Genode::env()->ram_session()->free(c); }
-};
-
 #endif /* _DRIVER_H_ */
diff --git a/repos/os/src/drivers/sd_card/spec/imx/sdhc.cc b/repos/os/src/drivers/sd_card/spec/imx/sdhc.cc
index cc22c44cbb..e02a6b3514 100644
--- a/repos/os/src/drivers/sd_card/spec/imx/sdhc.cc
+++ b/repos/os/src/drivers/sd_card/spec/imx/sdhc.cc
@@ -49,14 +49,14 @@ int Sdhc::_wait_for_card_ready_mbw()
 
 		/* send command as soon as the host allows it */
 		if (_wait_for_cmd_allowed()) { return -1; }
-		write<Cmdarg>(cmdarg);
-		write<Xfertyp>(xfertyp);
+		Mmio::write<Cmdarg>(cmdarg);
+		Mmio::write<Xfertyp>(xfertyp);
 
 		/* wait for command completion */
 		if (_wait_for_cmd_complete()) { return -1; }
 
 		/* check for errors */
-		R1_response_0::access_t const resp = read<Cmdrsp0>();
+		R1_response_0::access_t const resp = Mmio::read<Cmdrsp0>();
 		if (R1_response_0::Error::get(resp)) {
 			error("Reading card status after multiblock write failed");
 			return -1;
@@ -72,7 +72,7 @@ int Sdhc::_wait_for_card_ready_mbw()
 int Sdhc::_stop_transmission()
 {
 	/* write argument register */
-	write<Cmdarg>(0);
+	Mmio::write<Cmdarg>(0);
 
 	/* write command register */
 	Xfertyp::access_t xfertyp = 0;
@@ -82,7 +82,7 @@ int Sdhc::_stop_transmission()
 	Xfertyp::Cicen::set(xfertyp, 1);
 	Xfertyp::Rsptyp::set(xfertyp, Xfertyp::Rsptyp::_48BIT_BUSY);
 	_stop_transmission_finish_xfertyp(xfertyp);
-	write<Xfertyp>(xfertyp);
+	Mmio::write<Xfertyp>(xfertyp);
 
 	/* wait for command completion */
 	if (_wait_for_cmd_complete()) { return -1; }
@@ -90,8 +90,14 @@ int Sdhc::_stop_transmission()
 }
 
 
-int Sdhc::_wait_for_cmd_complete_mb(bool const r)
+void Sdhc::_handle_irq()
 {
+	_irq.ack_irq();
+
+	/* the handler is only for block transfers, on other commands we poll */
+	if (!_block_transfer.pending) {
+		return; }
+
 	/*
 	 * The host signals on multi-block transfers seem to be broken.
 	 * Synchronizing to "Transfer Complete" before returning from transfers
@@ -99,38 +105,33 @@ int Sdhc::_wait_for_cmd_complete_mb(bool const r)
 	 * done with other controllers - isn't sufficient. Instead, both "Transfer
 	 * Complete" and "Command Complete" must be gathered.
 	 */
-	Irqstat::access_t constexpr irq_goal =
-		Irq::Cc::reg_mask() | Irq::Tc::reg_mask();
-
-	/* wait for a first signal */
-	_wait_for_irq();
-	Irqstat::access_t const irq = read<Irqstat>();
-
-	/*
-	 * Poll for missing signal because interrupts are edge-triggered
-	 * and could thus got lost in the meantime.
-	 */
-	if (irq != irq_goal) {
-		if (!wait_for<Irqstat>(irq_goal, _delayer)) {
-			error("Completion host signal timed out");
-			return -1;
-		}
+	if (!wait_for<Irqstat::Cc>(1, _delayer) ||
+	    !wait_for<Irqstat::Tc>(1, _delayer))
+	{
+		error("Completion host signal timed out");
+		throw -1;
 	}
 	/* acknowledge completion signals */
-	write<Irqstat>(irq_goal);
-	return _wait_for_cmd_complete_mb_finish(r);
+	Irqstat::access_t irqstat = 0;
+	Irqstat::Cc::set(irqstat, 1);
+	Irqstat::Tc::set(irqstat, 1);
+	Mmio::write<Irqstat>(irqstat);
+
+	if (_wait_for_cmd_complete_mb_finish(_block_transfer.read)) {
+		throw -1; }
+
+	_block_transfer.pending = false;
+	ack_packet(_block_transfer.packet, true);
 }
 
 
 int Sdhc::_wait_for_cmd_complete()
 {
-	/* wait for "Command Completion" signal and acknowledge it */
-	_wait_for_irq();
-	if (read<Irqstat>() != Irqstat::Cc::reg_mask()) {
-		error("received unexpected host signal");
+	if (!wait_for<Irqstat::Cc>(1, _delayer, 200, 5000)) {
+		error("command timed out");
 		return -1;
 	}
-	write<Irqstat>(Irqstat::Cc::reg_mask());
+	Mmio::write<Irqstat>(Irqstat::Cc::reg_mask());
 	return 0;
 }
 
@@ -169,22 +170,21 @@ bool Sdhc::_issue_command(Command_base const & command)
 	}
 	/* version-dependent transfer settings and issue command */
 	_issue_cmd_finish_xfertyp(xfertyp, transfer, multiblock, reading);
-	write<Cmdarg>(command.arg);
-	write<Xfertyp>(xfertyp);
+	Mmio::write<Cmdarg>(command.arg);
+	Mmio::write<Xfertyp>(xfertyp);
 
-	/* wait for completion */
-	return multiblock ? !_wait_for_cmd_complete_mb(reading) :
-	                    !_wait_for_cmd_complete();
+	/* for block transfers there's a signal handler, on other commands poll */
+	return transfer ? true : !_wait_for_cmd_complete();
 }
 
 
 Cid Sdhc::_read_cid()
 {
 	Cid cid;
-	cid.raw_0 = read<Rsp136_0>();
-	cid.raw_1 = read<Rsp136_1>();
-	cid.raw_2 = read<Rsp136_2>();
-	cid.raw_3 = read<Rsp136_3>();
+	cid.raw_0 = Mmio::read<Rsp136_0>();
+	cid.raw_1 = Mmio::read<Rsp136_1>();
+	cid.raw_2 = Mmio::read<Rsp136_2>();
+	cid.raw_3 = Mmio::read<Rsp136_3>();
 	return cid;
 }
 
@@ -192,70 +192,89 @@ Cid Sdhc::_read_cid()
 Csd Sdhc::_read_csd()
 {
 	Csd csd;
-	csd.csd0 = read<Rsp136_0>();
-	csd.csd1 = read<Rsp136_1>();
-	csd.csd2 = read<Rsp136_2>();
-	csd.csd3 = read<Rsp136_3>();
+	csd.csd0 = Mmio::read<Rsp136_0>();
+	csd.csd1 = Mmio::read<Rsp136_1>();
+	csd.csd2 = Mmio::read<Rsp136_2>();
+	csd.csd3 = Mmio::read<Rsp136_3>();
 	return csd;
 }
 
 
 unsigned Sdhc::_read_rca()
 {
-	Cmdrsp0::access_t const rsp0 = read<Cmdrsp0>();
+	Cmdrsp0::access_t const rsp0 = Mmio::read<Cmdrsp0>();
 	return Send_relative_addr::Response::Rca::get(rsp0);
 }
 
 
 bool Sdhc::read_blocks(size_t, size_t, char *)
 {
-	error("block transfer without DMA not supported by now");
+	error("block transfer without DMA not supported");
 	return false;
 }
 
 
 bool Sdhc::write_blocks(size_t, size_t, char const *)
 {
-	error("block transfer without DMA not supported by now");
+	error("block transfer without DMA not supported");
 	return false;
 }
 
 
-bool Sdhc::read_blocks_dma(size_t blk_nr, size_t blk_cnt, addr_t buf_phys)
+bool Sdhc::read_blocks_dma(Block::Packet_descriptor packet,
+                           size_t                   blk_nr,
+                           size_t                   blk_cnt,
+                           addr_t                   buf_phys)
 {
-	if (_prepare_dma_mb(blk_cnt, buf_phys)) { return false; }
+	if (_prepare_dma_mb(packet, true, blk_cnt, buf_phys)) { return false; }
+
 	return issue_command(Read_multiple_block(blk_nr));
 }
 
 
-bool Sdhc::write_blocks_dma(size_t blk_nr, size_t blk_cnt,
-                                          addr_t buf_phys)
+bool Sdhc::write_blocks_dma(Block::Packet_descriptor packet,
+                            size_t                   blk_nr,
+                            size_t                   blk_cnt,
+                            addr_t                   buf_phys)
 {
-	if (_prepare_dma_mb(blk_cnt, buf_phys)) { return false; }
+	if (_prepare_dma_mb(packet, false, blk_cnt, buf_phys)) { return false; }
+
 	return issue_command(Write_multiple_block(blk_nr));
 }
 
 
-Sdhc::Sdhc(addr_t   const  base,
-           unsigned const  irq,
-           Delayer        &delayer,
-           bool     const  use_dma)
+Sdhc::Sdhc(Env &env)
 :
-	Mmio(base), _irq(irq), _delayer(delayer), _card_info(_init()),
-	_use_dma(use_dma)
-{ }
-
-
-int Sdhc::_prepare_dma_mb(size_t blk_cnt, addr_t buf_phys)
+	Attached_mmio(Board_base::SDHC_MMIO_BASE, Board_base::SDHC_MMIO_SIZE),
+	_irq_handler(env.ep(), *this, &Sdhc::_handle_irq), _irq(Board_base::SDHC_IRQ),
+	_card_info(_init()), _use_dma(true)
 {
+	log("SD card detected");
+	log("capacity: ", card_info().capacity_mb(), " MiB");
+}
+
+
+int Sdhc::_prepare_dma_mb(Block::Packet_descriptor packet,
+                          bool                     reading,
+                          size_t                   blk_cnt,
+                          addr_t                   buf_phys)
+{
+	if (_block_transfer.pending) {
+		throw Block::Driver::Request_congestion(); }
+
+
 	/* write ADMA2 table to DMA */
 	size_t const req_size = blk_cnt * BLOCK_SIZE;
 	if (_adma2_table.setup_request(req_size, buf_phys)) { return -1; }
 
 	/* configure DMA at host */
-	write<Adsaddr>(_adma2_table.base_phys());
-	write<Blkattr::Blksize>(BLOCK_SIZE);
-	write<Blkattr::Blkcnt>(blk_cnt);
+	Mmio::write<Adsaddr>(_adma2_table.base_phys());
+	Mmio::write<Blkattr::Blksize>(BLOCK_SIZE);
+	Mmio::write<Blkattr::Blkcnt>(blk_cnt);
+
+	_block_transfer.read    = reading;
+	_block_transfer.packet  = packet;
+	_block_transfer.pending = true;
 	return 0;
 }
 
@@ -279,24 +298,17 @@ int Sdhc::_wait_for_cmd_allowed()
 }
 
 
-void Sdhc::_wait_for_irq()
-{
-	/* acknowledge IRQ first, to activate IRQ propagation initially */
-	_irq.ack_irq();
-	_irq_rec.wait_for_signal();
-}
-
-
 Card_info Sdhc::_init()
 {
 	/* install IRQ signal */
-	_irq.sigh(_irq_rec.manage(&_irq_ctx));
+	_irq.sigh(_irq_handler);
+	_irq.ack_irq();
 
 	/* configure host for initialization stage */
 	if (_reset(_delayer)) { _detect_err("Host reset failed"); }
 	_disable_irqs();
 
-	if (!_supported_host_version(read<Hostver>())) {
+	if (!_supported_host_version(Mmio::read<Hostver>())) {
 		error("host version not supported");
 		throw Detection_failed();
 	}
@@ -332,7 +344,7 @@ Card_info Sdhc::_init()
 	if (!issue_command(Send_if_cond())) {
 		_detect_err("Send_if_cond command failed"); }
 
-	if (read<Cmdrsp0>() != 0x1aa) {
+	if (Mmio::read<Cmdrsp0>() != 0x1aa) {
 		_detect_err("Unexpected response of Send_if_cond command"); }
 
 	/*
@@ -352,7 +364,7 @@ Card_info Sdhc::_init()
 	if (!issue_command(Send_if_cond())) {
 		_detect_err("Send_if_cond failed"); }
 
-	if (read<Cmdrsp0>() != 0x1aa) {
+	if (Mmio::read<Cmdrsp0>() != 0x1aa) {
 		_detect_err("Unexpected response of Send_if_cond command"); }
 
 	/*
@@ -369,7 +381,7 @@ Card_info Sdhc::_init()
 		if (!issue_command(Sd_send_op_cond(0x200000, true))) {
 			_detect_err("Sd_send_op_cond command failed"); }
 
-		if (Ocr::Busy::get(read<Cmdrsp0>())) { break; }
+		if (Ocr::Busy::get(Mmio::read<Cmdrsp0>())) { break; }
 		_delayer.usleep(1000);
 	}
 	if (!i) { _detect_err("Could not power-on SD card"); }
@@ -405,16 +417,16 @@ Card_info Sdhc::_init()
 		_detect_err("Set_blocklen command failed"); }
 
 	/* configure host buffer */
-	Wml::access_t wml = read<Wml>();
+	Wml::access_t wml = Mmio::read<Wml>();
 	_watermark_level(wml);
-	write<Wml>(wml);
+	Mmio::write<Wml>(wml);
 
 	/* configure ADMA */
-	write<Proctl::Dmas>(Proctl::Dmas::ADMA2);
+	Mmio::write<Proctl::Dmas>(Proctl::Dmas::ADMA2);
 
 	/* configure interrupts for operational mode */
 	_disable_irqs();
-	write<Irqstat>(~0);
+	Mmio::write<Irqstat>(~0);
 	_enable_irqs();
 	return card_info;
 }
@@ -430,7 +442,7 @@ void Sdhc::_detect_err(char const * const err)
 int Sdhc::_reset(Delayer &delayer)
 {
 	/* start reset */
-	write<Sysctl::Rsta>(1);
+	Mmio::write<Sysctl::Rsta>(1);
 	_reset_amendments();
 
 	/* wait for reset completion */
@@ -444,8 +456,8 @@ int Sdhc::_reset(Delayer &delayer)
 
 void Sdhc::_disable_irqs()
 {
-	write<Irqstaten>(0);
-	write<Irqsigen>(0);
+	Mmio::write<Irqstaten>(0);
+	Mmio::write<Irqsigen>(0);
 }
 
 
@@ -464,16 +476,16 @@ void Sdhc::_enable_irqs()
 	Irq::Debe::set(irq, 1);
 	Irq::Ac12e::set(irq, 1);
 	Irq::Dmae::set(irq, 1);
-	write<Irqstaten>(irq);
-	write<Irqsigen>(irq);
+	Mmio::write<Irqstaten>(irq);
+	Mmio::write<Irqsigen>(irq);
 }
 
 
 void Sdhc::_bus_width(Bus_width bus_width)
 {
 	switch (bus_width) {
-	case BUS_WIDTH_1: write<Proctl::Dtw>(Proctl::Dtw::_1BIT); break;
-	case BUS_WIDTH_4: write<Proctl::Dtw>(Proctl::Dtw::_4BIT); break;
+	case BUS_WIDTH_1: Mmio::write<Proctl::Dtw>(Proctl::Dtw::_1BIT); break;
+	case BUS_WIDTH_4: Mmio::write<Proctl::Dtw>(Proctl::Dtw::_4BIT); break;
 	}
 }
 
@@ -481,19 +493,19 @@ void Sdhc::_bus_width(Bus_width bus_width)
 void Sdhc::_disable_clock()
 {
 	_disable_clock_preparation();
-	Sysctl::access_t sysctl = read<Sysctl>();
+	Sysctl::access_t sysctl = Mmio::read<Sysctl>();
 	Sysctl::Ipgen::set(sysctl, 0);
 	Sysctl::Hcken::set(sysctl, 0);
 	Sysctl::Peren::set(sysctl, 0);
 	Sysctl::Dvs::set(sysctl, Sysctl::Dvs::DIV1);
 	Sysctl::Sdclkfs::set(sysctl, Sysctl::Sdclkfs::DIV1);
-	write<Sysctl>(sysctl);
+	Mmio::write<Sysctl>(sysctl);
 }
 
 
 void Sdhc::_enable_clock(Clock_divider divider)
 {
-	Sysctl::access_t sysctl = read<Sysctl>();
+	Sysctl::access_t sysctl = Mmio::read<Sysctl>();
 	Sysctl::Ipgen::set(sysctl, 1);
 	Sysctl::Hcken::set(sysctl, 1);
 	Sysctl::Peren::set(sysctl, 1);
@@ -511,7 +523,7 @@ void Sdhc::_enable_clock(Clock_divider divider)
 		Sysctl::Sdclkfs::set(sysctl, Sysctl::Sdclkfs::DIV32);
 		break;
 	}
-	write<Sysctl>(sysctl);
+	Mmio::write<Sysctl>(sysctl);
 	_enable_clock_finish();
 	_delayer.usleep(1000);
 }
diff --git a/repos/os/src/drivers/sd_card/spec/imx/sdhc.h b/repos/os/src/drivers/sd_card/spec/imx/sdhc.h
index 8943475d88..cb8150722b 100644
--- a/repos/os/src/drivers/sd_card/spec/imx/sdhc.h
+++ b/repos/os/src/drivers/sd_card/spec/imx/sdhc.h
@@ -15,9 +15,16 @@
 #define _SDHC_H_
 
 /* Genode includes */
+#include <base/log.h>
+#include <os/attached_io_mem_dataspace.h>
+#include <timer_session/connection.h>
 #include <util/mmio.h>
 #include <irq_session/connection.h>
 #include <base/sleep.h>
+#include <block/driver.h>
+#include <block/component.h>
+#include <drivers/board_base.h>
+#include <os/attached_mmio.h>
 
 /* local includes */
 #include <sd_card.h>
@@ -26,7 +33,9 @@
 namespace Genode { class Sdhc; }
 
 
-class Genode::Sdhc : public Sd_card::Host_controller, public Mmio
+class Genode::Sdhc : public Attached_mmio,
+                     public Block::Driver,
+                     public Sd_card::Host_controller
 {
 	private:
 
@@ -179,17 +188,30 @@ class Genode::Sdhc : public Sd_card::Host_controller, public Mmio
 
 		enum Clock_divider { CLOCK_DIV_4, CLOCK_DIV_8, CLOCK_DIV_512 };
 
-		Irq_connection     _irq;
-		Signal_receiver    _irq_rec;
-		Signal_context     _irq_ctx;
-		Delayer &          _delayer;
-		Sd_card::Card_info _card_info;
-		bool const         _use_dma;
-		Adma2::Table       _adma2_table;
+		struct Timer_delayer : Timer::Connection, Mmio::Delayer
+		{
+			void usleep(unsigned us) { Timer::Connection::usleep(us); }
+
+		} _delayer;
+
+		struct Block_transfer
+		{
+			Block::Packet_descriptor packet;
+			bool                     pending = false;
+			bool                     read;
+
+		} _block_transfer;
+
+		Signal_handler<Sdhc> _irq_handler;
+		Irq_connection       _irq;
+		Sd_card::Card_info   _card_info;
+		bool const           _use_dma;
+		Adma2::Table         _adma2_table;
 
 		static bool _supported_host_version(Hostver::access_t hostver);
 		static void _watermark_level(Wml::access_t &wml);
 
+		void _handle_irq();
 		void _detect_err(char const * const err);
 		void _disable_irqs();
 		void _enable_irqs();
@@ -200,7 +222,6 @@ class Genode::Sdhc : public Sd_card::Host_controller, public Mmio
 		void _enable_clock_finish();
 		void _clock(Clock clock);
 		void _clock_finish(Clock clock);
-		void _wait_for_irq();
 		int  _reset(Delayer & delayer);
 		void _reset_amendments();
 		int  _wait_for_cmd_allowed();
@@ -208,9 +229,8 @@ class Genode::Sdhc : public Sd_card::Host_controller, public Mmio
 		int  _wait_for_card_ready_mbw();
 		int  _stop_transmission();
 		void _stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp);
-		int  _wait_for_cmd_complete_mb(bool const r);
 		int  _wait_for_cmd_complete_mb_finish(bool const reading);
-		int  _prepare_dma_mb(size_t blk_cnt, addr_t buf_phys);
+		int  _prepare_dma_mb(Block::Packet_descriptor packet, bool reading, size_t blk_cnt, addr_t buf_phys);
 		bool _issue_cmd_finish_xfertyp(Xfertyp::access_t &xfertyp,
 		                               bool const         transfer,
 		                               bool const         multiblock,
@@ -232,18 +252,8 @@ class Genode::Sdhc : public Sd_card::Host_controller, public Mmio
 
 		/**
 		 * Constructor
-		 *
-		 * \param base       local base address of MMIO registers
-		 * \param irq        host-interrupt ID
-		 * \param delayer    delayer timing of MMIO accesses
-		 * \param use_dma    wether to use DMA or direct IO for transfers
 		 */
-		Sdhc(addr_t   const  base,
-		     unsigned const  irq,
-		     Delayer        &delayer,
-		     bool     const  use_dma);
-
-		~Sdhc() { _irq_rec.dissolve(&_irq_ctx); }
+		Sdhc(Env &env);
 
 
 		/****************************************
@@ -252,10 +262,77 @@ class Genode::Sdhc : public Sd_card::Host_controller, public Mmio
 
 		bool read_blocks(size_t, size_t, char *);
 		bool write_blocks(size_t, size_t, char const *);
-		bool read_blocks_dma(size_t blk_nr, size_t blk_cnt, addr_t buf_phys);
-		bool write_blocks_dma(size_t blk_nr, size_t blk_cnt, addr_t buf_phys);
+		bool read_blocks_dma(Block::Packet_descriptor packet, size_t blk_nr, size_t blk_cnt, addr_t buf_phys);
+		bool write_blocks_dma(Block::Packet_descriptor packet, size_t blk_nr, size_t blk_cnt, addr_t buf_phys);
 
 		Sd_card::Card_info card_info() const { return _card_info; }
+
+
+
+		/*****************************
+		 ** Block::Driver interface **
+		 *****************************/
+
+		Genode::size_t block_size() { return 512; }
+
+		virtual Block::sector_t block_count()
+		{
+			return card_info().capacity_mb() * 1024 * 2;
+		}
+
+		Block::Session::Operations ops()
+		{
+			Block::Session::Operations o;
+			o.set_operation(Block::Packet_descriptor::READ);
+			o.set_operation(Block::Packet_descriptor::WRITE);
+			return o;
+		}
+
+		void read(Block::sector_t    block_number,
+		          Genode::size_t     block_count,
+		          char              *out_buffer,
+		          Block::Packet_descriptor &packet)
+		{
+//			if (!read_blocks(block_number, block_count, out_buffer))
+				throw Io_error();
+		}
+
+		void write(Block::sector_t    block_number,
+		           Genode::size_t     block_count,
+		           char const        *buffer,
+		           Block::Packet_descriptor &packet)
+		{
+//			if (!write_blocks(block_number, block_count, buffer))
+				throw Io_error();
+		}
+
+		void read_dma(Block::sector_t    block_number,
+		              Genode::size_t     block_count,
+		              Genode::addr_t     phys,
+		              Block::Packet_descriptor &packet)
+		{
+			if (!read_blocks_dma(packet, block_number, block_count, phys))
+				throw Io_error();
+		}
+
+		void write_dma(Block::sector_t    block_number,
+		               Genode::size_t     block_count,
+		               Genode::addr_t     phys,
+		               Block::Packet_descriptor &packet)
+		{
+			if (!write_blocks_dma(packet, block_number, block_count, phys))
+				throw Io_error();
+		}
+
+		bool dma_enabled() { return _use_dma; }
+
+		Genode::Ram_dataspace_capability alloc_dma_buffer(Genode::size_t size) {
+			return Genode::env()->ram_session()->alloc(size, UNCACHED); }
+
+		void free_dma_buffer(Genode::Ram_dataspace_capability c) {
+			return Genode::env()->ram_session()->free(c); }
 };
 
+namespace Block { using Sdhci_driver = Genode::Sdhc; }
+
 #endif /* _SDHC_H_ */
diff --git a/repos/os/src/drivers/sd_card/spec/imx53/sdhc.cc b/repos/os/src/drivers/sd_card/spec/imx53/sdhc.cc
index 8cf843d293..2de562c59a 100644
--- a/repos/os/src/drivers/sd_card/spec/imx53/sdhc.cc
+++ b/repos/os/src/drivers/sd_card/spec/imx53/sdhc.cc
@@ -98,21 +98,21 @@ void Sdhc::_reset_amendments()
 	 * nonetheless which disables clocks that card detection relies
 	 * on.
 	 */
-	Sysctl::access_t sysctl = read<Sysctl>();
+	Sysctl::access_t sysctl = Mmio::read<Sysctl>();
 	Sysctl::Ipgen::set(sysctl, 1);
 	Sysctl::Hcken::set(sysctl, 1);
 	Sysctl::Peren::set(sysctl, 1);
-	write<Sysctl>(sysctl);
+	Mmio::write<Sysctl>(sysctl);
 }
 
 
 void Sdhc::_clock_finish(Clock clock)
 {
-	write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_27);
+	Mmio::write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_27);
 	switch (clock) {
 	case CLOCK_INITIAL:     _enable_clock(CLOCK_DIV_512); break;
 	case CLOCK_OPERATIONAL: _enable_clock(CLOCK_DIV_8);   break; }
-	write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_27);
+	Mmio::write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_27);
 }
 
 
diff --git a/repos/os/src/drivers/sd_card/spec/imx6/sdhc.cc b/repos/os/src/drivers/sd_card/spec/imx6/sdhc.cc
index 5574443fa8..e89f313baf 100644
--- a/repos/os/src/drivers/sd_card/spec/imx6/sdhc.cc
+++ b/repos/os/src/drivers/sd_card/spec/imx6/sdhc.cc
@@ -20,7 +20,7 @@ using namespace Genode;
 
 void Sdhc::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
 {
-	Mixctrl::access_t mixctrl = read<Mixctrl>();
+	Mixctrl::access_t mixctrl = Mmio::read<Mixctrl>();
 	Mixctrl::Dmaen::set(mixctrl, 1);
 	Mixctrl::Bcen::set(mixctrl, 1);
 	Mixctrl::Ac12en::set(mixctrl, 0);
@@ -29,7 +29,7 @@ void Sdhc::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
 	Mixctrl::Msbsel::set(mixctrl, 1);
 	Mixctrl::Nibblepos::set(mixctrl, 0);
 	Mixctrl::Ac23en::set(mixctrl, 0);
-	write<Mixctrl>(mixctrl);
+	Mmio::write<Mixctrl>(mixctrl);
 }
 
 
@@ -45,7 +45,7 @@ bool Sdhc::_issue_cmd_finish_xfertyp(Xfertyp::access_t &,
                                      bool const         multiblock,
                                      bool const         reading)
 {
-	Mixctrl::access_t mixctrl = read<Mixctrl>();
+	Mixctrl::access_t mixctrl = Mmio::read<Mixctrl>();
 	Mixctrl::Dmaen    ::set(mixctrl, transfer && multiblock && _use_dma);
 	Mixctrl::Bcen     ::set(mixctrl, transfer);
 	Mixctrl::Ac12en   ::set(mixctrl, 0);
@@ -59,7 +59,7 @@ bool Sdhc::_issue_cmd_finish_xfertyp(Xfertyp::access_t &,
 	if (_wait_for_cmd_allowed()) {
 		return false; }
 
-	write<Mixctrl>(mixctrl);
+	Mmio::write<Mixctrl>(mixctrl);
 	return true;
 }
 
@@ -81,7 +81,7 @@ void Sdhc::_watermark_level(Wml::access_t &wml)
 void Sdhc::_reset_amendments()
 {
 	/* the USDHC doesn't reset the Mixer Control register automatically */
-	Mixctrl::access_t mixctrl = read<Mixctrl>();
+	Mixctrl::access_t mixctrl = Mmio::read<Mixctrl>();
 	Mixctrl::Dmaen::set(mixctrl, 0);
 	Mixctrl::Bcen::set(mixctrl, 0);
 	Mixctrl::Ac12en::set(mixctrl, 0);
@@ -91,7 +91,7 @@ void Sdhc::_reset_amendments()
 	Mixctrl::Nibblepos::set(mixctrl, 0);
 	Mixctrl::Ac23en::set(mixctrl, 0);
 	Mixctrl::Always_ones::set(mixctrl, 1);
-	write<Mixctrl>(mixctrl);
+	Mmio::write<Mixctrl>(mixctrl);
 }
 
 
@@ -99,18 +99,18 @@ void Sdhc::_clock_finish(Clock clock)
 {
 	switch (clock) {
 	case CLOCK_INITIAL:
-		write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_13);
+		Mmio::write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_13);
 		_enable_clock(CLOCK_DIV_512);
 		break;
 	case CLOCK_OPERATIONAL:
-		write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_28);
-		write<Sysctl::Ipp_rst_n>(0);
+		Mmio::write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_28);
+		Mmio::write<Sysctl::Ipp_rst_n>(0);
 		_enable_clock(CLOCK_DIV_4);
 		break;
 	}
 }
 
 
-void Sdhc::_disable_clock_preparation() { write<Vendspec::Frc_sdclk_on>(0); }
+void Sdhc::_disable_clock_preparation() { Mmio::write<Vendspec::Frc_sdclk_on>(0); }
 
-void Sdhc::_enable_clock_finish() { write<Vendspec::Frc_sdclk_on>(0); }
+void Sdhc::_enable_clock_finish() { Mmio::write<Vendspec::Frc_sdclk_on>(0); }
diff --git a/repos/os/src/drivers/sd_card/spec/omap4/driver.h b/repos/os/src/drivers/sd_card/spec/omap4/driver.h
index d99a75f3db..e11b9d38ab 100644
--- a/repos/os/src/drivers/sd_card/spec/omap4/driver.h
+++ b/repos/os/src/drivers/sd_card/spec/omap4/driver.h
@@ -18,7 +18,6 @@
 #include <os/attached_io_mem_dataspace.h>
 #include <base/log.h>
 #include <timer_session/connection.h>
-#include <block/component.h>
 #include <os/server.h>
 
 /* local includes */
@@ -30,7 +29,8 @@ namespace Block {
 }
 
 
-class Block::Sdhci_driver : public Block::Driver
+class Block::Sdhci_driver : public Block::Driver,
+                            public Sd_ack_handler
 {
 	private:
 
@@ -60,11 +60,11 @@ class Block::Sdhci_driver : public Block::Driver
 
 		struct Dma_not_supported : Exception { };
 
-		Sdhci_driver(Env &)
+		Sdhci_driver(Env &env)
 		:
 			_mmchs1_mmio(MMCHS1_MMIO_BASE, MMCHS1_MMIO_SIZE),
-			_controller((addr_t)_mmchs1_mmio.local_addr<void>(),
-			            _delayer, false),
+			_controller(env.ep(), (addr_t)_mmchs1_mmio.local_addr<void>(),
+			            _delayer, *this, false),
 			_use_dma(false)
 		{
 			if (_use_dma) { throw Dma_not_supported(); }
@@ -74,6 +74,9 @@ class Block::Sdhci_driver : public Block::Driver
 			Genode::log("capacity: ", card_info.capacity_mb(), " MiB");
 		}
 
+		void handle_ack(Block::Packet_descriptor pkt, bool success) {
+			ack_packet(pkt, success); }
+
 
 		/*****************************
 		 ** Block::Driver interface **
@@ -99,9 +102,7 @@ class Block::Sdhci_driver : public Block::Driver
 		          char              *out_buffer,
 		          Packet_descriptor &packet)
 		{
-			if (!_controller.read_blocks(block_number, block_count, out_buffer))
-				throw Io_error();
-			ack_packet(packet);
+			_controller.read_blocks(block_number, block_count, out_buffer, packet);
 		}
 
 		void write(Block::sector_t    block_number,
@@ -109,9 +110,7 @@ class Block::Sdhci_driver : public Block::Driver
 		           char const        *buffer,
 		           Packet_descriptor &packet)
 		{
-			if (!_controller.write_blocks(block_number, block_count, buffer))
-				throw Io_error();
-			ack_packet(packet);
+			_controller.write_blocks(block_number, block_count, buffer, packet);
 		}
 
 		void read_dma(Block::sector_t    block_number,
@@ -119,9 +118,7 @@ class Block::Sdhci_driver : public Block::Driver
 		              Genode::addr_t     phys,
 		              Packet_descriptor &packet)
 		{
-			if (!_controller.read_blocks_dma(block_number, block_count, phys))
-				throw Io_error();
-			ack_packet(packet);
+			_controller.read_blocks_dma(block_number, block_count, phys, packet);
 		}
 
 		void write_dma(Block::sector_t    block_number,
@@ -129,9 +126,7 @@ class Block::Sdhci_driver : public Block::Driver
 		               Genode::addr_t     phys,
 		               Packet_descriptor &packet)
 		{
-			if (!_controller.write_blocks_dma(block_number, block_count, phys))
-				throw Io_error();
-			ack_packet(packet);
+			_controller.write_blocks_dma(block_number, block_count, phys, packet);
 		}
 
 		bool dma_enabled() { return _use_dma; }
diff --git a/repos/os/src/drivers/sd_card/spec/omap4/mmchs.h b/repos/os/src/drivers/sd_card/spec/omap4/mmchs.h
index c65a729256..64ecb6f162 100644
--- a/repos/os/src/drivers/sd_card/spec/omap4/mmchs.h
+++ b/repos/os/src/drivers/sd_card/spec/omap4/mmchs.h
@@ -19,6 +19,7 @@
 #include <os/attached_ram_dataspace.h>
 #include <irq_session/connection.h>
 #include <drivers/board_base.h>
+#include <block/component.h>
 
 /* local includes */
 #include <sd_card.h>
@@ -355,7 +356,6 @@ struct Mmchs : Genode::Mmio
 			Genode::error("reset of cmd line timed out (src != 0)");
 			return false;
 		}
-
 		return true;
 	}
 
@@ -484,10 +484,22 @@ struct Mmchs : Genode::Mmio
 };
 
 
+struct Sd_ack_handler {
+	virtual void handle_ack(Block::Packet_descriptor pkt, bool success) = 0;
+};
+
+
 struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
 {
 	private:
 
+		struct Block_transfer
+		{
+			Block::Packet_descriptor packet;
+			bool                     pending = false;
+
+		} _block_transfer;
+
 		Delayer           &_delayer;
 		Sd_card::Card_info _card_info;
 		bool const         _use_dma;
@@ -500,9 +512,9 @@ struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
 		Adma_desc::access_t *    const _adma_desc;
 		Genode::addr_t           const _adma_desc_phys;
 
-		Genode::Irq_connection  _irq;
-		Genode::Signal_receiver _irq_rec;
-		Genode::Signal_context  _irq_ctx;
+		Sd_ack_handler                                 &_ack_handler;
+		Genode::Signal_handler<Omap4_hsmmc_controller>  _irq_handler;
+		Genode::Irq_connection                          _irq;
 
 		Sd_card::Card_info _init()
 		{
@@ -690,19 +702,6 @@ struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
 			return true;
 		}
 
-		bool _wait_for_transfer_complete()
-		{
-			if (!wait_for<Stat::Tc>(1, _delayer, 1000*1000, 0)
-			 && !wait_for<Stat::Tc>(1, _delayer)) {
-				Genode::error("Stat::Tc timed out");
-				return false;
-			}
-
-			/* clear transfer-completed bit */
-			write<Stat::Tc>(1);
-			return true;
-		}
-
 		bool _wait_for_bre()
 		{
 			if (!wait_for<Pstate::Bre>(1, _delayer, 1000*1000, 0)
@@ -723,38 +722,29 @@ struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
 			return true;
 		}
 
-		bool _wait_for_transfer_complete_irq()
+		void _handle_irq()
 		{
-			/*
-			 * XXX For now, the driver works fully synchronous. We merely use
-			 *     the interrupt mechanism to yield CPU time to concurrently
-			 *     running processes.
-			 */
-			for (;;) {
-				/*
-				 * We ack the IRQ first to implicitly active receiving
-				 * IRQ signals when entering this loop for the first time.
-				 */
-				_irq.ack_irq();
-				_irq_rec.wait_for_signal();
+			_irq.ack_irq();
 
-				/* check for transfer completion */
-				if (read<Stat::Tc>() == 1) {
-
-					/* clear transfer-completed bit */
-					write<Stat::Tc>(1);
-
-					if (read<Stat>() != 0)
-						Genode::warning("unexpected state ("
-						                "Stat: ", Genode::Hex(read<Stat>()), " "
-						                "Blen: ", Genode::Hex(read<Blk::Blen>()), " "
-						                "Nblk: ", read<Blk::Nblk>());
-
-					return true;
-				}
+			if (!_block_transfer.pending) {
+				return; }
 
+			if (read<Stat::Tc>() != 1) {
 				Genode::warning("unexpected interrupt, Stat: ", Genode::Hex(read<Stat>()));
+				return;
 			}
+
+			write<Stat::Tc>(1);
+
+			if (read<Stat>() != 0) {
+				Genode::warning("unexpected state ("
+				                "Stat: ", Genode::Hex(read<Stat>()), " "
+				                "Blen: ", Genode::Hex(read<Blk::Blen>()), " "
+				                "Nblk: ", read<Blk::Nblk>());
+				return;
+			}
+			_block_transfer.pending = false;
+			_ack_handler.handle_ack(_block_transfer.packet, true);
 		}
 
 	public:
@@ -766,8 +756,9 @@ struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
 		 *
 		 * \param mmio_base  local base address of MMIO registers
 		 */
-		Omap4_hsmmc_controller(Genode::addr_t const mmio_base, Delayer &delayer,
-		                       bool use_dma)
+		Omap4_hsmmc_controller(Genode::Entrypoint &ep,
+		                       Genode::addr_t const mmio_base, Delayer &delayer,
+		                       Sd_ack_handler &ack_handler, bool use_dma)
 		:
 			Mmchs(mmio_base), _delayer(delayer), _card_info(_init()),
 			_use_dma(use_dma),
@@ -776,13 +767,14 @@ struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
 			              Genode::UNCACHED),
 			_adma_desc(_adma_desc_ds.local_addr<Adma_desc::access_t>()),
 			_adma_desc_phys(Genode::Dataspace_client(_adma_desc_ds.cap()).phys_addr()),
+			_ack_handler(ack_handler),
+			_irq_handler(ep, *this, &Omap4_hsmmc_controller::_handle_irq),
 			_irq(IRQ_NUMBER)
 		{
-			_irq.sigh(_irq_rec.manage(&_irq_ctx));
+			_irq.sigh(_irq_handler);
+			_irq.ack_irq();
 		}
 
-		~Omap4_hsmmc_controller() { _irq_rec.dissolve(&_irq_ctx); }
-
 
 		/****************************************
 		 ** Sd_card::Host_controller interface **
@@ -897,22 +889,23 @@ struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
 			return Sd_card::Send_relative_addr::Response::Rca::get(read<Rsp10>());
 		}
 
-		/**
-		 * Read data blocks from SD card
-		 *
-		 * \return true on success
-		 */
-		bool read_blocks(size_t block_number, size_t block_count, char *out_buffer)
+		void read_blocks(size_t block_number, size_t block_count, char *out_buffer,
+		                 Block::Packet_descriptor pkt)
 		{
 			using namespace Sd_card;
 
+			if (_block_transfer.pending) {
+				throw Block::Driver::Request_congestion(); }
+
 			write<Blk::Blen>(0x200);
 			write<Blk::Nblk>(block_count);
 
+			_block_transfer.packet  = pkt;
+			_block_transfer.pending = true;
+
 			if (!issue_command(Read_multiple_block(block_number))) {
-				Genode::error("Read_multiple_block failed, Stat: ",
-				              Genode::Hex(read<Stat>()));
-				return false;
+				Genode::error("Read_multiple_block failed");
+				throw Block::Driver::Io_error();
 			}
 
 			size_t const num_accesses = block_count*512/sizeof(Data::access_t);
@@ -920,29 +913,29 @@ struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
 
 			for (size_t i = 0; i < num_accesses; i++) {
 				if (!_wait_for_bre())
-					return false;
+					throw Block::Driver::Io_error();
 
 				*dst++ = read<Data>();
 			}
-
-			return _wait_for_transfer_complete();
 		}
 
-		/**
-		 * Write data blocks to SD card
-		 *
-		 * \return true on success
-		 */
-		bool write_blocks(size_t block_number, size_t block_count, char const *buffer)
+		void write_blocks(size_t block_number, size_t block_count, char const *buffer,
+		                  Block::Packet_descriptor pkt)
 		{
 			using namespace Sd_card;
 
+			if (_block_transfer.pending) {
+				throw Block::Driver::Request_congestion(); }
+
 			write<Blk::Blen>(0x200);
 			write<Blk::Nblk>(block_count);
 
+			_block_transfer.packet  = pkt;
+			_block_transfer.pending = true;
+
 			if (!issue_command(Write_multiple_block(block_number))) {
 				Genode::error("Write_multiple_block failed");
-				return false;
+				throw Block::Driver::Io_error();
 			}
 
 			size_t const num_accesses = block_count*512/sizeof(Data::access_t);
@@ -950,59 +943,56 @@ struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
 
 			for (size_t i = 0; i < num_accesses; i++) {
 				if (!_wait_for_bwe())
-					return false;
+					throw Block::Driver::Io_error();
 
 				write<Data>(*src++);
 			}
-
-			return _wait_for_transfer_complete();
 		}
 
-		/**
-		 * Read data blocks from SD card via master DMA
-		 *
-		 * \return true on success
-		 */
-		bool read_blocks_dma(size_t block_number, size_t block_count,
-		                     Genode::addr_t out_buffer_phys)
+		void read_blocks_dma(size_t block_number, size_t block_count,
+		                     Genode::addr_t out_buffer_phys,
+		                     Block::Packet_descriptor pkt)
 		{
 			using namespace Sd_card;
 
+			if (_block_transfer.pending) {
+				throw Block::Driver::Request_congestion(); }
+
 			write<Blk::Blen>(0x200);
 			write<Blk::Nblk>(block_count);
 
 			_setup_adma_descriptor_table(block_count, out_buffer_phys);
 
-			if (!issue_command(Read_multiple_block(block_number))) {
-				Genode::error("Read_multiple_block failed, Stat: ",
-				              Genode::Hex(read<Stat>()));
-				return false;
-			}
+			_block_transfer.packet  = pkt;
+			_block_transfer.pending = true;
 
-			return _wait_for_transfer_complete_irq();
+			if (!issue_command(Read_multiple_block(block_number))) {
+				Genode::error("Read_multiple_block failed");
+				throw Block::Driver::Io_error();
+			}
 		}
 
-		/**
-		 * Write data blocks to SD card via master DMA
-		 *
-		 * \return true on success
-		 */
-		bool write_blocks_dma(size_t block_number, size_t block_count,
-		                      Genode::addr_t buffer_phys)
+		void write_blocks_dma(size_t block_number, size_t block_count,
+		                      Genode::addr_t buffer_phys,
+		                      Block::Packet_descriptor pkt)
 		{
 			using namespace Sd_card;
 
+			if (_block_transfer.pending) {
+				throw Block::Driver::Request_congestion(); }
+
 			write<Blk::Blen>(0x200);
 			write<Blk::Nblk>(block_count);
 
 			_setup_adma_descriptor_table(block_count, buffer_phys);
 
+			_block_transfer.packet  = pkt;
+			_block_transfer.pending = true;
+
 			if (!issue_command(Write_multiple_block(block_number))) {
 				Genode::error("Write_multiple_block failed");
-				return false;
+				throw Block::Driver::Io_error();
 			}
-
-			return _wait_for_transfer_complete_irq();
 		}
 };
 
diff --git a/repos/os/src/drivers/sd_card/spec/pl180/driver.h b/repos/os/src/drivers/sd_card/spec/pl180/driver.h
index c5e81ff2d2..4574b2186d 100644
--- a/repos/os/src/drivers/sd_card/spec/pl180/driver.h
+++ b/repos/os/src/drivers/sd_card/spec/pl180/driver.h
@@ -27,6 +27,7 @@ namespace Block {
 	class Sdhci_driver;
 }
 
+
 class Block::Sdhci_driver : public Block::Driver
 {
 	private:
diff --git a/repos/os/src/test/sd_card_bench/main.cc b/repos/os/src/test/sd_card_bench/main.cc
index ec6f977330..83c45b98fb 100644
--- a/repos/os/src/test/sd_card_bench/main.cc
+++ b/repos/os/src/test/sd_card_bench/main.cc
@@ -14,7 +14,6 @@
 
 /* Genode includes */
 #include <base/component.h>
-#include <base/log.h>
 #include <timer_session/connection.h>
 #include <os/attached_ram_dataspace.h>
 #include <os/config.h>
@@ -24,146 +23,137 @@
 
 using namespace Genode;
 
-struct Operation
-{
-	virtual void operator () (Block::Driver &driver,
-	                          Genode::addr_t block_number,
-	                          Genode::size_t block_count,
-	                          Genode::addr_t buffer_phys,
-	                          char          *buffer_virt) = 0;
-};
-
-
-/*
- * \param total_size    total number of bytes to read
- * \param request_size  number of bytes per request
- */
-static void run_benchmark(Block::Driver  &driver,
-                          Timer::Session &timer,
-                          char           *buffer_virt,
-                          Genode::addr_t  buffer_phys,
-                          Genode::size_t  buffer_size,
-                          Genode::size_t  request_size,
-                          Operation      &operation)
-{
-	using namespace Genode;
-
-	log("request_size=", request_size, " bytes");
-
-	size_t const time_before_ms = timer.elapsed_ms();
-
-	size_t num_requests = buffer_size / request_size;
-
-	/*
-	 * Trim number of requests if it would take to much time
-	 */
-	if (num_requests > 320) {
-		buffer_size = 320 * request_size;
-		num_requests = buffer_size / request_size;
-	}
-
-	for (size_t i = 0; i < num_requests; i++)
-	{
-		size_t const block_count  = request_size / driver.block_size();
-		addr_t const block_number = i*block_count;
-
-		operation(driver, block_number, block_count,
-		          buffer_phys + i*request_size,
-		          buffer_virt + i*request_size);
-	}
-
-	size_t const time_after_ms = timer.elapsed_ms();
-	size_t const duration_ms   = time_after_ms - time_before_ms;
-
-	/*
-	 * Convert bytes per milliseconds to kilobytes per seconds
-	 *
-	 * (total_size / 1024) / (duration_ms / 1000)
-	 */
-	size_t const buffer_size_kb = buffer_size / 1024;
-	size_t const throughput_kb_per_sec = (1000*buffer_size_kb) / duration_ms;
-
-	log("         duration:   ", duration_ms,           " ms");
-	log("         amount:     ", buffer_size_kb,        " KiB");
-	log("         throughput: ", throughput_kb_per_sec, " KiB/sec");
-}
-
 
 struct Main
 {
-	Main(Env &env)
+	using Packet_descriptor = Block::Packet_descriptor;
+
+	struct Block_operation_failed : Exception { };
+
+	enum Operation { READ, WRITE };
+
+	struct Driver_session : Block::Driver_session_base
 	{
-		log("--- SD card benchmark ---");
+		Signal_transmitter sig;
+		unsigned long      nr_of_acks { 0 };
 
+		Driver_session(Signal_context_capability sig) : sig(sig) { }
 
-		static Block::Sdhci_driver driver(env);
-		bool const use_dma = driver.dma_enabled();
-
-		static Timer::Connection timer;
-
-		long const request_sizes[] = {
-			512, 1024, 2048, 4096, 8192, 16384, 32768, 64*1024, 128*1024, 0 };
-
-		/* total size of communication buffer */
-		size_t const buffer_size = 10 * 1024 * 1024;
-
-		/* allocate read/write buffer */
-		static Attached_ram_dataspace buffer(&env.ram(), buffer_size, Genode::UNCACHED);
-		char * const buffer_virt = buffer.local_addr<char>();
-		addr_t const buffer_phys = Dataspace_client(buffer.cap()).phys_addr();
-
-		/*
-		 * Benchmark reading from SD card
-		 */
-
-		log("\n-- reading from SD card (", use_dma ? "" : "not ", "using DMA) --");
-
-		struct Read : Operation
+		void ack_packet(Packet_descriptor &, bool success)
 		{
-			void operator () (Block::Driver &driver,
-							  addr_t number, size_t count, addr_t phys, char *virt)
-			{
-				Block::Packet_descriptor p;
-				if (driver.dma_enabled())
-					driver.read_dma(number, count, phys, p);
-				else
-					driver.read(number, count, virt, p);
-			}
-		} read_operation;
-
-		for (unsigned i = 0; request_sizes[i]; i++) {
-			run_benchmark(driver, timer, buffer_virt, buffer_phys, buffer_size,
-						  request_sizes[i], read_operation);
+			if (!success) {
+				throw Block_operation_failed(); }
+			nr_of_acks++;
+			sig.submit();
 		}
+	};
 
-		/*
-		 * Benchmark writing to SD card
-		 *
-		 * We write back the content of the buffer, which we just filled during the
-		 * read benchmark. If both read and write succeed, the SD card will retain
-		 * its original content.
-		 */
+	Env                    &env;
+	Packet_descriptor       pkt;
+	unsigned long           time_before_ms;
+	Timer::Connection       timer;
+	Operation               operation    { READ };
+	Signal_handler<Main>    ack_handler  { env.ep(), *this, &Main::update_state };
+	Driver_session          drv_session  { ack_handler };
+	Block::Sdhci_driver     drv          { env };
+	size_t const            buf_size_kib { config()->xml_node()
+	                                       .attribute_value("buffer_size_kib",
+	                                                        (size_t)0) };
+	size_t const            buf_size     { buf_size_kib * 1024 };
+	Attached_ram_dataspace  buf          { &env.ram(), buf_size, UNCACHED };
+	char                   *buf_virt     { buf.local_addr<char>() };
+	addr_t                  buf_phys     { Dataspace_client(buf.cap())
+	                                       .phys_addr() };
+	size_t                  buf_off_done { 0 };
+	size_t                  buf_off_pend { 0 };
+	unsigned                req_size_id  { 0 };
+	size_t                  req_sizes[9] { 512, 1024, 1024 * 2,  1024 * 4,
+	                                       1024 * 8,  1024 * 16, 1024 * 32,
+	                                       1024 * 64, 1024 * 128 };
 
-		log("\n-- writing to SD card (", use_dma ? "" : "not ", "using DMA) --");
+	size_t req_size() const { return req_sizes[req_size_id]; }
 
-		struct Write : Operation
-		{
-			void operator () (Block::Driver &driver,
-			                  addr_t number, size_t count, addr_t phys, char *virt)
-			{
-				Block::Packet_descriptor p;
-				if (driver.dma_enabled())
-					driver.write_dma(number, count, phys, p);
-				else
-					driver.write(number, count, virt, p);
+	void update_state()
+	{
+		/* raise done counter and check if the buffer is full */
+		buf_off_done += drv_session.nr_of_acks * req_size();
+		drv_session.nr_of_acks = 0;
+		if (buf_off_done == buf_size) {
+
+			/* print stats for the current request size */
+			unsigned long const time_after_ms = timer.elapsed_ms();
+			unsigned long const duration_ms   = time_after_ms - time_before_ms;
+			size_t        const kib_per_sec   = (1000 * buf_size_kib) /
+			                                    duration_ms;
+			log("      duration:   ", duration_ms,  " ms");
+			log("      amount:     ", buf_size_kib, " KiB");
+			log("      throughput: ", kib_per_sec,  " KiB/sec");
+
+			/* go to next request size */
+			buf_off_pend = 0;
+			buf_off_done = 0;
+			req_size_id++;
+
+			/* check if we have done all request sizes for an operation */
+			if (req_size_id == sizeof(req_sizes)/sizeof(req_sizes[0])) {
+
+				/* go to next operation or end the test */
+				log("");
+				req_size_id = 0;
+				switch (operation) {
+				case READ:
+					operation = WRITE;
+					log("-- writing to SD card --");
+					break;
+				case WRITE:
+					log("--- SD card benchmark finished ---");
+					return;
+				}
 			}
-		} write_operation;
+			log("   request size ", req_size(), " bytes");
+			time_before_ms = timer.elapsed_ms();
+		}
+		/* issue as many requests for the current request size as possible */
+		try {
+			size_t const cnt = req_size() / drv.block_size();
+			for (; buf_off_pend < buf_size; buf_off_pend += req_size()) {
 
-		for (unsigned i = 0; request_sizes[i]; i++)
-			run_benchmark(driver, timer, buffer_virt, buffer_phys, buffer_size,
-			              request_sizes[i], write_operation);
+				/* calculate block offset */
+				addr_t const nr  = buf_off_pend / drv.block_size();
 
-		log("\n--- SD card benchmark finished ---");
+				if (drv.dma_enabled()) {
+
+					/* request with DMA */
+					addr_t const phys = buf_phys + buf_off_pend;
+					switch (operation) {
+					case READ:   drv.read_dma(nr, cnt, phys, pkt); break;
+					case WRITE: drv.write_dma(nr, cnt, phys, pkt); break; }
+
+				} else {
+
+					/* request without DMA */
+					char *const virt = buf_virt + buf_off_pend;
+					switch (operation) {
+					case READ:   drv.read(nr, cnt, virt, pkt); break;
+					case WRITE: drv.write(nr, cnt, virt, pkt); break; }
+				}
+			}
+		} catch (Block::Driver::Request_congestion) { }
+	}
+
+	Main(Env &env) : env(env)
+	{
+		log("");
+		log("--- SD card benchmark (", drv.dma_enabled() ? "with" : "no", " DMA) ---");
+
+		drv.session(&drv_session);
+
+		/* start issuing requests */
+		log("");
+		log("-- reading from SD card --");
+		log("   request size ", req_size(), " bytes");
+		time_before_ms = timer.elapsed_ms();
+		update_state();
 	}
 };