Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision 0bbd13e274302c7c89dd4ea7cad7dbcc3ecfc524)
+++ boot/Makefile.common	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -155,4 +155,5 @@
 	$(USPACE_PATH)/app/dltest2/dltest2 \
 	$(USPACE_PATH)/app/dload/dload \
+	$(USPACE_PATH)/app/dplay/dplay \
 	$(USPACE_PATH)/app/edit/edit \
 	$(USPACE_PATH)/app/ext2info/ext2info \
@@ -161,4 +162,5 @@
 	$(USPACE_PATH)/app/killall/killall \
 	$(USPACE_PATH)/app/loc/loc \
+	$(USPACE_PATH)/app/mixerctl/mixerctl \
 	$(USPACE_PATH)/app/mkfat/mkfat \
 	$(USPACE_PATH)/app/mkexfat/mkexfat \
Index: boot/arch/amd64/Makefile.inc
===================================================================
--- boot/arch/amd64/Makefile.inc	(revision 0bbd13e274302c7c89dd4ea7cad7dbcc3ecfc524)
+++ boot/arch/amd64/Makefile.inc	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -35,4 +35,6 @@
 
 RD_DRVS += \
+	audio/sb16 \
+	char/ns8250 \
 	infrastructure/rootpc \
 	bus/pci/pciintel \
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision 0bbd13e274302c7c89dd4ea7cad7dbcc3ecfc524)
+++ uspace/Makefile	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -38,4 +38,5 @@
 	app/bnchmark \
 	app/devctl \
+	app/dplay \
 	app/edit \
 	app/ext2info \
@@ -48,4 +49,5 @@
 	app/loc \
 	app/lsusb \
+	app/mixerctl \
 	app/mkfat \
 	app/mkexfat \
@@ -103,4 +105,5 @@
 	srv/hid/remcons \
 	srv/hw/char/s3c24xx_uart \
+	drv/audio/sb16 \
 	drv/infrastructure/root \
 	drv/infrastructure/rootvirt \
