Index: uspace/drv/audio/sb16/Makefile
===================================================================
--- uspace/drv/audio/sb16/Makefile	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
+++ uspace/drv/audio/sb16/Makefile	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
@@ -0,0 +1,47 @@
+#
+# Copyright (c) 2011 Jan Vesely
+# 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.
+#
+
+USPACE_PREFIX = ../../..
+
+LIBS = \
+	$(LIBDRV_PREFIX)/libdrv.a
+
+EXTRA_CFLAGS += \
+	-I$(LIBDRV_PREFIX)/include -I$(LIBPCM_PREFIX)/include
+
+BINARY = sb16
+
+SOURCES = \
+	dsp.c \
+	main.c \
+	mixer.c \
+	mixer_iface.c \
+	pcm_iface.c \
+	sb16.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/drv/audio/sb16/ddf_log.h
===================================================================
--- uspace/drv/audio/sb16/ddf_log.h	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
+++ uspace/drv/audio/sb16/ddf_log.h	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 drvaudiosb16
+ * @{
+ */
+/** @file
+ * @brief DDF log helper macros
+ */
+#ifndef DRV_AUDIO_SB16_DDF_LOG_H
+#define DRV_AUDIO_SB16_DDF_LOG_H
+
+#include <ddf/log.h>
+
+#define ddf_log_fatal(msg...) ddf_msg(LVL_FATAL, msg)
+#define ddf_log_error(msg...) ddf_msg(LVL_ERROR, msg)
+#define ddf_log_warning(msg...) ddf_msg(LVL_WARN, msg)
+#define ddf_log_note(msg...) ddf_msg(LVL_NOTE, msg)
+#define ddf_log_debug(msg...) ddf_msg(LVL_DEBUG, msg)
+#define ddf_log_verbose(msg...) ddf_msg(LVL_DEBUG2, msg)
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/dsp.c
===================================================================
--- uspace/drv/audio/sb16/dsp.c	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
+++ uspace/drv/audio/sb16/dsp.c	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
@@ -0,0 +1,541 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 drvaudiosb16
+ * @{
+ */
+/** @file
+ * @brief DSP helper functions implementation
+ */
+
+#include <as.h>
+#include <stdbool.h>
+#include <ddi.h>
+#include <devman.h>
+#include <device/hw_res.h>
+#include <libarch/ddi.h>
+#include <libarch/barrier.h>
+#include <macros.h>
+#include <str_error.h>
+#include <audio_pcm_iface.h>
+
+#include "ddf_log.h"
+#include "dsp_commands.h"
+#include "dsp.h"
+
+/* Maximum allowed transfer size for ISA DMA transfers is 64kB */
+#define MAX_BUFFER_SIZE (60 * 1024)
+
+#ifndef DSP_RETRY_COUNT
+#define DSP_RETRY_COUNT 100
+#endif
+
+#define DSP_RESET_RESPONSE 0xaa
+
+/* These are only for SB16 (DSP4.00+) */
+#define DSP_RATE_UPPER_LIMIT 44100
+#define DSP_RATE_LOWER_LIMIT 5000
+
+#define AUTO_DMA_MODE
+
+static inline const char * dsp_state_to_str(dsp_state_t state)
+{
+	static const char* state_names[] = {
+		[DSP_PLAYBACK_ACTIVE_EVENTS] = "PLAYBACK w/ EVENTS",
+		[DSP_CAPTURE_ACTIVE_EVENTS] = "CAPTURE w/ EVENTS",
+		[DSP_PLAYBACK_NOEVENTS] = "PLAYBACK w/o EVENTS",
+		[DSP_CAPTURE_NOEVENTS] = "CAPTURE w/o EVENTS",
+		[DSP_PLAYBACK_TERMINATE] = "PLAYBACK TERMINATE",
+		[DSP_CAPTURE_TERMINATE] = "CAPTURE TERMINATE",
+		[DSP_READY] = "READY",
+		[DSP_NO_BUFFER] = "NO BUFFER",
+	};
+	if (state < ARRAY_SIZE(state_names))
+		return state_names[state];
+	return "UNKNOWN";
+}
+
+
+static inline void dsp_change_state(sb_dsp_t *dsp, dsp_state_t state)
+{
+	assert(dsp);
+	ddf_log_verbose("Changing state from %s to %s",
+	    dsp_state_to_str(dsp->state), dsp_state_to_str(state));
+	dsp->state = state;
+}
+
+static inline int dsp_read(sb_dsp_t *dsp, uint8_t *data)
+{
+	assert(data);
+	assert(dsp);
+	uint8_t status;
+	size_t attempts = DSP_RETRY_COUNT;
+	do {
+		status = pio_read_8(&dsp->regs->dsp_read_status);
+	} while (--attempts && ((status & DSP_READ_READY) == 0));
+
+	if ((status & DSP_READ_READY) == 0)
+		return EIO;
+
+	*data = pio_read_8(&dsp->regs->dsp_data_read);
+	return EOK;
+}
+
+static inline int dsp_write(sb_dsp_t *dsp, uint8_t data)
+{
+	assert(dsp);
+	uint8_t status;
+	size_t attempts = DSP_RETRY_COUNT;
+	do {
+		status = pio_read_8(&dsp->regs->dsp_write);
+	} while (--attempts && ((status & DSP_WRITE_BUSY) != 0));
+
+	if ((status & DSP_WRITE_BUSY))
+		return EIO;
+
+	pio_write_8(&dsp->regs->dsp_write, data);
+	return EOK;
+}
+
+static inline void dsp_reset(sb_dsp_t *dsp)
+{
+	assert(dsp);
+	/* Reset DSP, see Chapter 2 of Sound Blaster HW programming guide */
+	pio_write_8(&dsp->regs->dsp_reset, 1);
+	udelay(3); /* Keep reset for 3 us */
+	pio_write_8(&dsp->regs->dsp_reset, 0);
+	/* "DSP takes about 100 microseconds to initialize itself" */
+	udelay(100);
+}
+
+static inline void dsp_start_current_active(sb_dsp_t *dsp, uint8_t command)
+{
+	dsp_write(dsp, command);
+	dsp_write(dsp, dsp->active.mode);
+	dsp_write(dsp, (dsp->active.samples - 1) & 0xff);
+	dsp_write(dsp, (dsp->active.samples - 1) >> 8);
+}
+
+static inline void dsp_set_sampling_rate(sb_dsp_t *dsp, unsigned rate)
+{
+	dsp_write(dsp, SET_SAMPLING_RATE_OUTPUT);
+	dsp_write(dsp, rate >> 8);
+	dsp_write(dsp, rate & 0xff);
+	ddf_log_verbose("Sampling rate: %hhx:%hhx.", rate >> 8, rate & 0xff);
+}
+
+static inline void dsp_report_event(sb_dsp_t *dsp, pcm_event_t event)
+{
+	assert(dsp);
+	if (!dsp->event_exchange)
+		ddf_log_warning("No one listening for event %u", event);
+	async_msg_1(dsp->event_exchange, event, dsp->active.frame_count);
+}
+
+static inline int setup_dma(sb_dsp_t *dsp, uintptr_t pa, size_t size)
+{
+	async_sess_t *sess = devman_parent_device_connect(EXCHANGE_ATOMIC,
+	    ddf_dev_get_handle(dsp->sb_dev), IPC_FLAG_BLOCKING);
+
+	const int ret = hw_res_dma_channel_setup(sess,
+	    dsp->dma16_channel, pa, size,
+	    DMA_MODE_READ | DMA_MODE_AUTO | DMA_MODE_ON_DEMAND);
+
+	async_hangup(sess);
+	return ret;
+}
+
+static inline int setup_buffer(sb_dsp_t *dsp, size_t size)
+{
+	assert(dsp);
+	if (size > MAX_BUFFER_SIZE || size == 0 || (size % 2) == 1)
+		size = MAX_BUFFER_SIZE;
+	void *buffer = NULL, *pa = NULL;
+	int ret = dmamem_map_anonymous(size, AS_AREA_WRITE | AS_AREA_READ,
+	    0, &pa, &buffer);
+	if (ret != EOK) {
+		ddf_log_error("Failed to allocate DMA buffer.");
+		return ENOMEM;
+	}
+
+	ddf_log_verbose("Setup dma buffer at %p(%p) %zu.", buffer, pa, size);
+	assert((uintptr_t)pa < (1 << 25));
+
+	/* Setup 16 bit channel */
+	ret = setup_dma(dsp, (uintptr_t)pa, size);
+	if (ret == EOK) {
+		dsp->buffer.data = buffer;
+		dsp->buffer.size = size;
+	} else {
+		ddf_log_error("Failed to setup DMA16 channel: %s.",
+		    str_error(ret));
+		dmamem_unmap_anonymous(buffer);
+	}
+	return ret;
+}
+
+static inline size_t sample_count(pcm_sample_format_t format, size_t byte_count)
+{
+	return byte_count / pcm_sample_format_size(format);
+}
+
+int sb_dsp_init(sb_dsp_t *dsp, sb16_regs_t *regs, ddf_dev_t *dev,
+    int dma8, int dma16)
+{
+	assert(dsp);
+	dsp->regs = regs;
+	dsp->dma8_channel = dma8;
+	dsp->dma16_channel = dma16;
+	dsp->event_session = NULL;
+	dsp->event_exchange = NULL;
+	dsp->sb_dev = dev;
+	dsp->state = DSP_NO_BUFFER;
+	dsp_reset(dsp);
+	uint8_t response;
+	const int ret = dsp_read(dsp, &response);
+	if (ret != EOK) {
+		ddf_log_error("Failed to read DSP reset response value.");
+		return ret;
+	}
+	if (response != DSP_RESET_RESPONSE) {
+		ddf_log_error("Invalid DSP reset response: %x.", response);
+		return EIO;
+	}
+
+	/* Get DSP version number */
+	dsp_write(dsp, DSP_VERSION);
+	dsp_read(dsp, &dsp->version.major);
+	dsp_read(dsp, &dsp->version.minor);
+
+	return ret;
+}
+
+void sb_dsp_interrupt(sb_dsp_t *dsp)
+{
+	assert(dsp);
+
+	dsp->active.frame_count +=
+	    dsp->active.samples / ((dsp->active.mode & DSP_MODE_STEREO) ? 2 : 1);
+
+	switch (dsp->state)
+	{
+	case DSP_PLAYBACK_ACTIVE_EVENTS:
+		dsp_report_event(dsp, PCM_EVENT_FRAMES_PLAYED);
+	case DSP_PLAYBACK_NOEVENTS:
+#ifndef AUTO_DMA_MODE
+	dsp_start_current_active(dsp, SINGLE_DMA_16B_DA);
+#endif
+		break;
+	case DSP_CAPTURE_ACTIVE_EVENTS:
+		dsp_report_event(dsp, PCM_EVENT_FRAMES_CAPTURED);
+	case DSP_CAPTURE_NOEVENTS:
+#ifndef AUTO_DMA_MODE
+	dsp_start_current_active(dsp, SINGLE_DMA_16B_DA);
+#endif
+		break;
+	case DSP_CAPTURE_TERMINATE:
+		dsp_report_event(dsp, PCM_EVENT_CAPTURE_TERMINATED);
+		async_exchange_end(dsp->event_exchange);
+		dsp->event_exchange = NULL;
+		dsp_change_state(dsp, DSP_READY);
+		break;
+	case DSP_PLAYBACK_TERMINATE:
+		dsp_report_event(dsp, PCM_EVENT_PLAYBACK_TERMINATED);
+		async_exchange_end(dsp->event_exchange);
+		dsp->event_exchange = NULL;
+		dsp_change_state(dsp, DSP_READY);
+		break;
+	default:
+		ddf_log_warning("Interrupt while DSP not active");
+	}
+}
+
+unsigned sb_dsp_query_cap(sb_dsp_t *dsp, audio_cap_t cap)
+{
+	ddf_log_verbose("Querying cap %s", audio_pcm_cap_str(cap));
+	switch(cap) {
+	case AUDIO_CAP_CAPTURE:
+	case AUDIO_CAP_PLAYBACK:
+	case AUDIO_CAP_INTERRUPT:
+	case AUDIO_CAP_BUFFER_POS:
+		return 1;
+	case AUDIO_CAP_MAX_BUFFER:
+		return MAX_BUFFER_SIZE;
+	case AUDIO_CAP_INTERRUPT_MIN_FRAMES:
+		return 1;
+	case AUDIO_CAP_INTERRUPT_MAX_FRAMES:
+		return 16535;
+	default:
+		return ENOTSUP;
+	}
+}
+
+int sb_dsp_get_buffer_position(sb_dsp_t *dsp, size_t *pos)
+{
+	if (dsp->state == DSP_NO_BUFFER)
+		return ENOENT;
+
+	assert(dsp->buffer.data);
+	async_sess_t *sess = devman_parent_device_connect(EXCHANGE_ATOMIC,
+	    ddf_dev_get_handle(dsp->sb_dev), IPC_FLAG_BLOCKING);
+
+	// TODO: Assumes DMA 16
+	const int remain = hw_res_dma_channel_remain(sess, dsp->dma16_channel);
+	async_hangup(sess);
+	if (remain >= 0) {
+		*pos = dsp->buffer.size - remain;
+		return EOK;
+	}
+	return remain;
+}
+
+int sb_dsp_test_format(sb_dsp_t *dsp, unsigned *channels, unsigned *rate,
+  pcm_sample_format_t *format)
+{
+	int ret = EOK;
+	if (*channels == 0 || *channels > 2) {
+		*channels = 2;
+		ret = ELIMIT;
+	}
+	//TODO 8bit DMA supports 8bit formats
+	if (*format != PCM_SAMPLE_SINT16_LE && *format != PCM_SAMPLE_UINT16_LE) {
+		*format = pcm_sample_format_is_signed(*format) ?
+		    PCM_SAMPLE_SINT16_LE : PCM_SAMPLE_UINT16_LE;
+		ret = ELIMIT;
+	}
+	if (*rate > DSP_RATE_UPPER_LIMIT) {
+		*rate = DSP_RATE_UPPER_LIMIT;
+		ret = ELIMIT;
+	}
+	if (*rate < DSP_RATE_LOWER_LIMIT) {
+		*rate = DSP_RATE_LOWER_LIMIT;
+		ret = ELIMIT;
+	}
+	return ret;
+}
+
+int sb_dsp_set_event_session(sb_dsp_t *dsp, async_sess_t *session)
+{
+	assert(dsp);
+	if (dsp->event_session && session)
+		return EBUSY;
+	dsp->event_session = session;
+	ddf_log_debug("Set event session to %p.", session);
+	return EOK;
+}
+
+async_sess_t * sb_dsp_get_event_session(sb_dsp_t *dsp)
+{
+	assert(dsp);
+	ddf_log_debug("Get event session: %p.", dsp->event_session);
+	return dsp->event_session;
+}
+
+int sb_dsp_get_buffer(sb_dsp_t *dsp, void **buffer, size_t *size)
+{
+	assert(dsp);
+	assert(size);
+
+	/* buffer is already setup by for someone, refuse to work until
+	 * it's released */
+	if (dsp->state != DSP_NO_BUFFER)
+		return EBUSY;
+	assert(dsp->buffer.data == NULL);
+
+	const int ret = setup_buffer(dsp, *size);
+	if (ret == EOK) {
+		ddf_log_debug("Providing buffer: %p, %zu B.",
+		    dsp->buffer.data, dsp->buffer.size);
+
+		if (buffer)
+			*buffer = dsp->buffer.data;
+		if (size)
+			*size = dsp->buffer.size;
+		dsp_change_state(dsp, DSP_READY);
+	}
+	return ret;
+}
+
+int sb_dsp_release_buffer(sb_dsp_t *dsp)
+{
+	assert(dsp);
+	if (dsp->state != DSP_READY)
+		return EINVAL;
+	assert(dsp->buffer.data);
+	dmamem_unmap_anonymous(dsp->buffer.data);
+	dsp->buffer.data = NULL;
+	dsp->buffer.size = 0;
+	ddf_log_debug("DSP buffer released.");
+	dsp_change_state(dsp, DSP_NO_BUFFER);
+	return EOK;
+}
+
+int sb_dsp_start_playback(sb_dsp_t *dsp, unsigned frames,
+    unsigned channels, unsigned sampling_rate, pcm_sample_format_t format)
+{
+	assert(dsp);
+
+	if (!dsp->buffer.data || dsp->state != DSP_READY)
+		return EINVAL;
+
+	/* Check supported parameters */
+	ddf_log_debug("Requested playback: %u frames, %uHz, %s, %u channel(s).",
+	    frames, sampling_rate, pcm_sample_format_str(format), channels);
+	if (sb_dsp_test_format(dsp, &channels, &sampling_rate, &format) != EOK)
+		return ENOTSUP;
+
+	/* Client requested regular events */
+	if (frames) {
+		if (!dsp->event_session)
+			return EINVAL;
+		dsp->event_exchange = async_exchange_begin(dsp->event_session);
+		if (!dsp->event_exchange)
+			return ENOMEM;
+	}
+
+	dsp->active.mode = 0
+	    | (pcm_sample_format_is_signed(format) ? DSP_MODE_SIGNED : 0)
+	    | (channels == 2 ? DSP_MODE_STEREO : 0);
+	dsp->active.samples = frames * channels;
+	dsp->active.frame_count = 0;
+
+	dsp_set_sampling_rate(dsp, sampling_rate);
+
+#ifdef AUTO_DMA_MODE
+	dsp_start_current_active(dsp, AUTO_DMA_16B_DA_FIFO);
+#else
+	dsp_start_current_active(dsp, SINGLE_DMA_16B_DA);
+#endif
+
+	ddf_log_verbose("Playback started, event every %u samples",
+	    dsp->active.samples);
+
+	dsp_change_state(dsp,
+	    frames ? DSP_PLAYBACK_ACTIVE_EVENTS : DSP_PLAYBACK_NOEVENTS);
+	if (dsp->state == DSP_PLAYBACK_ACTIVE_EVENTS)
+		dsp_report_event(dsp, PCM_EVENT_PLAYBACK_STARTED);
+
+	return EOK;
+}
+
+int sb_dsp_stop_playback(sb_dsp_t *dsp, bool immediate)
+{
+	assert(dsp);
+	if ((dsp->state == DSP_PLAYBACK_NOEVENTS ||
+	    dsp->state == DSP_PLAYBACK_ACTIVE_EVENTS) &&
+	    immediate)
+	{
+		dsp_write(dsp, DMA_16B_PAUSE);
+		dsp_reset(dsp);
+		ddf_log_debug("Stopped playback");
+		dsp_change_state(dsp, DSP_READY);
+		return EOK;
+	}
+	if (dsp->state == DSP_PLAYBACK_ACTIVE_EVENTS)
+	{
+		/* Stop after current fragment */
+		assert(!immediate);
+		dsp_write(dsp, DMA_16B_EXIT);
+		ddf_log_debug("Last playback fragment");
+		dsp_change_state(dsp, DSP_PLAYBACK_TERMINATE);
+		return EOK;
+	}
+	return EINVAL;
+}
+
+int sb_dsp_start_capture(sb_dsp_t *dsp, unsigned frames,
+    unsigned channels, unsigned sampling_rate, pcm_sample_format_t format)
+{
+	assert(dsp);
+	if (!dsp->buffer.data || dsp->state != DSP_READY)
+		return EINVAL;
+
+	/* Check supported parameters */
+	ddf_log_debug("Requested capture: %u frames, %uHz, %s, %u channel(s).",
+	    frames, sampling_rate, pcm_sample_format_str(format), channels);
+	if (sb_dsp_test_format(dsp, &channels, &sampling_rate, &format) != EOK)
+		return ENOTSUP;
+
+	/* Client requested regular events */
+	if (frames) {
+		if (!dsp->event_session)
+			return EINVAL;
+		dsp->event_exchange = async_exchange_begin(dsp->event_session);
+		if (!dsp->event_exchange)
+			return ENOMEM;
+	}
+
+	dsp->active.mode = 0
+	    | (pcm_sample_format_is_signed(format) ? DSP_MODE_SIGNED : 0)
+	    | (channels == 2 ? DSP_MODE_STEREO : 0);
+	dsp->active.samples = frames * channels;
+	dsp->active.frame_count = 0;
+
+	dsp_set_sampling_rate(dsp, sampling_rate);
+
+#ifdef AUTO_DMA_MODE
+	dsp_start_current_active(dsp, AUTO_DMA_16B_AD_FIFO);
+#else
+	dsp_start_current_active(dsp, SINGLE_DMA_16B_AD);
+#endif
+
+	ddf_log_verbose("Capture started started, event every %u samples",
+	    dsp->active.samples);
+	dsp_change_state(dsp,
+	    frames ? DSP_CAPTURE_ACTIVE_EVENTS : DSP_CAPTURE_NOEVENTS);
+	if (dsp->state == DSP_CAPTURE_ACTIVE_EVENTS)
+		dsp_report_event(dsp, PCM_EVENT_CAPTURE_STARTED);
+	return EOK;
+}
+
+int sb_dsp_stop_capture(sb_dsp_t *dsp, bool immediate)
+{
+	assert(dsp);
+	if ((dsp->state == DSP_CAPTURE_NOEVENTS ||
+	    dsp->state == DSP_CAPTURE_ACTIVE_EVENTS) &&
+	    immediate)
+	{
+		dsp_write(dsp, DMA_16B_PAUSE);
+		dsp_reset(dsp);
+		ddf_log_debug("Stopped capture fragment");
+		dsp_change_state(dsp, DSP_READY);
+		return EOK;
+	}
+	if (dsp->state == DSP_CAPTURE_ACTIVE_EVENTS)
+	{
+		/* Stop after current fragment */
+		assert(!immediate);
+		dsp_write(dsp, DMA_16B_EXIT);
+		ddf_log_debug("Last capture fragment");
+		dsp_change_state(dsp, DSP_CAPTURE_TERMINATE);
+		return EOK;
+	}
+	return EINVAL;
+}
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/dsp.h
===================================================================
--- uspace/drv/audio/sb16/dsp.h	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
+++ uspace/drv/audio/sb16/dsp.h	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 drvaudiosb16
+ * @{
+ */
+/** @file
+ * @brief Sound Blaster Digital Sound Processor (DSP) helper functions.
+ */
+#ifndef DRV_AUDIO_SB16_DSP_H
+#define DRV_AUDIO_SB16_DSP_H
+
+#include <ddf/driver.h>
+#include <libarch/ddi.h>
+#include <errno.h>
+#include <pcm/sample_format.h>
+#include <audio_pcm_iface.h>
+
+#include "registers.h"
+typedef enum {
+	DSP_PLAYBACK_ACTIVE_EVENTS,
+	DSP_CAPTURE_ACTIVE_EVENTS,
+	DSP_PLAYBACK_NOEVENTS,
+	DSP_CAPTURE_NOEVENTS,
+	DSP_PLAYBACK_TERMINATE,
+	DSP_CAPTURE_TERMINATE,
+	DSP_READY,
+	DSP_NO_BUFFER,
+} dsp_state_t;
+
+typedef struct sb_dsp {
+	sb16_regs_t *regs;
+	int dma8_channel;
+	int dma16_channel;
+	struct {
+		uint8_t major;
+		uint8_t minor;
+	} version;
+	struct {
+		uint8_t *data;
+		size_t size;
+	} buffer;
+	struct {
+		uint8_t mode;
+		uint16_t samples;
+		unsigned frame_count;
+	} active;
+	dsp_state_t state;
+	async_sess_t *event_session;
+	async_exch_t *event_exchange;
+	ddf_dev_t *sb_dev;
+} sb_dsp_t;
+
+int sb_dsp_init(sb_dsp_t *dsp, sb16_regs_t *regs, ddf_dev_t *dev,
+    int dma8, int dma16);
+void sb_dsp_interrupt(sb_dsp_t *dsp);
+unsigned sb_dsp_query_cap(sb_dsp_t *dsp, audio_cap_t cap);
+int sb_dsp_get_buffer_position(sb_dsp_t *dsp, size_t *size);
+int sb_dsp_test_format(sb_dsp_t *dsp, unsigned *channels, unsigned *rate,
+  pcm_sample_format_t *format);
+int sb_dsp_get_buffer(sb_dsp_t *dsp, void **buffer, size_t *size);
+int sb_dsp_set_event_session(sb_dsp_t *dsp, async_sess_t *session);
+async_sess_t * sb_dsp_get_event_session(sb_dsp_t *dsp);
+int sb_dsp_release_buffer(sb_dsp_t *dsp);
+int sb_dsp_start_playback(sb_dsp_t *dsp, unsigned frames,
+    unsigned channels, unsigned sample_rate, pcm_sample_format_t format);
+int sb_dsp_stop_playback(sb_dsp_t *dsp, bool immediate);
+int sb_dsp_start_capture(sb_dsp_t *dsp, unsigned frames,
+    unsigned channels, unsigned sample_rate, pcm_sample_format_t format);
+int sb_dsp_stop_capture(sb_dsp_t *dsp, bool immediate);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/dsp_commands.h
===================================================================
--- uspace/drv/audio/sb16/dsp_commands.h	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
+++ uspace/drv/audio/sb16/dsp_commands.h	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 drvaudiosb16
+ * @{
+ */
+/** @file
+ * @brief SB16 DSP Command constants
+ */
+#ifndef DRV_AUDIO_SB16_DSP_COMMANDS_H
+#define DRV_AUDIO_SB16_DSP_COMMANDS_H
+
+/** See Sound Blaster Series HW programming Guide Chapter 6. */
+typedef enum dsp_command {
+	DIRECT_8B_OUTPUT = 0x10, /* Followed by unsigned byte of digital data,
+	                          * software controls sampling rate */
+	DIRECT_8B_INPUT = 0x20,  /* Same as DIRECT_8B_OUTPUT but for input */
+
+	TRANSFER_TIME_CONSTANT = 0x40, /* Followed by time constant.
+	                                * TC = 65536 - (256 000 000 /
+					*   (channels * sampling rate))
+					* Send only high byte */
+
+	SINGLE_DMA_8B_OUTPUT = 0x14, /* Followed by length.high and length.low
+	                              * starts single-cycle DMA, length is -1 */
+	SINGLE_DMA_8B_INPUT = 0x24,  /* Same as SINGLE_DMA_8B_OUTPUT, but for
+	                              * input */
+	SINGLE_DMA_8B_ADPCM_2B_OUT = 0x16, /* Starts single-cycle DMA using
+	                                    * Creative ADPSM 8->2 bit compressed
+	                                    * data, Followed by length.low
+					    * and length.high. Length is -1 */
+	SINGLE_DMA_8B_ADPCM_2B_OUT_REF = 0x17, /* Starts single-cycle DMA using
+	                                        * DPSM 8->2 bit compressed data
+	                                        * with reference byte.
+						* Followed by length.low and
+						* length.high. Length is -1 */
+	SINGLE_DMA_8B_ADPCM_4B_OUT = 0x74, /* Same as
+	                                    * SINGLE_DMA_8B_ADPCM_2B_OUT */
+	SINGLE_DMA_8B_ADPCM_4B_OUT_REF = 0x75, /* Same as
+	                                        * SINGLE_DMA_8B_ADPCM_2B_OUT_REF
+						*/
+	SINGLE_DMA_8B_ADPCM_3B_OUT = 0x76, /* Same as
+	                                    * SINGLE_DMA_8B_ADPCM_2B_OUT */
+	SINGLE_DMA_8B_ADPCM_3B_OUT_REF = 0x77, /* Same as
+	                                        * SINGLE_DMA_8B_ADPCM_2B_OUT_REF
+						*/
+
+	DMA_8B_PAUSE = 0xd0, /* Stop sending DMA request,
+	                      * works for SINGLE and AUTO */
+	DMA_8B_CONTINUE = 0xd4, /* Resume transfers paused by DMA_8B_PAUSE */
+
+	SPEAKER_ON = 0xd1,  /* Connect speaker via internal amplifier,
+	                     * has no effect on 4.xx */
+	SPEAKER_OFF = 0xd3, /* Disconnect output from the amplifier,
+	                     * has no effect on 4.xx */
+
+	MIDI_POLLING = 0x30, /* Read DSP for MIDI data */
+	MIDI_INTERRUPT = 0x31, /* Start interrupt mode, interrupt will be
+	                        * generated when there is in-bound data.
+				* To exit send again */
+	MIDI_OUTPUT = 0x38, /* Followed by midi_data */
+
+	PAUSE = 0x80, /* Followed by duration.low, duration.high. Duration is -1
+	               * In the units of sampling period. Generates interrupt
+		       * at the end of period */
+	DSP_VERSION = 0xe1, /* Read 2 bytes, major and minor number */
+
+	AUTO_DMA_8B_OUTPUT = 0x1c, /* Starts auto-init DMA mode using 8-bit
+	                            * Interrupt after every block.
+				    * To terminate, switch to single or use
+				    * EXIT command*/
+	AUTO_DMA_8B_INPUT = 0x2c, /* Same as AUTO_DMA_8B_OUTPUT, but for input*/
+	AUTO_DMA_8B_ADPCM_2B_REF = 0x1f, /* Same as AUTO_DMA_8B_OUTPUT, but use
+	                                  * 8->2bit ADPCM audio format */
+	AUTO_DMA_8B_ADPCM_4B_REF = 0x7d, /* Same as AUTO_DMA_8B_ADPCM_2B_REF */
+	AUTO_DMA_8B_ADPCM_3B_REF = 0x7f, /* Same as AUTO_DMA_8B_ADPCM_2B_REF */
+
+	DMA_8B_EXIT = 0xda, /* Ends DMA transfer and terminates I/O process */
+
+	BLOCK_TRANSFER_SIZE = 0x48, /* Followed by size.low, size.high
+	                             * Used with HIGH_SPEED AUTO_DMA */
+
+	UART_MIDI_POLLING = 0x34, /* Start UART MIDI polling mode, read and
+	                           * write from/to DSP is interpreted as
+				   * read/write from/to MIDI.
+				   * To exit use reset signal. Note that reset
+				   * will restore previous state and won't do
+				   * complete reset */
+	UART_MIDI_INTERRUPT = 0x35, /* Same as UART_MIDI_POLLING, but use
+	                             * interrupts instead of polling. */
+	UART_MIDI_POLLING_TS = 0x36, /* Add time stamp to inbound data, the
+	                              * order is time.low time.mid time.high
+				      * data */
+	UART_MIDI_INTERRUPT_TS = 0x37, /* Same as UART_MIDI_POLLING_TS, but use
+	                                * interrupts instead of polling */
+
+	SPEAKER_STATUS = 0xd8, /* 0xff means amp is on, 0x00 means it's off */
+
+	AUTO_DMA_8B_HIGH_OUTPUT = 0x90, /* DSP will generate interrupt after
+	                                 * every block. No other commands are
+					 * accepted in this mode. To exit
+					 * the mode send RESET command.
+					 * Note that reset will restore
+					 * previous state. */
+	AUTO_DMA_8B_HIGH_INPUT = 0x98, /* Same as AUTO_DMA_8B_HIGH_OUTPUT */
+	SINGLE_DMA_8B_HIGH_OUTPUT = 0x91, /* Transfer one block and exit,
+	                                   * generates interrupt */
+	SINGLE_DMA_8B_HIGH_INPUT = 0x99, /* Same as SINGLE_DMA_8B_HIGH_OUTPUT */
+
+	SET_MONO_INPUT = 0xa0, /* Mono mode is the default, only on 3.xx */
+	SET_STEREO_INPUT = 0xa8, /* Switch to stereo recording, only on 3.xx */
+
+	SET_SAMPLING_RATE_OUTPUT = 0x41, /* Followed by sapling rate
+	                                  * 5000 to 45000 Hz, inclusive */
+	SET_SAMPLING_RATE_INPUT = 0x42, /* Same as SET_SAMPLING_RATE_OUTPUT */
+
+	SINGLE_DMA_16B_DA = 0xb0,     /* Followed by mode, size.low, size.high*/
+	SINGLE_DMA_16B_DA_FIFO = 0xb2,/* mode format is:                      */
+	AUTO_DMA_16B_DA = 0xb4,       /*    0x00 - unsigned mono              */
+	AUTO_DMA_16B_DA_FIFO = 0xb6,  /*    0x10 - signed mono                */
+	SINGLE_DMA_16B_AD = 0xb8,     /*    0x20 - unsigned stereo            */
+	SINGLE_DMA_16B_AD_FIFO = 0xba,/*    0x30 - signed stereo              */
+	AUTO_DMA_16B_AD = 0xbc,       /* Size is -1. Terminate by EXIT        */
+	AUTO_DMA_16B_AD_FIFO = 0xbe,  /* or switch to SINGLE_DMA              */
+
+	SINGLE_DMA_8B_DA = 0xc0,     /* Followed by mode, size.low, size.high */
+	SINGLE_DMA_8B_DA_FIFO = 0xc2,/* mode format is:                       */
+	AUTO_DMA_8B_DA = 0xc4,       /*    0x00 - unsigned mono               */
+	AUTO_DMA_8B_DA_FIFO = 0xc6,  /*    0x10 - signed mono                 */
+	SINGLE_DMA_8B_AD = 0xc8,     /*    0x20 - unsigned stereo             */
+	SINGLE_DMA_8B_AD_FIFO = 0xca,/*    0x30 - signed stereo               */
+	AUTO_DMA_8B_AD = 0xcc,       /* Size is -1. Terminate by EXIT         */
+	AUTO_DMA_8B_AD_FIFO = 0xce,  /* or switch to SINGLE_DMA               */
+
+	DMA_16B_PAUSE = 0xd5,/* Stop sending DMA request, both SINGLE and AUTO*/
+	DMA_16B_CONTINUE = 0xd6, /* Resume requests paused by DMA_16B_PAUSE */
+	DMA_16B_EXIT = 0xd9, /* Ends DMA transfer and terminates I/O process */
+} dsp_command_t;
+
+#define DSP_MODE_SIGNED 0x10
+#define DSP_MODE_STEREO 0x20
+
+static inline const char * mode_to_str(uint8_t mode)
+{
+	if (mode & 0xcf)
+		return "unknown";
+	static const char * names[] = {
+		"unsigned mono (8bit)",
+		"signed mono (16bit)",
+		"unsigned stereo (8bit)",
+		"signed stereo (16bit)",
+	};
+	return names[mode >> 4];
+}
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/main.c
===================================================================
--- uspace/drv/audio/sb16/main.c	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
+++ uspace/drv/audio/sb16/main.c	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * Copyright (c) 2011 Vojtech Horky
+ * 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 drvaudiosb16
+ * @{
+ */
+/** @file
+ * Main routines of Creative Labs SoundBlaster 16 driver
+ */
+#include <ddf/driver.h>
+#include <ddf/interrupt.h>
+#include <ddf/log.h>
+#include <device/hw_res_parsed.h>
+#include <devman.h>
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <str_error.h>
+
+#include "ddf_log.h"
+#include "sb16.h"
+
+#define NAME "sb16"
+
+static int sb_add_device(ddf_dev_t *device);
+static int sb_get_res(ddf_dev_t *device, uintptr_t *sb_regs,
+    size_t *sb_regs_size, uintptr_t *mpu_regs, size_t *mpu_regs_size,
+    int *irq, int *dma8, int *dma16);
+static int sb_enable_interrupts(ddf_dev_t *device);
+/*----------------------------------------------------------------------------*/
+static driver_ops_t sb_driver_ops = {
+	.dev_add = sb_add_device,
+};
+/*----------------------------------------------------------------------------*/
+static driver_t sb_driver = {
+	.name = NAME,
+	.driver_ops = &sb_driver_ops
+};
+//static ddf_dev_ops_t sb_ops = {};
+/*----------------------------------------------------------------------------*/
+/** Initializes global driver structures (NONE).
+ *
+ * @param[in] argc Nmber of arguments in argv vector (ignored).
+ * @param[in] argv Cmdline argument vector (ignored).
+ * @return Error code.
+ *
+ * Driver debug level is set here.
+ */
+int main(int argc, char *argv[])
+{
+	printf(NAME": HelenOS SB16 audio driver.\n");
+	ddf_log_init(NAME);
+	return ddf_driver_main(&sb_driver);
+}
+
+static void irq_handler(ddf_dev_t *dev, ipc_callid_t iid, ipc_call_t *call)
+{
+	assert(dev);
+	sb16_t *sb16_dev = ddf_dev_data_get(dev);
+	assert(sb16_dev);
+	sb16_interrupt(sb16_dev);
+}
+
+/** Initializes a new ddf driver instance of SB16.
+ *
+ * @param[in] device DDF instance of the device to initialize.
+ * @return Error code.
+ */
+static int sb_add_device(ddf_dev_t *device)
+{
+#define CHECK_RET_RETURN(ret, msg...) \
+if (ret != EOK) { \
+	ddf_log_error(msg); \
+	return ret; \
+} else (void)0
+
+	assert(device);
+
+	sb16_t *soft_state = ddf_dev_data_alloc(device, sizeof(sb16_t));
+	int ret = soft_state ? EOK : ENOMEM;
+	CHECK_RET_RETURN(ret, "Failed to allocate sb16 structure.");
+
+	uintptr_t sb_regs = 0, mpu_regs = 0;
+	size_t sb_regs_size = 0, mpu_regs_size = 0;
+	int irq = 0, dma8 = 0, dma16 = 0;
+
+	ret = sb_get_res(device, &sb_regs, &sb_regs_size, &mpu_regs,
+	    &mpu_regs_size, &irq, &dma8, &dma16);
+	CHECK_RET_RETURN(ret, "Failed to get resources: %s.", str_error(ret));
+
+	const size_t irq_cmd_count = sb16_irq_code_size();
+	irq_cmd_t irq_cmds[irq_cmd_count];
+	irq_pio_range_t irq_ranges[1];
+	sb16_irq_code((void*)sb_regs, dma8, dma16, irq_cmds, irq_ranges);
+
+	irq_code_t irq_code = {
+		.cmdcount = irq_cmd_count,
+		.cmds = irq_cmds,
+		.rangecount = 1,
+		.ranges = irq_ranges
+	};
+
+	ret = register_interrupt_handler(device, irq, irq_handler, &irq_code);
+	CHECK_RET_RETURN(ret,
+	    "Failed to register irq handler: %s.", str_error(ret));
+
+#define CHECK_RET_UNREG_DEST_RETURN(ret, msg...) \
+if (ret != EOK) { \
+	ddf_log_error(msg); \
+	unregister_interrupt_handler(device, irq); \
+	return ret; \
+} else (void)0
+
+	ret = sb_enable_interrupts(device);
+	CHECK_RET_UNREG_DEST_RETURN(ret, "Failed to enable interrupts: %s.",
+	    str_error(ret));
+
+	ret = sb16_init_sb16(
+	    soft_state, (void*)sb_regs, sb_regs_size, device, dma8, dma16);
+	CHECK_RET_UNREG_DEST_RETURN(ret,
+	    "Failed to init sb16 driver: %s.", str_error(ret));
+
+	ret = sb16_init_mpu(soft_state, (void*)mpu_regs, mpu_regs_size);
+	if (ret == EOK) {
+		ddf_fun_t *mpu_fun =
+		    ddf_fun_create(device, fun_exposed, "midi");
+		if (mpu_fun) {
+			ret = ddf_fun_bind(mpu_fun);
+			if (ret != EOK)
+				ddf_log_error(
+				    "Failed to bind midi function: %s.",
+				    str_error(ret));
+		} else {
+			ddf_log_error("Failed to create midi function.");
+		}
+	} else {
+	    ddf_log_warning("Failed to init mpu driver: %s.", str_error(ret));
+	}
+
+	/* MPU state does not matter */
+	return EOK;
+}
+
+static int sb_get_res(ddf_dev_t *device, uintptr_t *sb_regs,
+    size_t *sb_regs_size, uintptr_t *mpu_regs, size_t *mpu_regs_size,
+    int *irq, int *dma8, int *dma16)
+{
+	assert(device);
+
+	async_sess_t *parent_sess = devman_parent_device_connect(
+	    EXCHANGE_SERIALIZE, ddf_dev_get_handle(device), IPC_FLAG_BLOCKING);
+	if (!parent_sess)
+		return ENOMEM;
+
+	hw_res_list_parsed_t hw_res;
+	hw_res_list_parsed_init(&hw_res);
+	const int ret = hw_res_get_list_parsed(parent_sess, &hw_res, 0);
+	async_hangup(parent_sess);
+	if (ret != EOK) {
+		return ret;
+	}
+
+	/* 1x IRQ, 1-2x DMA(8,16), 1-2x IO (MPU is separate). */
+	if (hw_res.irqs.count != 1 ||
+	   (hw_res.io_ranges.count != 1 && hw_res.io_ranges.count != 2) ||
+	   (hw_res.dma_channels.count != 1 && hw_res.dma_channels.count != 2)) {
+		hw_res_list_parsed_clean(&hw_res);
+		return EINVAL;
+	}
+
+	if (irq)
+		*irq = hw_res.irqs.irqs[0];
+
+	if (dma8) {
+		if (hw_res.dma_channels.channels[0] < 4) {
+			*dma8 = hw_res.dma_channels.channels[0];
+		} else {
+			if (hw_res.dma_channels.count == 2 &&
+			    hw_res.dma_channels.channels[1] < 4) {
+				*dma8 = hw_res.dma_channels.channels[1];
+			}
+		}
+	}
+
+	if (dma16) {
+		if (hw_res.dma_channels.channels[0] > 4) {
+			*dma16 = hw_res.dma_channels.channels[0];
+		} else {
+			if (hw_res.dma_channels.count == 2 &&
+			    hw_res.dma_channels.channels[1] > 4) {
+				*dma16 = hw_res.dma_channels.channels[1];
+			}
+		}
+	}
+
+
+	if (hw_res.io_ranges.count == 1) {
+		if (sb_regs)
+			*sb_regs = hw_res.io_ranges.ranges[0].address;
+		if (sb_regs_size)
+			*sb_regs_size = hw_res.io_ranges.ranges[0].size;
+	} else {
+		const int sb =
+		    (hw_res.io_ranges.ranges[0].size >= sizeof(sb16_regs_t))
+		        ? 1 : 0;
+		const int mpu = 1 - sb;
+		if (sb_regs)
+			*sb_regs = hw_res.io_ranges.ranges[sb].address;
+		if (sb_regs_size)
+			*sb_regs_size = hw_res.io_ranges.ranges[sb].size;
+		if (mpu_regs)
+			*sb_regs = hw_res.io_ranges.ranges[mpu].address;
+		if (mpu_regs_size)
+			*sb_regs_size = hw_res.io_ranges.ranges[mpu].size;
+	}
+
+	return EOK;
+}
+
+int sb_enable_interrupts(ddf_dev_t *device)
+{
+	async_sess_t *parent_sess = devman_parent_device_connect(
+	    EXCHANGE_SERIALIZE, ddf_dev_get_handle(device), IPC_FLAG_BLOCKING);
+	if (!parent_sess)
+		return ENOMEM;
+
+	bool enabled = hw_res_enable_interrupt(parent_sess);
+	async_hangup(parent_sess);
+
+	return enabled ? EOK : EIO;
+}
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/mixer.c
===================================================================
--- uspace/drv/audio/sb16/mixer.c	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
+++ uspace/drv/audio/sb16/mixer.c	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <libarch/ddi.h>
+#include <macros.h>
+#include <unistd.h>
+
+#include "ddf_log.h"
+#include "mixer.h"
+
+typedef struct channel {
+	const char *name;
+	uint8_t address;
+	unsigned shift;
+	unsigned volume_levels;
+	bool preserve_bits;
+} channel_t;
+
+/* CT1335 channels */
+static const channel_t channels_table_ct1335[] = {
+	{ "Master", 0x02, 1, 8, false }, /* Master, Mono, 3bit volume level */
+	{ "Midi", 0x06, 1, 8, false }, /* Midi, Mono, 3bit volume level */
+	{ "CD", 0x08, 1, 8, false }, /* CD, Mono, 3bit volume level */
+	{ "Voice", 0x0a, 1, 4, false }, /* Voice, Mono, 2bit volume level */
+};
+
+/* CT1345 channels */
+static const channel_t channels_table_ct1345[] = {
+	{ "Master Left", 0x22, 5, 8, true }, /* Master, Left, 3bit volume level */
+	{ "Master Right", 0x22, 1, 8, true }, /* Master, Right, 3bit volume level */
+	{ "MIDI Left", 0x26, 5, 8, true }, /* Midi, Left, 3bit volume level */
+	{ "MIDI Right", 0x26, 1, 8, true }, /* Midi, Right, 3bit volume level */
+	{ "CD Left", 0x28, 5, 8, true }, /* CD, Left, 3bit volume level */
+	{ "CD Right", 0x28, 1, 8, true }, /* CD, Right, 3bit volume level */
+	{ "Line In Left", 0x2e, 5, 8, true }, /* Line, Left, 3bit volume level */
+	{ "Line In Right", 0x2e, 1, 8, true }, /* Line, Right, 3bit volume level */
+	{ "Voice Left", 0x04, 5, 8, true }, /* Voice, Left, 3bit volume level */
+	{ "Voice Right", 0x04, 1, 8, true }, /* Voice, Right, 3bit volume level */
+	{ "Mic", 0x0a, 1, 4, false }, /* Mic, Mono, 2bit volume level */
+};
+
+/* CT1745 channels */
+static const channel_t channels_table_ct1745[] = {
+	{ "Master Left", 0x30, 3, 32, false },  /* Master, Left, 5bit volume level */
+	{ "Master Right", 0x31, 3, 32, false }, /* Master, Right, 5bit volume level */
+	{ "Voice Left", 0x32, 3, 32, false },  /* Voice, Left, 5bit volume level */
+	{ "Voice Right", 0x33, 3, 32, false }, /* Voice, Right, 5bit volume level */
+	{ "MIDI Left", 0x34, 3, 32, false }, /* MIDI, Left, 5bit volume level */
+	{ "MIDI Right", 0x35, 3, 32, false }, /* MIDI, Right, 5bit volume level */
+	{ "CD Left", 0x36, 3, 32, false }, /* CD, Left, 5bit volume level */
+	{ "CD Right", 0x37, 3, 32, false }, /* CD, Right, 5bit volume level */
+	{ "Line In Left", 0x38, 3, 32, false }, /* Line, Left, 5bit volume level */
+	{ "Line In Right", 0x39, 3, 32, false }, /* Line, Right, 5bit volume level */
+	{ "Mic", 0x3a, 3, 32, false }, /* Mic, Mono, 5bit volume level */
+	{ "PC Speaker", 0x3b, 6, 4, false }, /* PC speaker, Mono, 2bit level */
+	{ "Input Gain Left", 0x3f, 6, 4, false }, /* Input Gain, Left, 2bit level */
+	{ "Input Gain Right", 0x40, 6, 4, false }, /* Input Gain, Right, 2bit level */
+	{ "Output Gain Left", 0x41, 6, 4, false }, /* Output Gain, Left, 2bit level */
+	{ "Output Gain Right", 0x42, 6, 4, false }, /* Output Gain, Right, 2bit level */
+	{ "Treble Left", 0x44, 4, 16, false }, /* Treble, Left, 4bit volume level */
+	{ "Treble Right", 0x45, 4, 16, false }, /* Treble, Right, 4bit volume level */
+	{ "Bass Left", 0x46, 4, 16, false }, /* Bass, Left, 4bit volume level */
+	{ "Bass Right", 0x47, 4, 16, false }, /* Bass, Right, 4bit volume level */
+};
+
+static const struct {
+	const channel_t *table;
+	size_t count;
+} volume_table[] = {
+	[SB_MIXER_NONE] = { NULL, 0 },
+	[SB_MIXER_UNKNOWN] = { NULL, 0 },
+	[SB_MIXER_CT1335] = {
+	    channels_table_ct1335,
+	    ARRAY_SIZE(channels_table_ct1335),
+	},
+	[SB_MIXER_CT1345] = {
+	    channels_table_ct1345,
+	    ARRAY_SIZE(channels_table_ct1345),
+	},
+	[SB_MIXER_CT1745] = {
+	    channels_table_ct1745,
+	    ARRAY_SIZE(channels_table_ct1745),
+	},
+};
+
+const char * sb_mixer_type_str(sb_mixer_type_t type)
+{
+	static const char *names[] = {
+		[SB_MIXER_NONE] = "No mixer",
+		[SB_MIXER_CT1335] = "CT 1335",
+		[SB_MIXER_CT1345] = "CT 1345",
+		[SB_MIXER_CT1745] = "CT 1745",
+		[SB_MIXER_UNKNOWN] = "Unknown mixer",
+	};
+	return names[type];
+}
+
+int sb_mixer_init(sb_mixer_t *mixer, sb16_regs_t *regs, sb_mixer_type_t type)
+{
+	assert(mixer);
+	mixer->regs = regs;
+	mixer->type = type;
+	if (type == SB_MIXER_UNKNOWN)
+		return ENOTSUP;
+
+	if (type != SB_MIXER_NONE) {
+		pio_write_8(&regs->mixer_address, MIXER_RESET_ADDRESS);
+		pio_write_8(&regs->mixer_data, 1);
+	}
+	pio_write_8(&regs->mixer_address, MIXER_PNP_IRQ_ADDRESS);
+	const uint8_t irq = pio_read_8(&regs->mixer_data);
+	pio_write_8(&regs->mixer_address, MIXER_PNP_DMA_ADDRESS);
+	const uint8_t dma = pio_read_8(&regs->mixer_data);
+	ddf_log_debug("SB16 setup with IRQ 0x%hhx and DMA 0x%hhx.", irq, dma);
+	return EOK;
+}
+
+int sb_mixer_get_control_item_count(const sb_mixer_t *mixer)
+{
+	assert(mixer);
+	return volume_table[mixer->type].count;
+}
+
+int sb_mixer_get_control_item_info(const sb_mixer_t *mixer, unsigned item,
+    const char** name, unsigned *levels)
+{
+	assert(mixer);
+	if (item > volume_table[mixer->type].count)
+		return ENOENT;
+
+	const channel_t *ch = &volume_table[mixer->type].table[item];
+	if (name)
+		*name = ch->name;
+	if (levels)
+		*levels = ch->volume_levels;
+	return EOK;
+}
+
+/**
+ * Write new volume level from mixer registers.
+ * @param mixer SB Mixer to use.
+ * @param index Control item(channel) index.
+ * @param value New volume level.
+ * @return Error code.
+ */
+int sb_mixer_get_control_item_value(const sb_mixer_t *mixer, unsigned item,
+    unsigned *value)
+{
+	assert(mixer);
+	if (!value)
+		return EBADMEM;
+	if (item > volume_table[mixer->type].count)
+		return ENOENT;
+
+	const channel_t *chan = &volume_table[mixer->type].table[item];
+	pio_write_8(&mixer->regs->mixer_address, chan->address);
+	*value = (pio_read_8(&mixer->regs->mixer_data) >> chan->shift)
+	    & (chan->volume_levels - 1);
+	return EOK;
+}
+
+/**
+ * Write new volume level to mixer registers.
+ * @param mixer SB Mixer to use.
+ * @param index Control item(channel) index.
+ * @param value New volume level.
+ * @return Error code.
+ */
+int sb_mixer_set_control_item_value(const sb_mixer_t *mixer, unsigned item,
+    unsigned value)
+{
+	assert(mixer);
+	if (item > volume_table[mixer->type].count)
+		return ENOENT;
+
+	const channel_t *chan = &volume_table[mixer->type].table[item];
+	if (value >= chan->volume_levels)
+		value = chan->volume_levels - 1;
+
+	pio_write_8(&mixer->regs->mixer_address, chan->address);
+
+	uint8_t regv = 0;
+	if (chan->preserve_bits) {
+		regv = pio_read_8(&mixer->regs->mixer_data);
+		regv &= ~(uint8_t)((chan->volume_levels - 1) << chan->shift);
+	}
+
+	regv |= value << chan->shift;
+	pio_write_8(&mixer->regs->mixer_data, regv);
+	ddf_log_note("Item %s new value is: %u.",
+	    volume_table[mixer->type].table[item].name, value);
+	return EOK;
+}
Index: uspace/drv/audio/sb16/mixer.h
===================================================================
--- uspace/drv/audio/sb16/mixer.h	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
+++ uspace/drv/audio/sb16/mixer.h	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 drvaudiosb16
+ * @{
+ */
+/** @file
+ * @brief SB16 main structure combining all functionality
+ */
+#ifndef DRV_AUDIO_SB16_MIXER_H
+#define DRV_AUDIO_SB16_MIXER_H
+
+#include "registers.h"
+
+typedef enum mixer_type {
+	SB_MIXER_NONE,
+	SB_MIXER_CT1335,
+	SB_MIXER_CT1345,
+	SB_MIXER_CT1745,
+	SB_MIXER_UNKNOWN,
+} sb_mixer_type_t;
+
+typedef struct sb_mixer {
+	sb16_regs_t *regs;
+	sb_mixer_type_t type;
+} sb_mixer_t;
+
+const char * sb_mixer_type_str(sb_mixer_type_t type);
+int sb_mixer_init(sb_mixer_t *mixer, sb16_regs_t *regs, sb_mixer_type_t type);
+int sb_mixer_get_control_item_count(const sb_mixer_t *mixer);
+int sb_mixer_get_control_item_info(const sb_mixer_t *mixer, unsigned index,
+    const char **name, unsigned *levels);
+int sb_mixer_get_control_item_value(const sb_mixer_t *mixer, unsigned index,
+    unsigned *value);
+int sb_mixer_set_control_item_value(const sb_mixer_t *mixer, unsigned index,
+    unsigned value);
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/mixer_iface.c
===================================================================
--- uspace/drv/audio/sb16/mixer_iface.c	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
+++ uspace/drv/audio/sb16/mixer_iface.c	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 drvaudiosb16
+ * @{
+ */
+/** @file
+ * Main routines of Creative Labs SoundBlaster 16 driver
+ */
+
+#include <errno.h>
+#include <audio_mixer_iface.h>
+
+#include "mixer.h"
+
+static int sb_get_info(ddf_fun_t *fun, const char** name, unsigned *items)
+{
+	assert(fun);
+	const sb_mixer_t *mixer = ddf_fun_data_get(fun);
+	assert(mixer);
+	if (name)
+		*name = sb_mixer_type_str(mixer->type);
+	if (items)
+		*items = sb_mixer_get_control_item_count(mixer);
+
+	return EOK;
+}
+
+static int sb_get_item_info(ddf_fun_t *fun, unsigned item, const char** name,
+    unsigned *max_level)
+{
+	assert(fun);
+	const sb_mixer_t *mixer = ddf_fun_data_get(fun);
+	assert(mixer);
+	return
+	    sb_mixer_get_control_item_info(mixer, item, name, max_level);
+}
+
+static int sb_set_item_level(ddf_fun_t *fun, unsigned item, unsigned value)
+{
+	assert(fun);
+	const sb_mixer_t *mixer = ddf_fun_data_get(fun);
+	assert(mixer);
+	return sb_mixer_set_control_item_value(mixer, item, value);
+}
+
+static int sb_get_item_level(ddf_fun_t *fun, unsigned item, unsigned *value)
+{
+	assert(fun);
+	const sb_mixer_t *mixer = ddf_fun_data_get(fun);
+	assert(mixer);
+	return sb_mixer_get_control_item_value(mixer, item, value);
+}
+
+audio_mixer_iface_t sb_mixer_iface = {
+	.get_info = sb_get_info,
+	.get_item_info = sb_get_item_info,
+
+	.get_item_level = sb_get_item_level,
+	.set_item_level = sb_set_item_level,
+};
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/pcm_iface.c
===================================================================
--- uspace/drv/audio/sb16/pcm_iface.c	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
+++ uspace/drv/audio/sb16/pcm_iface.c	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 drvaudiosb16
+ * @{
+ */
+/** @file
+ * Main routines of Creative Labs SoundBlaster 16 driver
+ */
+
+#include <async.h>
+#include <errno.h>
+#include <audio_pcm_iface.h>
+#include <pcm/sample_format.h>
+
+#include "dsp.h"
+
+static inline sb_dsp_t * fun_to_dsp(ddf_fun_t *fun)
+{
+	assert(fun);
+	sb_dsp_t *dsp = ddf_fun_data_get(fun);
+	assert(dsp);
+	return dsp;
+}
+
+static int sb_get_info_str(ddf_fun_t *fun, const char** name)
+{
+	if (name)
+		*name = "SB 16 DSP";
+	return EOK;
+}
+
+static unsigned sb_query_cap(ddf_fun_t *fun, audio_cap_t cap)
+{
+	return sb_dsp_query_cap(fun_to_dsp(fun), cap);
+}
+
+static int sb_test_format(ddf_fun_t *fun, unsigned *channels, unsigned *rate,
+    pcm_sample_format_t *format)
+{
+	return sb_dsp_test_format(fun_to_dsp(fun), channels, rate, format);
+}
+static int sb_get_buffer(ddf_fun_t *fun, void **buffer, size_t *size)
+{
+	return sb_dsp_get_buffer(fun_to_dsp(fun), buffer, size);
+}
+
+static int sb_get_buffer_position(ddf_fun_t *fun, size_t *size)
+{
+	return sb_dsp_get_buffer_position(fun_to_dsp(fun), size);
+}
+
+static int sb_set_event_session(ddf_fun_t *fun, async_sess_t *sess)
+{
+	return sb_dsp_set_event_session(fun_to_dsp(fun), sess);
+}
+
+static async_sess_t * sb_get_event_session(ddf_fun_t *fun)
+{
+	return sb_dsp_get_event_session(fun_to_dsp(fun));
+}
+
+static int sb_release_buffer(ddf_fun_t *fun)
+{
+	return sb_dsp_release_buffer(fun_to_dsp(fun));
+}
+
+static int sb_start_playback(ddf_fun_t *fun, unsigned frames,
+    unsigned channels, unsigned sample_rate, pcm_sample_format_t format)
+{
+	return sb_dsp_start_playback(
+	    fun_to_dsp(fun), frames, channels, sample_rate, format);
+}
+
+static int sb_stop_playback(ddf_fun_t *fun, bool immediate)
+{
+	return sb_dsp_stop_playback(fun_to_dsp(fun), immediate);
+}
+
+static int sb_start_capture(ddf_fun_t *fun, unsigned frames,
+    unsigned channels, unsigned sample_rate, pcm_sample_format_t format)
+{
+	return sb_dsp_start_capture(
+	    fun_to_dsp(fun), frames, channels, sample_rate, format);
+}
+
+static int sb_stop_capture(ddf_fun_t *fun, bool immediate)
+{
+	return sb_dsp_stop_capture(fun_to_dsp(fun), immediate);
+}
+
+audio_pcm_iface_t sb_pcm_iface = {
+	.get_info_str = sb_get_info_str,
+	.test_format = sb_test_format,
+	.query_cap = sb_query_cap,
+
+	.get_buffer = sb_get_buffer,
+	.release_buffer = sb_release_buffer,
+	.set_event_session = sb_set_event_session,
+	.get_event_session = sb_get_event_session,
+	.get_buffer_pos = sb_get_buffer_position,
+
+	.start_playback = sb_start_playback,
+	.stop_playback = sb_stop_playback,
+
+	.start_capture = sb_start_capture,
+	.stop_capture = sb_stop_capture,
+};
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/registers.h
===================================================================
--- uspace/drv/audio/sb16/registers.h	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
+++ uspace/drv/audio/sb16/registers.h	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 drvaudiosb16
+ * @{
+ */
+/** @file
+ * @brief SB16 main structure combining all functionality
+ */
+#ifndef DRV_AUDIO_SB16_REGISTERS_H
+#define DRV_AUDIO_SB16_REGISTERS_H
+
+#include <ddi.h>
+
+typedef struct sb16_regs {
+	ioport8_t fm_address_status;
+	ioport8_t fm_data;
+	ioport8_t afm_address_status;
+	ioport8_t afm_data;
+	ioport8_t mixer_address;
+#define MIXER_RESET_ADDRESS 0x00
+#define MIXER_PNP_IRQ_ADDRESS 0x80
+#define MIXER_PNP_DMA_ADDRESS 0x81
+#define MIXER_IRQ_STATUS_ADDRESS 0x82 /* The Interrupt Status register,
+                                       * addressed as register 82h on the
+                                       * Mixer register map p.27 */
+	ioport8_t mixer_data;
+	ioport8_t dsp_reset;
+	ioport8_t __reserved1; /* 0x7 */
+	ioport8_t fm_address_status2;
+	ioport8_t fm_data2;
+	ioport8_t dsp_data_read;
+	ioport8_t __reserved2; /*0xb*/
+	ioport8_t dsp_write; /* Both command and data, bit 7 is write status */
+#define DSP_WRITE_BUSY (1 << 7)
+	ioport8_t __reserved3; /*0xd*/
+	ioport8_t dsp_read_status; /* Bit 7 */
+#define DSP_READ_READY (1 << 7)
+	ioport8_t dma16_ack; /*0xf*/
+	ioport8_t cd_command_data;
+	ioport8_t cd_status;
+	ioport8_t cd_reset;
+	ioport8_t cd_enable;
+} sb16_regs_t;
+
+typedef struct mpu_regs {
+	ioport8_t data;
+#define MPU_CMD_ACK (0xfe)
+
+	ioport8_t status_command;
+#define MPU_STATUS_OUTPUT_BUSY (1 << 6)
+#define MPU_STATUS_INPUT_BUSY (1 << 7)
+
+#define MPU_CMD_RESET (0xff)
+#define MPU_CMD_ENTER_UART (0x3f)
+} mpu_regs_t;
+
+#endif
+/**
+ * @}
+ */
+
Index: uspace/drv/audio/sb16/sb16.c
===================================================================
--- uspace/drv/audio/sb16/sb16.c	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
+++ uspace/drv/audio/sb16/sb16.c	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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.
+ */
+
+#define _DDF_DATA_IMPLANT
+
+#include <errno.h>
+#include <str_error.h>
+#include <macros.h>
+#include <audio_mixer_iface.h>
+#include <audio_pcm_iface.h>
+
+#include "ddf_log.h"
+#include "dsp_commands.h"
+#include "dsp.h"
+#include "sb16.h"
+
+extern audio_mixer_iface_t sb_mixer_iface;
+extern audio_pcm_iface_t sb_pcm_iface;
+
+static ddf_dev_ops_t sb_mixer_ops = {
+	.interfaces[AUDIO_MIXER_IFACE] = &sb_mixer_iface,
+};
+
+static ddf_dev_ops_t sb_pcm_ops = {
+	.interfaces[AUDIO_PCM_BUFFER_IFACE] = &sb_pcm_iface,
+};
+
+/* ISA interrupts should be edge-triggered so there should be no need for
+ * irq code magic, but we still need to ack those interrupts ASAP. */
+static const irq_cmd_t irq_cmds[] = {
+	{ .cmd = CMD_PIO_READ_8, .dstarg = 1 }, /* Address patched at runtime */
+	{ .cmd = CMD_PIO_READ_8, .dstarg = 1 }, /* Address patched at runtime */
+	{ .cmd = CMD_ACCEPT },
+};
+
+static inline sb_mixer_type_t sb_mixer_type_by_dsp_version(
+    unsigned major, unsigned minor)
+{
+	switch (major)
+	{
+	case 1: return SB_MIXER_NONE; /* SB 1.5 and early 2.0 = no mixer chip */
+	case 2: return (minor == 0) ? SB_MIXER_NONE : SB_MIXER_CT1335;
+	case 3: return SB_MIXER_CT1345; /* SB Pro */
+	case 4: return SB_MIXER_CT1745; /* SB 16  */
+	default: return SB_MIXER_UNKNOWN;
+	}
+}
+
+size_t sb16_irq_code_size(void)
+{
+	return ARRAY_SIZE(irq_cmds);
+}
+
+void sb16_irq_code(void *regs, int dma8, int dma16, irq_cmd_t cmds[], irq_pio_range_t ranges[])
+{
+	assert(regs);
+	assert(dma8 > 0 && dma8 < 4);
+	sb16_regs_t *registers = regs;
+	memcpy(cmds, irq_cmds, sizeof(irq_cmds));
+	cmds[0].addr = (void*)&registers->dsp_read_status;
+	ranges[0].base = (uintptr_t)registers;
+	ranges[0].size = sizeof(*registers);
+	if (dma16 > 4 && dma16 < 8) {
+		/* Valid dma16 */
+		cmds[1].addr = (void*)&registers->dma16_ack;
+	} else {
+		cmds[1].cmd = CMD_ACCEPT;
+	}
+}
+
+int sb16_init_sb16(sb16_t *sb, void *regs, size_t size,
+    ddf_dev_t *dev, int dma8, int dma16)
+{
+	assert(sb);
+	/* Setup registers */
+	int ret = pio_enable(regs, size, (void**)&sb->regs);
+	if (ret != EOK)
+		return ret;
+	ddf_log_debug("PIO registers at %p accessible.", sb->regs);
+
+	/* Initialize DSP */
+	ddf_fun_t *dsp_fun = ddf_fun_create(dev, fun_exposed, "pcm");
+	if (!dsp_fun) {
+		ddf_log_error("Failed to create dsp function.");
+		return ENOMEM;
+	}
+
+	ret = sb_dsp_init(&sb->dsp, sb->regs, dev, dma8, dma16);
+	if (ret != EOK) {
+		ddf_log_error("Failed to initialize SB DSP: %s.",
+		    str_error(ret));
+		ddf_fun_destroy(dsp_fun);
+		return ret;
+	}
+	//TODO remove data implant
+	ddf_fun_data_implant(dsp_fun, &sb->dsp);
+	ddf_fun_set_ops(dsp_fun, &sb_pcm_ops);
+	ddf_log_note("Sound blaster DSP (%x.%x) initialized.",
+	    sb->dsp.version.major, sb->dsp.version.minor);
+
+	ret = ddf_fun_bind(dsp_fun);
+	if (ret != EOK) {
+		ddf_log_error(
+		    "Failed to bind PCM function: %s.", str_error(ret));
+		// TODO implanted data
+		ddf_fun_destroy(dsp_fun);
+		return ret;
+	}
+
+	ret = ddf_fun_add_to_category(dsp_fun, "audio-pcm");
+	if (ret != EOK) {
+		ddf_log_error("Failed register PCM function in category: %s.",
+		    str_error(ret));
+		ddf_fun_unbind(dsp_fun);
+		// TODO implanted data
+		ddf_fun_destroy(dsp_fun);
+		return ret;
+	}
+
+	/* Initialize mixer */
+	const sb_mixer_type_t mixer_type = sb_mixer_type_by_dsp_version(
+	    sb->dsp.version.major, sb->dsp.version.minor);
+
+	ddf_fun_t *mixer_fun = ddf_fun_create(dev, fun_exposed, "control");
+	if (!mixer_fun) {
+		ddf_log_error("Failed to create mixer function.");
+		ddf_fun_unbind(dsp_fun);
+		// TODO implanted data
+		ddf_fun_destroy(dsp_fun);
+		return ENOMEM;
+	}
+	ret = sb_mixer_init(&sb->mixer, sb->regs, mixer_type);
+	if (ret != EOK) {
+		ddf_log_error("Failed to initialize SB mixer: %s.",
+		    str_error(ret));
+		ddf_fun_unbind(dsp_fun);
+		// TODO implanted data
+		ddf_fun_destroy(dsp_fun);
+		ddf_fun_destroy(mixer_fun);
+		return ret;
+	}
+
+	ddf_log_note("Initialized mixer: %s.",
+	    sb_mixer_type_str(sb->mixer.type));
+	ddf_fun_data_implant(mixer_fun, &sb->mixer);
+	ddf_fun_set_ops(mixer_fun, &sb_mixer_ops);
+
+	ret = ddf_fun_bind(mixer_fun);
+	if (ret != EOK) {
+		ddf_log_error(
+		    "Failed to bind mixer function: %s.", str_error(ret));
+		// TODO implanted data
+		ddf_fun_destroy(mixer_fun);
+
+		ddf_fun_unbind(dsp_fun);
+		// TODO implanted data
+		ddf_fun_destroy(dsp_fun);
+		return ret;
+	}
+
+	return EOK;
+}
+
+int sb16_init_mpu(sb16_t *sb, void *regs, size_t size)
+{
+	sb->mpu_regs = NULL;
+	return ENOTSUP;
+}
+
+void sb16_interrupt(sb16_t *sb)
+{
+	assert(sb);
+	/* The acknowledgment of interrupts on DSP version 4.xx is different;
+	 * It can contain MPU-401 indicator and DMA16 transfers are acked
+	 * differently */
+	if (sb->dsp.version.major >= 4) {
+		pio_write_8(&sb->regs->mixer_address, MIXER_IRQ_STATUS_ADDRESS);
+		const uint8_t irq_mask = pio_read_8(&sb->regs->mixer_data);
+		/* Third bit is MPU-401 interrupt */
+		if (irq_mask & 0x4) {
+			return;
+		}
+	} else {
+		ddf_log_debug("SB16 interrupt.");
+	}
+	sb_dsp_interrupt(&sb->dsp);
+}
Index: uspace/drv/audio/sb16/sb16.h
===================================================================
--- uspace/drv/audio/sb16/sb16.h	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
+++ uspace/drv/audio/sb16/sb16.h	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 drvaudiosb16
+ * @{
+ */
+/** @file
+ * @brief SB16 main structure combining all functionality
+ */
+#ifndef DRV_AUDIO_SB16_SB16_H
+#define DRV_AUDIO_SB16_SB16_H
+
+#include <ddf/driver.h>
+#include <ddi.h>
+
+#include "dsp.h"
+#include "mixer.h"
+#include "registers.h"
+
+typedef struct sb16 {
+	sb16_regs_t *regs;
+	mpu_regs_t *mpu_regs;
+	sb_dsp_t dsp;
+	sb_mixer_t mixer;
+} sb16_t;
+
+size_t sb16_irq_code_size(void);
+void sb16_irq_code(void *regs, int dma8, int dma16, irq_cmd_t cmds[], irq_pio_range_t ranges[]);
+int sb16_init_sb16(sb16_t *sb, void *regs, size_t size,
+    ddf_dev_t *dev, int dma8, int dma16);
+int sb16_init_mpu(sb16_t *sb, void *regs, size_t size);
+void sb16_interrupt(sb16_t *sb);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/sb16.ma
===================================================================
--- uspace/drv/audio/sb16/sb16.ma	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
+++ uspace/drv/audio/sb16/sb16.ma	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
@@ -0,0 +1,1 @@
+100 isa/sb16
Index: uspace/drv/bus/isa/i8237.c
===================================================================
--- uspace/drv/bus/isa/i8237.c	(revision 2e842f2f44fea53af241bdcbe95c939ad9e028b5)
+++ uspace/drv/bus/isa/i8237.c	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
@@ -38,4 +38,6 @@
 #include <stdbool.h>
 #include <errno.h>
+#include <ddi.h>
+#include <ddf/log.h>
 #include <fibril_synch.h>
 #include <ddi.h>
@@ -198,69 +200,69 @@
 	.channels = {
 		/* The first chip 8-bit */
-		{
-			(uint8_t *) 0x00,
-			(uint8_t *) 0x01,
-			(uint8_t *) 0x87,
-			(uint8_t *) 0x0a,
-			(uint8_t *) 0x0b,
-			(uint8_t *) 0x0c,
-		},
-		{
-			(uint8_t *) 0x02,
-			(uint8_t *) 0x03,
-			(uint8_t *) 0x83,
-			(uint8_t *) 0x0a,
-			(uint8_t *) 0x0b,
-			(uint8_t *) 0x0c,
-		},
-		{
-			(uint8_t *) 0x04,
-			(uint8_t *) 0x05,
-			(uint8_t *) 0x81,
-			(uint8_t *) 0x0a,
-			(uint8_t *) 0x0b,
-			(uint8_t *) 0x0c,
-		},
-		{
-			(uint8_t *) 0x06,
-			(uint8_t *) 0x07,
-			(uint8_t *) 0x82,
-			(uint8_t *) 0x0a,
-			(uint8_t *) 0x0b,
-			(uint8_t *) 0x0c,
+		{ /* Channel 0 - Unusable*/
+			.offset_reg_address = (uint8_t *) 0x00,
+			.size_reg_address = (uint8_t *) 0x01,
+			.page_reg_address = (uint8_t *) 0x87,
+			.single_mask_address = (uint8_t *) 0x0a,
+			.mode_address = (uint8_t *) 0x0b,
+			.flip_flop_address = (uint8_t *) 0x0c,
+		},
+		{ /* Channel 1 */
+			.offset_reg_address = (uint8_t *) 0x02,
+			.size_reg_address = (uint8_t *) 0x03,
+			.page_reg_address = (uint8_t *) 0x83,
+			.single_mask_address = (uint8_t *) 0x0a,
+			.mode_address = (uint8_t *) 0x0b,
+			.flip_flop_address = (uint8_t *) 0x0c,
+		},
+		{ /* Channel 2 */
+			.offset_reg_address = (uint8_t *) 0x04,
+			.size_reg_address = (uint8_t *) 0x05,
+			.page_reg_address = (uint8_t *) 0x81,
+			.single_mask_address = (uint8_t *) 0x0a,
+			.mode_address = (uint8_t *) 0x0b,
+			.flip_flop_address = (uint8_t *) 0x0c,
+		},
+		{ /* Channel 3 */
+			.offset_reg_address = (uint8_t *) 0x06,
+			.size_reg_address = (uint8_t *) 0x07,
+			.page_reg_address = (uint8_t *) 0x82,
+			.single_mask_address = (uint8_t *) 0x0a,
+			.mode_address = (uint8_t *) 0x0b,
+			.flip_flop_address = (uint8_t *) 0x0c,
 		},
 		
 		/* The second chip 16-bit */
-		{
-			(uint8_t *) 0xc0,
-			(uint8_t *) 0xc2,
-			(uint8_t *) 0x8f,
-			(uint8_t *) 0xd4,
-			(uint8_t *) 0xd6,
-			(uint8_t *) 0xd8,
-		},
-		{
-			(uint8_t *) 0xc4,
-			(uint8_t *) 0xc6,
-			(uint8_t *) 0x8b,
-			(uint8_t *) 0xd4,
-			(uint8_t *) 0xd6,
-			(uint8_t *) 0xd8,
-		},
-		{
-			(uint8_t *) 0xc8,
-			(uint8_t *) 0xca,
-			(uint8_t *) 0x89,
-			(uint8_t *) 0xd4,
-			(uint8_t *) 0xd6,
-			(uint8_t *) 0xd8,
-		},
-		{
-			(uint8_t *) 0xcc,
-			(uint8_t *) 0xce,
-			(uint8_t *) 0x8a,
-			(uint8_t *) 0xd4,
-			(uint8_t *) 0xd6,
-			(uint8_t *) 0xd8,
+		{ /* Channel 4 - Unusable */
+			.offset_reg_address = (uint8_t *) 0xc0,
+			.size_reg_address = (uint8_t *) 0xc2,
+			.page_reg_address = (uint8_t *) 0x8f,
+			.single_mask_address = (uint8_t *) 0xd4,
+			.mode_address = (uint8_t *) 0xd6,
+			.flip_flop_address = (uint8_t *) 0xd8,
+		},
+		{ /* Channel 5 */
+			.offset_reg_address = (uint8_t *) 0xc4,
+			.size_reg_address = (uint8_t *) 0xc6,
+			.page_reg_address = (uint8_t *) 0x8b,
+			.single_mask_address = (uint8_t *) 0xd4,
+			.mode_address = (uint8_t *) 0xd6,
+			.flip_flop_address = (uint8_t *) 0xd8,
+		},
+		{ /* Channel 6 */
+			.offset_reg_address = (uint8_t *) 0xc8,
+			.size_reg_address = (uint8_t *) 0xca,
+			.page_reg_address = (uint8_t *) 0x89,
+			.single_mask_address = (uint8_t *) 0xd4,
+			.mode_address = (uint8_t *) 0xd6,
+			.flip_flop_address = (uint8_t *) 0xd8,
+		},
+		{ /* Channel 7 */
+			.offset_reg_address = (uint8_t *) 0xcc,
+			.size_reg_address = (uint8_t *) 0xce,
+			.page_reg_address = (uint8_t *) 0x8a,
+			.single_mask_address = (uint8_t *) 0xd4,
+			.mode_address = (uint8_t *) 0xd6,
+			.flip_flop_address = (uint8_t *) 0xd8,
 		},
 	},
@@ -272,10 +274,9 @@
 };
 
-/* Initialize I/O access to DMA controller I/O ports.
+/** Initialize I/O access to DMA controller I/O ports.
  *
  * @param controller DMA Controller structure to initialize.
  *
  * @return Error code.
- *
  */
 static inline int dma_controller_init(dma_controller_t *controller)
@@ -304,4 +305,22 @@
 	
 	return EOK;
+}
+
+/** Helper function. Channels 4,5,6, and 7 are 8 bit DMA.
+ * @pram channel DMA channel.
+ * @reutrn True, if channel is 4,5,6, or 7, false otherwise.
+ */
+static inline bool is_dma16(unsigned channel)
+{
+	return (channel >= 4) && (channel < 8);
+}
+
+/** Helper function. Channels 0,1,2, and 3 are 8 bit DMA.
+ * @pram channel DMA channel.
+ * @reutrn True, if channel is 0,1,2, or 3, false otherwise.
+ */
+static inline bool is_dma8(unsigned channel)
+{
+	return (channel < 4);
 }
 
@@ -320,14 +339,13 @@
  *
  * @return Error code.
- *
- */
-int dma_setup_channel(unsigned int channel, uint32_t pa, uint16_t size,
+ */
+int dma_channel_setup(unsigned int channel, uint32_t pa, uint16_t size,
     uint8_t mode)
 {
+	if (!is_dma8(channel) && !is_dma16(channel))
+		return ENOENT;
+
 	if ((channel == 0) || (channel == 4))
 		return ENOTSUP;
-	
-	if (channel > 7)
-		return ENOENT;
 	
 	/* DMA is limited to 24bit addresses. */
@@ -336,5 +354,9 @@
 	
 	/* 8 bit channels use only 4 bits from the page register. */
-	if ((channel > 0) && (channel < 4) && (pa >= (1 << 20)))
+	if (is_dma8(channel) && (pa >= (1 << 20)))
+		return EINVAL;
+
+	/* Buffers cannot cross 64K page boundaries */
+	if ((pa & 0xffff0000) !=  ((pa + size) & 0xffff0000))
 		return EINVAL;
 	
@@ -352,5 +374,5 @@
 	ddf_msg(LVL_DEBUG, "Unspoiled address %#" PRIx32 " (size %" PRIu16 ")",
 	    pa, size);
-	if (channel > 4) {
+	if (is_dma16(channel)) {
 		/* Size must be aligned to 16 bits */
 		if ((size & 1) != 0) {
@@ -358,7 +380,6 @@
 			return EINVAL;
 		}
-		
+		/* Size is in 2byte words */
 		size >>= 1;
-		
 		/* Address is fun: lower 16 bits need to be shifted by 1 */
 		pa = ((pa & 0xffff) >> 1) | (pa & 0xff0000);
@@ -426,4 +447,51 @@
 }
 
+/** Query remaining buffer size.
+ *
+ * @param channel DMA Channel 1, 2, 3 for 8 bit transfers,
+ *                    5, 6, 7 for 16 bit.
+ * @param size    Place to store number of bytes pending in the assigned buffer.
+ *
+ * @return Error code.
+ */
+int dma_channel_remain(unsigned channel, size_t *size)
+{
+	assert(size);
+	if (!is_dma8(channel) && !is_dma16(channel))
+		return ENOENT;
+	
+	if ((channel == 0) || (channel == 4))
+		return ENOTSUP;
+	
+	fibril_mutex_lock(&guard);
+	if (!controller_8237.initialized) {
+		fibril_mutex_unlock(&guard);
+		return EIO;
+	}
+
+	const dma_channel_t dma_channel = controller_8237.channels[channel];
+	/* Get size - reset flip-flop */
+	pio_write_8(dma_channel.flip_flop_address, 0);
+	
+	/* Low byte */
+	const uint8_t value_low = pio_read_8(dma_channel.size_reg_address);
+	ddf_msg(LVL_DEBUG2, "Read size low byte: %p:%zx.",
+	    dma_channel.size_reg_address, value_low);
+	
+	/* High byte */
+	const uint8_t value_high = pio_read_8(dma_channel.size_reg_address);
+	ddf_msg(LVL_DEBUG2, "Read size high byte: %p:%zx.",
+	    dma_channel.size_reg_address, value_high);
+	fibril_mutex_unlock(&guard);
+
+	uint16_t remain = (value_high << 8 | value_low) ;
+	/* 16 bit DMA size is in words,
+	 * the upper bits are bogus for 16bit transfers so we need to get
+	 * rid of them. Using limited type works well.*/
+	if (is_dma16(channel))
+		remain <<= 1;
+	*size =  is_dma16(channel) ? remain + 2: remain + 1;
+	return EOK;
+}
 /**
  * @}
Index: uspace/drv/bus/isa/i8237.h
===================================================================
--- uspace/drv/bus/isa/i8237.h	(revision 2e842f2f44fea53af241bdcbe95c939ad9e028b5)
+++ uspace/drv/bus/isa/i8237.h	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
@@ -38,5 +38,6 @@
 #define DRV_BUS_ISA_I8237_H
 
-extern int dma_setup_channel(unsigned int, uint32_t, uint16_t, uint8_t);
+extern int dma_channel_setup(unsigned, uint32_t, uint16_t, uint8_t);
+extern int dma_channel_remain(unsigned, size_t *);
 
 #endif
Index: uspace/drv/bus/isa/isa.c
===================================================================
--- uspace/drv/bus/isa/isa.c	(revision 2e842f2f44fea53af241bdcbe95c939ad9e028b5)
+++ uspace/drv/bus/isa/isa.c	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
@@ -85,4 +85,5 @@
 	fibril_mutex_t mutex;
 	ddf_fun_t *fnode;
+	hw_resource_t resources[ISA_MAX_HW_RES];
 	hw_resource_list_t hw_resources;
 	link_t bus_link;
@@ -103,15 +104,16 @@
 static hw_resource_list_t *isa_get_fun_resources(ddf_fun_t *fnode)
 {
-	isa_fun_t *fun = isa_fun(fnode);
-	assert(fun != NULL);
-
-	return &fun->hw_resources;
-}
-
-static bool isa_enable_fun_interrupt(ddf_fun_t *fnode)
+	isa_fun_t *isa = isa_fun(fnode);
+	assert(isa);
+
+	return &isa->hw_resources;
+}
+
+static bool isa_fun_enable_interrupt(ddf_fun_t *fnode)
 {
 	/* This is an old ugly way, copied from pci driver */
 	assert(fnode);
-	isa_fun_t *fun = isa_fun(fnode);
+	isa_fun_t *isa = isa_fun(fnode);
+	assert(isa);
 
 	sysarg_t apic;
@@ -129,5 +131,5 @@
 		return false;
 
-	const hw_resource_list_t *res = &fun->hw_resources;
+	const hw_resource_list_t *res = &isa->hw_resources;
 	assert(res);
 	for (size_t i = 0; i < res->count; ++i) {
@@ -151,22 +153,46 @@
 }
 
-static int isa_dma_channel_fun_setup(ddf_fun_t *fnode,
+static int isa_fun_setup_dma(ddf_fun_t *fnode,
     unsigned int channel, uint32_t pa, uint16_t size, uint8_t mode)
 {
 	assert(fnode);
-	isa_fun_t *fun = isa_fun(fnode);
-	const hw_resource_list_t *res = &fun->hw_resources;
+	isa_fun_t *isa = isa_fun(fnode);
+	assert(isa);
+	const hw_resource_list_t *res = &isa->hw_resources;
 	assert(res);
-	
-	const unsigned int ch = channel;
+
 	for (size_t i = 0; i < res->count; ++i) {
+		/* Check for assigned channel */
 		if (((res->resources[i].type == DMA_CHANNEL_16) &&
-		    (res->resources[i].res.dma_channel.dma16 == ch)) ||
+		    (res->resources[i].res.dma_channel.dma16 == channel)) ||
 		    ((res->resources[i].type == DMA_CHANNEL_8) &&
-		    (res->resources[i].res.dma_channel.dma8 == ch))) {
-			return dma_setup_channel(channel, pa, size, mode);
-		}
-	}
-	
+		    (res->resources[i].res.dma_channel.dma8 == channel))) {
+			return dma_channel_setup(channel, pa, size, mode);
+		}
+	}
+
+	return EINVAL;
+}
+
+static int isa_fun_remain_dma(ddf_fun_t *fnode,
+    unsigned channel, size_t *size)
+{
+	assert(size);
+	assert(fnode);
+	isa_fun_t *isa = isa_fun(fnode);
+	assert(isa);
+	const hw_resource_list_t *res = &isa->hw_resources;
+	assert(res);
+
+	for (size_t i = 0; i < res->count; ++i) {
+		/* Check for assigned channel */
+		if (((res->resources[i].type == DMA_CHANNEL_16) &&
+		    (res->resources[i].res.dma_channel.dma16 == channel)) ||
+		    ((res->resources[i].type == DMA_CHANNEL_8) &&
+		    (res->resources[i].res.dma_channel.dma8 == channel))) {
+			return dma_channel_remain(channel, size);
+		}
+	}
+
 	return EINVAL;
 }
@@ -174,9 +200,12 @@
 static hw_res_ops_t isa_fun_hw_res_ops = {
 	.get_resource_list = isa_get_fun_resources,
-	.enable_interrupt = isa_enable_fun_interrupt,
-	.dma_channel_setup = isa_dma_channel_fun_setup,
+	.enable_interrupt = isa_fun_enable_interrupt,
+	.dma_channel_setup = isa_fun_setup_dma,
+	.dma_channel_remain = isa_fun_remain_dma,
 };
 
-static ddf_dev_ops_t isa_fun_ops;
+static ddf_dev_ops_t isa_fun_ops= {
+	.interfaces[HW_RES_DEV_IFACE] = &isa_fun_hw_res_ops,
+};
 
 static int isa_dev_add(ddf_dev_t *dev);
@@ -212,4 +241,6 @@
 
 	fibril_mutex_initialize(&fun->mutex);
+	fun->hw_resources.resources = fun->resources;
+
 	fun->fnode = fnode;
 	return fun;
@@ -270,7 +301,7 @@
 {
 	char *line = str;
+	*next = NULL;
 
 	if (str == NULL) {
-		*next = NULL;
 		return NULL;
 	}
@@ -282,6 +313,4 @@
 	if (*str != '\0') {
 		*next = str + 1;
-	} else {
-		*next = NULL;
 	}
 
@@ -310,18 +339,8 @@
 	/* Get the name part of the rest of the line. */
 	strtok(line, ":");
-
-	/* Allocate output buffer. */
-	size_t size = str_size(line) + 1;
-	char *name = malloc(size);
-
-	if (name != NULL) {
-		/* Copy the result to the output buffer. */
-		str_cpy(name, size, line);
-	}
-
-	return name;
-}
-
-static inline char *skip_spaces(char *line)
+	return line;
+}
+
+static inline const char *skip_spaces(const char *line)
 {
 	/* Skip leading spaces. */
@@ -332,5 +351,5 @@
 }
 
-static void isa_fun_set_irq(isa_fun_t *fun, int irq)
+static void isa_fun_add_irq(isa_fun_t *fun, int irq)
 {
 	size_t count = fun->hw_resources.count;
@@ -348,5 +367,5 @@
 }
 
-static void isa_fun_set_dma(isa_fun_t *fun, int dma)
+static void isa_fun_add_dma(isa_fun_t *fun, int dma)
 {
 	size_t count = fun->hw_resources.count;
@@ -381,5 +400,5 @@
 }
 
-static void isa_fun_set_io_range(isa_fun_t *fun, size_t addr, size_t len)
+static void isa_fun_add_io_range(isa_fun_t *fun, size_t addr, size_t len)
 {
 	size_t count = fun->hw_resources.count;
@@ -400,5 +419,5 @@
 }
 
-static void fun_parse_irq(isa_fun_t *fun, char *val)
+static void fun_parse_irq(isa_fun_t *fun, const char *val)
 {
 	int irq = 0;
@@ -409,20 +428,19 @@
 
 	if (val != end)
-		isa_fun_set_irq(fun, irq);
-}
-
-static void fun_parse_dma(isa_fun_t *fun, char *val)
-{
-	unsigned int dma = 0;
+		isa_fun_add_irq(fun, irq);
+}
+
+static void fun_parse_dma(isa_fun_t *fun, const char *val)
+{
 	char *end = NULL;
 	
 	val = skip_spaces(val);
-	dma = (unsigned int) strtol(val, &end, 10);
+	const int dma = strtol(val, &end, 10);
 	
 	if (val != end)
-		isa_fun_set_dma(fun, dma);
-}
-
-static void fun_parse_io_range(isa_fun_t *fun, char *val)
+		isa_fun_add_dma(fun, dma);
+}
+
+static void fun_parse_io_range(isa_fun_t *fun, const char *val)
 {
 	size_t addr, len;
@@ -441,10 +459,10 @@
 		return;
 
-	isa_fun_set_io_range(fun, addr, len);
-}
-
-static void get_match_id(char **id, char *val)
-{
-	char *end = val;
+	isa_fun_add_io_range(fun, addr, len);
+}
+
+static void get_match_id(char **id, const char *val)
+{
+	const char *end = val;
 
 	while (!isspace(*end))
@@ -456,14 +474,12 @@
 }
 
-static void fun_parse_match_id(isa_fun_t *fun, char *val)
+static void fun_parse_match_id(isa_fun_t *fun, const char *val)
 {
 	char *id = NULL;
-	int score = 0;
 	char *end = NULL;
-	int rc;
 
 	val = skip_spaces(val);
 
-	score = (int)strtol(val, &end, 10);
+	int score = (int)strtol(val, &end, 10);
 	if (val == end) {
 		ddf_msg(LVL_ERROR, "Cannot read match score for function "
@@ -483,5 +499,5 @@
 	    "function %s", id, score, ddf_fun_get_name(fun->fnode));
 
-	rc = ddf_fun_add_match_id(fun->fnode, id, score);
+	int rc = ddf_fun_add_match_id(fun->fnode, id, score);
 	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Failed adding match ID: %s",
@@ -492,6 +508,6 @@
 }
 
-static bool prop_parse(isa_fun_t *fun, char *line, const char *prop,
-    void (*read_fn)(isa_fun_t *, char *))
+static bool prop_parse(isa_fun_t *fun, const char *line, const char *prop,
+    void (*read_fn)(isa_fun_t *, const char *))
 {
 	size_t proplen = str_size(prop);
@@ -508,5 +524,5 @@
 }
 
-static void fun_prop_parse(isa_fun_t *fun, char *line)
+static void fun_prop_parse(isa_fun_t *fun, const char *line)
 {
 	/* Skip leading spaces. */
@@ -523,23 +539,10 @@
 }
 
-static void fun_hw_res_alloc(isa_fun_t *fun)
-{
-	fun->hw_resources.resources =
-	    (hw_resource_t *) malloc(sizeof(hw_resource_t) * ISA_MAX_HW_RES);
-}
-
-static void fun_hw_res_free(isa_fun_t *fun)
-{
-	free(fun->hw_resources.resources);
-	fun->hw_resources.resources = NULL;
-}
-
 static char *isa_fun_read_info(char *fun_conf, isa_bus_t *isa)
 {
 	char *line;
-	char *fun_name = NULL;
 
 	/* Skip empty lines. */
-	while (true) {
+	do {
 		line = str_get_line(fun_conf, &fun_conf);
 
@@ -549,21 +552,15 @@
 		}
 
-		if (!line_empty(line))
-			break;
-	}
+	} while (line_empty(line));
 
 	/* Get device name. */
-	fun_name = get_device_name(line);
+	const char *fun_name = get_device_name(line);
 	if (fun_name == NULL)
 		return NULL;
 
 	isa_fun_t *fun = isa_fun_create(isa, fun_name);
-	free(fun_name);
 	if (fun == NULL) {
 		return NULL;
 	}
-
-	/* Allocate buffer for the list of hardware resources of the device. */
-	fun_hw_res_alloc(fun);
 
 	/* Get properties of the device (match ids, irq and io range). */
@@ -596,30 +593,19 @@
 }
 
-static void fun_conf_parse(char *conf, isa_bus_t *isa)
-{
+static void isa_functions_add(isa_bus_t *isa)
+{
+	char *conf = fun_conf_read(CHILD_FUN_CONF_PATH);
 	while (conf != NULL && *conf != '\0') {
 		conf = isa_fun_read_info(conf, isa);
 	}
-}
-
-static void isa_functions_add(isa_bus_t *isa)
-{
-	char *fun_conf;
-
-	fun_conf = fun_conf_read(CHILD_FUN_CONF_PATH);
-	if (fun_conf != NULL) {
-		fun_conf_parse(fun_conf, isa);
-		free(fun_conf);
-	}
+	free(conf);
 }
 
 static int isa_dev_add(ddf_dev_t *dev)
 {
-	isa_bus_t *isa;
-
 	ddf_msg(LVL_DEBUG, "isa_dev_add, device handle = %d",
 	    (int) ddf_dev_get_handle(dev));
 
-	isa = ddf_dev_data_alloc(dev, sizeof(isa_bus_t));
+	isa_bus_t *isa = ddf_dev_data_alloc(dev, sizeof(isa_bus_t));
 	if (isa == NULL)
 		return ENOMEM;
@@ -658,5 +644,4 @@
 {
 	isa_bus_t *isa = isa_bus(dev);
-	int rc;
 
 	fibril_mutex_lock(&isa->mutex);
@@ -666,5 +651,5 @@
 		    isa_fun_t, bus_link);
 
-		rc = ddf_fun_offline(fun->fnode);
+		int rc = ddf_fun_offline(fun->fnode);
 		if (rc != EOK) {
 			fibril_mutex_unlock(&isa->mutex);
@@ -682,5 +667,4 @@
 		list_remove(&fun->bus_link);
 
-		fun_hw_res_free(fun);
 		ddf_fun_destroy(fun->fnode);
 	}
@@ -709,15 +693,8 @@
 }
 
-
-static void isa_init()
-{
+int main(int argc, char *argv[])
+{
+	printf(NAME ": HelenOS ISA bus driver\n");
 	ddf_log_init(NAME);
-	isa_fun_ops.interfaces[HW_RES_DEV_IFACE] = &isa_fun_hw_res_ops;
-}
-
-int main(int argc, char *argv[])
-{
-	printf(NAME ": HelenOS ISA bus driver\n");
-	isa_init();
 	return ddf_driver_main(&isa_driver);
 }
Index: uspace/drv/infrastructure/root/root.c
===================================================================
--- uspace/drv/infrastructure/root/root.c	(revision 2e842f2f44fea53af241bdcbe95c939ad9e028b5)
+++ uspace/drv/infrastructure/root/root.c	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
@@ -208,11 +208,13 @@
 	/*
 	 * Register virtual devices root.
-	 * We ignore error occurrence because virtual devices shall not be
+	 * We warn on error occurrence because virtual devices shall not be
 	 * vital for the system.
 	 */
-	(void) add_virtual_root_fun(dev);
+	int res = add_virtual_root_fun(dev);
+	if (res != EOK)
+		ddf_msg(LVL_WARN, "Failed to add virtual child.");
 
 	/* Register root device's children. */
-	int res = add_platform_fun(dev);
+	res = add_platform_fun(dev);
 	if (EOK != res)
 		ddf_msg(LVL_ERROR, "Failed adding child device for platform.");
Index: uspace/drv/nic/e1k/e1k.c
===================================================================
--- uspace/drv/nic/e1k/e1k.c	(revision 2e842f2f44fea53af241bdcbe95c939ad9e028b5)
+++ uspace/drv/nic/e1k/e1k.c	(revision 03c971f2e2115454ea0fbcaca3ac08bd88ac0a3c)
@@ -59,7 +59,7 @@
 #define E1000_DEFAULT_INTERRUPT_INTERVAL_USEC  250
 
-/* Must be power of 8 */
-#define E1000_RX_FRAME_COUNT  128
-#define E1000_TX_FRAME_COUNT  128
+/* Must be power of 8 */ //TODO power or multiple? previous value was 128
+#define E1000_RX_FRAME_COUNT  8
+#define E1000_TX_FRAME_COUNT  8
 
 #define E1000_RECEIVE_ADDRESS  16