Index: uspace/app/dplay/Makefile
===================================================================
--- uspace/app/dplay/Makefile	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/app/dplay/Makefile	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,42 @@
+#
+# 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 = ../..
+BINARY = dplay
+
+LIBS = \
+	$(LIBDRV_PREFIX)/libdrv.a
+
+EXTRA_CFLAGS = \
+	-I$(LIBDRV_PREFIX)/include
+
+SOURCES = \
+	dplay.c \
+	wave.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/dplay/dplay.c
===================================================================
--- uspace/app/dplay/dplay.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/app/dplay/dplay.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,207 @@
+/*
+ * 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 dplay
+ * @{
+ */
+/**
+ * @file PCM playback audio devices
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <str_error.h>
+#include <str.h>
+#include <devman.h>
+#include <audio_pcm_buffer_iface.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <macros.h>
+
+#include "wave.h"
+
+#define DEFAULT_DEVICE "/hw/pci0/00:01.0/sb16/dsp"
+
+static void play(async_exch_t *device, unsigned buffer_id,
+    void *buffer, size_t size, FILE *source,
+    unsigned sampling_rate, unsigned sample_size, unsigned channels, bool sign)
+{
+	assert(device);
+	const size_t update_size = size / 8;
+
+	/* Time to play half the buffer. */
+	const suseconds_t interval = 1000000 /
+	    (sampling_rate /  (update_size / (channels * (sample_size / 8))));
+	printf("Time to play half buffer: %ld us.\n", interval);
+	/* Initialize buffer. */
+	const size_t bytes = fread(buffer, sizeof(uint8_t), size, source);
+	if (bytes != size)
+		return;
+
+	struct timeval time;
+	gettimeofday(&time, NULL);
+	printf("Starting playback.\n");
+	int ret = audio_pcm_buffer_start_playback(device, buffer_id,
+	    sampling_rate, sample_size, channels, sign);
+	if (ret != EOK) {
+		printf("Failed to start playback: %s.\n", str_error(ret));
+		return;
+	}
+	void *buffer_place = buffer;
+	while (true) {
+		tv_add(&time, interval); /* Next update point */
+
+		struct timeval current;
+		gettimeofday(&current, NULL);
+
+		const suseconds_t delay = min(tv_sub(&time, &current), interval);
+		if (delay > 0)
+			usleep(delay);
+
+		const size_t bytes =
+		    fread(buffer_place, sizeof(uint8_t), update_size, source);
+		if (bytes == 0)
+			break;
+		if (bytes < update_size) {
+			bzero(buffer_place + bytes, update_size - bytes);
+		}
+		buffer_place += update_size;
+
+		if (buffer_place == buffer + size) {
+			buffer_place = buffer;
+		}
+	}
+
+	printf("Stopping playback.\n");
+	ret = audio_pcm_buffer_stop_playback(device, buffer_id);
+	if (ret != EOK) {
+		printf("Failed to stop playback: %s.\n", str_error(ret));
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	const char *device = DEFAULT_DEVICE;
+	const char *file;
+	switch (argc) {
+	case 2:
+		file = argv[1];
+		break;
+	case 3:
+		device = argv[1];
+		file = argv[2];
+		break;
+	default:
+		printf("Usage: %s [device] file.\n", argv[0]);
+		return 1;
+	}
+
+	devman_handle_t pcm_handle;
+	int ret = devman_fun_get_handle(device, &pcm_handle, 0);
+	if (ret != EOK) {
+		printf("Failed to get device(%s) handle: %s.\n",
+		    device, str_error(ret));
+		return 1;
+	}
+
+	async_sess_t *session = devman_device_connect(
+	    EXCHANGE_ATOMIC, pcm_handle, IPC_FLAG_BLOCKING);
+	if (!session) {
+		printf("Failed to connect to device.\n");
+		return 1;
+	}
+
+	async_exch_t *exch = async_exchange_begin(session);
+	if (!exch) {
+		printf("Failed to start session exchange.\n");
+		async_hangup(session);
+		return 1;
+	}
+	const char* info;
+	ret = audio_pcm_buffer_get_info_str(exch, &info);
+	if (ret != EOK) {
+		printf("Failed to get PCM info.\n");
+		async_exchange_end(exch);
+		async_hangup(session);
+		return 1;
+	}
+	printf("Playing on %s.\n", info);
+	free(info);
+
+	void *buffer = NULL;
+	size_t size = 0;
+	unsigned id = 0;
+	ret = audio_pcm_buffer_get_buffer(exch, &buffer, &size, &id);
+	if (ret != EOK) {
+		printf("Failed to get PCM buffer: %s.\n", str_error(ret));
+		async_exchange_end(exch);
+		async_hangup(session);
+		return 1;
+	}
+	printf("Buffer (%u): %p %zu.\n", id, buffer, size);
+
+	FILE *source = fopen(file, "rb");
+	if (source == NULL) {
+		printf("Failed to open %s.\n", file);
+		munmap(buffer, size);
+		audio_pcm_buffer_release_buffer(exch, id);
+		async_exchange_end(exch);
+		async_hangup(session);
+		return 1;
+	}
+	wave_header_t header;
+	fread(&header, sizeof(header), 1, source);
+	unsigned rate, sample_size, channels;
+	bool sign;
+	const char *error;
+	ret = wav_parse_header(&header, NULL, NULL, &rate, &sample_size,
+	    &channels, &sign, &error);
+	if (ret != EOK) {
+		printf("Error parsing wav header: %s.\n", error);
+		fclose(source);
+		munmap(buffer, size);
+		audio_pcm_buffer_release_buffer(exch, id);
+		async_exchange_end(exch);
+		async_hangup(session);
+		return 1;
+	}
+
+	play(exch, id, buffer, size, source, rate, sample_size, channels, sign);
+
+	munmap(buffer, size);
+	audio_pcm_buffer_release_buffer(exch, id);
+	async_exchange_end(exch);
+	async_hangup(session);
+	return 0;
+}
+/**
+ * @}
+ */
Index: uspace/app/dplay/wave.c
===================================================================
--- uspace/app/dplay/wave.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/app/dplay/wave.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,109 @@
+/*
+ * 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 dplay
+ * @{
+ */
+/**
+ * @file PCM playback audio devices
+ */
+
+#include <byteorder.h>
+#include <str.h>
+#include <errno.h>
+
+#include "wave.h"
+
+int wav_parse_header(void *file, void ** data, size_t *data_size,
+    unsigned *sampling_rate, unsigned *sample_size, unsigned *channels,
+    bool *sign, const char **error)
+{
+	if (!file) {
+		if (error)
+			*error = "file not present";
+		return EINVAL;
+	}
+
+	wave_header_t *header = file;
+	if (str_lcmp(header->chunk_id, CHUNK_ID, 4) != 0) {
+		if (error)
+			*error = "invalid chunk id";
+		return EINVAL;
+	}
+
+	if (str_lcmp(header->format, FORMAT_STR, 4) != 0) {
+		if (error)
+			*error = "invalid format string";
+		return EINVAL;
+	}
+
+	if (str_lcmp(header->subchunk1_id, SUBCHUNK1_ID, 4) != 0) {
+		if (error)
+			*error = "invalid subchunk1 id";
+		return EINVAL;
+	}
+
+	if (uint16_t_le2host(header->subchunk1_size) != PCM_SUBCHUNK1_SIZE) {
+		if (error)
+			*error = "invalid subchunk1 size";
+		return EINVAL;
+	}
+
+	if (uint16_t_le2host(header->audio_format) != FORMAT_LINEAR_PCM) {
+		if (error)
+			*error = "unknown format";
+		return ENOTSUP;
+	}
+
+	if (str_lcmp(header->subchunk2_id, SUBCHUNK2_ID, 4) != 0) {
+		if (error)
+			*error = "invalid subchunk2 id";
+		return EINVAL;
+	}
+
+	if (data)
+		*data = header->data;
+	if (data_size)
+		*data_size = uint32_t_le2host(header->subchunk2_size);
+
+	if (sampling_rate)
+		*sampling_rate = uint32_t_le2host(header->sampling_rate);
+	if (sample_size)
+		*sample_size = uint32_t_le2host(header->sample_size);
+	if (channels)
+		*channels = uint16_t_le2host(header->channels);
+	if (sign)
+		*sign = uint32_t_le2host(header->sample_size) == 16
+		    ? true : false;
+	if (error)
+		*error = "no error";
+
+	return EOK;
+}
+/**
+ * @}
+ */
Index: uspace/app/dplay/wave.h
===================================================================
--- uspace/app/dplay/wave.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/app/dplay/wave.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,97 @@
+/*
+ * 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 dplay
+ * @{
+ */
+/** @file
+ * @brief .wav file format.
+ */
+#ifndef WAVE_H
+#define WAVE_H
+
+#include <stdint.h>
+#include <bool.h>
+
+/** Wave file header format.
+ *
+ * https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
+ * @note: 8-bit samples are stored as unsigned bytes,
+ * 16-bit samples are stored as signed integers.
+ * @note: The default byte ordering assumed for WAVE data files is
+ * little-endian. Files written using the big-endian byte ordering scheme have
+ * the identifier RIFX instead of RIFF.
+ */
+typedef struct wave_header {
+	/** Should be 'R', 'I', 'F', 'F'. */
+	char chunk_id[4];
+#define CHUNK_ID "RIFF"
+
+	/** Total size minus the first 8 bytes */
+	uint32_t chunk_size;
+	/** Should be 'W', 'A', 'V', 'E'. */
+	char format[4];
+#define FORMAT_STR "WAVE"
+
+	/** Should be 'f', 'm', 't', ' '. */
+	char subchunk1_id[4];
+#define SUBCHUNK1_ID "fmt "
+
+	/** Size of the ret of this subchunk. 16 for PCM file. */
+	uint32_t subchunk1_size;
+#define PCM_SUBCHUNK1_SIZE 16
+	/** Format. 1 for Linear PCM */
+	uint16_t audio_format;
+#define FORMAT_LINEAR_PCM 1
+	/** Number of channels. */
+	uint16_t channels;
+	/** Sampling rate. */
+	uint32_t sampling_rate;
+	/** Byte rate. */
+	uint32_t byte_rate;
+	/** Block align. Bytes in one block (samples for all channels). */
+	uint16_t block_align;
+	/** Bits per sample (one channel). */
+	uint16_t sample_size;
+
+	/** Should be 'd', 'a', 't', 'a'. */
+	char subchunk2_id[4];
+#define SUBCHUNK2_ID "data"
+	/** Audio data size. */
+	uint32_t subchunk2_size;
+	/** Audio data. */
+	uint8_t data[];
+
+} wave_header_t;
+
+int wav_parse_header(void *, void**, size_t *, unsigned *, unsigned *,
+    unsigned *, bool *, const char **error_str);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/app/mixerctl/Makefile
===================================================================
--- uspace/app/mixerctl/Makefile	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/app/mixerctl/Makefile	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,41 @@
+#
+# 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 = ../..
+BINARY = mixerctl
+
+LIBS = \
+	$(LIBDRV_PREFIX)/libdrv.a
+
+EXTRA_CFLAGS = \
+	-I$(LIBDRV_PREFIX)/include
+
+SOURCES = \
+	mixerctl.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/mixerctl/mixerctl.c
===================================================================
--- uspace/app/mixerctl/mixerctl.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/app/mixerctl/mixerctl.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,199 @@
+/*
+ * 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 mixerctl
+ * @{
+ */
+/** @file Mixer control for audio devices
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <str_error.h>
+#include <str.h>
+#include <devman.h>
+#include <audio_mixer_iface.h>
+#include <stdio.h>
+
+#define DEFAULT_DEVICE "/hw/pci0/00:01.0/sb16/mixer"
+
+static void print_levels(async_exch_t *exch)
+{
+	const char* name = NULL;
+	unsigned count = 0;
+	int ret = audio_mixer_get_info(exch, &name, &count);
+	if (ret != EOK) {
+		printf("Failed to get mixer info: %s.\n", str_error(ret));
+		return;
+	}
+	printf("MIXER %s:\n", name);
+
+	for (unsigned i = 0; i < count; ++i) {
+		const char *name = NULL;
+		unsigned channels = 0;
+		const int ret =
+		    audio_mixer_get_item_info(exch, i, &name, &channels);
+		if (ret != EOK) {
+			printf("Failed to get item %u info: %s.\n",
+			    i, str_error(ret));
+			continue;
+		}
+		for (unsigned j = 0; j < channels; ++j) {
+			const char *chan = NULL;
+			int ret = audio_mixer_get_channel_info(
+			    exch, i, j, &chan, NULL);
+			if (ret != EOK) {
+				printf(
+				    "Failed to get channel %u-%u info: %s.\n",
+				    i, j, str_error(ret));
+			}
+			unsigned level = 0, max = 0;
+			ret = audio_mixer_channel_volume_get(
+			    exch, i, j, &level, &max);
+			if (ret != EOK) {
+				printf("Failed to get channel %u-%u volume:"
+				    " %s.\n", i, j, str_error(ret));
+			}
+			bool mute = false;
+			ret = audio_mixer_channel_mute_get(
+			    exch, i, j, &mute);
+			if (ret != EOK) {
+				printf("Failed to get channel %u-%u mute"
+				    " status: %s.\n", i, j, str_error(ret));
+			}
+
+			printf("\tChannel(%u/%u) %s %s volume: %u/%u%s.\n",
+			    i, j, name, chan, level, max, mute ? " (M)":"");
+			free(chan);
+		}
+		free(name);
+
+	}
+}
+/*----------------------------------------------------------------------------*/
+static unsigned get_number(const char* str)
+{
+	uint16_t num;
+	str_uint16_t(str, NULL, 10, false, &num);
+	return num;
+}
+/*----------------------------------------------------------------------------*/
+static void set_volume(async_exch_t *exch, int argc, char *argv[])
+{
+	assert(exch);
+	if (argc != 5 && argc != 6) {
+		printf("%s [device] setvolume item channel value\n", argv[0]);
+	}
+	unsigned params = argc == 6 ? 3 : 2;
+	const unsigned item = get_number(argv[params++]);
+	const unsigned channel = get_number(argv[params++]);
+	const unsigned value = get_number(argv[params]);
+	int ret = audio_mixer_channel_volume_set(exch, item, channel, value);
+	if (ret != EOK) {
+		printf("Failed to set mixer volume: %s.\n", str_error(ret));
+		return;
+	}
+	printf("Channel %u-%u volume set to %u.\n", item, channel, value);
+}
+/*----------------------------------------------------------------------------*/
+static void get_volume(async_exch_t *exch, int argc, char *argv[])
+{
+	assert(exch);
+	if (argc != 4 && argc != 5) {
+		printf("%s [device] getvolume item channel\n", argv[0]);
+	}
+	unsigned params = argc == 5 ? 3 : 2;
+	const unsigned item = get_number(argv[params++]);
+	const unsigned channel = get_number(argv[params++]);
+	unsigned value = 0, max = 0;
+
+	int ret = audio_mixer_channel_volume_get(
+	    exch, item, channel, &value, &max);
+	if (ret != EOK) {
+		printf("Failed to get mixer volume: %s.\n", str_error(ret));
+		return;
+	}
+	printf("Channel %u-%u volume: %u/%u.\n", item, channel, value, max);
+}
+/*----------------------------------------------------------------------------*/
+int main(int argc, char *argv[])
+{
+	const char *device = DEFAULT_DEVICE;
+	void (*command)(async_exch_t *, int, char*[]) = NULL;
+
+	if (argc >= 2 && str_cmp(argv[1], "setvolume") == 0) {
+		command = set_volume;
+		if (argc == 6)
+			device = argv[1];
+	}
+
+	if (argc >= 2 && str_cmp(argv[1], "getvolume") == 0) {
+		command = get_volume;
+		if (argc == 5)
+			device = argv[1];
+	}
+
+	if ((argc == 2 && command == NULL))
+		device = argv[1];
+
+
+	devman_handle_t mixer_handle;
+	int ret = devman_fun_get_handle(device, &mixer_handle, 0);
+	if (ret != EOK) {
+		printf("Failed to get device(%s) handle: %s.\n",
+		    device, str_error(ret));
+		return 1;
+	}
+
+	async_sess_t *session = devman_device_connect(
+	    EXCHANGE_ATOMIC, mixer_handle, IPC_FLAG_BLOCKING);
+	if (!session) {
+		printf("Failed to connect to device.\n");
+		return 1;
+	}
+
+	async_exch_t *exch = async_exchange_begin(session);
+	if (!exch) {
+		printf("Failed to start session exchange.\n");
+		async_hangup(session);
+		return 1;
+	}
+
+	if (command) {
+		command(exch, argc, argv);
+	} else {
+		print_levels(exch);
+	}
+
+	async_exchange_end(exch);
+	async_hangup(session);
+	return 0;
+}
+
+/** @}
+ */
Index: uspace/drv/audio/sb16/Makefile
===================================================================
--- uspace/drv/audio/sb16/Makefile	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/drv/audio/sb16/Makefile	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -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
+
+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 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/drv/audio/sb16/ddf_log.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -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/dma.h
===================================================================
--- uspace/drv/audio/sb16/dma.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/drv/audio/sb16/dma.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,94 @@
+/*
+ * 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 DMA memory management
+ */
+#ifndef DRV_AUDIO_SB16_DMA_H
+#define DRV_AUDIO_SB16_DMA_H
+
+#include <assert.h>
+#include <errno.h>
+#include <malloc.h>
+#include <mem.h>
+#include <as.h>
+
+#define DMA_ALIGNENT 1024
+
+/** Get physical address translation
+ *
+ * @param[in] addr Virtual address to translate
+ * @return Physical address if exists, NULL otherwise.
+ */
+static inline uintptr_t addr_to_phys(const void *addr)
+{
+	if (addr == NULL)
+		return 0;
+
+	uintptr_t result;
+	const int ret = as_get_physical_mapping(addr, &result);
+	if (ret != EOK)
+		return 0;
+	return (result | ((uintptr_t)addr & 0xfff));
+}
+/*----------------------------------------------------------------------------*/
+/** DMA mallocator simulator
+ *
+ * @param[in] size Size of the required memory space
+ * @return Address of the aligned and big enough memory place, NULL on failure.
+ */
+static inline void *dma_create_buffer24(size_t size)
+{
+	void *free_address = as_get_mappable_page(size);
+	if (free_address == 0)
+		return NULL;
+	void *address =
+	    as_area_create(free_address, size, AS_AREA_READ | AS_AREA_WRITE);
+	if (address != free_address)
+		return NULL;
+	bzero(address, size);
+	return address;
+}
+/*----------------------------------------------------------------------------*/
+/** DMA mallocator simulator
+ *
+ * @param[in] addr Address of the place allocated by dma_create_buffer24
+ */
+static inline void dma_destroy_buffer(void *page)
+{
+        if (page)
+		as_area_destroy(page);
+}
+
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/dsp.c
===================================================================
--- uspace/drv/audio/sb16/dsp.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/drv/audio/sb16/dsp.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,285 @@
+/*
+ * 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 <devman.h>
+#include <device/hw_res.h>
+#include <libarch/ddi.h>
+#include <libarch/barrier.h>
+#include <str_error.h>
+#include <bool.h>
+
+#include "dma.h"
+#include "ddf_log.h"
+#include "dsp_commands.h"
+#include "dsp.h"
+
+#define BUFFER_ID 1
+#define BUFFER_SIZE (PAGE_SIZE)
+#define PLAY_BLOCK_SIZE (BUFFER_SIZE / 2)
+
+#ifndef DSP_RETRY_COUNT
+#define DSP_RETRY_COUNT 100
+#endif
+
+#define DSP_RESET_RESPONSE 0xaa
+
+#define AUTO_DMA_MODE
+
+static inline int sb_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 sb_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 sb_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);
+}
+/*----------------------------------------------------------------------------*/
+static inline int sb_setup_dma(sb_dsp_t *dsp, uintptr_t pa, size_t size)
+{
+	async_sess_t *sess = devman_parent_device_connect(EXCHANGE_ATOMIC,
+	    dsp->sb_dev->handle, IPC_FLAG_BLOCKING);
+	if (!sess)
+		return ENOMEM;
+
+	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 sb_setup_buffer(sb_dsp_t *dsp)
+{
+	assert(dsp);
+	uint8_t *buffer = dma_create_buffer24(BUFFER_SIZE);
+	if (buffer == NULL) {
+		ddf_log_error("Failed to allocate buffer.\n");
+		return ENOMEM;
+	}
+
+	const uintptr_t pa = addr_to_phys(buffer);
+	assert(pa < (1 << 25));
+	/* Set 16 bit channel */
+	const int ret = sb_setup_dma(dsp, pa, BUFFER_SIZE);
+	if (ret == EOK) {
+		dsp->buffer.data = buffer;
+		dsp->buffer.size = BUFFER_SIZE;
+		bzero(buffer, BUFFER_SIZE);
+	} else {
+		ddf_log_error("Failed to setup DMA16 channel %s.\n",
+		    str_error(ret));
+		dma_destroy_buffer(buffer);
+	}
+	return ret;
+}
+/*----------------------------------------------------------------------------*/
+static inline void sb_clear_buffer(sb_dsp_t *dsp)
+{
+	dma_destroy_buffer(dsp->buffer.data);
+	dsp->buffer.data = NULL;
+	dsp->buffer.size = 0;
+}
+/*----------------------------------------------------------------------------*/
+static inline size_t sample_count(unsigned sample_size, size_t byte_count)
+{
+	if (sample_size == 16) {
+		return byte_count / 2;
+	}
+	return byte_count;
+}
+/*----------------------------------------------------------------------------*/
+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->sb_dev = dev;
+	sb_dsp_reset(dsp);
+	/* "DSP takes about 100 microseconds to initialize itself" */
+	udelay(100);
+	uint8_t response;
+	const int ret = sb_dsp_read(dsp, &response);
+	if (ret != EOK) {
+		ddf_log_error("Failed to read DSP reset response value.\n");
+		return ret;
+	}
+	if (response != DSP_RESET_RESPONSE) {
+		ddf_log_error("Invalid DSP reset response: %x.\n", response);
+		return EIO;
+	}
+
+	/* Get DSP version number */
+	sb_dsp_write(dsp, DSP_VERSION);
+	sb_dsp_read(dsp, &dsp->version.major);
+	sb_dsp_read(dsp, &dsp->version.minor);
+
+	return ret;
+}
+/*----------------------------------------------------------------------------*/
+void sb_dsp_interrupt(sb_dsp_t *dsp)
+{
+#ifndef AUTO_DMA_MODE
+	assert(dsp);
+	sb_dsp_write(dsp, SINGLE_DMA_16B_DA);
+	sb_dsp_write(dsp, dsp->playing.mode);
+	sb_dsp_write(dsp, (dsp->playing.samples - 1) & 0xff);
+	sb_dsp_write(dsp, (dsp->playing.samples - 1) >> 8);
+#endif
+}
+/*----------------------------------------------------------------------------*/
+int sb_dsp_get_buffer(sb_dsp_t *dsp, void **buffer, size_t *size, unsigned *id)
+{
+	assert(dsp);
+	const int ret = sb_setup_buffer(dsp);
+	ddf_log_debug("Providing buffer(%u): %p, %zu.\n",
+	    BUFFER_ID, dsp->buffer.data, dsp->buffer.size);
+	if (ret == EOK && buffer)
+		*buffer = dsp->buffer.data;
+	if (ret == EOK && size)
+		*size = dsp->buffer.size;
+	if (ret == EOK && id)
+		*id = BUFFER_ID;
+	return ret;
+}
+/*----------------------------------------------------------------------------*/
+int sb_dsp_release_buffer(sb_dsp_t *dsp, unsigned id)
+{
+	assert(dsp);
+	if (id != BUFFER_ID)
+		return ENOENT;
+	sb_clear_buffer(dsp);
+	return EOK;
+}
+/*----------------------------------------------------------------------------*/
+int sb_dsp_start_playback(sb_dsp_t *dsp, unsigned id, unsigned sampling_rate,
+    unsigned sample_size, unsigned channels, bool sign)
+{
+	assert(dsp);
+
+	/* Check supported parameters */
+	ddf_log_debug("Starting playback on buffer(%u): rate: %u, size: %u, "
+	    " channels: %u, signed: %s.\n", id, sampling_rate, sample_size,
+	    channels, sign ? "YES" : "NO" );
+	if (id != BUFFER_ID)
+		return ENOENT;
+	if (sample_size != 16) // FIXME We only support 16 bit playback
+		return ENOTSUP;
+	if (channels != 1 && channels != 2)
+		return ENOTSUP;
+	if (sampling_rate > 44100)
+		return ENOTSUP;
+
+
+	sb_dsp_write(dsp, SET_SAMPLING_RATE_OUTPUT);
+	sb_dsp_write(dsp, sampling_rate >> 8);
+	sb_dsp_write(dsp, sampling_rate & 0xff);
+
+	ddf_log_debug("Sampling rate: %hhx:%hhx.\n",
+	    sampling_rate >> 8, sampling_rate & 0xff);
+
+#ifdef AUTO_DMA_MODE
+	sb_dsp_write(dsp, AUTO_DMA_16B_DA_FIFO);
+#else
+	sb_dsp_write(dsp, SINGLE_DMA_16B_DA_FIFO);
+#endif
+
+	dsp->playing.mode = 0 |
+	    (sign ? DSP_MODE_SIGNED : 0) | (channels == 2 ? DSP_MODE_STEREO : 0);
+	sb_dsp_write(dsp, dsp->playing.mode);
+
+	dsp->playing.samples = sample_count(sample_size, PLAY_BLOCK_SIZE);
+	sb_dsp_write(dsp, (dsp->playing.samples - 1) & 0xff);
+	sb_dsp_write(dsp, (dsp->playing.samples - 1) >> 8);
+
+	return EOK;
+	return ENOTSUP;
+}
+/*----------------------------------------------------------------------------*/
+int sb_dsp_stop_playback(sb_dsp_t *dsp, unsigned id)
+{
+	assert(dsp);
+	if (id != BUFFER_ID)
+		return ENOENT;
+	sb_dsp_write(dsp, DMA_16B_EXIT);
+	return EOK;
+}
+/*----------------------------------------------------------------------------*/
+int sb_dsp_start_record(sb_dsp_t *dsp, unsigned id, unsigned sample_rate,
+    unsigned sample_size, unsigned channels, bool sign)
+{
+	return ENOTSUP;
+}
+/*----------------------------------------------------------------------------*/
+int sb_dsp_stop_record(sb_dsp_t *dsp, unsigned id)
+{
+	return ENOTSUP;
+}
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/dsp.h
===================================================================
--- uspace/drv/audio/sb16/dsp.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/drv/audio/sb16/dsp.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,78 @@
+/*
+ * 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 "registers.h"
+
+typedef struct sb_dsp_t {
+	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;
+	} playing;
+	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);
+
+int sb_dsp_get_buffer(sb_dsp_t *dsp, void **buffer, size_t *size, unsigned *id);
+int sb_dsp_release_buffer(sb_dsp_t *dsp, unsigned id);
+int sb_dsp_start_playback(sb_dsp_t *dsp, unsigned id, unsigned sample_rate,
+    unsigned sample_size, unsigned channels, bool sign);
+int sb_dsp_stop_playback(sb_dsp_t *dsp, unsigned id);
+int sb_dsp_start_record(sb_dsp_t *dsp, unsigned id, unsigned sample_rate,
+    unsigned sample_size, unsigned channels, bool sign);
+int sb_dsp_stop_record(sb_dsp_t *dsp, unsigned id);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/dsp_commands.h
===================================================================
--- uspace/drv/audio/sb16/dsp_commands.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/drv/audio/sb16/dsp_commands.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -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 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/drv/audio/sb16/main.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,254 @@
+/*
+ * 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(const 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 = {
+	.add_device = 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, LVL_DEBUG2);
+	return ddf_driver_main(&sb_driver);
+}
+/*----------------------------------------------------------------------------*/
+static void irq_handler(ddf_dev_t *dev, ipc_callid_t iid, ipc_call_t *call)
+{
+	assert(dev);
+	assert(dev->driver_data);
+	sb16_interrupt(dev->driver_data);
+}
+/*----------------------------------------------------------------------------*/
+/** 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.\n");
+
+	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.\n", str_error(ret));
+
+	const size_t irq_cmd_count = sb16_irq_code_size();
+	irq_cmd_t irq_cmds[irq_cmd_count];
+	sb16_irq_code((void*)sb_regs, dma8, dma16, irq_cmds);
+	irq_code_t irq_code = { .cmdcount = irq_cmd_count, .cmds = irq_cmds };
+
+	ret = register_interrupt_handler(device, irq, irq_handler, &irq_code);
+	CHECK_RET_RETURN(ret,
+	    "Failed to register irq handler: %s.\n", 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.\n",
+	    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.\n", 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.\n",
+				    str_error(ret));
+		} else {
+			ddf_log_error("Failed to create midi function.\n");
+		}
+	} else {
+	    ddf_log_warning("Failed to init mpu driver: %s.\n", str_error(ret));
+	}
+
+	/* MPU state does not matter */
+	return EOK;
+}
+/*----------------------------------------------------------------------------*/
+static int sb_get_res(const 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, device->handle,
+            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, device->handle,
+	    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 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/drv/audio/sb16/mixer.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,260 @@
+/*
+ * 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 <bool.h>
+#include <errno.h>
+#include <libarch/ddi.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;
+
+typedef struct volume_item {
+	const char *description;
+	uint8_t channels;
+	const channel_t *channel_table;
+} volume_item_t;
+
+/* CT1335 channels */
+static const channel_t channels_table_ct1335[] = {
+	{ "", 0x02, 1, 8, false }, /* Master, Mono, 3bit volume level */
+	{ "", 0x06, 1, 8, false }, /* Midi, Mono, 3bit volume level */
+	{ "", 0x08, 1, 8, false }, /* CD, Mono, 3bit volume level */
+	{ "", 0x0a, 1, 4, false }, /* Voice, Mono, 2bit volume level */
+};
+
+/* CT1345 channels */
+static const channel_t channels_table_ct1345[] = {
+	{ "Left", 0x22, 5, 8, true }, /* Master, Left, 3bit volume level */
+	{ "Right", 0x22, 1, 8, true }, /* Master, Right, 3bit volume level */
+	{ "Left", 0x26, 5, 8, true }, /* Midi, Left, 3bit volume level */
+	{ "Right", 0x26, 1, 8, true }, /* Midi, Right, 3bit volume level */
+	{ "Left", 0x28, 5, 8, true }, /* CD, Left, 3bit volume level */
+	{ "Right", 0x28, 1, 8, true }, /* CD, Right, 3bit volume level */
+	{ "Left", 0x2e, 5, 8, true }, /* Line, Left, 3bit volume level */
+	{ "Right", 0x2e, 1, 8, true }, /* Line, Right, 3bit volume level */
+	{ "Left", 0x04, 5, 8, true }, /* Voice, Left, 3bit volume level */
+	{ "Right", 0x04, 1, 8, true }, /* Voice, Right, 3bit volume level */
+	{ "", 0x0a, 1, 4, false }, /* Mic, Mono, 2bit volume level */
+};
+
+/* CT1745 channels */
+static const channel_t channels_table_ct1745[] = {
+	{ "Left", 0x30, 3, 32, false },  /* Master, Left, 5bit volume level */
+	{ "Right", 0x31, 3, 32, false }, /* Master, Right, 5bit volume level */
+	{ "Left", 0x32, 3, 32, false },  /* Voice, Left, 5bit volume level */
+	{ "Right", 0x33, 3, 32, false }, /* Voice, Right, 5bit volume level */
+	{ "Left", 0x34, 3, 32, false }, /* MIDI, Left, 5bit volume level */
+	{ "Right", 0x35, 3, 32, false }, /* MIDI, Right, 5bit volume level */
+	{ "Left", 0x36, 3, 32, false }, /* CD, Left, 5bit volume level */
+	{ "Right", 0x37, 3, 32, false }, /* CD, Right, 5bit volume level */
+	{ "Left", 0x38, 3, 32, false }, /* Line, Left, 5bit volume level */
+	{ "Right", 0x39, 3, 32, false }, /* Line, Right, 5bit volume level */
+	{ "", 0x3a, 3, 32, false }, /* Mic, Mono, 5bit volume level */
+	{ "", 0x3b, 6, 4, false }, /* PC speaker, Mono, 2bit level */
+	{ "Left", 0x3f, 6, 4, false }, /* Input Gain, Left, 2bit level */
+	{ "Right", 0x40, 6, 4, false }, /* Input Gain, Right, 2bit level */
+	{ "Left", 0x41, 6, 4, false }, /* Output Gain, Left, 2bit level */
+	{ "Right", 0x42, 6, 4, false }, /* Output Gain, Right, 2bit level */
+	{ "Left", 0x44, 4, 16, false }, /* Treble, Left, 4bit volume level */
+	{ "Right", 0x45, 4, 16, false }, /* Treble, Right, 4bit volume level */
+	{ "Left", 0x46, 4, 16, false }, /* Bass, Left, 4bit volume level */
+	{ "Right", 0x47, 4, 16, false }, /* Bass, Right, 4bit volume level */
+};
+
+static const volume_item_t volume_ct1335[] = {
+	{ "Master", 1, &channels_table_ct1335[0] },
+	{ "MIDI", 1, &channels_table_ct1335[1] },
+	{ "CD", 1, &channels_table_ct1335[2] },
+	{ "Voice", 1, &channels_table_ct1335[3] },
+};
+
+static const volume_item_t volume_ct1345[] = {
+	{ "Master", 2, &channels_table_ct1345[0] },
+	{ "Voice", 2, &channels_table_ct1345[8] },
+	{ "Mic", 1, &channels_table_ct1345[10] },
+	{ "MIDI", 2, &channels_table_ct1345[2] },
+	{ "CD", 2, &channels_table_ct1345[4] },
+	{ "Line", 2, &channels_table_ct1345[6] },
+};
+
+static const volume_item_t volume_ct1745[] = {
+	{ "Master", 2, &channels_table_ct1745[0] },
+	{ "Voice", 2, &channels_table_ct1745[2] },
+	{ "MIDI", 2, &channels_table_ct1745[4] },
+	{ "CD", 2, &channels_table_ct1745[6] },
+	{ "Line", 2, &channels_table_ct1745[8] },
+	{ "Mic", 1, &channels_table_ct1745[10] },
+	{ "PC Speaker", 1, &channels_table_ct1745[11] },
+	{ "Input Gain", 2, &channels_table_ct1745[12] },
+	{ "Output Gain", 2, &channels_table_ct1745[14] },
+	{ "Treble", 2, &channels_table_ct1745[16] },
+	{ "Bass", 2, &channels_table_ct1745[18] },
+};
+
+static const struct {
+	size_t count;
+	const volume_item_t *table;
+} volume_table[] = {
+	[SB_MIXER_NONE] = { 0, NULL },
+	[SB_MIXER_UNKNOWN] = { 0, NULL },
+	[SB_MIXER_CT1335] = {
+	    sizeof(volume_ct1335) / sizeof(volume_item_t), volume_ct1335 },
+	[SB_MIXER_CT1345] = {
+	    sizeof(volume_ct1345) / sizeof(volume_item_t), volume_ct1345 },
+	[SB_MIXER_CT1745] = {
+	    sizeof(volume_ct1745) / sizeof(volume_item_t), volume_ct1745 },
+};
+/*----------------------------------------------------------------------------*/
+const char * sb_mixer_type_str(sb_mixer_type_t type)
+{
+	static const char * names[] = {
+		[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.\n", 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 index,
+    const char** name, unsigned *channels)
+{
+	assert(mixer);
+	if (index > volume_table[mixer->type].count)
+		return ENOENT;
+
+	const volume_item_t *item = &volume_table[mixer->type].table[index];
+	if (name)
+		*name = item->description;
+	if (channels)
+		*channels = item->channels;
+	return EOK;
+}
+/*----------------------------------------------------------------------------*/
+int sb_mixer_get_channel_info(const sb_mixer_t *mixer, unsigned index,
+    unsigned channel, const char **name, unsigned *levels)
+{
+	assert(mixer);
+	if (index > volume_table[mixer->type].count)
+		return ENOENT;
+
+	const volume_item_t *item = &volume_table[mixer->type].table[index];
+	if (channel > item->channels)
+		return ENOENT;
+
+	const channel_t *chan = &item->channel_table[channel];
+	if (name)
+		*name = chan->name;
+	if (levels)
+		*levels = chan->volume_levels;
+	return EOK;
+}
+/*----------------------------------------------------------------------------*/
+int sb_mixer_set_volume_level(const sb_mixer_t *mixer,
+    unsigned index, unsigned channel, unsigned level)
+{
+	if (mixer->type == SB_MIXER_UNKNOWN || mixer->type == SB_MIXER_NONE)
+		return ENOTSUP;
+	if (index >= volume_table[mixer->type].count)
+		return ENOENT;
+	if (channel >= volume_table[mixer->type].table[index].channels)
+		return ENOENT;
+
+	const channel_t *chan =
+	    &volume_table[mixer->type].table[index].channel_table[channel];
+
+	if (level >= chan->volume_levels)
+		level = chan->volume_levels - 1;
+
+	pio_write_8(&mixer->regs->mixer_address, chan->address);
+
+	uint8_t value = 0;
+	if (chan->preserve_bits) {
+		value = pio_read_8(&mixer->regs->mixer_data);
+		value &= ~(uint8_t)((chan->volume_levels - 1) << chan->shift);
+	}
+
+	value |= level << chan->shift;
+	pio_write_8(&mixer->regs->mixer_data, value);
+	ddf_log_note("Channel %s %s volume set to: %u.\n",
+	    volume_table[mixer->type].table[index].description,
+	    chan->name, level);
+	return EOK;
+}
+/*----------------------------------------------------------------------------*/
+unsigned sb_mixer_get_volume_level(const sb_mixer_t *mixer, unsigned index,
+    unsigned channel)
+{
+	assert(mixer);
+	if (mixer->type == SB_MIXER_UNKNOWN
+	    || mixer->type == SB_MIXER_NONE
+	    || (index >= volume_table[mixer->type].count)
+	    || (channel >= volume_table[mixer->type].table[index].channels))
+		return 0;
+
+	const channel_t *chan =
+	    &volume_table[mixer->type].table[index].channel_table[channel];
+
+	pio_write_8(&mixer->regs->mixer_address, chan->address);
+	return (pio_read_8(&mixer->regs->mixer_data) >> chan->shift)
+	    & (chan->volume_levels - 1);
+}
Index: uspace/drv/audio/sb16/mixer.h
===================================================================
--- uspace/drv/audio/sb16/mixer.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/drv/audio/sb16/mixer.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,66 @@
+/*
+ * 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 *channels);
+int sb_mixer_get_channel_info(const sb_mixer_t *mixer, unsigned index,
+    unsigned channel, const char **name, unsigned *levels);
+int sb_mixer_set_volume_level(const sb_mixer_t *mixer,
+    unsigned item, unsigned channel, unsigned level);
+unsigned sb_mixer_get_volume_level(const sb_mixer_t *mixer,
+    unsigned item, unsigned channel);
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/mixer_iface.c
===================================================================
--- uspace/drv/audio/sb16/mixer_iface.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/drv/audio/sb16/mixer_iface.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,125 @@
+/*
+ * 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);
+	assert(fun->driver_data);
+	const sb_mixer_t *mixer = fun->driver_data;
+	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 *channels)
+{
+	assert(fun);
+	assert(fun->driver_data);
+	const sb_mixer_t *mixer = fun->driver_data;
+	return
+	    sb_mixer_get_control_item_info(mixer, item, name, channels);
+}
+/*----------------------------------------------------------------------------*/
+static int sb_get_channel_info(ddf_fun_t *fun, unsigned item, unsigned channel,
+    const char** name, unsigned *levels)
+{
+	assert(fun);
+	assert(fun->driver_data);
+	const sb_mixer_t *mixer = fun->driver_data;
+	return sb_mixer_get_channel_info(mixer, item, channel, name, levels);
+}
+/*----------------------------------------------------------------------------*/
+static int sb_channel_mute_set(ddf_fun_t *fun, unsigned item, unsigned channel,
+    bool mute)
+{
+	return ENOTSUP;
+}
+/*----------------------------------------------------------------------------*/
+static int sb_channel_mute_get(ddf_fun_t *fun, unsigned item, unsigned channel,
+    bool *mute)
+{
+	*mute = false;
+	return EOK;
+}
+/*----------------------------------------------------------------------------*/
+static int sb_channel_volume_set(ddf_fun_t *fun, unsigned item, unsigned channel,
+    unsigned volume)
+{
+	assert(fun);
+	assert(fun->driver_data);
+	const sb_mixer_t *mixer = fun->driver_data;
+	return sb_mixer_set_volume_level(mixer, item, channel, volume);
+}
+/*----------------------------------------------------------------------------*/
+static int sb_channel_volume_get(ddf_fun_t *fun, unsigned item, unsigned channel,
+    unsigned *level, unsigned *max)
+{
+	assert(fun);
+	assert(fun->driver_data);
+	const sb_mixer_t *mixer = fun->driver_data;
+	unsigned levels;
+	const int ret =
+	    sb_mixer_get_channel_info(mixer, item, channel, NULL, &levels);
+	if (ret == EOK && max)
+		*max = --levels;
+	if (ret == EOK && level)
+		*level = sb_mixer_get_volume_level(mixer, item, channel);
+
+	return ret;
+}
+
+audio_mixer_iface_t sb_mixer_iface = {
+	.get_info = sb_get_info,
+	.get_item_info = sb_get_item_info,
+	.get_channel_info = sb_get_channel_info,
+
+	.channel_mute_set = sb_channel_mute_set,
+	.channel_mute_get = sb_channel_mute_get,
+
+	.channel_volume_set = sb_channel_volume_set,
+	.channel_volume_get = sb_channel_volume_get,
+
+};
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/pcm_iface.c
===================================================================
--- uspace/drv/audio/sb16/pcm_iface.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/drv/audio/sb16/pcm_iface.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,115 @@
+/*
+ * 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_pcm_buffer_iface.h>
+
+#include "dsp.h"
+
+static int sb_get_info_str(ddf_fun_t *fun, const char** name)
+{
+	if (name)
+		*name = "SB 16 DSP";
+	return EOK;
+}
+/*----------------------------------------------------------------------------*/
+static int sb_get_buffer(ddf_fun_t *fun,
+    void **buffer, size_t *size, unsigned *id)
+{
+	assert(fun);
+	assert(fun->driver_data);
+	sb_dsp_t *dsp = fun->driver_data;
+	return sb_dsp_get_buffer(dsp, buffer, size, id);
+}
+/*----------------------------------------------------------------------------*/
+static int sb_release_buffer(ddf_fun_t *fun, unsigned id)
+{
+	assert(fun);
+	assert(fun->driver_data);
+	sb_dsp_t *dsp = fun->driver_data;
+	return sb_dsp_release_buffer(dsp, id);
+}
+/*----------------------------------------------------------------------------*/
+static int sb_start_playback(ddf_fun_t *fun, unsigned id,
+    unsigned sample_rate, unsigned sample_size, unsigned channels, bool sign)
+{
+	assert(fun);
+	assert(fun->driver_data);
+	sb_dsp_t *dsp = fun->driver_data;
+	return sb_dsp_start_playback(
+	    dsp, id, sample_rate, sample_size, channels, sign);
+}
+/*----------------------------------------------------------------------------*/
+static int sb_stop_playback(ddf_fun_t *fun, unsigned id)
+{
+	assert(fun);
+	assert(fun->driver_data);
+	sb_dsp_t *dsp = fun->driver_data;
+	return sb_dsp_stop_playback(dsp, id);
+}
+/*----------------------------------------------------------------------------*/
+static int sb_start_record(ddf_fun_t *fun, unsigned id,
+    unsigned sample_rate, unsigned sample_size, unsigned channels, bool sign)
+{
+	assert(fun);
+	assert(fun->driver_data);
+	sb_dsp_t *dsp = fun->driver_data;
+	return sb_dsp_start_record(
+	    dsp, id, sample_rate, sample_size, channels, sign);
+}
+/*----------------------------------------------------------------------------*/
+static int sb_stop_record(ddf_fun_t *fun, unsigned id)
+{
+	assert(fun);
+	assert(fun->driver_data);
+	sb_dsp_t *dsp = fun->driver_data;
+	return sb_dsp_stop_record(dsp, id);
+}
+/*----------------------------------------------------------------------------*/
+
+audio_pcm_buffer_iface_t sb_pcm_iface = {
+	.get_info_str = sb_get_info_str,
+
+	.get_buffer = sb_get_buffer,
+	.release_buffer = sb_release_buffer,
+
+	.start_playback = sb_start_playback,
+	.stop_playback = sb_stop_playback,
+
+	.start_record = sb_start_record,
+	.stop_record = sb_stop_record
+};
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/registers.h
===================================================================
--- uspace/drv/audio/sb16/registers.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/drv/audio/sb16/registers.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -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 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/drv/audio/sb16/sb16.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,195 @@
+/*
+ * 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 <errno.h>
+#include <str_error.h>
+#include <audio_mixer_iface.h>
+#include <audio_pcm_buffer_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_buffer_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 sizeof(irq_cmds) / sizeof(irq_cmds[0]);
+}
+/*----------------------------------------------------------------------------*/
+void sb16_irq_code(void *regs, int dma8, int dma16, irq_cmd_t cmds[])
+{
+	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;
+	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.\n", sb->regs);
+
+	/* Initialize DSP */
+	ddf_fun_t *dsp_fun = ddf_fun_create(dev, fun_exposed, "dsp");
+	if (!dsp_fun) {
+		ddf_log_error("Failed to create dsp function.\n");
+		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.\n",
+		    str_error(ret));
+		return ret;
+	}
+	dsp_fun->driver_data = &sb->dsp;
+	dsp_fun->ops = &sb_pcm_ops;
+	ddf_log_note("Sound blaster DSP (%x.%x) initialized.\n",
+	    sb->dsp.version.major, sb->dsp.version.minor);
+
+	ret = ddf_fun_bind(dsp_fun);
+	if (ret != EOK) {
+		ddf_log_error(
+		    "Failed to bind DSP function: %s.\n", str_error(ret));
+		dsp_fun->driver_data = NULL;
+		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, "mixer");
+	if (!mixer_fun) {
+		ddf_log_error("Failed to create mixer function.\n");
+		ddf_fun_unbind(dsp_fun);
+		dsp_fun->driver_data = NULL;
+		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.\n",
+		    str_error(ret));
+		ddf_fun_unbind(dsp_fun);
+		dsp_fun->driver_data = NULL;
+		ddf_fun_destroy(dsp_fun);
+		ddf_fun_destroy(mixer_fun);
+		return ret;
+	}
+
+	ddf_log_note("Initialized mixer: %s.\n",
+	    sb_mixer_type_str(sb->mixer.type));
+	mixer_fun->driver_data = &sb->mixer;
+	mixer_fun->ops = &sb_mixer_ops;
+
+	ret = ddf_fun_bind(mixer_fun);
+	if (ret != EOK) {
+		ddf_log_error(
+		    "Failed to bind mixer function: %s.\n", str_error(ret));
+		mixer_fun->driver_data = NULL;
+		ddf_fun_destroy(mixer_fun);
+
+		ddf_fun_unbind(dsp_fun);
+		dsp_fun->driver_data = NULL;
+		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.\n");
+	}
+	sb_dsp_interrupt(&sb->dsp);
+}
Index: uspace/drv/audio/sb16/sb16.h
===================================================================
--- uspace/drv/audio/sb16/sb16.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/drv/audio/sb16/sb16.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -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[]);
+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 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/drv/audio/sb16/sb16.ma	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,1 @@
+100 isa/sb16
Index: uspace/drv/bus/isa/dma_controller.c
===================================================================
--- uspace/drv/bus/isa/dma_controller.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/drv/bus/isa/dma_controller.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,356 @@
+/*
+ * 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 isa
+ * @{
+ */
+/** @file
+ * @brief DMA controller management
+ */
+#include <assert.h>
+#include <bool.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <ddi.h> /* pio_enable */
+#include <libarch/ddi.h> /* pio_write */
+#include <ddf/log.h>
+
+#include "dma_controller.h"
+
+/** DMA Slave controller I/O Address. */
+#define DMA_CONTROLLER_FIRST_BASE ((void*)0x0)
+typedef struct dma_controller_regs_first {
+	uint8_t channel_start0;
+	uint8_t channel_count0;
+	uint8_t channel_start1;
+	uint8_t channel_count1;
+	uint8_t channel_start2;
+	uint8_t channel_count2;
+	uint8_t channel_start3;
+	uint8_t channel_count3;
+
+	uint8_t command_status;
+#define DMA_STATUS_REQ(x) (1 << ((x % 4) + 4))
+#define DMA_STATUS_COMPLETE(x) (1 << (x % 4))
+/* http://wiki.osdev.org/DMA: The only bit that works is COND(bit 2) */
+#define DMA_COMMAND_COND (1 << 2) /* Disables DMA controller */
+
+	uint8_t request; /* Memory to memory transfers, NOT implemented on PCs*/
+	uint8_t single_mask;
+#define DMA_SINGLE_MASK_CHAN_SEL_MASK (0x3)
+#define DMA_SINGLE_MASK_CHAN_SEL_SHIFT (0)
+#define DMA_SINGLE_MASK_CHAN_TO_REG(x) \
+    ((x & DMA_SINGLE_MASK_CHAN_SEL_MASK) << DMA_SINGLE_MASK_CHAN_SEL_SHIFT)
+#define DMA_SINGLE_MASK_MASKED_FLAG (1 << 2)
+
+	uint8_t mode;
+#define DMA_MODE_CHAN_SELECT_MASK (0x3)
+#define DMA_MODE_CHAN_SELECT_SHIFT (0)
+#define DMA_MODE_CHAN_TO_REG(x) \
+    ((x & DMA_MODE_CHAN_SELECT_MASK) << DMA_MODE_CHAN_SELECT_SHIFT)
+#define DMA_MODE_CHAN_TRA_MASK (0x3)
+#define DMA_MODE_CHAN_TRA_SHIFT (2)
+#define DMA_MODE_CHAN_TRA_SELF_TEST (0)
+#define DMA_MODE_CHAN_TRA_WRITE (0x1)
+#define DMA_MODE_CHAN_TRA_READ (0x2)
+#define DMA_MODE_CHAN_AUTO_FLAG (1 << 4)
+#define DMA_MODE_CHAN_DOWN_FLAG (1 << 5)
+#define DMA_MODE_CHAN_MODE_MASK (0x3)
+#define DMA_MODE_CHAN_MODE_SHIFT (6)
+#define DMA_MODE_CHAN_MODE_DEMAND (0)
+#define DMA_MODE_CHAN_MODE_SINGLE (1)
+#define DMA_MODE_CHAN_MODE_BLOCK (2)
+#define DMA_MODE_CHAN_MODE_CASCADE (3)
+
+	uint8_t flip_flop;
+	/* Master reset sets Flip-Flop low, clears status,
+	 * sets all mask bits on */
+	uint8_t master_reset; /* Intermediate is not implemented on PCs */
+	uint8_t mask_reset;
+
+	uint8_t multi_mask;
+#define DMA_MULTI_MASK_CHAN(x) (1 << (x % 4))
+
+} dma_controller_regs_first_t;
+
+/** DMA Master controller I/O Address. */
+#define DMA_CONTROLLER_SECOND_BASE ((void*)0xc0)
+/* See dma_controller_regs_first_t for register values */
+typedef struct dma_controller_regs_second {
+	uint8_t channel_start4;
+	uint8_t reserved0;
+	uint8_t channel_count4;
+	uint8_t reserved1;
+	uint8_t channel_start5;
+	uint8_t reserved2;
+	uint8_t channel_count5;
+	uint8_t reserved3;
+	uint8_t channel_start6;
+	uint8_t reserved4;
+	uint8_t channel_count6;
+	uint8_t reserved5;
+	uint8_t channel_start7;
+	uint8_t reserved6;
+	uint8_t channel_count7;
+
+	uint8_t command_status;
+	uint8_t reserved8;
+	uint8_t request;
+	uint8_t reserved9;
+	uint8_t single_mask;
+	uint8_t reserveda;
+	uint8_t mode;
+	uint8_t reservedb;
+	uint8_t flip_flop;
+	uint8_t reservedc;
+	uint8_t master_reset;
+	uint8_t reservedd;
+	uint8_t multi_mask;
+} dma_controller_regs_second_t;
+
+/** Shared DMA page address register I/O address. */
+#define DMA_CONTROLLER_PAGE_BASE ((void*)0x81)
+typedef struct dma_page_regs {
+	uint8_t channel2;
+	uint8_t channel3;
+	uint8_t channel1;
+	uint8_t reserved0;
+	uint8_t reserved1;
+	uint8_t reserved2;
+	uint8_t channel0;
+	uint8_t reserved3;
+	uint8_t channel6;
+	uint8_t channel7;
+	uint8_t channel5;
+	uint8_t reserved4;
+	uint8_t reserved5;
+	uint8_t reserved6;
+	uint8_t channel4;
+} dma_page_regs_t;
+
+/** Addresses needed to setup a DMA channel. */
+typedef struct dma_channel {
+	ioport8_t *offset_reg_address;
+	ioport8_t *size_reg_address;
+	ioport8_t *page_reg_address;
+	ioport8_t *single_mask_address;
+	ioport8_t *mode_address;
+	ioport8_t *flip_flop_address;
+} dma_channel_t;
+
+typedef struct dma_controller {
+	dma_channel_t channels[8];
+	dma_page_regs_t *page_table;
+	dma_controller_regs_first_t *first;
+	dma_controller_regs_second_t *second;
+	bool initialized;
+} dma_controller_t;
+
+
+/** Standard i8237 DMA controller.
+ * http://zet.aluzina.org/index.php/8237_DMA_controller#DMA_Channel_Registers
+ */
+static dma_controller_t controller_8237 = {
+	.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, },
+
+	    /* 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, }, },
+
+	.page_table = NULL,
+	.first = NULL,
+	.second = NULL,
+	.initialized = false,
+};
+
+/* 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)
+{
+	assert(controller);
+	int ret = pio_enable(
+	    DMA_CONTROLLER_PAGE_BASE, sizeof(dma_page_regs_t),
+	    (void**)&controller->page_table);
+	if (ret != EOK)
+		return EIO;
+
+	ret = pio_enable(
+	    DMA_CONTROLLER_FIRST_BASE, sizeof(dma_controller_regs_first_t),
+	    (void**)&controller->first);
+	if (ret != EOK)
+		return EIO;
+
+	ret = pio_enable(
+	    DMA_CONTROLLER_SECOND_BASE, sizeof(dma_controller_regs_second_t),
+	    (void**)&controller->second);
+	if (ret != EOK)
+		return EIO;
+	controller->initialized = true;
+
+	/* Reset the controller */
+	pio_write_8(&controller->second->master_reset, 0xff);
+	pio_write_8(&controller->first->master_reset, 0xff);
+
+
+	return EOK;
+}
+/*----------------------------------------------------------------------------*/
+/**
+ * Setup DMA channel to specified place and mode.
+ * @param channel DMA Channel 1,2,3 for 8 bit transfers, 5,6,7 for 16 bit.
+ * @param pa Physical address of the buffer. Must be < 16MB for 16 bit and < 1MB
+ *           for 8 bit transfers.
+ * @param size DMA buffer size, limited to 64K.
+ * @param mode Mode of the DMA channel:
+ *              - Read or Write
+ *              - Allow automatic reset
+ *              - Use address decrement instead of increment
+ *              - Use SINGLE/BLOCK/ON DEMAND transfer mode
+ * @return Error code.
+ */
+int dma_setup_channel(
+    unsigned channel, uint32_t pa, uint16_t size, uint8_t mode)
+{
+	if (channel == 0 || channel == 4)
+		return ENOTSUP;
+	if (channel > 7)
+		return ENOENT;
+
+	/* DMA is limited to 24 but addresses. */
+	if (pa >= (1 << 24))
+		return EINVAL;
+
+	/* 8 bit channels use only 4 bits from the page register. */
+	if (channel > 0 && channel < 4 && pa >= (1 << 20))
+		return EINVAL;
+
+	static fibril_mutex_t guard = FIBRIL_MUTEX_INITIALIZER(guard);
+
+	fibril_mutex_lock(&guard);
+
+	if (!controller_8237.initialized)
+		dma_controller_init(&controller_8237);
+
+	if (!controller_8237.initialized) {
+		fibril_mutex_unlock(&guard);
+		return EIO;
+	}
+
+	/* 16 bit transfers are a bit special */
+	ddf_msg(LVL_DEBUG, "Unspoiled address: %p and size: %zu.", pa, size);
+	if (channel > 4) {
+		/* Size must be aligned to 16 bits */
+		if ((size & 1) != 0) {
+			fibril_mutex_unlock(&guard);
+			return EINVAL;
+		}
+		size >>= 1;
+		/* Address is fun: lower 16bits need to be shifted by 1 */
+		pa = ((pa & 0xffff) >> 1) | (pa & 0xff0000);
+	}
+
+	const dma_channel_t dma_channel = controller_8237.channels[channel];
+
+	ddf_msg(LVL_DEBUG,
+	    "Setting channel %u, to address %p(%zu), mode %hhx.",
+	    channel, pa, size, mode);
+
+	/* Mask DMA request */
+	uint8_t value = DMA_SINGLE_MASK_CHAN_TO_REG(channel)
+	    | DMA_SINGLE_MASK_MASKED_FLAG;
+	pio_write_8(dma_channel.single_mask_address, value);
+
+	/* Set mode */
+	value = DMA_MODE_CHAN_TO_REG(channel) | mode;
+	ddf_msg(LVL_DEBUG2, "Writing mode byte: %p:%hhx.",
+	    dma_channel.mode_address, value);
+	pio_write_8(dma_channel.mode_address, value);
+
+	/* Set address -- reset flip-flop*/
+	pio_write_8(dma_channel.flip_flop_address, 0);
+
+	/* Low byte */
+	value = pa & 0xff;
+	ddf_msg(LVL_DEBUG2, "Writing address low byte: %p:%hhx.",
+	    dma_channel.offset_reg_address, value);
+	pio_write_8(dma_channel.offset_reg_address, value);
+
+	/* High byte */
+	value = (pa >> 8) & 0xff;
+	ddf_msg(LVL_DEBUG2, "Writing address high byte: %p:%hhx.",
+	    dma_channel.offset_reg_address, value);
+	pio_write_8(dma_channel.offset_reg_address, value);
+
+	/* Page address - third byte */
+	value = (pa >> 16) & 0xff;
+	ddf_msg(LVL_DEBUG2, "Writing address page byte: %p:%hhx.",
+	    dma_channel.page_reg_address, value);
+	pio_write_8(dma_channel.page_reg_address, value);
+
+	/* Set size -- reset flip-flop */
+	pio_write_8(dma_channel.flip_flop_address, 0);
+
+	/* Low byte */
+	value = (size - 1) & 0xff;
+	ddf_msg(LVL_DEBUG2, "Writing size low byte: %p:%hhx.",
+	    dma_channel.size_reg_address, value);
+	pio_write_8(dma_channel.size_reg_address, value);
+
+	/* High byte */
+	value = ((size - 1) >> 8) & 0xff;
+	ddf_msg(LVL_DEBUG2, "Writing size high byte: %p:%hhx.",
+	    dma_channel.size_reg_address, value);
+	pio_write_8(dma_channel.size_reg_address, value);
+
+	/* Unmask DMA request */
+	value = DMA_SINGLE_MASK_CHAN_TO_REG(channel);
+	pio_write_8(dma_channel.single_mask_address, value);
+
+	fibril_mutex_unlock(&guard);
+
+	return EOK;
+}
+/**
+ * @}
+ */
Index: uspace/drv/bus/isa/dma_controller.h
===================================================================
--- uspace/drv/bus/isa/dma_controller.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/drv/bus/isa/dma_controller.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,41 @@
+/*
+ * 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 isa
+ * @{
+ */
+/** @file
+ * @brief DMA memory management
+ */
+#ifndef DRV_BUS_ISA_DMA_CONTROLLER_H
+#define DRV_BUS_ISA_DMA_CONTROLLER_H
+
+int dma_setup_channel(unsigned, uint32_t, uint16_t, uint8_t);
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/bus/isa/isa.c
===================================================================
--- uspace/drv/bus/isa/isa.c	(revision 0bbd13e274302c7c89dd4ea7cad7dbcc3ecfc524)
+++ uspace/drv/bus/isa/isa.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -91,4 +91,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;
@@ -207,4 +208,6 @@
 
 	fibril_mutex_initialize(&fun->mutex);
+	fun->hw_resources.resources = fun->resources;
+
 	fun->fnode = fnode;
 	return fun;
@@ -518,16 +521,4 @@
 }
 
-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)
 {
@@ -558,7 +549,4 @@
 		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). */
@@ -677,5 +665,4 @@
 		list_remove(&fun->bus_link);
 
-		fun_hw_res_free(fun);
 		ddf_fun_destroy(fun->fnode);
 	}
Index: uspace/lib/c/generic/device/hw_res.c
===================================================================
--- uspace/lib/c/generic/device/hw_res.c	(revision 0bbd13e274302c7c89dd4ea7cad7dbcc3ecfc524)
+++ uspace/lib/c/generic/device/hw_res.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -42,14 +42,16 @@
 {
 	sysarg_t count = 0;
-	
+
 	async_exch_t *exch = async_exchange_begin(sess);
+	if (exch == NULL)
+		return ENOMEM;
 	int rc = async_req_1_1(exch, DEV_IFACE_ID(HW_RES_DEV_IFACE),
 	    HW_RES_GET_RESOURCE_LIST, &count);
-	
+
 	if (rc != EOK) {
 		async_exchange_end(exch);
 		return rc;
 	}
-	
+
 	size_t size = count * sizeof(hw_resource_t);
 	hw_resource_t *resources = (hw_resource_t *) malloc(size);
@@ -59,16 +61,16 @@
 		return ENOMEM;
 	}
-	
+
 	rc = async_data_read_start(exch, resources, size);
 	async_exchange_end(exch);
-	
+
 	if (rc != EOK) {
 		free(resources);
 		return rc;
 	}
-	
+
 	hw_resources->resources = resources;
 	hw_resources->count = count;
-	
+
 	return EOK;
 }
@@ -77,9 +79,38 @@
 {
 	async_exch_t *exch = async_exchange_begin(sess);
+	if (exch == NULL)
+		return false;
 	int rc = async_req_1_0(exch, DEV_IFACE_ID(HW_RES_DEV_IFACE),
 	    HW_RES_ENABLE_INTERRUPT);
 	async_exchange_end(exch);
-	
+
 	return (rc == EOK);
+}
+
+/**
+ * Setup DMA channel to specified place and mode.
+ * @param channel DMA Channel 1,2,3 for 8 bit transfers, 5,6,7 for 16 bit.
+ * @param pa Physical address of the buffer. Must be < 16MB for 16 bit and < 1MB
+ *           for 8 bit transfers.
+ * @param size DMA buffer size, limited to 64K.
+ * @param mode Mode of the DMA channel:
+ *              - Read or Write
+ *              - Allow automatic reset
+ *              - Use address decrement instead of increment
+ *              - Use SINGLE/BLOCK/ON DEMAND transfer mode
+ * @return Error code.
+ */
+int hw_res_dma_channel_setup(async_sess_t *sess,
+    unsigned channel, uint32_t pa, uint16_t size, uint8_t mode)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	if (exch == NULL)
+		return ENOMEM;
+	const uint32_t packed = size | (mode << 16);
+	const int ret = async_req_4_0(exch, DEV_IFACE_ID(HW_RES_DEV_IFACE),
+	    HW_RES_DMA_CHANNEL_SETUP, channel, pa, packed);
+	async_exchange_end(exch);
+
+	return ret;
 }
 
Index: uspace/lib/c/generic/device/hw_res_parsed.c
===================================================================
--- uspace/lib/c/generic/device/hw_res_parsed.c	(revision 0bbd13e274302c7c89dd4ea7cad7dbcc3ecfc524)
+++ uspace/lib/c/generic/device/hw_res_parsed.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -38,6 +38,28 @@
 #include <errno.h>
 
-static void hw_res_parse_add_irq(hw_res_list_parsed_t *out, hw_resource_t *res,
-    int flags)
+static void hw_res_parse_add_dma_channel(hw_res_list_parsed_t *out,
+    const hw_resource_t *res, int flags)
+{
+	assert(res);
+	assert((res->type == DMA_CHANNEL_8) || (res->type == DMA_CHANNEL_16));
+	
+	const int channel = (res->type == DMA_CHANNEL_8) ?
+	    res->res.dma_channel.dma8 : res->res.dma_channel.dma16;
+	const size_t count = out->dma_channels.count;
+	const int keep_duplicit = flags & HW_RES_KEEP_DUPLICIT;
+	
+	if (!keep_duplicit) {
+		for (size_t i = 0; i < count; ++i) {
+			if (out->dma_channels.channels[i] == channel)
+				return;
+		}
+	}
+	
+	out->dma_channels.channels[count] = channel;
+	++out->dma_channels.count;
+}
+
+static void hw_res_parse_add_irq(hw_res_list_parsed_t *out,
+    const hw_resource_t *res, int flags)
 {
 	assert(res && (res->type == INTERRUPT));
@@ -59,5 +81,5 @@
 
 static void hw_res_parse_add_io_range(hw_res_list_parsed_t *out,
-    hw_resource_t *res, int flags)
+    const hw_resource_t *res, int flags)
 {
 	assert(res && (res->type == IO_RANGE));
@@ -90,5 +112,5 @@
 
 static void hw_res_parse_add_mem_range(hw_res_list_parsed_t *out,
-    hw_resource_t *res, int flags)
+    const hw_resource_t *res, int flags)
 {
 	assert(res && (res->type == MEM_RANGE));
@@ -132,5 +154,5 @@
  *
  */
-int hw_res_list_parse(hw_resource_list_t *hw_resources,
+int hw_res_list_parse(const hw_resource_list_t *hw_resources,
     hw_res_list_parsed_t *out, int flags)
 {
@@ -141,11 +163,17 @@
 	hw_res_list_parsed_clean(out);
 	
-	out->irqs.irqs = malloc(res_count * sizeof(int));
-	out->io_ranges.ranges = malloc(res_count * sizeof(io_range_t));
-	out->mem_ranges.ranges = malloc(res_count * sizeof(mem_range_t));
+	out->irqs.irqs = calloc(res_count, sizeof(int));
+	out->dma_channels.channels = calloc(res_count, sizeof(int));
+	out->io_ranges.ranges = calloc(res_count, sizeof(io_range_t));
+	out->mem_ranges.ranges = calloc(res_count, sizeof(mem_range_t));
+	if (!out->irqs.irqs || !out->dma_channels.channels ||
+	    !out->io_ranges.ranges || !out->mem_ranges.ranges) {
+		hw_res_list_parsed_clean(out);
+		return ENOMEM;
+	}
 	
 	for (size_t i = 0; i < res_count; ++i) {
-		hw_resource_t *resource = &(hw_resources->resources[i]);
-		
+		const hw_resource_t *resource = &(hw_resources->resources[i]);
+
 		switch (resource->type) {
 		case INTERRUPT:
@@ -158,9 +186,14 @@
 			hw_res_parse_add_mem_range(out, resource, flags);
 			break;
+		case DMA_CHANNEL_8:
+		case DMA_CHANNEL_16:
+			hw_res_parse_add_dma_channel(out, resource, flags);
+			break;
 		default:
+			hw_res_list_parsed_clean(out);
 			return EINVAL;
 		}
 	}
-	
+
 	return EOK;
 };
Index: uspace/lib/c/include/device/hw_res_parsed.h
===================================================================
--- uspace/lib/c/include/device/hw_res_parsed.h	(revision 0bbd13e274302c7c89dd4ea7cad7dbcc3ecfc524)
+++ uspace/lib/c/include/device/hw_res_parsed.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -100,4 +100,7 @@
 	/** List of IRQs */
 	irq_list_t irqs;
+
+	/** List of DMA channels */
+	dma_list_t dma_channels;
 	
 	/** List of DMA channels */
@@ -139,5 +142,6 @@
 }
 
-extern int hw_res_list_parse(hw_resource_list_t *, hw_res_list_parsed_t *, int);
+extern int hw_res_list_parse(
+    const hw_resource_list_t *, hw_res_list_parsed_t *, int);
 extern int hw_res_get_list_parsed(async_sess_t *, hw_res_list_parsed_t *, int);
 
Index: uspace/lib/c/include/ipc/dev_iface.h
===================================================================
--- uspace/lib/c/include/ipc/dev_iface.h	(revision 0bbd13e274302c7c89dd4ea7cad7dbcc3ecfc524)
+++ uspace/lib/c/include/ipc/dev_iface.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -36,4 +36,10 @@
 typedef enum {
 	HW_RES_DEV_IFACE = 0,
+
+	/** Audio device mixer interface */
+	AUDIO_MIXER_IFACE,
+	/** Audio device pcm buffer interface */
+	AUDIO_PCM_BUFFER_IFACE,
+
 	/** Character device interface */
 	CHAR_DEV_IFACE,
Index: uspace/lib/drv/Makefile
===================================================================
--- uspace/lib/drv/Makefile	(revision 0bbd13e274302c7c89dd4ea7cad7dbcc3ecfc524)
+++ uspace/lib/drv/Makefile	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -38,4 +38,6 @@
 	generic/log.c \
 	generic/logbuf.c \
+	generic/remote_audio_mixer.c \
+	generic/remote_audio_pcm_buffer.c \
 	generic/remote_hw_res.c \
 	generic/remote_char_dev.c \
Index: uspace/lib/drv/generic/dev_iface.c
===================================================================
--- uspace/lib/drv/generic/dev_iface.c	(revision 0bbd13e274302c7c89dd4ea7cad7dbcc3ecfc524)
+++ uspace/lib/drv/generic/dev_iface.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -46,14 +46,18 @@
 #include "remote_usbhid.h"
 #include "remote_pci.h"
+#include "remote_audio_mixer.h"
+#include "remote_audio_pcm_buffer.h"
 
-static iface_dipatch_table_t remote_ifaces = {
+static const iface_dipatch_table_t remote_ifaces = {
 	.ifaces = {
-		&remote_hw_res_iface,
-		&remote_char_dev_iface,
-		&remote_nic_iface,
-		&remote_pci_iface,
-		&remote_usb_iface,
-		&remote_usbhc_iface,
-		&remote_usbhid_iface
+		[AUDIO_MIXER_IFACE] = &remote_audio_mixer_iface,
+		[AUDIO_PCM_BUFFER_IFACE] = &remote_audio_pcm_buffer_iface,
+		[HW_RES_DEV_IFACE] = &remote_hw_res_iface,
+		[CHAR_DEV_IFACE] = &remote_char_dev_iface,
+		[NIC_DEV_IFACE] = &remote_nic_iface,
+		[PCI_DEV_IFACE] = &remote_pci_iface,
+		[USB_DEV_IFACE] = &remote_usb_iface,
+		[USBHC_DEV_IFACE] = &remote_usbhc_iface,
+		[USBHID_DEV_IFACE] = &remote_usbhid_iface,
 	}
 };
Index: uspace/lib/drv/generic/remote_audio_mixer.c
===================================================================
--- uspace/lib/drv/generic/remote_audio_mixer.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/lib/drv/generic/remote_audio_mixer.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,447 @@
+/*
+ * 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 libdrv
+ * @{
+ */
+/** @file
+ */
+
+#include <async.h>
+#include <errno.h>
+#include <assert.h>
+#include <str.h>
+
+#include "audio_mixer_iface.h"
+#include "ddf/driver.h"
+
+typedef enum {
+	/** Asks for basic mixer info: Mixer name and number of controllable
+	 * items.
+	 * Answer:
+	 * - ENOTSUP - call not supported
+	 * - EOK - call successful, info is valid
+	 * Answer arguments:
+	 * - Mixer name
+	 * - Number of controllable items
+	 */
+	IPC_M_AUDIO_MIXER_GET_INFO,
+
+	/** Asks for item mixer info: Item name and number of controllable
+	 * channels.
+	 * Answer:
+	 * - ENOTSUP - call not supported
+	 * - ENOENT - no such item
+	 * - EOK - call successful, info is valid
+	 * Answer arguments:
+	 * - Item name
+	 * - Number of controllable channels
+	 */
+	IPC_M_AUDIO_MIXER_GET_ITEM_INFO,
+
+	/** Asks for channel name and number of volume levels.
+	 * Answer:
+	 * - ENOTSUP - call not supported
+	 * - ENOENT - no such channel
+	 * - EOK - call successful, info is valid
+	 * Answer arguments:
+	 * - Channel name
+	 * - Volume levels
+	 */
+	IPC_M_AUDIO_MIXER_GET_CHANNEL_INFO,
+
+	/** Set channel mute status
+	 * Answer:
+	 * - ENOTSUP - call not supported
+	 * - ENOENT - no such channel
+	 * - EOK - call successful, info is valid
+	 */
+	IPC_M_AUDIO_MIXER_CHANNEL_MUTE_SET,
+
+	/** Get channel mute status
+	 * Answer:
+	 * - ENOTSUP - call not supported
+	 * - ENOENT - no such channel
+	 * - EOK - call successful, info is valid
+	 * Answer arguments:
+	 * - Channel mute status
+	 */
+	IPC_M_AUDIO_MIXER_CHANNEL_MUTE_GET,
+
+	/** Set channel volume level
+	 * Answer:
+	 * - ENOTSUP - call not supported
+	 * - ENOENT - no such channel
+	 * - EOK - call successful, info is valid
+	 */
+	IPC_M_AUDIO_MIXER_CHANNEL_VOLUME_SET,
+
+	/** Get channel volume level
+	 * Answer:
+	 * - ENOTSUP - call not supported
+	 * - ENOENT - no such channel
+	 * - EOK - call successful, info is valid
+	 * Answer arguments:
+	 * - Channel volume level
+	 * - Channel maximum volume level
+	 */
+	IPC_M_AUDIO_MIXER_CHANNEL_VOLUME_GET,
+} audio_mixer_iface_funcs_t;
+
+/*
+ * CLIENT SIDE
+ */
+int audio_mixer_get_info(async_exch_t *exch, const char **name, unsigned *items)
+{
+	if (!exch)
+		return EINVAL;
+	sysarg_t name_size, itemc;
+	const int ret = async_req_1_2(exch, DEV_IFACE_ID(AUDIO_MIXER_IFACE),
+	    IPC_M_AUDIO_MIXER_GET_INFO, &name_size, &itemc);
+	if (ret == EOK && name) {
+		char *name_place = calloc(1, name_size);
+		if (!name_place) {
+			/* Make the other side fail
+			 * as it waits for read request */
+			async_data_read_start(exch, (void*)-1, 0);
+			return ENOMEM;
+		}
+		const int ret =
+		    async_data_read_start(exch, name_place, name_size);
+		if (ret != EOK) {
+			free(name_place);
+			return ret;
+		}
+		*name = name_place;
+	}
+	if (ret == EOK && items)
+		*items = itemc;
+	return ret;
+}
+/*----------------------------------------------------------------------------*/
+int audio_mixer_get_item_info(async_exch_t *exch, unsigned item,
+    const char ** name, unsigned *channels)
+{
+	if (!exch)
+		return EINVAL;
+	sysarg_t name_size, chans;
+	const int ret = async_req_2_2(exch, DEV_IFACE_ID(AUDIO_MIXER_IFACE),
+	    IPC_M_AUDIO_MIXER_GET_ITEM_INFO, item, &name_size, &chans);
+	if (ret == EOK && name) {
+		char *name_place = calloc(1, name_size);
+		if (!name_place) {
+			/* Make the other side fail
+			 * as it waits for read request */
+			async_data_read_start(exch, (void*)-1, 0);
+			return ENOMEM;
+		}
+		const int ret =
+		    async_data_read_start(exch, name_place, name_size);
+		if (ret != EOK) {
+			free(name_place);
+			return ret;
+		}
+		*name = name_place;
+	}
+	if (ret == EOK && chans)
+		*channels = chans;
+	return ret;
+}
+/*----------------------------------------------------------------------------*/
+int audio_mixer_get_channel_info(async_exch_t *exch, unsigned item,
+    unsigned channel, const char **name, unsigned *volume_levels)
+{
+	if (!exch)
+		return EINVAL;
+	sysarg_t name_size, levels;
+	const int ret = async_req_3_2(exch, DEV_IFACE_ID(AUDIO_MIXER_IFACE),
+	    IPC_M_AUDIO_MIXER_GET_CHANNEL_INFO, item, channel,
+	    &name_size, &levels);
+	if (ret == EOK && name) {
+		char *name_place = calloc(1, name_size);
+		if (!name_place) {
+			/* Make the other side fail
+			 * as it waits for read request */
+			async_data_read_start(exch, (void*)-1, 0);
+			return ENOMEM;
+		}
+		const int ret =
+		    async_data_read_start(exch, name_place, name_size);
+		if (ret != EOK) {
+			free(name_place);
+			return ret;
+		}
+		*name = name_place;
+	}
+	if (ret == EOK && volume_levels)
+		*volume_levels = levels;
+	return ret;
+}
+/*----------------------------------------------------------------------------*/
+int audio_mixer_channel_mute_set(async_exch_t *exch, unsigned item,
+    unsigned channel, bool mute_status)
+{
+	if (!exch)
+		return EINVAL;
+	return async_req_4_0(exch, DEV_IFACE_ID(AUDIO_MIXER_IFACE),
+	    IPC_M_AUDIO_MIXER_CHANNEL_MUTE_SET, item, channel, mute_status);
+}
+/*----------------------------------------------------------------------------*/
+int audio_mixer_channel_mute_get(async_exch_t *exch, unsigned item,
+    unsigned channel, bool *mute_status)
+{
+	if (!exch)
+		return EINVAL;
+	sysarg_t mute;
+	const int ret = async_req_3_1(exch, DEV_IFACE_ID(AUDIO_MIXER_IFACE),
+	    IPC_M_AUDIO_MIXER_CHANNEL_MUTE_GET, item, channel, &mute);
+	if (ret == EOK && mute_status)
+		*mute_status = (bool)mute;
+	return ret;
+}
+/*----------------------------------------------------------------------------*/
+int audio_mixer_channel_volume_set(async_exch_t *exch, unsigned item,
+    unsigned channel, unsigned volume)
+{
+	if (!exch)
+		return EINVAL;
+	return async_req_4_0(exch, DEV_IFACE_ID(AUDIO_MIXER_IFACE),
+	    IPC_M_AUDIO_MIXER_CHANNEL_VOLUME_SET, item, channel, volume);
+}
+/*----------------------------------------------------------------------------*/
+int audio_mixer_channel_volume_get(async_exch_t *exch, unsigned item,
+    unsigned channel, unsigned *volume_current, unsigned *volume_max)
+{
+	if (!exch)
+		return EINVAL;
+	sysarg_t current, max;
+	const int ret = async_req_3_2(exch, DEV_IFACE_ID(AUDIO_MIXER_IFACE),
+	    IPC_M_AUDIO_MIXER_CHANNEL_VOLUME_GET, item, channel,
+	    &current, &max);
+	if (ret == EOK && volume_current)
+		*volume_current = current;
+	if (ret == EOK && volume_max)
+		*volume_max = max;
+	return ret;
+}
+
+/*
+ * SERVER SIDE
+ */
+static void remote_audio_mixer_get_info(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_mixer_get_item_info(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_mixer_get_channel_info(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_mixer_channel_mute_set(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_mixer_channel_mute_get(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_mixer_channel_volume_set(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_mixer_channel_volume_get(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+
+/** Remote audio mixer interface operations. */
+static remote_iface_func_ptr_t remote_audio_mixer_iface_ops[] = {
+	[IPC_M_AUDIO_MIXER_GET_INFO] = remote_audio_mixer_get_info,
+	[IPC_M_AUDIO_MIXER_GET_ITEM_INFO] = remote_audio_mixer_get_item_info,
+	[IPC_M_AUDIO_MIXER_GET_CHANNEL_INFO] = remote_audio_mixer_get_channel_info,
+	[IPC_M_AUDIO_MIXER_CHANNEL_MUTE_SET] = remote_audio_mixer_channel_mute_set,
+	[IPC_M_AUDIO_MIXER_CHANNEL_MUTE_GET] = remote_audio_mixer_channel_mute_get,
+	[IPC_M_AUDIO_MIXER_CHANNEL_VOLUME_SET] = remote_audio_mixer_channel_volume_set,
+	[IPC_M_AUDIO_MIXER_CHANNEL_VOLUME_GET] = remote_audio_mixer_channel_volume_get,
+};
+
+/** Remote audio mixer interface structure. */
+remote_iface_t remote_audio_mixer_iface = {
+	.method_count = sizeof(remote_audio_mixer_iface_ops) /
+	    sizeof(remote_audio_mixer_iface_ops[0]),
+	.methods = remote_audio_mixer_iface_ops
+};
+/*----------------------------------------------------------------------------*/
+void remote_audio_mixer_get_info(
+    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	audio_mixer_iface_t *mixer_iface = iface;
+
+	if (!mixer_iface->get_info) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	const char *name = NULL;
+	unsigned items = 0;
+	const int ret = mixer_iface->get_info(fun, &name, &items);
+	const size_t name_size = name ? str_size(name) + 1 : 0;
+	async_answer_2(callid, ret, name_size, items);
+	/* Send the name. */
+	if (ret == EOK && name_size > 0) {
+		size_t size;
+		ipc_callid_t name_id;
+		if (!async_data_read_receive(&name_id, &size)) {
+			async_answer_0(name_id, EPARTY);
+			return;
+		}
+		if (size != name_size) {
+			async_answer_0(name_id, ELIMIT);
+			return;
+		}
+		async_data_read_finalize(name_id, name, name_size);
+	}
+}
+/*----------------------------------------------------------------------------*/
+void remote_audio_mixer_get_item_info(
+    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	audio_mixer_iface_t *mixer_iface = iface;
+
+	if (!mixer_iface->get_item_info) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	const unsigned item = DEV_IPC_GET_ARG1(*call);
+	const char *name = NULL;
+	unsigned channels = 0;
+	const int ret = mixer_iface->get_item_info(fun, item, &name, &channels);
+	const size_t name_size = name ? str_size(name) + 1 : 0;
+	async_answer_2(callid, ret, name_size, channels);
+	/* Send the name. */
+	if (ret == EOK && name_size > 0) {
+		size_t size;
+		ipc_callid_t name_id;
+		if (!async_data_read_receive(&name_id, &size)) {
+			async_answer_0(name_id, EPARTY);
+			return;
+		}
+		if (size != name_size) {
+			async_answer_0(name_id, ELIMIT);
+			return;
+		}
+		async_data_read_finalize(name_id, name, name_size);
+	}
+}
+/*----------------------------------------------------------------------------*/
+void remote_audio_mixer_get_channel_info(
+    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	audio_mixer_iface_t *mixer_iface = iface;
+
+	if (!mixer_iface->get_channel_info) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	const unsigned item = DEV_IPC_GET_ARG1(*call);
+	const unsigned channel = DEV_IPC_GET_ARG2(*call);
+	const char *name = NULL;
+	unsigned levels = 0;
+	const int ret =
+	    mixer_iface->get_channel_info(fun, item, channel, &name, &levels);
+	const size_t name_size = name ? str_size(name) + 1 : 0;
+	async_answer_2(callid, ret, name_size, levels);
+	/* Send the name. */
+	if (ret == EOK && name_size > 0) {
+		size_t size;
+		ipc_callid_t name_id;
+		if (!async_data_read_receive(&name_id, &size)) {
+			async_answer_0(name_id, EPARTY);
+			return;
+		}
+		if (size != name_size) {
+			async_answer_0(name_id, ELIMIT);
+			return;
+		}
+		async_data_read_finalize(name_id, name, name_size);
+	}
+}
+/*----------------------------------------------------------------------------*/
+void remote_audio_mixer_channel_mute_set(
+    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	audio_mixer_iface_t *mixer_iface = iface;
+
+	if (!mixer_iface->channel_mute_set) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	const unsigned item = DEV_IPC_GET_ARG1(*call);
+	const unsigned channel = DEV_IPC_GET_ARG2(*call);
+	const bool mute = DEV_IPC_GET_ARG3(*call);
+	const int ret = mixer_iface->channel_mute_set(fun, item, channel, mute);
+	async_answer_0(callid, ret);
+}
+/*----------------------------------------------------------------------------*/
+void remote_audio_mixer_channel_mute_get(
+    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	audio_mixer_iface_t *mixer_iface = iface;
+
+	if (!mixer_iface->channel_mute_get) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	const unsigned item = DEV_IPC_GET_ARG1(*call);
+	const unsigned channel = DEV_IPC_GET_ARG2(*call);
+	bool mute = false;
+	const int ret =
+	    mixer_iface->channel_mute_get(fun, item, channel, &mute);
+	async_answer_1(callid, ret, mute);
+}
+/*----------------------------------------------------------------------------*/
+void remote_audio_mixer_channel_volume_set(
+    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	audio_mixer_iface_t *mixer_iface = iface;
+
+	if (!mixer_iface->channel_volume_set) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	const unsigned item = DEV_IPC_GET_ARG1(*call);
+	const unsigned channel = DEV_IPC_GET_ARG2(*call);
+	const unsigned level = DEV_IPC_GET_ARG3(*call);
+	const int ret =
+	    mixer_iface->channel_volume_set(fun, item, channel, level);
+	async_answer_0(callid, ret);
+}
+/*----------------------------------------------------------------------------*/
+void remote_audio_mixer_channel_volume_get(
+    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	audio_mixer_iface_t *mixer_iface = iface;
+
+	if (!mixer_iface->channel_volume_get) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	const unsigned item = DEV_IPC_GET_ARG1(*call);
+	const unsigned channel = DEV_IPC_GET_ARG2(*call);
+	unsigned current = 0, max = 0;
+	const int ret =
+	    mixer_iface->channel_volume_get(fun, item, channel, &current, &max);
+	async_answer_2(callid, ret, current, max);
+}
+
+/**
+ * @}
+ */
Index: uspace/lib/drv/generic/remote_audio_pcm_buffer.c
===================================================================
--- uspace/lib/drv/generic/remote_audio_pcm_buffer.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/lib/drv/generic/remote_audio_pcm_buffer.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,479 @@
+/*
+ * 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 libdrv
+ * @{
+ */
+/** @file
+ */
+
+#include <async.h>
+#include <errno.h>
+#include <assert.h>
+#include <str.h>
+#include <as.h>
+#include <sys/mman.h>
+
+#include "audio_pcm_buffer_iface.h"
+#include "ddf/driver.h"
+
+typedef enum {
+	IPC_M_AUDIO_PCM_GET_INFO_STR,
+	IPC_M_AUDIO_PCM_GET_BUFFER,
+	IPC_M_AUDIO_PCM_RELEASE_BUFFER,
+	IPC_M_AUDIO_PCM_START_PLAYBACK,
+	IPC_M_AUDIO_PCM_STOP_PLAYBACK,
+	IPC_M_AUDIO_PCM_START_RECORD,
+	IPC_M_AUDIO_PCM_STOP_RECORD,
+} audio_pcm_iface_funcs_t;
+
+/*
+ * CLIENT SIDE
+ */
+int audio_pcm_buffer_get_info_str(async_exch_t *exch, const char **name)
+{
+	if (!exch)
+		return EINVAL;
+	sysarg_t name_size;
+	const int ret = async_req_1_1(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_GET_INFO_STR, &name_size);
+	if (ret == EOK && name) {
+		char *name_place = calloc(1, name_size);
+		if (!name_place) {
+			/* Make the other side fail
+			 * as it waits for read request */
+			async_data_read_start(exch, (void*)-1, 0);
+			return ENOMEM;
+		}
+		const int ret =
+		    async_data_read_start(exch, name_place, name_size);
+		if (ret != EOK) {
+			free(name_place);
+			return ret;
+		}
+		*name = name_place;
+	}
+	return ret;
+}
+/*----------------------------------------------------------------------------*/
+int audio_pcm_buffer_get_buffer(async_exch_t *exch, void **buffer, size_t *size,
+    unsigned *id)
+{
+	if (!exch || !buffer || !size)
+		return EINVAL;
+	sysarg_t buffer_size, buffer_id;
+	const int ret = async_req_1_2(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE), IPC_M_AUDIO_PCM_GET_BUFFER,
+	    &buffer_size, &buffer_id);
+	if (ret == EOK) {
+		void *dst = as_get_mappable_page(buffer_size);
+		// FIXME Should we create as_area?
+		// FIXME Do we need to know the flags?
+		const int ret =
+		    async_share_in_start_0_0(exch, dst, buffer_size);
+		if (ret != EOK) {
+			return ret;
+		}
+		*buffer = dst;
+		*size = buffer_size;
+	}
+	if (ret == EOK && id)
+		*id = buffer_id;
+	return ret;
+}
+/*----------------------------------------------------------------------------*/
+int audio_pcm_buffer_release_buffer(async_exch_t *exch, unsigned id)
+{
+	if (!exch)
+		return EINVAL;
+	return async_req_2_0(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_RELEASE_BUFFER, id);
+}
+/*----------------------------------------------------------------------------*/
+int audio_pcm_buffer_start_playback(async_exch_t *exch, unsigned id,
+    unsigned sample_rate, uint16_t sample_size, uint8_t channels, bool sign)
+{
+	if (!exch)
+		return EINVAL;
+	sysarg_t packed =
+	    (sample_size << 16) | (channels << 8) | (sign ? 1 : 0);
+	return async_req_4_0(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_START_PLAYBACK, id, sample_rate, packed);
+}
+/*----------------------------------------------------------------------------*/
+int audio_pcm_buffer_stop_playback(async_exch_t *exch, unsigned id)
+{
+	if (!exch)
+		return EINVAL;
+	return async_req_2_0(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_STOP_PLAYBACK, id);
+}
+/*----------------------------------------------------------------------------*/
+int audio_pcm_buffer_start_record(async_exch_t *exch, unsigned id,
+    unsigned sample_rate, unsigned sample_size, unsigned channels, bool sign)
+{
+	if (!exch || sample_size > UINT16_MAX || channels > (UINT16_MAX >> 1))
+		return EINVAL;
+	sysarg_t packed = sample_size << 16 | channels << 1 | sign ? 1 : 0;
+	return async_req_4_0(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_START_RECORD, id, sample_rate, packed);
+}
+/*----------------------------------------------------------------------------*/
+int audio_pcm_buffer_stop_record(async_exch_t *exch, unsigned id)
+{
+	if (!exch)
+		return EINVAL;
+	return async_req_2_0(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_STOP_RECORD, id);
+}
+
+/*
+ * SERVER SIDE
+ */
+static void remote_audio_pcm_get_info_str(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_get_buffer(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_release_buffer(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_start_playback(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_stop_playback(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_start_record(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_stop_record(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+
+/** Remote audio pcm buffer interface operations. */
+static remote_iface_func_ptr_t remote_audio_pcm_iface_ops[] = {
+	[IPC_M_AUDIO_PCM_GET_INFO_STR] = remote_audio_pcm_get_info_str,
+	[IPC_M_AUDIO_PCM_GET_BUFFER] = remote_audio_pcm_get_buffer,
+	[IPC_M_AUDIO_PCM_RELEASE_BUFFER] = remote_audio_pcm_release_buffer,
+	[IPC_M_AUDIO_PCM_START_PLAYBACK] = remote_audio_pcm_start_playback,
+	[IPC_M_AUDIO_PCM_STOP_PLAYBACK] = remote_audio_pcm_stop_playback,
+	[IPC_M_AUDIO_PCM_START_RECORD] = remote_audio_pcm_start_record,
+	[IPC_M_AUDIO_PCM_STOP_RECORD] = remote_audio_pcm_stop_record,
+};
+
+/** Remote audio mixer interface structure. */
+remote_iface_t remote_audio_pcm_buffer_iface = {
+	.method_count = sizeof(remote_audio_pcm_iface_ops) /
+	    sizeof(remote_audio_pcm_iface_ops[0]),
+	.methods = remote_audio_pcm_iface_ops
+};
+/*----------------------------------------------------------------------------*/
+void remote_audio_pcm_get_info_str(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_buffer_iface_t *pcm_iface = iface;
+
+	if (!pcm_iface->get_info_str) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	const char *name = NULL;
+	const int ret = pcm_iface->get_info_str(fun, &name);
+	const size_t name_size = name ? str_size(name) + 1 : 0;
+	async_answer_1(callid, ret, name_size);
+	/* Send the string. */
+	if (ret == EOK && name_size > 0) {
+		size_t size;
+		ipc_callid_t name_id;
+		if (!async_data_read_receive(&name_id, &size)) {
+			async_answer_0(name_id, EPARTY);
+			return;
+		}
+		if (size != name_size) {
+			async_answer_0(name_id, ELIMIT);
+			return;
+		}
+		async_data_read_finalize(name_id, name, name_size);
+	}
+}
+/*----------------------------------------------------------------------------*/
+void remote_audio_pcm_get_buffer(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_buffer_iface_t *pcm_iface = iface;
+
+	if (!pcm_iface->get_buffer) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	void *buffer = NULL;
+	size_t size = 0;
+	unsigned id = 0;
+	const int ret = pcm_iface->get_buffer(fun, &buffer, &size, &id);
+	async_answer_2(callid, ret, size, id);
+	/* Share the buffer. */
+	if (ret == EOK && size > 0) {
+		size_t share_size;
+		ipc_callid_t name_id;
+		if (!async_share_in_receive(&name_id, &share_size)) {
+			async_answer_0(name_id, EPARTY);
+			return;
+		}
+		if (share_size != size) {
+			async_answer_0(name_id, ELIMIT);
+			return;
+		}
+		async_share_in_finalize(name_id, buffer, PROTO_READ | PROTO_WRITE);
+	}
+}
+/*----------------------------------------------------------------------------*/
+void remote_audio_pcm_release_buffer(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_buffer_iface_t *pcm_iface = iface;
+
+	const unsigned id = DEV_IPC_GET_ARG1(*call);
+	const int ret = pcm_iface->release_buffer ?
+	    pcm_iface->release_buffer(fun, id) : ENOTSUP;
+	async_answer_0(callid, ret);
+}
+/*----------------------------------------------------------------------------*/
+void remote_audio_pcm_start_playback(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_buffer_iface_t *pcm_iface = iface;
+
+	const unsigned id = DEV_IPC_GET_ARG1(*call);
+	const unsigned rate = DEV_IPC_GET_ARG2(*call);
+	const unsigned size = DEV_IPC_GET_ARG3(*call) >> 16;
+	const unsigned channels = (DEV_IPC_GET_ARG3(*call) >> 8) && UINT8_MAX;
+	const bool sign = (bool)(DEV_IPC_GET_ARG3(*call) & 1);
+
+	const int ret = pcm_iface->start_playback
+	    ? pcm_iface->start_playback(fun, id, rate, size, channels, sign)
+	    : ENOTSUP;
+	async_answer_0(callid, ret);
+}
+/*----------------------------------------------------------------------------*/
+void remote_audio_pcm_stop_playback(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_buffer_iface_t *pcm_iface = iface;
+
+	const unsigned id = DEV_IPC_GET_ARG1(*call);
+	const int ret = pcm_iface->stop_playback ?
+	    pcm_iface->stop_playback(fun, id) : ENOTSUP;
+	async_answer_0(callid, ret);
+}
+/*----------------------------------------------------------------------------*/
+void remote_audio_pcm_start_record(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_buffer_iface_t *pcm_iface = iface;
+
+	const unsigned id = DEV_IPC_GET_ARG1(*call);
+	const unsigned rate = DEV_IPC_GET_ARG2(*call);
+	const unsigned size = DEV_IPC_GET_ARG3(*call) >> 16;
+	const unsigned channels = (DEV_IPC_GET_ARG3(*call) & UINT16_MAX) >> 1;
+	const bool sign = (bool)(DEV_IPC_GET_ARG3(*call) & 1);
+
+	const int ret = pcm_iface->start_record
+	    ? pcm_iface->start_record(fun, id, rate, size, channels, sign)
+	    : ENOTSUP;
+	async_answer_0(callid, ret);
+}
+/*----------------------------------------------------------------------------*/
+void remote_audio_pcm_stop_record(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_buffer_iface_t *pcm_iface = iface;
+
+	const unsigned id = DEV_IPC_GET_ARG1(*call);
+	const int ret = pcm_iface->stop_record ?
+	    pcm_iface->stop_record(fun, id) : ENOTSUP;
+	async_answer_0(callid, ret);
+}
+
+#if 0
+/*----------------------------------------------------------------------------*/
+void remote_audio_mixer_get_info(
+    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	audio_mixer_iface_t *mixer_iface = iface;
+
+	if (!mixer_iface->get_info) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	const char *name = NULL;
+	unsigned items = 0;
+	const int ret = mixer_iface->get_info(fun, &name, &items);
+	const size_t name_size = name ? str_size(name) + 1 : 0;
+	async_answer_2(callid, ret, name_size, items);
+	/* Send the name. */
+	if (ret == EOK && name_size > 0) {
+		size_t size;
+		ipc_callid_t name_id;
+		if (!async_data_read_receive(&name_id, &size)) {
+			async_answer_0(name_id, EPARTY);
+			return;
+		}
+		if (size != name_size) {
+			async_answer_0(name_id, ELIMIT);
+			return;
+		}
+		async_data_read_finalize(name_id, name, name_size);
+	}
+}
+/*----------------------------------------------------------------------------*/
+void remote_audio_mixer_get_item_info(
+    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	audio_mixer_iface_t *mixer_iface = iface;
+
+	if (!mixer_iface->get_item_info) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	const unsigned item = DEV_IPC_GET_ARG1(*call);
+	const char *name = NULL;
+	unsigned channels = 0;
+	const int ret = mixer_iface->get_item_info(fun, item, &name, &channels);
+	const size_t name_size = name ? str_size(name) + 1 : 0;
+	async_answer_2(callid, ret, name_size, channels);
+	/* Send the name. */
+	if (ret == EOK && name_size > 0) {
+		size_t size;
+		ipc_callid_t name_id;
+		if (!async_data_read_receive(&name_id, &size)) {
+			async_answer_0(name_id, EPARTY);
+			return;
+		}
+		if (size != name_size) {
+			async_answer_0(name_id, ELIMIT);
+			return;
+		}
+		async_data_read_finalize(name_id, name, name_size);
+	}
+}
+/*----------------------------------------------------------------------------*/
+void remote_audio_mixer_get_channel_info(
+    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	audio_mixer_iface_t *mixer_iface = iface;
+
+	if (!mixer_iface->get_channel_info) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	const unsigned item = DEV_IPC_GET_ARG1(*call);
+	const unsigned channel = DEV_IPC_GET_ARG2(*call);
+	const char *name = NULL;
+	unsigned levels = 0;
+	const int ret =
+	    mixer_iface->get_channel_info(fun, item, channel, &name, &levels);
+	const size_t name_size = name ? str_size(name) + 1 : 0;
+	async_answer_2(callid, ret, name_size, levels);
+	/* Send the name. */
+	if (ret == EOK && name_size > 0) {
+		size_t size;
+		ipc_callid_t name_id;
+		if (!async_data_read_receive(&name_id, &size)) {
+			async_answer_0(name_id, EPARTY);
+			return;
+		}
+		if (size != name_size) {
+			async_answer_0(name_id, ELIMIT);
+			return;
+		}
+		async_data_read_finalize(name_id, name, name_size);
+	}
+}
+/*----------------------------------------------------------------------------*/
+void remote_audio_mixer_channel_mute_set(
+    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	audio_mixer_iface_t *mixer_iface = iface;
+
+	if (!mixer_iface->channel_mute_set) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	const unsigned item = DEV_IPC_GET_ARG1(*call);
+	const unsigned channel = DEV_IPC_GET_ARG2(*call);
+	const bool mute = DEV_IPC_GET_ARG3(*call);
+	const int ret = mixer_iface->channel_mute_set(fun, item, channel, mute);
+	async_answer_0(callid, ret);
+}
+/*----------------------------------------------------------------------------*/
+void remote_audio_mixer_channel_mute_get(
+    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	audio_mixer_iface_t *mixer_iface = iface;
+
+	if (!mixer_iface->channel_mute_get) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	const unsigned item = DEV_IPC_GET_ARG1(*call);
+	const unsigned channel = DEV_IPC_GET_ARG2(*call);
+	bool mute = false;
+	const int ret =
+	    mixer_iface->channel_mute_get(fun, item, channel, &mute);
+	async_answer_1(callid, ret, mute);
+}
+/*----------------------------------------------------------------------------*/
+void remote_audio_mixer_channel_volume_set(
+    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	audio_mixer_iface_t *mixer_iface = iface;
+
+	if (!mixer_iface->channel_volume_set) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	const unsigned item = DEV_IPC_GET_ARG1(*call);
+	const unsigned channel = DEV_IPC_GET_ARG2(*call);
+	const unsigned level = DEV_IPC_GET_ARG3(*call);
+	const int ret =
+	    mixer_iface->channel_volume_set(fun, item, channel, level);
+	async_answer_0(callid, ret);
+}
+/*----------------------------------------------------------------------------*/
+void remote_audio_mixer_channel_volume_get(
+    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	audio_mixer_iface_t *mixer_iface = iface;
+
+	if (!mixer_iface->channel_volume_get) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	const unsigned item = DEV_IPC_GET_ARG1(*call);
+	const unsigned channel = DEV_IPC_GET_ARG2(*call);
+	unsigned current = 0, max = 0;
+	const int ret =
+	    mixer_iface->channel_volume_get(fun, item, channel, &current, &max);
+	async_answer_2(callid, ret, current, max);
+}
+#endif
+
+/**
+ * @}
+ */
+
Index: uspace/lib/drv/generic/remote_hw_res.c
===================================================================
--- uspace/lib/drv/generic/remote_hw_res.c	(revision 0bbd13e274302c7c89dd4ea7cad7dbcc3ecfc524)
+++ uspace/lib/drv/generic/remote_hw_res.c	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2011 Jan Vesely
  * All rights reserved.
  *
@@ -43,8 +44,11 @@
 static void remote_hw_res_enable_interrupt(ddf_fun_t *, void *, ipc_callid_t,
     ipc_call_t *);
+static void remote_hw_res_dma_channel_setup(ddf_fun_t *, void *, ipc_callid_t,
+    ipc_call_t *);
 
 static remote_iface_func_ptr_t remote_hw_res_iface_ops [] = {
-	&remote_hw_res_get_resource_list,
-	&remote_hw_res_enable_interrupt
+	[HW_RES_GET_RESOURCE_LIST] = &remote_hw_res_get_resource_list,
+	[HW_RES_ENABLE_INTERRUPT] = &remote_hw_res_enable_interrupt,
+	[HW_RES_DMA_CHANNEL_SETUP] = &remote_hw_res_dma_channel_setup,
 };
 
@@ -94,4 +98,23 @@
 }
 
+static void remote_hw_res_dma_channel_setup(ddf_fun_t *fun, void *ops,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	hw_res_ops_t *hw_res_ops = ops;
+
+	if (hw_res_ops->dma_channel_setup == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	const unsigned channel = DEV_IPC_GET_ARG1(*call);
+	const uint32_t address = DEV_IPC_GET_ARG2(*call);
+	const uint16_t size = DEV_IPC_GET_ARG3(*call) & 0xffff;
+	const uint8_t mode = DEV_IPC_GET_ARG3(*call) >> 16;
+
+	const int ret = hw_res_ops->dma_channel_setup(
+	    fun, channel, address, size, mode);
+	async_answer_0(callid, ret);
+}
+
 /**
  * @}
Index: uspace/lib/drv/include/audio_mixer_iface.h
===================================================================
--- uspace/lib/drv/include/audio_mixer_iface.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/lib/drv/include/audio_mixer_iface.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,74 @@
+/*
+ * 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 libdrv
+ * @addtogroup usb
+ * @{
+ */
+/** @file
+ * @brief Audio mixer control interface.
+ */
+
+#ifndef LIBDRV_AUDIO_MIXER_IFACE_H_
+#define LIBDRV_AUDIO_MIXER_IFACE_H_
+
+#include <async.h>
+#include <bool.h>
+
+#include "ddf/driver.h"
+
+int audio_mixer_get_info(async_exch_t *, const char **, unsigned *);
+int audio_mixer_get_item_info(async_exch_t *, unsigned,
+    const char **, unsigned *);
+int audio_mixer_get_channel_info(async_exch_t *, unsigned, unsigned,
+    const char **, unsigned *);
+int audio_mixer_channel_mute_set(async_exch_t *, unsigned, unsigned, bool);
+int audio_mixer_channel_mute_get(async_exch_t *, unsigned, unsigned, bool *);
+int audio_mixer_channel_volume_set(async_exch_t *, unsigned, unsigned,
+    unsigned);
+int audio_mixer_channel_volume_get(async_exch_t *, unsigned, unsigned,
+    unsigned *, unsigned *);
+
+
+/** Audio mixer communication interface. */
+typedef struct {
+	int (*get_info)(ddf_fun_t *, const char **, unsigned *);
+	int (*get_item_info)(ddf_fun_t *, unsigned, const char **, unsigned *);
+	int (*get_channel_info)(ddf_fun_t *, unsigned, unsigned,
+	    const char **, unsigned *);
+	int (*channel_mute_set)(ddf_fun_t *, unsigned, unsigned, bool);
+	int (*channel_mute_get)(ddf_fun_t *, unsigned, unsigned, bool *);
+	int (*channel_volume_set)(ddf_fun_t *, unsigned, unsigned, unsigned);
+	int (*channel_volume_get)(ddf_fun_t *, unsigned, unsigned,
+	    unsigned *, unsigned *);
+} audio_mixer_iface_t;
+
+#endif
+/**
+ * @}
+ */
Index: uspace/lib/drv/include/audio_pcm_buffer_iface.h
===================================================================
--- uspace/lib/drv/include/audio_pcm_buffer_iface.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/lib/drv/include/audio_pcm_buffer_iface.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,72 @@
+/*
+ * 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 libdrv
+ * @addtogroup usb
+ * @{
+ */
+/** @file
+ * @brief Audio PCM buffer interface.
+ */
+
+#ifndef LIBDRV_AUDIO_PCM_BUFFER_IFACE_H_
+#define LIBDRV_AUDIO_PCM_BUFFER_IFACE_H_
+
+#include <async.h>
+#include <bool.h>
+
+#include "ddf/driver.h"
+
+int audio_pcm_buffer_get_info_str(async_exch_t *, const char **);
+int audio_pcm_buffer_get_buffer(async_exch_t *, void **, size_t *, unsigned *);
+int audio_pcm_buffer_release_buffer(async_exch_t *, unsigned);
+
+int audio_pcm_buffer_start_playback(async_exch_t *, unsigned,
+    unsigned, uint16_t, uint8_t, bool);
+int audio_pcm_buffer_stop_playback(async_exch_t *, unsigned);
+
+int audio_pcm_buffer_start_record(async_exch_t *, unsigned,
+    unsigned, unsigned, unsigned, bool);
+int audio_pcm_buffer_stop_record(async_exch_t *, unsigned);
+
+/** Audio pcm communication interface. */
+typedef struct {
+	int (*get_info_str)(ddf_fun_t *, const char **);
+	int (*get_buffer)(ddf_fun_t *, void **, size_t *, unsigned *);
+	int (*release_buffer)(ddf_fun_t *, unsigned);
+	int (*start_playback)(ddf_fun_t *, unsigned,
+	    unsigned, unsigned, unsigned, bool);
+	int (*stop_playback)(ddf_fun_t *, unsigned);
+	int (*start_record)(ddf_fun_t *, unsigned,
+	    unsigned, unsigned, unsigned, bool);
+	int (*stop_record)(ddf_fun_t *, unsigned);
+} audio_pcm_buffer_iface_t;
+
+#endif
+/**
+ * @}
+ */
Index: uspace/lib/drv/include/remote_audio_mixer.h
===================================================================
--- uspace/lib/drv/include/remote_audio_mixer.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/lib/drv/include/remote_audio_mixer.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,45 @@
+/*
+ * 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 libdrv
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDRV_REMOTE_AUDIO_MIXER_H_
+#define LIBDRV_REMOTE_AUDIO_MIXER_H_
+
+extern remote_iface_t remote_audio_mixer_iface;
+
+#endif
+
+/**
+ * @}
+ */
+
Index: uspace/lib/drv/include/remote_audio_pcm_buffer.h
===================================================================
--- uspace/lib/drv/include/remote_audio_pcm_buffer.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/lib/drv/include/remote_audio_pcm_buffer.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,44 @@
+/*
+ * 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 libdrv
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDRV_REMOTE_AUDIO_PCM_H_
+#define LIBDRV_REMOTE_AUDIO_PCM_H_
+
+extern remote_iface_t remote_audio_pcm_buffer_iface;
+
+#endif
+
+/**
+ * @}
+ */
+
Index: uspace/srv/net/tcp/tcp_header.h
===================================================================
--- uspace/srv/net/tcp/tcp_header.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
+++ uspace/srv/net/tcp/tcp_header.h	(revision 6843a9ccc55ea4287771f720d4f1117afbcb5bee)
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2009 Lukas Mejdrech
+ * 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 tcp
+ * @{
+ */
+
+/** @file
+ * TCP header definition.
+ * Based on the RFC 793.
+ */
+
+#ifndef NET_TCP_HEADER_H_
+#define NET_TCP_HEADER_H_
+
+#include <sys/types.h>
+
+/** TCP header size in bytes. */
+#define TCP_HEADER_SIZE				sizeof(tcp_header_t)
+
+/** Returns the actual TCP header length in bytes.
+ * @param[in] header The TCP packet header.
+ */
+#define TCP_HEADER_LENGTH(header)		(GET_TCP_HEADER_LENGTH(header) * 4U)
+
+/** Returns the TCP header length.
+ * @param[in] length The TCP header length in bytes.
+ */
+#define TCP_COMPUTE_HEADER_LENGTH(length)	((uint8_t) ((length) / 4U))
+
+/** Type definition of the transmission datagram header.
+ * @see tcp_header
+ */
+typedef struct tcp_header tcp_header_t;
+
+/** Type definition of the transmission datagram header option.
+ * @see tcp_option
+ */
+typedef struct tcp_option tcp_option_t;
+
+/** Type definition of the Maximum segment size TCP option. */
+typedef struct tcp_max_segment_size_option tcp_max_segment_size_option_t;
+
+/** Transmission datagram header. */
+struct tcp_header {
+	uint16_t source_port;
+	uint16_t destination_port;
+	uint32_t sequence_number;
+	uint32_t acknowledgement_number;
+
+	uint8_t hlr; /* header length, reserved1 */
+
+#define GET_TCP_HEADER_LENGTH(header) \
+	(((header)->hlr & 0xf0) >> 4)
+#define SET_TCP_HEADER_LENGTH(header, length) \
+	((header)->hlr = \
+	 ((length & 0x0f) << 4) | ((header)->hlr & 0x0f))
+
+#define GET_TCP_HEADER_RESERVED1(header) \
+	((header)->hlr & 0x0f)
+#define SET_TCP_HEADER_RESERVED1(header, reserved1) \
+	((header)->hlr = \
+	 (reserved1 & 0x0f) | ((header)->hlr & 0xf0))
+
+	/* reserved2, urgent, acknowledge, push, reset, synchronize, finalize */
+	uint8_t ruaprsf;  
+
+#define GET_TCP_HEADER_RESERVED2(header) \
+	(((header)->ruaprsf & 0xc0) >> 6)
+#define SET_TCP_HEADER_RESERVED2(header, reserved2) \
+	((header)->ruaprsf = \
+	 ((reserved2 & 0x03) << 6) | ((header)->ruaprsf & 0x3f))
+
+#define GET_TCP_HEADER_URGENT(header) \
+	(((header)->ruaprsf & 0x20) >> 5)
+#define SET_TCP_HEADER_URGENT(header, urgent) \
+	((header)->ruaprsf = \
+	 ((urgent & 0x01) << 5) | ((header)->ruaprsf & 0xdf))
+
+#define GET_TCP_HEADER_ACKNOWLEDGE(header) \
+	(((header)->ruaprsf & 0x10) >> 4)
+#define SET_TCP_HEADER_ACKNOWLEDGE(header, acknowledge) \
+	((header)->ruaprsf = \
+	 ((acknowledge & 0x01) << 4) | ((header)->ruaprsf & 0xef))
+
+#define GET_TCP_HEADER_PUSH(header) \
+	(((header)->ruaprsf & 0x08) >> 3)
+#define SET_TCP_HEADER_PUSH(header, push) \
+	((header)->ruaprsf = \
+	 ((push & 0x01) << 3) | ((header)->ruaprsf & 0xf7))
+
+#define GET_TCP_HEADER_RESET(header) \
+	(((header)->ruaprsf & 0x04) >> 2)
+#define SET_TCP_HEADER_RESET(header, reset) \
+	((header)->ruaprsf = \
+	 ((reset & 0x01) << 2) | ((header)->ruaprsf & 0xfb))
+
+#define GET_TCP_HEADER_SYNCHRONIZE(header) \
+	(((header)->ruaprsf & 0x02) >> 1)
+#define SET_TCP_HEADER_SYNCHRONIZE(header, synchronize) \
+	((header)->ruaprsf = \
+	 ((synchronize & 0x01) << 1) | ((header)->ruaprsf & 0xfd))
+
+#define GET_TCP_HEADER_FINALIZE(header) \
+	((header)->ruaprsf & 0x01)
+#define SET_TCP_HEADER_FINALIZE(header, finalize) \
+	((header)->ruaprsf = \
+	 (finalize & 0x01) | ((header)->ruaprsf & 0xfe))
+
+	uint16_t window;
+	uint16_t checksum;
+	uint16_t urgent_pointer;
+} __attribute__ ((packed));
+
+/** Transmission datagram header option. */
+struct tcp_option {
+	/** Option type. */
+	uint8_t type;
+	/** Option length. */
+	uint8_t length;
+};
+
+/** Maximum segment size TCP option. */
+struct tcp_max_segment_size_option {
+	/** TCP option.
+	 * @see TCPOPT_MAX_SEGMENT_SIZE
+	 * @see TCPOPT_MAX_SEGMENT_SIZE_LENGTH
+	 */
+	tcp_option_t option;
+	
+	/** Maximum segment size in bytes. */
+	uint16_t max_segment_size;
+} __attribute__ ((packed));
+
+#endif
+
+/** @}
+ */
