Index: uspace/lib/c/generic/async.c
===================================================================
--- uspace/lib/c/generic/async.c	(revision 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/c/generic/async.c	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -2281,14 +2281,35 @@
 bool async_data_read_receive(ipc_callid_t *callid, size_t *size)
 {
+	ipc_call_t data;
+	return async_data_read_receive_call(callid, &data, size);
+}
+
+/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
+ *
+ * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ
+ * calls so that the user doesn't have to remember the meaning of each IPC
+ * argument.
+ *
+ * So far, this wrapper is to be used from within a connection fibril.
+ *
+ * @param callid Storage for the hash of the IPC_M_DATA_READ.
+ * @param size   Storage for the maximum size. Can be NULL.
+ *
+ * @return True on success, false on failure.
+ *
+ */
+bool async_data_read_receive_call(ipc_callid_t *callid, ipc_call_t *data,
+    size_t *size)
+{
 	assert(callid);
-	
-	ipc_call_t data;
-	*callid = async_get_call(&data);
-	
-	if (IPC_GET_IMETHOD(data) != IPC_M_DATA_READ)
+	assert(data);
+	
+	*callid = async_get_call(data);
+	
+	if (IPC_GET_IMETHOD(*data) != IPC_M_DATA_READ)
 		return false;
 	
 	if (size)
-		*size = (size_t) IPC_GET_ARG2(data);
+		*size = (size_t) IPC_GET_ARG2(*data);
 	
 	return true;
@@ -2385,14 +2406,36 @@
 bool async_data_write_receive(ipc_callid_t *callid, size_t *size)
 {
+	ipc_call_t data;
+	return async_data_write_receive_call(callid, &data, size);
+}
+
+/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
+ *
+ * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE
+ * calls so that the user doesn't have to remember the meaning of each IPC
+ * argument.
+ *
+ * So far, this wrapper is to be used from within a connection fibril.
+ *
+ * @param callid Storage for the hash of the IPC_M_DATA_WRITE.
+ * @param data   Storage for the ipc call data.
+ * @param size   Storage for the suggested size. May be NULL.
+ *
+ * @return True on success, false on failure.
+ *
+ */
+bool async_data_write_receive_call(ipc_callid_t *callid, ipc_call_t *data,
+    size_t *size)
+{
 	assert(callid);
-	
-	ipc_call_t data;
-	*callid = async_get_call(&data);
-	
-	if (IPC_GET_IMETHOD(data) != IPC_M_DATA_WRITE)
+	assert(data);
+	
+	*callid = async_get_call(data);
+	
+	if (IPC_GET_IMETHOD(*data) != IPC_M_DATA_WRITE)
 		return false;
 	
 	if (size)
-		*size = (size_t) IPC_GET_ARG2(data);
+		*size = (size_t) IPC_GET_ARG2(*data);
 	
 	return true;
Index: uspace/lib/c/generic/device/hw_res.c
===================================================================
--- uspace/lib/c/generic/device/hw_res.c	(revision 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/c/generic/device/hw_res.c	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -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,57 @@
 {
 	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, uint32_t size, uint8_t mode)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	if (exch == NULL)
+		return ENOMEM;
+	const uint32_t packed = (channel & 0xffff) | (mode << 16);
+	const int ret = async_req_4_0(exch, DEV_IFACE_ID(HW_RES_DEV_IFACE),
+	    HW_RES_DMA_CHANNEL_SETUP, packed, pa, size);
+	async_exchange_end(exch);
+
+	return ret;
+}
+
+/**
+ * Query remaining bytes in the buffer.
+ * @param channel DMA Channel 1,2,3 for 8 bit transfers, 5,6,7 for 16 bit.
+ * @return Number of bytes remaining in the buffer(>=0) or error code(<0).
+ */
+int hw_res_dma_channel_remain(async_sess_t *sess, unsigned channel)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	if (exch == NULL)
+		return ENOMEM;
+	sysarg_t remain;
+	const int ret = async_req_2_1(exch, DEV_IFACE_ID(HW_RES_DEV_IFACE),
+	    HW_RES_DMA_CHANNEL_REMAIN, channel, &remain);
+	async_exchange_end(exch);
+	if (ret == EOK)
+		return remain;
+	return ret;
 }
 
Index: uspace/lib/c/generic/device/hw_res_parsed.c
===================================================================
--- uspace/lib/c/generic/device/hw_res_parsed.c	(revision 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/c/generic/device/hw_res_parsed.c	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -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 unsigned 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/generic/inet/addr.c
===================================================================
--- uspace/lib/c/generic/inet/addr.c	(revision 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/c/generic/inet/addr.c	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -52,4 +52,8 @@
 };
 
+static const addr48_t inet_addr48_solicited_node = {
+	0x33, 0x33, 0xff, 0, 0, 0
+};
+
 static const inet_addr_t inet_addr_any_addr = {
 	.family = AF_INET,
@@ -72,7 +76,24 @@
 }
 
+int addr48_compare(const addr48_t a, const addr48_t b)
+{
+	return memcmp(a, b, 6);
+}
+
 int addr128_compare(const addr128_t a, const addr128_t b)
 {
 	return memcmp(a, b, 16);
+}
+
+/** Compute solicited node MAC multicast address from target IPv6 address
+ *
+ * @param ip  Target IPv6 address
+ * @param mac Solicited MAC address to be assigned
+ *
+ */
+void addr48_solicited_node(const addr128_t ip, addr48_t mac)
+{
+	memcpy(mac, inet_addr48_solicited_node, 3);
+	memcpy(mac + 3, ip + 13, 3);
 }
 
Index: uspace/lib/c/include/async.h
===================================================================
--- uspace/lib/c/include/async.h	(revision 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/c/include/async.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -399,4 +399,5 @@
 extern int async_data_read_start(async_exch_t *, void *, size_t);
 extern bool async_data_read_receive(ipc_callid_t *, size_t *);
+extern bool async_data_read_receive_call(ipc_callid_t *, ipc_call_t *, size_t *);
 extern int async_data_read_finalize(ipc_callid_t, const void *, size_t);
 
@@ -437,4 +438,5 @@
 extern int async_data_write_start(async_exch_t *, const void *, size_t);
 extern bool async_data_write_receive(ipc_callid_t *, size_t *);
+extern bool async_data_write_receive_call(ipc_callid_t *, ipc_call_t *, size_t *);
 extern int async_data_write_finalize(ipc_callid_t, void *, size_t);
 
Index: uspace/lib/c/include/device/hw_res.h
===================================================================
--- uspace/lib/c/include/device/hw_res.h	(revision 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/c/include/device/hw_res.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -53,4 +53,5 @@
 	HW_RES_ENABLE_INTERRUPT,
 	HW_RES_DMA_CHANNEL_SETUP,
+	HW_RES_DMA_CHANNEL_REMAIN,
 } hw_res_method_t;
 
@@ -115,5 +116,6 @@
 
 extern int hw_res_dma_channel_setup(async_sess_t *, unsigned int, uint32_t,
-    uint16_t, uint8_t);
+    uint32_t, uint8_t);
+extern int hw_res_dma_channel_remain(async_sess_t *, unsigned);
 
 #endif
Index: uspace/lib/c/include/device/hw_res_parsed.h
===================================================================
--- uspace/lib/c/include/device/hw_res_parsed.h	(revision 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/c/include/device/hw_res_parsed.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -139,5 +139,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/inet/addr.h
===================================================================
--- uspace/lib/c/include/inet/addr.h	(revision 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/c/include/inet/addr.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -73,5 +73,8 @@
 extern void addr128(const addr128_t, addr128_t);
 
+extern int addr48_compare(const addr48_t, const addr48_t);
 extern int addr128_compare(const addr128_t, const addr128_t);
+
+extern void addr48_solicited_node(const addr128_t, addr48_t);
 
 extern void host2addr128_t_be(const addr128_t, addr128_t);
Index: uspace/lib/c/include/ipc/dev_iface.h
===================================================================
--- uspace/lib/c/include/ipc/dev_iface.h	(revision 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/c/include/ipc/dev_iface.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -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 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/drv/Makefile	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -29,5 +29,5 @@
 
 USPACE_PREFIX = ../..
-EXTRA_CFLAGS = -Iinclude -I$(LIBUSB_PREFIX)/include
+EXTRA_CFLAGS = -Iinclude -I$(LIBUSB_PREFIX)/include -I$(LIBPCM_PREFIX)/include
 LIBRARY = libdrv
 
@@ -38,4 +38,6 @@
 	generic/log.c \
 	generic/logbuf.c \
+	generic/remote_audio_mixer.c \
+	generic/remote_audio_pcm.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 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/drv/generic/dev_iface.c	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -49,19 +49,23 @@
 #include "remote_usbhid.h"
 #include "remote_pci.h"
+#include "remote_audio_mixer.h"
+#include "remote_audio_pcm.h"
 #include "remote_ahci.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_graph_dev_iface,
-		&remote_nic_iface,
-		&remote_pci_iface,
-		&remote_usb_iface,
-		&remote_usbhc_iface,
-		&remote_usbhid_iface,
-		&remote_clock_dev_iface,
-		&remote_battery_dev_iface,
-		&remote_ahci_iface
+		[AUDIO_MIXER_IFACE] = &remote_audio_mixer_iface,
+		[AUDIO_PCM_BUFFER_IFACE] = &remote_audio_pcm_iface,
+		[HW_RES_DEV_IFACE] = &remote_hw_res_iface,
+		[CHAR_DEV_IFACE] = &remote_char_dev_iface,
+		[GRAPH_DEV_IFACE] = &remote_graph_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,
+		[CLOCK_DEV_IFACE] = &remote_clock_dev_iface,
+		[BATTERY_DEV_IFACE] = &remote_battery_dev_iface,
+		[AHCI_DEV_IFACE] = &remote_ahci_iface,
 	}
 };
Index: uspace/lib/drv/generic/remote_audio_mixer.c
===================================================================
--- uspace/lib/drv/generic/remote_audio_mixer.c	(revision 5856b627dd9852525f88109f58d955b5b633266d)
+++ uspace/lib/drv/generic/remote_audio_mixer.c	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -0,0 +1,315 @@
+/*
+ * 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,
+
+	/** Set new control item setting
+	 * Answer:
+	 * - ENOTSUP - call not supported
+	 * - ENOENT - no such control item
+	 * - EOK - call successful, info is valid
+	 */
+	IPC_M_AUDIO_MIXER_SET_ITEM_LEVEL,
+
+	/** Get control item setting
+	 * Answer:
+	 * - ENOTSUP - call not supported
+	 * - ENOENT - no such control item
+	 * - EOK - call successful, info is valid
+	 */
+	IPC_M_AUDIO_MIXER_GET_ITEM_LEVEL,
+} audio_mixer_iface_funcs_t;
+
+/*
+ * CLIENT SIDE
+ */
+
+/**
+ * Query audio mixer for basic info (name and items count).
+ * @param[in] exch IPC exchange connected to the device
+ * @param[out] name Audio mixer string identifier
+ * @param[out] items Number of items controlled by the mixer.
+ * @return Error code.
+ */
+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;
+}
+
+/**
+ * Query audio mixer for item specific info (name and channels count).
+ * @param[in] exch IPC exchange connected to the device
+ * @param[in] item The control item
+ * @param[out] name Control item string identifier.
+ * @param[out] channles Number of channels associated with this control item.
+ * @return Error code.
+ */
+int audio_mixer_get_item_info(async_exch_t *exch, unsigned item,
+    const char **name, unsigned *levels)
+{
+	if (!exch)
+		return EINVAL;
+	sysarg_t name_size, lvls;
+	const int ret = async_req_2_2(exch, DEV_IFACE_ID(AUDIO_MIXER_IFACE),
+	    IPC_M_AUDIO_MIXER_GET_ITEM_INFO, item, &name_size, &lvls);
+	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 && levels)
+		*levels = lvls;
+	return ret;
+}
+
+/**
+ * Set control item to a new level.
+ * @param[in] exch IPC exchange connected to the device.
+ * @param[in] item The control item controlling the channel.
+ * @param[in] level The new value.
+ * @return Error code.
+ */
+int audio_mixer_set_item_level(async_exch_t *exch, unsigned item,
+    unsigned level)
+{
+	if (!exch)
+		return EINVAL;
+	return async_req_3_0(exch, DEV_IFACE_ID(AUDIO_MIXER_IFACE),
+	    IPC_M_AUDIO_MIXER_SET_ITEM_LEVEL, item, level);
+}
+
+/**
+ * Get current level of a control item.
+ * @param[in] exch IPC exchange connected to the device.
+ * @param[in] item The control item controlling the channel.
+ * @param[in] channel The channel index.
+ * @param[out] level Currently set value.
+ * @return Error code.
+ */
+int audio_mixer_get_item_level(async_exch_t *exch, unsigned item,
+    unsigned *level)
+{
+	if (!exch)
+		return EINVAL;
+	sysarg_t current;
+	const int ret = async_req_2_1(exch, DEV_IFACE_ID(AUDIO_MIXER_IFACE),
+	    IPC_M_AUDIO_MIXER_GET_ITEM_LEVEL, item, &current);
+	if (ret == EOK && level)
+		*level = current;
+	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_item_level(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_mixer_set_item_level(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_ITEM_LEVEL] = remote_audio_mixer_get_item_level,
+	[IPC_M_AUDIO_MIXER_SET_ITEM_LEVEL] = remote_audio_mixer_set_item_level,
+};
+
+/** 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 values = 0;
+	const int ret = mixer_iface->get_item_info(fun, item, &name, &values);
+	const size_t name_size = name ? str_size(name) + 1 : 0;
+	async_answer_2(callid, ret, name_size, values);
+	/* 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_set_item_level(
+    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	audio_mixer_iface_t *mixer_iface = iface;
+
+	if (!mixer_iface->set_item_level) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	const unsigned item = DEV_IPC_GET_ARG1(*call);
+	const unsigned value = DEV_IPC_GET_ARG2(*call);
+	const int ret = mixer_iface->set_item_level(fun, item, value);
+	async_answer_0(callid, ret);
+}
+
+void remote_audio_mixer_get_item_level(
+    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_level) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	const unsigned item = DEV_IPC_GET_ARG1(*call);
+	unsigned current = 0;
+	const int ret =
+	    mixer_iface->get_item_level(fun, item, &current);
+	async_answer_1(callid, ret, current);
+}
+
+/**
+ * @}
+ */
Index: uspace/lib/drv/generic/remote_audio_pcm.c
===================================================================
--- uspace/lib/drv/generic/remote_audio_pcm.c	(revision 5856b627dd9852525f88109f58d955b5b633266d)
+++ uspace/lib/drv/generic/remote_audio_pcm.c	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -0,0 +1,858 @@
+/*
+ * Copyright (c) 2012 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 <devman.h>
+#include <ddf/log.h>
+#include <errno.h>
+#include <macros.h>
+#include <str.h>
+#include <as.h>
+#include <sys/mman.h>
+
+#include "audio_pcm_iface.h"
+#include "ddf/driver.h"
+
+typedef enum {
+	IPC_M_AUDIO_PCM_GET_INFO_STR,
+	IPC_M_AUDIO_PCM_QUERY_CAPS,
+	IPC_M_AUDIO_PCM_REGISTER_EVENTS,
+	IPC_M_AUDIO_PCM_UNREGISTER_EVENTS,
+	IPC_M_AUDIO_PCM_TEST_FORMAT,
+	IPC_M_AUDIO_PCM_GET_BUFFER,
+	IPC_M_AUDIO_PCM_RELEASE_BUFFER,
+	IPC_M_AUDIO_PCM_GET_BUFFER_POS,
+	IPC_M_AUDIO_PCM_START_PLAYBACK,
+	IPC_M_AUDIO_PCM_STOP_PLAYBACK,
+	IPC_M_AUDIO_PCM_START_CAPTURE,
+	IPC_M_AUDIO_PCM_STOP_CAPTURE,
+} audio_pcm_iface_funcs_t;
+
+/**
+ * Get human readable capability name.
+ * @param cap audio capability.
+ * @return Valid string
+ */
+const char *audio_pcm_cap_str(audio_cap_t cap)
+{
+	static const char *caps[] = {
+		[AUDIO_CAP_CAPTURE] = "CAPTURE",
+		[AUDIO_CAP_PLAYBACK] = "PLAYBACK",
+		[AUDIO_CAP_MAX_BUFFER] = "MAXIMUM BUFFER SIZE",
+		[AUDIO_CAP_BUFFER_POS] = "KNOWS BUFFER POSITION",
+		[AUDIO_CAP_INTERRUPT] = "FRAGMENT INTERRUPTS",
+		[AUDIO_CAP_INTERRUPT_MIN_FRAMES] = "MINIMUM FRAGMENT SIZE",
+		[AUDIO_CAP_INTERRUPT_MAX_FRAMES] = "MAXIMUM FRAGMENT SIZE",
+	};
+	if (cap >= ARRAY_SIZE(caps))
+		return "UNKNOWN CAP";
+	return caps[cap];
+
+}
+
+/**
+ * Get human readable event name.
+ * @param event Audio device event
+ * @return Valid string
+ */
+const char *audio_pcm_event_str(pcm_event_t event)
+{
+	static const char *events[] = {
+		[PCM_EVENT_PLAYBACK_STARTED] = "PLAYBACK STARTED",
+		[PCM_EVENT_CAPTURE_STARTED] = "CAPTURE STARTED",
+		[PCM_EVENT_FRAMES_PLAYED] = "FRAGMENT PLAYED",
+		[PCM_EVENT_FRAMES_CAPTURED] = "FRAGMENT CAPTURED",
+		[PCM_EVENT_PLAYBACK_TERMINATED] = "PLAYBACK TERMINATED",
+		[PCM_EVENT_CAPTURE_TERMINATED] = "CAPTURE TERMINATED",
+	};
+	if (event >= ARRAY_SIZE(events))
+		return "UNKNOWN EVENT";
+	return events[event];
+}
+
+/*
+ * CLIENT SIDE
+ */
+
+/**
+ * Open audio session with the first registered device.
+ *
+ * @return Pointer to a new audio device session, NULL on failure.
+ */
+audio_pcm_sess_t *audio_pcm_open_default(void)
+{
+	static bool resolved = false;
+	static category_id_t pcm_id = 0;
+	if (!resolved) {
+		const int ret = loc_category_get_id("audio-pcm", &pcm_id,
+		    IPC_FLAG_BLOCKING);
+		if (ret != EOK)
+			return NULL;
+		resolved = true;
+	}
+
+	service_id_t *svcs = NULL;
+	size_t count = 0;
+	const int ret = loc_category_get_svcs(pcm_id, &svcs, &count);
+	if (ret != EOK)
+		return NULL;
+
+	audio_pcm_sess_t *session = NULL;
+	if (count)
+		session = audio_pcm_open_service(svcs[0]);
+	free(svcs);
+	return session;
+}
+
+/**
+ * Open audio session with device identified by location service string.
+ *
+ * @param name Location service string.
+ * @return Pointer to a new audio device session, NULL on failure.
+ */
+audio_pcm_sess_t *audio_pcm_open(const char *name)
+{
+	devman_handle_t device_handle = 0;
+	const int ret = devman_fun_get_handle(name, &device_handle, 0);
+	if (ret != EOK)
+		return NULL;
+	return devman_device_connect(EXCHANGE_SERIALIZE, device_handle,
+	    IPC_FLAG_BLOCKING);
+}
+
+/**
+ * Open audio session with device identified by location service id
+ *
+ * @param name Location service id.
+ * @return Pointer to a new audio device session, NULL on failure.
+ */
+audio_pcm_sess_t *audio_pcm_open_service(service_id_t id)
+{
+	return loc_service_connect(EXCHANGE_SERIALIZE, id, IPC_FLAG_BLOCKING);
+}
+
+/**
+ * Close open audio device session.
+ *
+ * @param name Open audio device session.
+ *
+ * @note Calling this function on already closed or invalid session results
+ * in undefined behavior.
+ */
+void audio_pcm_close(audio_pcm_sess_t *sess)
+{
+	if (sess)
+		async_hangup(sess);
+}
+
+/**
+ * Get a short description string.
+ *
+ * @param sess Audio device session.
+ * @param name Place to store newly allocated string.
+ *
+ * @return Error code.
+ *
+ * @note Caller is responsible for freeing newly allocated memory.
+ */
+int audio_pcm_get_info_str(audio_pcm_sess_t *sess, const char **name)
+{
+	if (!name)
+		return EINVAL;
+	async_exch_t *exch = async_exchange_begin(sess);
+	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) {
+		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);
+			async_exchange_end(exch);
+			return ENOMEM;
+		}
+		const int ret =
+		    async_data_read_start(exch, name_place, name_size);
+		if (ret != EOK) {
+			free(name_place);
+			async_exchange_end(exch);
+			return ret;
+		}
+		*name = name_place;
+	}
+	async_exchange_end(exch);
+	return ret;
+}
+
+
+/**
+ * Query value of specified capability.
+ *
+ * @param sess Audio device session.
+ * @param cap  Audio device capability.
+ * @param val  Place to store queried value.
+ *
+ * @return Error code.
+ */
+int audio_pcm_query_cap(audio_pcm_sess_t *sess, audio_cap_t cap)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	sysarg_t value = 0;
+	const int ret = async_req_2_1(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE), IPC_M_AUDIO_PCM_QUERY_CAPS,
+	    cap, &value);
+	async_exchange_end(exch);
+	if (ret == EOK)
+		return value;
+	return ret;
+}
+
+/**
+ * Query current position in device buffer.
+ *
+ * @param sess Audio device session.
+ * @param pos Place to store the result.
+ *
+ * @return Error code.
+ *
+ * Works for both playback and capture.
+ */
+int audio_pcm_get_buffer_pos(audio_pcm_sess_t *sess, size_t *pos)
+{
+	if (!pos)
+		return EINVAL;
+	async_exch_t *exch = async_exchange_begin(sess);
+	sysarg_t value = 0;
+	const int ret = async_req_1_1(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_GET_BUFFER_POS, &value);
+	if (ret == EOK)
+		*pos = value;
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Test format parameters for device support.
+ *
+ * @param sess Audio device session.
+ * @param channels Number of channels
+ * @param rate Sampling rate.
+ * @format Sample format.
+ *
+ * @return Error code.
+ *
+ * Works for both playback and capture. This function modifies provided
+ * parameters to the nearest values supported by the device.
+ */
+int audio_pcm_test_format(audio_pcm_sess_t *sess, unsigned *channels,
+    unsigned *rate, pcm_sample_format_t *format)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	sysarg_t channels_arg = channels ? *channels : 0;
+	sysarg_t rate_arg = rate ? *rate : 0;
+	sysarg_t format_arg = format ? *format : 0;
+	const int ret = async_req_4_3(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_TEST_FORMAT, channels_arg, rate_arg, format_arg,
+	    &channels_arg, &rate_arg, &format_arg);
+	async_exchange_end(exch);
+
+	/* All OK or something has changed. Verify that it was not one of the
+	 * params we care about */
+	if ((ret == EOK || ret == ELIMIT)
+	    && (!channels || *channels == channels_arg)
+	    && (!rate || *rate == rate_arg)
+	    && (!format || *format == format_arg))
+		return EOK;
+	if (channels)
+		*channels = channels_arg;
+	if (rate)
+		*rate = rate_arg;
+	if (format)
+		*format = format_arg;
+	return ret;
+}
+
+/**
+ * Register callback for device generated events.
+ *
+ * @param sess Audio device session.
+ * @param event_rec Event callback function.
+ * @param arg Event callback custom parameter.
+ *
+ * @return Error code.
+ */
+int audio_pcm_register_event_callback(audio_pcm_sess_t *sess,
+    async_client_conn_t event_callback, void *arg)
+{
+	if (!event_callback)
+		return EINVAL;
+
+	async_exch_t *exch = async_exchange_begin(sess);
+	int ret = async_req_1_0(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_REGISTER_EVENTS);
+	if (ret == EOK) {
+		ret = async_connect_to_me(exch, 0, 0, 0, event_callback, arg);
+	}
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Unregister callback for device generated events.
+ *
+ * @param sess Audio device session.
+ *
+ * @return Error code.
+ */
+int audio_pcm_unregister_event_callback(audio_pcm_sess_t *sess)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret = async_req_1_0(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_UNREGISTER_EVENTS);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Get device accessible playback/capture buffer.
+ *
+ * @param sess Audio device session.
+ * @param buffer Place to store pointer to the buffer.
+ * @param size Place to store buffer size (bytes).
+ *
+ * @return Error code.
+ */
+int audio_pcm_get_buffer(audio_pcm_sess_t *sess, void **buffer, size_t *size)
+{
+	if (!buffer || !size)
+		return EINVAL;
+
+	async_exch_t *exch = async_exchange_begin(sess);
+
+	sysarg_t buffer_size = *size;
+	int ret = async_req_2_1(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_GET_BUFFER, (sysarg_t)buffer_size, &buffer_size);
+	if (ret == EOK) {
+		void *dst = NULL;
+		ret = async_share_in_start_0_0(exch, buffer_size, &dst);
+		if (ret != EOK) {
+			async_exchange_end(exch);
+			return ret;
+		}
+		*buffer = dst;
+		*size = buffer_size;
+	}
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Release device accessible playback/capture buffer.
+ *
+ * @param sess Audio device session.
+ *
+ * @return Error code.
+ */
+int audio_pcm_release_buffer(audio_pcm_sess_t *sess)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret = async_req_1_0(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_RELEASE_BUFFER);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Start playback on buffer from position 0.
+ *
+ * @param sess Audio device session.
+ * @param frames Size of fragment (in frames).
+ * @param channels Number of channels.
+ * @param sample_rate Sampling rate (for one channel).
+ * @param format Sample format.
+ *
+ * @return Error code.
+ *
+ * Event will be generated after every fragment. Set fragment size to
+ * 0 to turn off event generation.
+ */
+int audio_pcm_start_playback_fragment(audio_pcm_sess_t *sess, unsigned frames,
+    unsigned channels, unsigned sample_rate, pcm_sample_format_t format)
+{
+	if (channels > UINT16_MAX)
+		return EINVAL;
+	assert((format & UINT16_MAX) == format);
+	const sysarg_t packed = (channels << 16) | (format & UINT16_MAX);
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret = async_req_4_0(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_START_PLAYBACK,
+	    frames, sample_rate, packed);
+	async_exchange_end(exch);
+	return ret;
+}
+/**
+ * Stops playback after current fragment.
+ *
+ * @param sess Audio device session.
+ *
+ * @return Error code.
+ */
+int audio_pcm_last_playback_fragment(audio_pcm_sess_t *sess)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret = async_req_2_0(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_STOP_PLAYBACK, false);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Start playback on buffer from the current position.
+ *
+ * @param sess Audio device session.
+ * @param channels Number of channels.
+ * @param sample_rate Sampling rate (for one channel).
+ * @param format Sample format.
+ *
+ * @return Error code.
+ */
+int audio_pcm_start_playback(audio_pcm_sess_t *sess,
+    unsigned channels, unsigned sample_rate, pcm_sample_format_t format)
+{
+	return audio_pcm_start_playback_fragment(
+	    sess, 0, channels, sample_rate, format);
+}
+
+/**
+ * Immediately stops current playback.
+ *
+ * @param sess Audio device session.
+ *
+ * @return Error code.
+ */
+int audio_pcm_stop_playback_immediate(audio_pcm_sess_t *sess)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret = async_req_2_0(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_STOP_PLAYBACK, true);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Stops playback at the end of the current fragment.
+ *
+ * @param sess Audio device session.
+ *
+ * @return Error code.
+ */
+int audio_pcm_stop_playback(audio_pcm_sess_t *sess)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret = async_req_2_0(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_STOP_PLAYBACK, false);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Start capture on buffer from the current position.
+ *
+ * @param sess Audio device session.
+ * @param frames Size of fragment (in frames).
+ * @param channels Number of channels.
+ * @param sample_rate Sampling rate (for one channel).
+ * @param format Sample format.
+ *
+ * @return Error code.
+ *
+ * Event will be generated after every fragment. Set fragment size to
+ * 0 to turn off event generation.
+ */
+int audio_pcm_start_capture_fragment(audio_pcm_sess_t *sess, unsigned frames,
+    unsigned channels, unsigned sample_rate, pcm_sample_format_t format)
+{
+	if (channels > UINT16_MAX)
+		return EINVAL;
+	assert((format & UINT16_MAX) == format);
+	const sysarg_t packed = (channels << 16) | (format & UINT16_MAX);
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret = async_req_4_0(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE), IPC_M_AUDIO_PCM_START_CAPTURE,
+	    frames, sample_rate, packed);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Start capture on buffer from the current position.
+ *
+ * @param sess Audio device session.
+ * @param channels Number of channels.
+ * @param sample_rate Sampling rate (for one channel).
+ * @param format Sample format.
+ *
+ * @return Error code.
+ */
+int audio_pcm_start_capture(audio_pcm_sess_t *sess,
+    unsigned channels, unsigned sample_rate, pcm_sample_format_t format)
+{
+	return audio_pcm_start_capture_fragment(
+	    sess, 0, channels, sample_rate, format);
+}
+
+/**
+ * Stops capture at the end of current fragment.
+ *
+ * Won't work if capture was started with fragment size 0.
+ * @param sess Audio device session.
+ *
+ * @return Error code.
+ */
+int audio_pcm_last_capture_fragment(audio_pcm_sess_t *sess)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret = async_req_2_0(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_STOP_CAPTURE, false);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Immediately stops current capture.
+ *
+ * @param sess Audio device session.
+ *
+ * @return Error code.
+ */
+int audio_pcm_stop_capture_immediate(audio_pcm_sess_t *sess)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret = async_req_2_0(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_STOP_CAPTURE, true);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Stops capture at the end of the current fragment.
+ *
+ * @param sess Audio device session.
+ *
+ * @return Error code.
+ */
+int audio_pcm_stop_capture(audio_pcm_sess_t *sess)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret = async_req_2_0(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_STOP_CAPTURE, false);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/*
+ * 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_query_caps(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_events_register(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_events_unregister(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_get_buffer_pos(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_test_format(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_capture(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_stop_capture(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_QUERY_CAPS] = remote_audio_pcm_query_caps,
+	[IPC_M_AUDIO_PCM_REGISTER_EVENTS] = remote_audio_pcm_events_register,
+	[IPC_M_AUDIO_PCM_UNREGISTER_EVENTS] = remote_audio_pcm_events_unregister,
+	[IPC_M_AUDIO_PCM_GET_BUFFER_POS] = remote_audio_pcm_get_buffer_pos,
+	[IPC_M_AUDIO_PCM_TEST_FORMAT] = remote_audio_pcm_test_format,
+	[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_CAPTURE] = remote_audio_pcm_start_capture,
+	[IPC_M_AUDIO_PCM_STOP_CAPTURE] = remote_audio_pcm_stop_capture,
+};
+
+/** Remote audio mixer interface structure. */
+remote_iface_t remote_audio_pcm_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_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_query_caps(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+	const audio_cap_t cap = DEV_IPC_GET_ARG1(*call);
+	if (pcm_iface->query_cap) {
+		const unsigned value = pcm_iface->query_cap(fun, cap);
+		async_answer_1(callid, EOK, value);
+	} else {
+		async_answer_0(callid, ENOTSUP);
+	}
+}
+
+static void remote_audio_pcm_events_register(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+	if (!pcm_iface->get_event_session ||
+	    !pcm_iface->set_event_session) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	async_answer_0(callid, EOK);
+
+	ipc_call_t callback_call;
+	ipc_callid_t callback_id = async_get_call(&callback_call);
+	async_sess_t *sess =
+	    async_callback_receive_start(EXCHANGE_ATOMIC, &callback_call);
+	if (sess == NULL) {
+		ddf_msg(LVL_DEBUG, "Failed to create event callback");
+		async_answer_0(callback_id, EAGAIN);
+		return;
+	}
+	const int ret = pcm_iface->set_event_session(fun, sess);
+	if (ret != EOK) {
+		ddf_msg(LVL_DEBUG, "Failed to set event callback.");
+		async_hangup(sess);
+		async_answer_0(callback_id, ret);
+		return;
+	}
+	async_answer_0(callback_id, EOK);
+}
+
+static void remote_audio_pcm_events_unregister(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+	if (!pcm_iface->get_event_session ||
+	    !pcm_iface->set_event_session) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	async_sess_t *sess = pcm_iface->get_event_session(fun);
+	if (sess) {
+		async_hangup(sess);
+		pcm_iface->set_event_session(fun, NULL);
+	}
+	async_answer_0(callid, EOK);
+}
+
+void remote_audio_pcm_get_buffer_pos(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+	size_t pos = 0;
+	const int ret = pcm_iface->get_buffer_pos ?
+	    pcm_iface->get_buffer_pos(fun, &pos) : ENOTSUP;
+	async_answer_1(callid, ret, pos);
+}
+
+void remote_audio_pcm_test_format(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+	unsigned channels = DEV_IPC_GET_ARG1(*call);
+	unsigned rate = DEV_IPC_GET_ARG2(*call);
+	pcm_sample_format_t format = DEV_IPC_GET_ARG3(*call);
+	const int ret = pcm_iface->test_format ?
+	    pcm_iface->test_format(fun, &channels, &rate, &format) : ENOTSUP;
+	async_answer_3(callid, ret, channels, rate, format);
+}
+
+void remote_audio_pcm_get_buffer(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+
+	if (!pcm_iface->get_buffer ||
+	    !pcm_iface->release_buffer) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	void *buffer = NULL;
+	size_t size = DEV_IPC_GET_ARG1(*call);
+	int ret = pcm_iface->get_buffer(fun, &buffer, &size);
+	async_answer_1(callid, ret, size);
+	if (ret != EOK || size == 0)
+		return;
+
+	/* Share the buffer. */
+	size_t share_size = 0;
+	ipc_callid_t share_id = 0;
+
+	ddf_msg(LVL_DEBUG2, "Receiving share request.");
+	if (!async_share_in_receive(&share_id, &share_size)) {
+		ddf_msg(LVL_DEBUG, "Failed to share pcm buffer.");
+		pcm_iface->release_buffer(fun);
+		async_answer_0(share_id, EPARTY);
+		return;
+	}
+
+	ddf_msg(LVL_DEBUG2, "Checking requested share size.");
+	if (share_size != size) {
+		ddf_msg(LVL_DEBUG, "Incorrect pcm buffer size requested.");
+		pcm_iface->release_buffer(fun);
+		async_answer_0(share_id, ELIMIT);
+		return;
+	}
+
+	ddf_msg(LVL_DEBUG2, "Calling share finalize.");
+	ret = async_share_in_finalize(share_id, buffer, AS_AREA_WRITE
+	| AS_AREA_READ);
+	if (ret != EOK) {
+		ddf_msg(LVL_DEBUG, "Failed to share buffer.");
+		pcm_iface->release_buffer(fun);
+		return;
+	}
+
+	ddf_msg(LVL_DEBUG2, "Buffer shared with size %zu.", share_size);
+}
+
+void remote_audio_pcm_release_buffer(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+
+	const int ret = pcm_iface->release_buffer ?
+	    pcm_iface->release_buffer(fun) : 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_iface_t *pcm_iface = iface;
+
+	const unsigned frames = DEV_IPC_GET_ARG1(*call);
+	const unsigned rate = DEV_IPC_GET_ARG2(*call);
+	const unsigned channels = (DEV_IPC_GET_ARG3(*call) >> 16) & UINT8_MAX;
+	const pcm_sample_format_t format = DEV_IPC_GET_ARG3(*call) & UINT16_MAX;
+
+	const int ret = pcm_iface->start_playback
+	    ? pcm_iface->start_playback(fun, frames, channels, rate, format)
+	    : 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_iface_t *pcm_iface = iface;
+	const bool immediate = DEV_IPC_GET_ARG1(*call);
+
+	const int ret = pcm_iface->stop_playback ?
+	    pcm_iface->stop_playback(fun, immediate) : ENOTSUP;
+	async_answer_0(callid, ret);
+}
+
+void remote_audio_pcm_start_capture(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+
+	const unsigned frames = DEV_IPC_GET_ARG1(*call);
+	const unsigned rate = DEV_IPC_GET_ARG2(*call);
+	const unsigned channels = (DEV_IPC_GET_ARG3(*call) >> 16) & UINT16_MAX;
+	const pcm_sample_format_t format = DEV_IPC_GET_ARG3(*call) & UINT16_MAX;
+
+	const int ret = pcm_iface->start_capture
+	    ? pcm_iface->start_capture(fun, frames, channels, rate, format)
+	    : ENOTSUP;
+	async_answer_0(callid, ret);
+}
+
+void remote_audio_pcm_stop_capture(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+	const bool immediate = DEV_IPC_GET_ARG1(*call);
+
+	const int ret = pcm_iface->stop_capture ?
+	    pcm_iface->stop_capture(fun, immediate) : ENOTSUP;
+	async_answer_0(callid, ret);
+}
+
+/**
+ * @}
+ */
+
Index: uspace/lib/drv/generic/remote_hw_res.c
===================================================================
--- uspace/lib/drv/generic/remote_hw_res.c	(revision 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/drv/generic/remote_hw_res.c	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2011 Jan Vesely
  * All rights reserved.
  *
@@ -43,8 +44,14 @@
 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 void remote_hw_res_dma_channel_remain(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,
+	[HW_RES_DMA_CHANNEL_REMAIN] = &remote_hw_res_dma_channel_remain,
 };
 
@@ -94,4 +101,37 @@
 }
 
+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) & 0xffff;
+	const uint8_t  mode = DEV_IPC_GET_ARG1(*call) >> 16;
+	const uint32_t address = DEV_IPC_GET_ARG2(*call);
+	const uint32_t size = DEV_IPC_GET_ARG3(*call);
+
+	const int ret = hw_res_ops->dma_channel_setup(
+	    fun, channel, address, size, mode);
+	async_answer_0(callid, ret);
+}
+
+static void remote_hw_res_dma_channel_remain(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);
+	size_t remain = 0;
+	const int ret = hw_res_ops->dma_channel_remain(fun, channel, &remain);
+	async_answer_1(callid, ret, remain);
+}
 /**
  * @}
Index: uspace/lib/drv/include/audio_mixer_iface.h
===================================================================
--- uspace/lib/drv/include/audio_mixer_iface.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
+++ uspace/lib/drv/include/audio_mixer_iface.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -0,0 +1,63 @@
+/*
+ * 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 <stdbool.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_item_level(async_exch_t *, unsigned, unsigned *);
+int audio_mixer_set_item_level(async_exch_t *, 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_item_level)(ddf_fun_t *, unsigned, unsigned *);
+	int (*set_item_level)(ddf_fun_t *, unsigned, unsigned);
+} audio_mixer_iface_t;
+
+#endif
+/**
+ * @}
+ */
Index: uspace/lib/drv/include/audio_pcm_iface.h
===================================================================
--- uspace/lib/drv/include/audio_pcm_iface.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
+++ uspace/lib/drv/include/audio_pcm_iface.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2012 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_IFACE_H_
+#define LIBDRV_AUDIO_PCM_IFACE_H_
+
+#include <async.h>
+#include <stdbool.h>
+#include <loc.h>
+#include <pcm/sample_format.h>
+
+#include "ddf/driver.h"
+
+typedef enum {
+	/** Device is capable of audio capture */
+	AUDIO_CAP_CAPTURE,
+	/** Device is capable of audio playback */
+	AUDIO_CAP_PLAYBACK,
+	/** Maximum size of device buffer */
+	AUDIO_CAP_MAX_BUFFER,
+	/** Device is capable of providing accurate buffer position info */
+	AUDIO_CAP_BUFFER_POS,
+	/** Device is capable of event based playback or capture */
+	AUDIO_CAP_INTERRUPT,
+	/** Minimal size of playback/record fragment */
+	AUDIO_CAP_INTERRUPT_MIN_FRAMES,
+	/** Maximum size of playback/record fragment */
+	AUDIO_CAP_INTERRUPT_MAX_FRAMES,
+} audio_cap_t;
+
+typedef enum {
+	PCM_EVENT_PLAYBACK_STARTED = IPC_FIRST_USER_METHOD,
+	PCM_EVENT_CAPTURE_STARTED,
+	PCM_EVENT_FRAMES_PLAYED,
+	PCM_EVENT_FRAMES_CAPTURED,
+	PCM_EVENT_PLAYBACK_TERMINATED,
+	PCM_EVENT_CAPTURE_TERMINATED,
+} pcm_event_t;
+
+const char *audio_pcm_cap_str(audio_cap_t);
+const char *audio_pcm_event_str(pcm_event_t);
+
+typedef async_sess_t audio_pcm_sess_t;
+
+audio_pcm_sess_t *audio_pcm_open(const char *);
+audio_pcm_sess_t *audio_pcm_open_default(void);
+audio_pcm_sess_t *audio_pcm_open_service(service_id_t service);
+void audio_pcm_close(audio_pcm_sess_t *);
+
+int audio_pcm_get_info_str(audio_pcm_sess_t *, const char **);
+int audio_pcm_test_format(audio_pcm_sess_t *, unsigned *, unsigned *,
+    pcm_sample_format_t *);
+int audio_pcm_query_cap(audio_pcm_sess_t *, audio_cap_t);
+int audio_pcm_register_event_callback(audio_pcm_sess_t *,
+    async_client_conn_t, void *);
+int audio_pcm_unregister_event_callback(audio_pcm_sess_t *);
+
+int audio_pcm_get_buffer(audio_pcm_sess_t *, void **, size_t *);
+int audio_pcm_get_buffer_pos(audio_pcm_sess_t *, size_t *);
+int audio_pcm_release_buffer(audio_pcm_sess_t *);
+
+int audio_pcm_start_playback_fragment(audio_pcm_sess_t *, unsigned,
+    unsigned, unsigned, pcm_sample_format_t);
+int audio_pcm_last_playback_fragment(audio_pcm_sess_t *);
+
+int audio_pcm_start_playback(audio_pcm_sess_t *,
+    unsigned, unsigned, pcm_sample_format_t);
+int audio_pcm_stop_playback_immediate(audio_pcm_sess_t *);
+int audio_pcm_stop_playback(audio_pcm_sess_t *);
+
+int audio_pcm_start_capture_fragment(audio_pcm_sess_t *, unsigned,
+    unsigned, unsigned, pcm_sample_format_t);
+int audio_pcm_last_capture_fragment(audio_pcm_sess_t *);
+
+int audio_pcm_start_capture(audio_pcm_sess_t *,
+    unsigned, unsigned, pcm_sample_format_t);
+int audio_pcm_stop_capture_immediate(audio_pcm_sess_t *);
+int audio_pcm_stop_capture(audio_pcm_sess_t *);
+
+/** Audio pcm communication interface. */
+typedef struct {
+	int (*get_info_str)(ddf_fun_t *, const char **);
+	int (*test_format)(ddf_fun_t *, unsigned *, unsigned *,
+	    pcm_sample_format_t *);
+	unsigned (*query_cap)(ddf_fun_t *, audio_cap_t);
+	int (*get_buffer_pos)(ddf_fun_t *, size_t *);
+	int (*get_buffer)(ddf_fun_t *, void **, size_t *);
+	int (*release_buffer)(ddf_fun_t *);
+	int (*set_event_session)(ddf_fun_t *, async_sess_t *);
+	async_sess_t * (*get_event_session)(ddf_fun_t *);
+	int (*start_playback)(ddf_fun_t *, unsigned,
+	    unsigned, unsigned, pcm_sample_format_t);
+	int (*stop_playback)(ddf_fun_t *, bool);
+	int (*start_capture)(ddf_fun_t *, unsigned,
+	    unsigned, unsigned, pcm_sample_format_t);
+	int (*stop_capture)(ddf_fun_t *, bool);
+} audio_pcm_iface_t;
+
+#endif
+/**
+ * @}
+ */
Index: uspace/lib/drv/include/ops/hw_res.h
===================================================================
--- uspace/lib/drv/include/ops/hw_res.h	(revision 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/drv/include/ops/hw_res.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -44,5 +44,6 @@
 	hw_resource_list_t *(*get_resource_list)(ddf_fun_t *);
 	bool (*enable_interrupt)(ddf_fun_t *);
-	int (*dma_channel_setup)(ddf_fun_t *, unsigned, uint32_t, uint16_t, uint8_t);
+	int (*dma_channel_setup)(ddf_fun_t *, unsigned, uint32_t, uint32_t, uint8_t);
+	int (*dma_channel_remain)(ddf_fun_t *, unsigned, size_t *);
 } hw_res_ops_t;
 
Index: uspace/lib/drv/include/remote_audio_mixer.h
===================================================================
--- uspace/lib/drv/include/remote_audio_mixer.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
+++ uspace/lib/drv/include/remote_audio_mixer.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -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.h
===================================================================
--- uspace/lib/drv/include/remote_audio_pcm.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
+++ uspace/lib/drv/include/remote_audio_pcm.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -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_iface;
+
+#endif
+
+/**
+ * @}
+ */
+
Index: uspace/lib/hound/Makefile
===================================================================
--- uspace/lib/hound/Makefile	(revision 5856b627dd9852525f88109f58d955b5b633266d)
+++ uspace/lib/hound/Makefile	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2012 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 = ../..
+EXTRA_CFLAGS = -Iinclude/hound -Iinclude -I$(LIBPCM_PREFIX)/include
+LIBRARY = libhound
+
+SOURCES = \
+	src/protocol.c \
+	src/client.c
+include $(USPACE_PREFIX)/Makefile.common
+
Index: uspace/lib/hound/include/hound/client.h
===================================================================
--- uspace/lib/hound/include/hound/client.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
+++ uspace/lib/hound/include/hound/client.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2012 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 libhound
+ * @addtogroup audio
+ * @{
+ */
+/** @file
+ * @brief Audio PCM buffer interface.
+ */
+
+#ifndef LIBHOUND_CLIENT_H_
+#define LIBHOUND_CLIENT_H_
+
+#include <async.h>
+#include <stdbool.h>
+#include <pcm/format.h>
+#include <hound/protocol.h>
+
+#define HOUND_DEFAULT_TARGET "default"
+#define HOUND_ALL_TARGETS "all"
+
+typedef struct hound_context hound_context_t;
+typedef struct hound_stream hound_stream_t;
+
+hound_context_t * hound_context_create_playback(const char *name,
+    pcm_format_t format, size_t bsize);
+hound_context_t * hound_context_create_capture(const char *name,
+    pcm_format_t format, size_t bsize);
+void hound_context_destroy(hound_context_t *hound);
+
+int hound_context_set_main_stream_params(hound_context_t *hound,
+    pcm_format_t format, size_t bsize);
+
+int hound_context_get_available_targets(hound_context_t *hound,
+    const char ***names, size_t *count);
+int hound_context_get_connected_targets(hound_context_t *hound,
+    const char ***names, size_t *count);
+
+int hound_context_connect_target(hound_context_t *hound, const char* target);
+int hound_context_disconnect_target(hound_context_t *hound, const char* target);
+
+hound_stream_t *hound_stream_create(hound_context_t *hound, unsigned flags,
+    pcm_format_t format, size_t bsize);
+void hound_stream_destroy(hound_stream_t *stream);
+
+int hound_stream_write(hound_stream_t *stream, const void *data, size_t size);
+int hound_stream_read(hound_stream_t *stream, void *data, size_t size);
+int hound_stream_drain(hound_stream_t *stream);
+
+int hound_write_main_stream(hound_context_t *hound,
+    const void *data, size_t size);
+int hound_read_main_stream(hound_context_t *hound, void *data, size_t size);
+int hound_write_replace_main_stream(hound_context_t *hound,
+    const void *data, size_t size);
+int hound_write_immediate(hound_context_t *hound,
+    pcm_format_t format, const void *data, size_t size);
+
+#endif
Index: uspace/lib/hound/include/hound/protocol.h
===================================================================
--- uspace/lib/hound/include/hound/protocol.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
+++ uspace/lib/hound/include/hound/protocol.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2013 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 libhound
+ * @addtogroup audio
+ * @{
+ */
+/** @file
+ * @brief Audio PCM buffer interface.
+ */
+
+#ifndef LIBHOUND_PROTOCOL_H_
+#define LIBHOUND_PROTOCOL_H_
+
+#include <async.h>
+#include <errno.h>
+#include <pcm/format.h>
+
+extern const char *HOUND_SERVICE;
+
+typedef enum {
+	HOUND_SINK_APPS = 0x1,
+	HOUND_SINK_DEVS = 0x2,
+	HOUND_SOURCE_APPS = 0x4,
+	HOUND_SOURCE_DEVS = 0x8,
+	HOUND_CONNECTED = 0x10,
+
+	HOUND_STREAM_DRAIN_ON_EXIT = 0x1,
+	HOUND_STREAM_IGNORE_UNDERFLOW = 0x2,
+	HOUND_STREAM_IGNORE_OVERFLOW = 0x4,
+} hound_flags_t;
+
+typedef async_sess_t hound_sess_t;
+typedef intptr_t hound_context_id_t;
+
+/**
+ * Check context id for errors.
+ * @param id Context id
+ * @return Error code.
+ */
+static inline int hound_context_id_err(hound_context_id_t id)
+{
+	return id > 0 ? EOK : (id == 0 ? ENOENT : id);
+}
+
+hound_sess_t *hound_service_connect(const char *service);
+void hound_service_disconnect(hound_sess_t *sess);
+
+hound_context_id_t hound_service_register_context(hound_sess_t *sess,
+    const char *name, bool record);
+int hound_service_unregister_context(hound_sess_t *sess, hound_context_id_t id);
+
+int hound_service_get_list(hound_sess_t *sess, const char ***ids, size_t *count,
+    int flags, const char *connection);
+
+/**
+ * Wrapper for list queries with no connection parameter.
+ * @param[in] sess hound daemon session.
+ * @param[out] ids list of string identifiers
+ * @param[out] count Number of elements in @p ids
+ * @param[in] flags Flags limiting the query.
+ * @return Error code.
+ */
+static inline int hound_service_get_list_all(hound_sess_t *sess,
+    const char ***ids, size_t *count, int flags)
+{
+	return hound_service_get_list(sess, ids, count, flags, NULL);
+}
+
+int hound_service_connect_source_sink(hound_sess_t *sess, const char *source,
+    const char *sink);
+int hound_service_disconnect_source_sink(hound_sess_t *sess, const char *source,
+    const char *sink);
+
+int hound_service_stream_enter(async_exch_t *exch, hound_context_id_t id,
+    int flags, pcm_format_t format, size_t bsize);
+int hound_service_stream_drain(async_exch_t *exch);
+int hound_service_stream_exit(async_exch_t *exch);
+
+int hound_service_stream_write(async_exch_t *exch, const void *data, size_t size);
+int hound_service_stream_read(async_exch_t *exch, void *data, size_t size);
+
+/* Server */
+
+/** Hound server interace structure */
+typedef struct hound_server_iface {
+	/** Create new context */
+	int (*add_context)(void *, hound_context_id_t *, const char *, bool);
+	/** Destroy existing context */
+	int (*rem_context)(void *, hound_context_id_t);
+	/** Query context direction */
+	bool (*is_record_context)(void *, hound_context_id_t);
+	/** Get string identifiers of specified objects */
+	int (*get_list)(void *, const char ***, size_t *, const char *, int);
+	/** Create connection between source and sink */
+	int (*connect)(void *, const char *, const char *);
+	/** Destroy connection between source and sink */
+	int (*disconnect)(void *, const char *, const char *);
+	/** Create new stream tied to the context */
+	int (*add_stream)(void *, hound_context_id_t, int, pcm_format_t, size_t,
+	    void **);
+	/** Destroy existing stream */
+	int (*rem_stream)(void *, void *);
+	/** Block until the stream buffer is empty */
+	int (*drain_stream)(void *);
+	/** Write new data to the stream */
+	int (*stream_data_write)(void *, const void *, size_t);
+	/** Read data from the stream */
+	int (*stream_data_read)(void *, void *, size_t);
+	void *server;
+} hound_server_iface_t;
+
+void hound_service_set_server_iface(const hound_server_iface_t *iface);
+
+void hound_connection_handler(ipc_callid_t iid, ipc_call_t *icall, void *arg);
+
+#endif
+/** @}
+ */
Index: uspace/lib/hound/include/hound/server.h
===================================================================
--- uspace/lib/hound/include/hound/server.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
+++ uspace/lib/hound/include/hound/server.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2012 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 libhound
+ * @addtogroup audio
+ * @{
+ */
+/** @file
+ * @brief Audio PCM buffer interface.
+ */
+
+#ifndef LIBHOUND_SERVER_H_
+#define LIBHOUND_SERVER_H_
+
+#include <async.h>
+#include <loc.h>
+
+typedef void (*dev_change_callback_t)(void);
+typedef int (*device_callback_t)(service_id_t, const char *);
+
+int hound_server_register(const char *name, service_id_t *id);
+void hound_server_unregister(service_id_t id);
+int hound_server_set_device_change_callback(dev_change_callback_t cb);
+int hound_server_devices_iterate(device_callback_t callback);
+
+#endif
Index: uspace/lib/hound/src/client.c
===================================================================
--- uspace/lib/hound/src/client.c	(revision 5856b627dd9852525f88109f58d955b5b633266d)
+++ uspace/lib/hound/src/client.c	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -0,0 +1,496 @@
+/*
+ * Copyright (c) 2012 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 libhound
+ * @addtogroup audio
+ * @{
+ */
+/** @file
+ * Common USB functions.
+ */
+#include <adt/list.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <loc.h>
+#include <str.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <libarch/types.h>
+
+#include "protocol.h"
+#include "client.h"
+
+/** Stream structure */
+typedef struct hound_stream {
+	/** link in context's list */
+	link_t link;
+	/** audio data format fo the stream */
+	pcm_format_t format;
+	/** IPC exchange representing the stream (in STREAM MODE) */
+	async_exch_t *exch;
+	/** parent context */
+	hound_context_t *context;
+	/** Stream flags */
+	int flags;
+} hound_stream_t;
+
+/**
+ * Linked list isntacen helper function.
+ * @param l link
+ * @return hound stream isntance.
+ */
+static inline hound_stream_t * hound_stream_from_link(link_t *l)
+{
+	return l ? list_get_instance(l, hound_stream_t, link) : NULL;
+}
+
+/** Hound client context structure */
+typedef struct hound_context {
+	/** Audio session */
+	hound_sess_t *session;
+	/** context name, reported to the daemon */
+	const char *name;
+	/** True if the instance is record context */
+	bool record;
+	/** List of associated streams */
+	list_t stream_list;
+	/** Main stream helper structure */
+	struct {
+		hound_stream_t *stream;
+		pcm_format_t format;
+		size_t bsize;
+	} main;
+	/** Assigned context id */
+	hound_context_id_t id;
+} hound_context_t;
+
+/**
+ * Alloc and initialize context structure.
+ * @param name Base for the real context name, will add task id.
+ * @param record True if the new context should capture audio data.
+ * @param format PCM format of the main pipe.
+ * @param bsize Server size buffer size of the main stream.
+ * @return valid pointer to initialized structure on success, NULL on failure
+ */
+static hound_context_t *hound_context_create(const char *name, bool record,
+    pcm_format_t format, size_t bsize)
+{
+	hound_context_t *new_context = malloc(sizeof(hound_context_t));
+	if (new_context) {
+		char *cont_name;
+		int ret = asprintf(&cont_name, "%" PRIu64 ":%s",
+		    task_get_id(), name);
+		if (ret < 0) {
+			free(new_context);
+			return NULL;
+		}
+		list_initialize(&new_context->stream_list);
+		new_context->name = cont_name;
+		new_context->record = record;
+		new_context->session = hound_service_connect(HOUND_SERVICE);
+		new_context->main.stream = NULL;
+		new_context->main.format = format;
+		new_context->main.bsize = bsize;
+		if (!new_context->session) {
+			free(new_context->name);
+			free(new_context);
+			return NULL;
+		}
+		new_context->id = hound_service_register_context(
+		    new_context->session, new_context->name, record);
+		if (hound_context_id_err(new_context->id) != EOK) {
+			hound_service_disconnect(new_context->session);
+			free(new_context->name);
+			free(new_context);
+			return NULL;
+		}
+	}
+	return new_context;
+}
+
+/**
+ * Playback context helper function.
+ * @param name Base for the real context name, will add task id.
+ * @param format PCM format of the main pipe.
+ * @param bsize Server size buffer size of the main stream.
+ * @return valid pointer to initialized structure on success, NULL on failure
+ */
+hound_context_t * hound_context_create_playback(const char *name,
+    pcm_format_t format, size_t bsize)
+{
+	return hound_context_create(name, false, format, bsize);
+}
+
+/**
+ * Record context helper function.
+ * @param name Base for the real context name, will add task id.
+ * @param format PCM format of the main pipe.
+ * @param bsize Server size buffer size of the main stream.
+ * @return valid pointer to initialized structure on success, NULL on failure
+ */
+hound_context_t * hound_context_create_capture(const char *name,
+    pcm_format_t format, size_t bsize)
+{
+	return hound_context_create(name, true, format, bsize);
+}
+
+/**
+ * Correctly dispose of the hound context structure.
+ * @param hound context to remove.
+ *
+ * The function will destroy all associated streams first. Pointers
+ * to these structures will become invalid and the function will block
+ * if any of these stream needs to be drained first.
+ */
+void hound_context_destroy(hound_context_t *hound)
+{
+	assert(hound);
+
+	while (!list_empty(&hound->stream_list)) {
+		link_t *first = list_first(&hound->stream_list);
+		hound_stream_t *stream = hound_stream_from_link(first);
+		hound_stream_destroy(stream);
+	}
+
+	hound_service_unregister_context(hound->session, hound->id);
+	hound_service_disconnect(hound->session);
+	free(hound->name);
+	free(hound);
+}
+
+/**
+ * Get a list of possible connection targets.
+ * @param[in] hound Hound context.
+ * @param[out] names list of target string ids.
+ * @param[out] count Number of elements in @p names list
+ * @return Error code.
+ *
+ * The function will return deice sinks or source based on the context type.
+ */
+int hound_context_get_available_targets(hound_context_t *hound,
+    const char ***names, size_t *count)
+{
+	assert(hound);
+	assert(names);
+	assert(count);
+	return hound_service_get_list_all(hound->session, names, count,
+	    hound->record ? HOUND_SOURCE_DEVS : HOUND_SINK_DEVS);
+}
+
+/**
+ * Get a list of targets connected to the context.
+ * @param[in] hound Hound context.
+ * @param[out] names list of target string ids.
+ * @param[out] count Number of elements in @p names list
+ * @return Error code.
+ */
+int hound_context_get_connected_targets(hound_context_t *hound,
+    const char ***names, size_t *count)
+{
+	assert(hound);
+	assert(names);
+	assert(count);
+	return hound_service_get_list(hound->session, names, count,
+	    HOUND_CONNECTED | (hound->record ?
+	        HOUND_SOURCE_DEVS : HOUND_SINK_DEVS), hound->name);
+}
+
+/**
+ * Create a new connection to the target.
+ * @param hound Hound context.
+ * @param target String identifier of the desired target.
+ * @return Error code.
+ *
+ * The function recognizes special 'HOUND_DEFAULT_TARGET' and will
+ * connect to the first possible target if it is passed this value.
+ */
+int hound_context_connect_target(hound_context_t *hound, const char* target)
+{
+	assert(hound);
+	assert(target);
+
+	const char **tgt = NULL;
+	size_t count = 1;
+	int ret = EOK;
+	if (str_cmp(target, HOUND_DEFAULT_TARGET) == 0) {
+		ret = hound_context_get_available_targets(hound, &tgt, &count);
+		if (ret != EOK)
+			return ret;
+		target = tgt[0];
+	}
+	//TODO handle all-targets
+
+	if (hound->record) {
+		ret = hound_service_connect_source_sink(
+		    hound->session, target, hound->name);
+	} else {
+		ret = hound_service_connect_source_sink(
+		    hound->session, hound->name, target);
+	}
+	if (tgt)
+		free(tgt[0]);
+	free(tgt);
+	return ret;
+}
+
+/**
+ * Destroy a connection to the target.
+ * @param hound Hound context.
+ * @param target String identifier of the desired target.
+ * @return Error code.
+ */
+int hound_context_disconnect_target(hound_context_t *hound, const char* target)
+{
+	assert(hound);
+	assert(target);
+	//TODO handle all-targets
+	if (hound->record) {
+		return hound_service_disconnect_source_sink(
+		    hound->session, target, hound->name);
+	} else {
+		return hound_service_disconnect_source_sink(
+		    hound->session, hound->name, target);
+	}
+}
+
+/**
+ * Create a new stream associated with the context.
+ * @param hound Hound context.
+ * @param flags new stream flags.
+ * @param format new stream PCM format.
+ * @param bzise new stream server side buffer size (in bytes)
+ * @return Valid pointer to a stream instance, NULL on failure.
+ */
+hound_stream_t *hound_stream_create(hound_context_t *hound, unsigned flags,
+    pcm_format_t format, size_t bsize)
+{
+	assert(hound);
+	async_exch_t *stream_exch = async_exchange_begin(hound->session);
+	if (!stream_exch)
+		return NULL;
+	hound_stream_t *new_stream = malloc(sizeof(hound_stream_t));
+	if (new_stream) {
+		link_initialize(&new_stream->link);
+		new_stream->exch = stream_exch;
+		new_stream->format = format;
+		new_stream->context = hound;
+		new_stream->flags = flags;
+		const int ret = hound_service_stream_enter(new_stream->exch,
+		    hound->id, flags, format, bsize);
+		if (ret != EOK) {
+			async_exchange_end(new_stream->exch);
+			free(new_stream);
+			return NULL;
+		}
+		list_append(&new_stream->link, &hound->stream_list);
+	}
+	return new_stream;
+}
+
+/**
+ * Destroy existing stream
+ * @param stream The stream to destroy.
+ *
+ * Function will wait until the server side buffer is empty if the
+ * HOUND_STREAM_DRAIN_ON_EXIT flag was set on creation.
+ */
+void hound_stream_destroy(hound_stream_t *stream)
+{
+	if (stream) {
+		if (stream->flags & HOUND_STREAM_DRAIN_ON_EXIT)
+			hound_service_stream_drain(stream->exch);
+		hound_service_stream_exit(stream->exch);
+		async_exchange_end(stream->exch);
+		list_remove(&stream->link);
+		free(stream);
+	}
+}
+
+/**
+ * Send new data to a stream.
+ * @param stream The target stream
+ * @param data data buffer
+ * @param size size of the @p data buffer.
+ * @return error code.
+ */
+int hound_stream_write(hound_stream_t *stream, const void *data, size_t size)
+{
+	assert(stream);
+	if (!data || size == 0)
+		return EBADMEM;
+	return hound_service_stream_write(stream->exch, data, size);
+}
+
+/**
+ * Get data from a stream.
+ * @param stream The target stream.
+ * @param data data buffer.
+ * @param size size of the @p data buffer.
+ * @return error code.
+ */
+int hound_stream_read(hound_stream_t *stream, void *data, size_t size)
+{
+	assert(stream);
+	if (!data || size == 0)
+		return EBADMEM;
+	return hound_service_stream_read(stream->exch, data, size);
+}
+
+/**
+ * Wait until the server side buffer is empty.
+ * @param stream The stream that shoulod be drained.
+ * @return Error code.
+ */
+int hound_stream_drain(hound_stream_t *stream)
+{
+	assert(stream);
+	return hound_service_stream_drain(stream->exch);
+}
+
+/**
+ * Main stream getter function.
+ * @param hound Houndcontext.
+ * @return Valid stream pointer, NULL on failure.
+ *
+ * The function will create new stream, or return a pointer to the exiting one
+ * if it exists.
+ */
+static hound_stream_t * hound_get_main_stream(hound_context_t *hound)
+{
+	assert(hound);
+	if (!hound->main.stream)
+		hound->main.stream = hound_stream_create(hound,
+		    HOUND_STREAM_DRAIN_ON_EXIT,hound->main.format,
+		    hound->main.bsize);
+	return hound->main.stream;
+}
+
+/**
+ * Send new data to the main stream.
+ * @param stream The target stream
+ * @param data data buffer
+ * @param size size of the @p data buffer.
+ * @return error code.
+ */
+int hound_write_main_stream(hound_context_t *hound,
+    const void *data, size_t size)
+{
+	assert(hound);
+	if (hound->record)
+		return EINVAL;
+
+	hound_stream_t *mstream = hound_get_main_stream(hound);
+	if (!mstream)
+		return ENOMEM;
+	return hound_stream_write(mstream, data, size);
+}
+
+/**
+ * Get data from the main stream.
+ * @param stream The target stream
+ * @param data data buffer
+ * @param size size of the @p data buffer.
+ * @return error code.
+ */
+int hound_read_main_stream(hound_context_t *hound, void *data, size_t size)
+{
+	assert(hound);
+	if (!hound->record)
+		return EINVAL;
+	hound_stream_t *mstream = hound_get_main_stream(hound);
+	if (!mstream)
+		return ENOMEM;
+	return hound_stream_read(mstream, data, size);
+}
+
+/**
+ * Destroy the old main stream and replace it with a new one with fresh data.
+ * @param hound Hound context.
+ * @param data data buffer.
+ * @param size size of the @p data buffer.
+ * @return error code.
+ *
+ * NOT IMPLEMENTED
+ */
+int hound_write_replace_main_stream(hound_context_t *hound,
+    const void *data, size_t size)
+{
+	assert(hound);
+	if (!data || size == 0)
+		return EBADMEM;
+	// TODO implement
+	return ENOTSUP;
+}
+
+/**
+ * Destroy the old main stream and replace it with a new one using new params.
+ * @param hound Hound context.
+ * @param channels
+ * @return error code.
+ *
+ * NOT IMPLEMENTED
+ */
+int hound_context_set_main_stream_params(hound_context_t *hound,
+    pcm_format_t format, size_t bsize)
+{
+	assert(hound);
+	// TODO implement
+	return ENOTSUP;
+}
+
+/**
+ * Write data immediately to a new stream, and wait for it to drain.
+ * @param hound Hound context.
+ * @param format pcm data format.
+ * @param data data buffer
+ * @param size @p data buffer size
+ * @return Error code.
+ *
+ * This functnion creates a new stream writes the data, ti waits for the stream
+ * to drain and destroys it before returning.
+ */
+int hound_write_immediate(hound_context_t *hound, pcm_format_t format,
+    const void *data, size_t size)
+{
+	assert(hound);
+	if (hound->record)
+		return EINVAL;
+	hound_stream_t *tmpstream = hound_stream_create(hound, 0, format, size);
+	if (!tmpstream)
+		return ENOMEM;
+	const int ret = hound_stream_write(tmpstream, data, size);
+	if (ret == EOK) {
+		//TODO drain?
+		hound_service_stream_drain(tmpstream->exch);
+	}
+	hound_stream_destroy(tmpstream);
+	return ret;
+}
+/**
+ * @}
+ */
Index: uspace/lib/hound/src/protocol.c
===================================================================
--- uspace/lib/hound/src/protocol.c	(revision 5856b627dd9852525f88109f58d955b5b633266d)
+++ uspace/lib/hound/src/protocol.c	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -0,0 +1,781 @@
+/*
+ * Copyright (c) 2012 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 libhound
+ * @addtogroup audio
+ * @{
+ */
+/** @file
+ * Common USB functions.
+ */
+#include <adt/list.h>
+#include <errno.h>
+#include <loc.h>
+#include <macros.h>
+#include <str.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <libarch/types.h>
+
+#include "protocol.h"
+#include "client.h"
+#include "server.h"
+
+enum ipc_methods {
+	/** Create new context representation on the server side */
+	IPC_M_HOUND_CONTEXT_REGISTER = IPC_FIRST_USER_METHOD,
+	/** Release existing context representation on the server side */
+	IPC_M_HOUND_CONTEXT_UNREGISTER,
+	/** Request list of objects */
+	IPC_M_HOUND_GET_LIST,
+	/** Create new connection */
+	IPC_M_HOUND_CONNECT,
+	/** Destroy connection */
+	IPC_M_HOUND_DISCONNECT,
+	/** Switch IPC pipe to stream mode */
+	IPC_M_HOUND_STREAM_ENTER,
+	/** Switch IPC pipe back to general mode */
+	IPC_M_HOUND_STREAM_EXIT,
+	/** Wait until there is no data in the stream */
+	IPC_M_HOUND_STREAM_DRAIN,
+};
+
+
+/** PCM format conversion helper structure */
+typedef union {
+	struct {
+		uint16_t rate;
+		uint8_t channels;
+		uint8_t format;
+	} f __attribute__((packed));
+	sysarg_t arg;
+} format_convert_t;
+
+
+/****
+ * CLIENT
+ ****/
+
+/** Well defined service name */
+const char *HOUND_SERVICE = "audio/hound";
+
+/**
+ * Start a new audio session.
+ * @param service Named service typically 'HOUND_SERVICE' constant.
+ * @return Valid session on success, NULL on failure.
+ */
+hound_sess_t *hound_service_connect(const char *service)
+{
+	service_id_t id = 0;
+	const int ret =
+	    loc_service_get_id(service, &id, IPC_FLAG_BLOCKING);
+	if (ret != EOK)
+		return NULL;
+	return loc_service_connect(EXCHANGE_PARALLEL, id, IPC_FLAG_BLOCKING);
+}
+
+/**
+ * End an existing audio session.
+ * @param sess The session.
+ */
+void hound_service_disconnect(hound_sess_t *sess)
+{
+	if (sess)
+		async_hangup(sess);
+}
+
+/**
+ * Register a named application context to the audio server.
+ * @param sess Valid audio session.
+ * @param name Valid string identifier
+ * @param record True if the application context wishes to receive data.
+ * @return Valid ID on success, Error code on failure.
+ */
+hound_context_id_t hound_service_register_context(hound_sess_t *sess,
+    const char *name, bool record)
+{
+	assert(sess);
+	assert(name);
+	ipc_call_t call;
+	async_exch_t *exch = async_exchange_begin(sess);
+	aid_t mid =
+	    async_send_1(exch, IPC_M_HOUND_CONTEXT_REGISTER, record, &call);
+	int ret = mid ? EOK : EPARTY;
+
+	if (ret == EOK)
+		ret = async_data_write_start(exch, name, str_size(name));
+	else
+		async_forget(mid);
+
+	if (ret == EOK)
+		async_wait_for(mid, (sysarg_t *)&ret);
+
+	async_exchange_end(exch);
+	return ret == EOK ? (hound_context_id_t)IPC_GET_ARG1(call) : ret;
+}
+
+/**
+ * Remove application context from the server's list.
+ * @param sess Valid audio session.
+ * @param id Valid context id.
+ * @return Error code.
+ */
+int hound_service_unregister_context(hound_sess_t *sess, hound_context_id_t id)
+{
+	assert(sess);
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret =
+	    async_req_1_0(exch, IPC_M_HOUND_CONTEXT_UNREGISTER, id);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Retrieve a list of server side actors.
+ * @param[in] sess Valid audio session.
+ * @param[out] ids list of string identifiers.
+ * @param[out] count Number of elements int the @p ids list.
+ * @param[in] flags list requirements.
+ * @param[in] connection name of target actor. Used only if the list should
+ *            contain connected actors.
+ * @retval Error code.
+ */
+int hound_service_get_list(hound_sess_t *sess, const char ***ids, size_t *count,
+    int flags, const char *connection)
+{
+	assert(sess);
+	assert(ids);
+	assert(count);
+
+	if (connection && !(flags & HOUND_CONNECTED))
+		return EINVAL;
+
+	async_exch_t *exch = async_exchange_begin(sess);
+	if (!exch)
+		return ENOMEM;
+
+	ipc_call_t res_call;
+	aid_t mid = async_send_3(exch, IPC_M_HOUND_GET_LIST, flags, *count,
+	    (bool)connection, &res_call);
+
+	int ret = EOK;
+	if (mid && connection)
+		ret = async_data_write_start(exch, connection,
+		    str_size(connection));
+
+	if (ret == EOK)
+		async_wait_for(mid, (sysarg_t*)&ret);
+
+	if (ret != EOK) {
+		async_exchange_end(exch);
+		return ret;
+	}
+	unsigned name_count = IPC_GET_ARG1(res_call);
+
+	/* Start receiving names */
+	const char **names = NULL;
+	if (name_count) {
+		size_t *sizes = calloc(name_count, sizeof(size_t));
+		names = calloc(name_count, sizeof(char *));
+		if (!names || !sizes)
+			ret = ENOMEM;
+
+		if (ret == EOK)
+			ret = async_data_read_start(exch, sizes,
+			    name_count * sizeof(size_t));
+		for (unsigned i = 0; i < name_count && ret == EOK; ++i) {
+			char *name = malloc(sizes[i] + 1);
+			if (name) {
+				memset(name, 0, sizes[i] + 1);
+				ret = async_data_read_start(exch, name, sizes[i]);
+				names[i] = name;
+			} else {
+				ret = ENOMEM;
+			}
+		}
+		free(sizes);
+	}
+	async_exchange_end(exch);
+	if (ret != EOK) {
+		for (unsigned i = 0; i < name_count; ++i)
+			free(names[i]);
+		free(names);
+	} else {
+		*ids = names;
+		*count = name_count;
+	}
+	return ret;
+}
+
+/**
+ * Create a new connection between a source and a sink.
+ * @param sess Valid audio session.
+ * @param source Source name, valid string.
+ * @param sink Sink name, valid string.
+ * @return Error code.
+ */
+int hound_service_connect_source_sink(hound_sess_t *sess, const char *source,
+    const char *sink)
+{
+	assert(sess);
+	assert(source);
+	assert(sink);
+
+	async_exch_t *exch = async_exchange_begin(sess);
+	if (!exch)
+		return ENOMEM;
+	ipc_call_t call;
+	aid_t id = async_send_0(exch, IPC_M_HOUND_CONNECT, &call);
+	int ret = id ? EOK : EPARTY;
+	if (ret == EOK)
+		ret = async_data_write_start(exch, source, str_size(source));
+	if (ret == EOK)
+		ret = async_data_write_start(exch, sink, str_size(sink));
+	async_wait_for(id, (sysarg_t*)&ret);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Destroy an existing connection between a source and a sink.
+ * @param sess Valid audio session.
+ * @param source Source name, valid string.
+ * @param sink Sink name, valid string.
+ * @return Error code.
+ */
+int hound_service_disconnect_source_sink(hound_sess_t *sess, const char *source,
+    const char *sink)
+{
+	assert(sess);
+	async_exch_t *exch = async_exchange_begin(sess);
+	if (!exch)
+		return ENOMEM;
+	ipc_call_t call;
+	aid_t id = async_send_0(exch, IPC_M_HOUND_DISCONNECT, &call);
+	int ret = id ? EOK : EPARTY;
+	if (ret == EOK)
+		ret = async_data_write_start(exch, source, str_size(source));
+	if (ret == EOK)
+		ret = async_data_write_start(exch, sink, str_size(sink));
+	async_wait_for(id, (sysarg_t*)&ret);
+	async_exchange_end(exch);
+	return ENOTSUP;
+}
+
+/**
+ * Switch IPC exchange to a STREAM mode.
+ * @param exch IPC exchange.
+ * @param id context id this stream should be associated with
+ * @param flags set stream properties
+ * @param format format of the new stream.
+ * @param bsize size of the server side buffer.
+ * @return Error code.
+ */
+int hound_service_stream_enter(async_exch_t *exch, hound_context_id_t id,
+    int flags, pcm_format_t format, size_t bsize)
+{
+	const format_convert_t c = { .f = {
+		.channels = format.channels,
+		.rate = format.sampling_rate / 100,
+		.format = format.sample_format,
+	}};
+	return async_req_4_0(exch, IPC_M_HOUND_STREAM_ENTER, id, flags,
+	    c.arg, bsize);
+}
+
+/**
+ * Destroy existing stream and return IPC exchange to general mode.
+ * @param exch IPC exchange.
+ * @return Error code.
+ */
+int hound_service_stream_exit(async_exch_t *exch)
+{
+	return async_req_0_0(exch, IPC_M_HOUND_STREAM_EXIT);
+}
+
+/**
+ * Wait until the server side buffer is empty.
+ * @param exch IPC exchange.
+ * @return Error code.
+ */
+int hound_service_stream_drain(async_exch_t *exch)
+{
+	return async_req_0_0(exch, IPC_M_HOUND_STREAM_DRAIN);
+}
+
+/**
+ * Write audio data to a stream.
+ * @param exch IPC exchange in STREAM MODE.
+ * @param data Audio data buffer.
+ * @size size of the buffer
+ * @return Error code.
+ */
+int hound_service_stream_write(async_exch_t *exch, const void *data, size_t size)
+{
+	return async_data_write_start(exch, data, size);
+}
+
+/**
+ * Read data from a stream.
+ * @param exch IPC exchange in STREAM MODE.
+ * @param data Audio data buffer.
+ * @size size of the buffer
+ * @return Error code.
+ */
+int hound_service_stream_read(async_exch_t *exch, void *data, size_t size)
+{
+	return async_data_read_start(exch, data, size);
+}
+
+/****
+ * SERVER
+ ****/
+
+static void hound_server_read_data(void *stream);
+static void hound_server_write_data(void *stream);
+static const hound_server_iface_t *server_iface;
+
+/**
+ * Set hound server interface implementation.
+ * @param iface Initialized Hound server interface.
+ */
+void hound_service_set_server_iface(const hound_server_iface_t *iface)
+{
+	server_iface = iface;
+}
+
+/**
+ * Server side implementation of the hound protocol. IPC connection handler.
+ * @param iid initial call id
+ * @param icall pointer to initial call structure.
+ * @param arg (unused)
+ */
+void hound_connection_handler(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	/* Accept connection if there is a valid iface*/
+	if (server_iface) {
+		async_answer_0(iid, EOK);
+	} else {
+		async_answer_0(iid, ENOTSUP);
+		return;
+	}
+
+	while (1) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		switch (IPC_GET_IMETHOD(call)) {
+		case IPC_M_HOUND_CONTEXT_REGISTER: {
+			/* check interface functions */
+			if (!server_iface || !server_iface->add_context) {
+				async_answer_0(callid, ENOTSUP);
+				break;
+			}
+			bool record = IPC_GET_ARG1(call);
+			void *name;
+
+			/* Get context name */
+			int ret =
+			    async_data_write_accept(&name, true, 0, 0, 0, 0);
+			if (ret != EOK) {
+				async_answer_0(callid, ret);
+				break;
+			}
+			hound_context_id_t id = 0;
+			ret = server_iface->add_context(server_iface->server,
+			    &id, name, record);
+			/** new context should create a copy */
+			free(name);
+			if (ret != EOK) {
+				async_answer_0(callid, ret);
+			} else {
+				async_answer_1(callid, EOK, id);
+			}
+			break;
+		}
+		case IPC_M_HOUND_CONTEXT_UNREGISTER: {
+			/* check interface functions */
+			if (!server_iface || !server_iface->rem_context) {
+				async_answer_0(callid, ENOTSUP);
+				break;
+			}
+
+			/* get id, 1st param */
+			hound_context_id_t id = IPC_GET_ARG1(call);
+			const int ret =
+			    server_iface->rem_context(server_iface->server, id);
+			async_answer_0(callid, ret);
+			break;
+		}
+		case IPC_M_HOUND_GET_LIST: {
+			/* check interface functions */
+			if (!server_iface || !server_iface->get_list) {
+				async_answer_0(callid, ENOTSUP);
+				break;
+			}
+
+			const char **list = NULL;
+			const int flags = IPC_GET_ARG1(call);
+			size_t count = IPC_GET_ARG2(call);
+			const bool conn = IPC_GET_ARG3(call);
+			char *conn_name = NULL;
+			int ret = EOK;
+
+			/* get connected actor name if provided */
+			if (conn)
+				ret = async_data_write_accept(
+				    (void**)&conn_name, true, 0, 0, 0, 0);
+
+			if (ret == EOK)
+				ret = server_iface->get_list(
+				    server_iface->server, &list, &count,
+				    conn_name, flags);
+			free(conn_name);
+
+			/* Alloc string sizes array */
+			size_t *sizes = NULL;
+			if (count)
+				sizes = calloc(count, sizeof(size_t));
+			if (count && !sizes)
+				ret = ENOMEM;
+			async_answer_1(callid, ret, count);
+
+			/* We are done */
+			if (count == 0 || ret != EOK)
+				break;
+
+			/* Prepare sizes table */
+			for (unsigned i = 0; i < count; ++i)
+				sizes[i] = str_size(list[i]);
+
+			/* Send sizes table */
+			ipc_callid_t id;
+			if (async_data_read_receive(&id, NULL)) {
+				ret = async_data_read_finalize(id, sizes,
+				    count * sizeof(size_t));
+			}
+			free(sizes);
+
+			/* Proceed to send names */
+			for (unsigned i = 0; i < count; ++i) {
+				size_t size = str_size(list[i]);
+				ipc_callid_t id;
+				if (ret == EOK &&
+				    async_data_read_receive(&id, NULL)) {
+					ret = async_data_read_finalize(id,
+					    list[i], size);
+				}
+				free(list[i]);
+			}
+			free(list);
+			break;
+		}
+		case IPC_M_HOUND_CONNECT: {
+			/* check interface functions */
+			if (!server_iface || !server_iface->connect) {
+				async_answer_0(callid, ENOTSUP);
+				break;
+			}
+
+			void *source = NULL;
+			void *sink = NULL;
+
+			/* read source name */
+			int ret =
+			    async_data_write_accept(&source, true, 0, 0, 0, 0);
+			/* read sink name */
+			if (ret == EOK)
+				ret = async_data_write_accept(&sink,
+				    true, 0, 0, 0, 0);
+
+			if (ret == EOK)
+				ret = server_iface->connect(
+				    server_iface->server, source, sink);
+			free(source);
+			free(sink);
+			async_answer_0(callid, ret);
+			break;
+		}
+		case IPC_M_HOUND_DISCONNECT: {
+			/* check interface functions */
+			if (!server_iface || !server_iface->disconnect) {
+				async_answer_0(callid, ENOTSUP);
+				break;
+			}
+
+			void *source = NULL;
+			void *sink = NULL;
+
+			/* read source name */
+			int ret =
+			    async_data_write_accept(&source, true, 0, 0, 0, 0);
+			/*read sink name */
+			if (ret == EOK)
+				ret = async_data_write_accept(&sink,
+				    true, 0, 0, 0, 0);
+			if (ret == EOK)
+				ret = server_iface->connect(
+				    server_iface->server, source, sink);
+			free(source);
+			free(sink);
+			async_answer_0(callid, ret);
+			break;
+		}
+		case IPC_M_HOUND_STREAM_ENTER: {
+			/* check interface functions */
+			if (!server_iface || !server_iface->is_record_context
+			    || !server_iface->add_stream
+			    || !server_iface->rem_stream) {
+				async_answer_0(callid, ENOTSUP);
+				break;
+			}
+
+			/* get parameters */
+			hound_context_id_t id = IPC_GET_ARG1(call);
+			const int flags = IPC_GET_ARG2(call);
+			const format_convert_t c = {.arg = IPC_GET_ARG3(call)};
+			const pcm_format_t f = {
+			    .sampling_rate = c.f.rate * 100,
+			    .channels = c.f.channels,
+			    .sample_format = c.f.format,
+			};
+			size_t bsize = IPC_GET_ARG4(call);
+
+			void *stream;
+			int ret = server_iface->add_stream(server_iface->server,
+			    id, flags, f, bsize, &stream);
+			if (ret != EOK) {
+				async_answer_0(callid, ret);
+				break;
+			}
+			const bool rec = server_iface->is_record_context(
+			    server_iface->server, id);
+			if (rec) {
+				if(server_iface->stream_data_read) {
+					async_answer_0(callid, EOK);
+					/* start answering read calls */
+					hound_server_write_data(stream);
+					server_iface->rem_stream(
+					    server_iface->server, stream);
+				} else {
+					async_answer_0(callid, ENOTSUP);
+				}
+			} else {
+				if (server_iface->stream_data_write) {
+					async_answer_0(callid, EOK);
+					/* accept write calls */
+					hound_server_read_data(stream);
+					server_iface->rem_stream(
+					    server_iface->server, stream);
+				} else {
+					async_answer_0(callid, ENOTSUP);
+				}
+			}
+			break;
+		}
+		case IPC_M_HOUND_STREAM_EXIT:
+		case IPC_M_HOUND_STREAM_DRAIN:
+			/* Stream exit/drain is only allowed in stream context*/
+			async_answer_0(callid, EINVAL);
+			break;
+		default:
+			async_answer_0(callid, ENOTSUP);
+			return;
+		}
+	}
+}
+
+/**
+ * Read data and push it to the stream.
+ * @param stream target stream, will push data there.
+ */
+static void hound_server_read_data(void *stream)
+{
+	ipc_callid_t callid;
+	ipc_call_t call;
+	size_t size = 0;
+	int ret_answer = EOK;
+	/* accept data write or drain */
+	while (async_data_write_receive_call(&callid, &call, &size)
+	    || (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN)) {
+		/* check drain first */
+		if (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN) {
+			int ret = ENOTSUP;
+			if (server_iface->drain_stream)
+				ret = server_iface->drain_stream(stream);
+			async_answer_0(callid, ret);
+			continue;
+		}
+
+		/* there was an error last time */
+		if (ret_answer != EOK) {
+			async_answer_0(callid, ret_answer);
+			continue;
+		}
+
+		char *buffer = malloc(size);
+		if (!buffer) {
+			async_answer_0(callid, ENOMEM);
+			continue;
+		}
+		const int ret = async_data_write_finalize(callid, buffer, size);
+		if (ret == EOK) {
+			/* push data to stream */
+			ret_answer = server_iface->stream_data_write(
+			    stream, buffer, size);
+		}
+	}
+	const int ret = IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_EXIT
+	    ? EOK : EINVAL;
+
+	async_answer_0(callid, ret);
+}
+
+/**
+ * Accept reads and pull data from the stream.
+ * @param stream target stream, will pull data from there.
+ */
+static void hound_server_write_data(void *stream)
+{
+
+	ipc_callid_t callid;
+	ipc_call_t call;
+	size_t size = 0;
+	int ret_answer = EOK;
+	/* accept data read and drain */
+	while (async_data_read_receive_call(&callid, &call, &size)
+	    || (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN)) {
+		/* drain does not make much sense but it is allowed */
+		if (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN) {
+			int ret = ENOTSUP;
+			if (server_iface->drain_stream)
+				ret = server_iface->drain_stream(stream);
+			async_answer_0(callid, ret);
+			continue;
+		}
+		/* there was an error last time */
+		if (ret_answer != EOK) {
+			async_answer_0(callid, ret_answer);
+			continue;
+		}
+		char *buffer = malloc(size);
+		if (!buffer) {
+			async_answer_0(callid, ENOMEM);
+			continue;
+		}
+		int ret = server_iface->stream_data_read(stream, buffer, size);
+		if (ret == EOK) {
+			ret_answer =
+			    async_data_read_finalize(callid, buffer, size);
+		}
+	}
+	const int ret = IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_EXIT
+	    ? EOK : EINVAL;
+
+	async_answer_0(callid, ret);
+}
+
+
+/***
+ * SERVER SIDE
+ ***/
+
+/**
+ * Register new hound service to the location service.
+ * @param[in] name server name
+ * @param[out] id assigned service id.
+ * @return Error code.
+ */
+int hound_server_register(const char *name, service_id_t *id)
+{
+	if (!name || !id)
+		return EINVAL;
+
+	int ret = loc_server_register(name);
+	if (ret != EOK)
+		return ret;
+
+	return loc_service_register(HOUND_SERVICE, id);
+}
+
+/**
+ * Unregister server from the location service.
+ * @param id previously assigned service id.
+ */
+void hound_server_unregister(service_id_t id)
+{
+	loc_service_unregister(id);
+}
+
+/**
+ * Set callback on device category change event.
+ * @param cb Callback function.
+ * @return Error code.
+ */
+int hound_server_set_device_change_callback(dev_change_callback_t cb)
+{
+	return loc_register_cat_change_cb(cb);
+}
+
+/**
+ * Walk through all device in the audio-pcm category.
+ * @param callback Function to call on every device.
+ * @return Error code.
+ */
+int hound_server_devices_iterate(device_callback_t callback)
+{
+	if (!callback)
+		return EINVAL;
+	static bool resolved = false;
+	static category_id_t cat_id = 0;
+
+	if (!resolved) {
+		const int ret = loc_category_get_id("audio-pcm", &cat_id,
+		    IPC_FLAG_BLOCKING);
+		if (ret != EOK)
+			return ret;
+		resolved = true;
+	}
+
+	service_id_t *svcs = NULL;
+	size_t count = 0;
+	const int ret = loc_category_get_svcs(cat_id, &svcs, &count);
+	if (ret != EOK)
+		return ret;
+
+	for (unsigned i = 0; i < count; ++i) {
+		char *name = NULL;
+		loc_service_get_name(svcs[i], &name);
+		callback(svcs[i], name);
+		free(name);
+	}
+	free(svcs);
+	return EOK;
+}
+/**
+ * @}
+ */
Index: uspace/lib/pcm/Makefile
===================================================================
--- uspace/lib/pcm/Makefile	(revision 5856b627dd9852525f88109f58d955b5b633266d)
+++ uspace/lib/pcm/Makefile	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2012 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 = ../..
+EXTRA_CFLAGS = -Iinclude/pcm -Iinclude
+LIBRARY = libpcm
+
+SOURCES = \
+	src/format.c
+include $(USPACE_PREFIX)/Makefile.common
+
+
Index: uspace/lib/pcm/include/pcm/format.h
===================================================================
--- uspace/lib/pcm/include/pcm/format.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
+++ uspace/lib/pcm/include/pcm/format.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2012 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 audio
+ * @brief HelenOS sound server
+ * @{
+ */
+/** @file
+ */
+
+#ifndef PCM_FORMAT_H_
+#define PCM_FORMAT_H_
+
+#include <assert.h>
+#include <stdbool.h>
+#include <pcm/sample_format.h>
+
+/** Linear PCM audio parameters */
+typedef struct {
+	unsigned channels;
+	unsigned sampling_rate;
+	pcm_sample_format_t sample_format;
+} pcm_format_t;
+
+extern const pcm_format_t AUDIO_FORMAT_DEFAULT;
+extern const pcm_format_t AUDIO_FORMAT_ANY;
+
+/**
+ * Frame size helper function.
+ * @param a pointer to a PCM format structure.
+ * @return Size in bytes.
+ */
+static inline size_t pcm_format_frame_size(const pcm_format_t *a)
+{
+	return pcm_sample_format_frame_size(a->channels, a->sample_format);
+}
+
+/**
+ * Convert byte size to frame count.
+ * @param size Byte-size.
+ * @param a pointer to a PCM format structure.
+ * @return Frame count.
+ */
+static inline size_t pcm_format_size_to_frames(size_t size,
+    const pcm_format_t *a)
+{
+	return pcm_sample_format_size_to_frames(size, a->channels,
+	    a->sample_format);
+}
+
+/**
+ * Convert byte size to audio playback time.
+ * @param size Byte-size.
+ * @param a pointer to a PCM format structure.
+ * @return Number of microseconds.
+ */
+static inline useconds_t pcm_format_size_to_usec(size_t size,
+    const pcm_format_t *a)
+{
+	return pcm_sample_format_size_to_usec(size, a->sampling_rate,
+	    a->channels, a->sample_format);
+}
+
+bool pcm_format_same(const pcm_format_t *a, const pcm_format_t* b);
+
+/**
+ * Helper function, compares with ANY metaformat.
+ * @param f pointer to format structure.
+ * @return True if @p f points to ANY format, false otherwise.
+ */
+static inline bool pcm_format_is_any(const pcm_format_t *f)
+{
+	return pcm_format_same(f, &AUDIO_FORMAT_ANY);
+}
+void pcm_format_silence(void *dst, size_t size, const pcm_format_t *f);
+int pcm_format_convert_and_mix(void *dst, size_t dst_size, const void *src,
+    size_t src_size, const pcm_format_t *sf, const pcm_format_t *df);
+int pcm_format_mix(void *dst, const void *src, size_t size, const pcm_format_t *f);
+int pcm_format_convert(pcm_format_t a, void* srca, size_t sizea,
+    pcm_format_t b, void* srcb, size_t *sizeb);
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/lib/pcm/include/pcm/sample_format.h
===================================================================
--- uspace/lib/pcm/include/pcm/sample_format.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
+++ uspace/lib/pcm/include/pcm/sample_format.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2012 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 audio
+ * @brief PCM sample format
+ * @{
+ */
+/** @file
+ */
+
+#ifndef PCM_SAMPLE_FORMAT_H_
+#define PCM_SAMPLE_FORMAT_H_
+
+#include <stdbool.h>
+#include <time.h>
+
+/** Known and supported PCM sample formats */
+typedef enum {
+	PCM_SAMPLE_UINT8,
+	PCM_SAMPLE_SINT8,
+	PCM_SAMPLE_UINT16_LE,
+	PCM_SAMPLE_UINT16_BE,
+	PCM_SAMPLE_SINT16_LE,
+	PCM_SAMPLE_SINT16_BE,
+	PCM_SAMPLE_UINT24_LE,
+	PCM_SAMPLE_UINT24_BE,
+	PCM_SAMPLE_SINT24_LE,
+	PCM_SAMPLE_SINT24_BE,
+	PCM_SAMPLE_UINT24_32_LE,
+	PCM_SAMPLE_UINT24_32_BE,
+	PCM_SAMPLE_SINT24_32_LE,
+	PCM_SAMPLE_SINT24_32_BE,
+	PCM_SAMPLE_UINT32_LE,
+	PCM_SAMPLE_UINT32_BE,
+	PCM_SAMPLE_SINT32_LE,
+	PCM_SAMPLE_SINT32_BE,
+	PCM_SAMPLE_FLOAT32,
+	PCM_SAMPLE_FORMAT_LAST = PCM_SAMPLE_FLOAT32,
+} pcm_sample_format_t;
+
+/**
+ * Query if the format uses signed values.
+ * @param format PCM sample format.
+ * @return True if the format uses signed values, false otherwise.
+ */
+static inline bool pcm_sample_format_is_signed(pcm_sample_format_t format)
+{
+	switch(format) {
+	case PCM_SAMPLE_SINT8:
+	case PCM_SAMPLE_SINT16_LE:
+	case PCM_SAMPLE_SINT16_BE:
+	case PCM_SAMPLE_SINT24_LE:
+	case PCM_SAMPLE_SINT24_BE:
+	case PCM_SAMPLE_SINT24_32_LE:
+	case PCM_SAMPLE_SINT24_32_BE:
+	case PCM_SAMPLE_SINT32_LE:
+	case PCM_SAMPLE_SINT32_BE:
+		return true;
+	case PCM_SAMPLE_UINT8:
+	case PCM_SAMPLE_UINT16_LE:
+	case PCM_SAMPLE_UINT16_BE:
+	case PCM_SAMPLE_UINT24_LE:
+	case PCM_SAMPLE_UINT24_BE:
+	case PCM_SAMPLE_UINT24_32_LE:
+	case PCM_SAMPLE_UINT24_32_BE:
+	case PCM_SAMPLE_UINT32_LE:
+	case PCM_SAMPLE_UINT32_BE:
+	case PCM_SAMPLE_FLOAT32:
+	default:
+		return false;
+	}
+}
+
+/**
+ * Query byte-size of samples.
+ * @param format PCM sample format.
+ * @return Size in bytes of a single sample.
+ */
+static inline size_t pcm_sample_format_size(pcm_sample_format_t format)
+{
+	switch(format) {
+	case PCM_SAMPLE_UINT8:
+	case PCM_SAMPLE_SINT8:
+		return 1;
+	case PCM_SAMPLE_UINT16_LE:
+	case PCM_SAMPLE_UINT16_BE:
+	case PCM_SAMPLE_SINT16_LE:
+	case PCM_SAMPLE_SINT16_BE:
+		return 2;
+	case PCM_SAMPLE_UINT24_LE:
+	case PCM_SAMPLE_UINT24_BE:
+	case PCM_SAMPLE_SINT24_LE:
+	case PCM_SAMPLE_SINT24_BE:
+		return 3;
+	case PCM_SAMPLE_UINT24_32_LE:
+	case PCM_SAMPLE_UINT24_32_BE:
+	case PCM_SAMPLE_SINT24_32_LE:
+	case PCM_SAMPLE_SINT24_32_BE:
+	case PCM_SAMPLE_UINT32_LE:
+	case PCM_SAMPLE_UINT32_BE:
+	case PCM_SAMPLE_SINT32_LE:
+	case PCM_SAMPLE_SINT32_BE:
+	case PCM_SAMPLE_FLOAT32:
+		return 4;
+	default:
+		return 0;
+	}
+}
+
+/**
+ * Query sie of the entire frame.
+ * @param channels Number of samples in every frame.
+ * @param format PCM sample format.
+ * @return Size in bytes.
+ */
+static inline size_t pcm_sample_format_frame_size(unsigned channels,
+    pcm_sample_format_t format)
+{
+	return pcm_sample_format_size(format) * channels;
+}
+
+/**
+ * Count number of frames that fit into a buffer (even incomplete frames).
+ * @param size Size of the buffer.
+ * @param channels Number of samples in every frame.
+ * @param format PCM sample format.
+ * @return Number of frames (even incomplete).
+ */
+static inline size_t pcm_sample_format_size_to_frames(size_t size,
+    unsigned channels, pcm_sample_format_t format)
+{
+	const size_t frame_size = pcm_sample_format_frame_size(channels, format);
+	return (size + frame_size - 1) / frame_size;
+}
+
+/**
+ * Convert byte size to time.
+ * @param size Size of the buffer.
+ * @param sample_rate Samples per second.
+ * @param channels Number of samples in every frame.
+ * @param format PCM sample format.
+ * @return Number of useconds of audio data.
+ */
+static inline useconds_t pcm_sample_format_size_to_usec(size_t size,
+    unsigned sample_rate, unsigned channels, pcm_sample_format_t format)
+{
+	const unsigned long long frames =
+	    pcm_sample_format_size_to_frames(size, channels, format);
+	return (frames * 1000000ULL) / sample_rate;
+}
+
+/**
+ * Get readable name of a sample format.
+ * @param format PCM sample format.
+ * @return Valid string representation.
+ */
+static inline const char * pcm_sample_format_str(pcm_sample_format_t format)
+{
+	switch(format) {
+	case PCM_SAMPLE_UINT8:
+		return "8 bit unsinged";
+	case PCM_SAMPLE_SINT8:
+		return "8 bit singed";
+	case PCM_SAMPLE_UINT16_LE:
+		return "16 bit unsigned(LE)";
+	case PCM_SAMPLE_SINT16_LE:
+		return "16 bit singed(LE)";
+	case PCM_SAMPLE_UINT16_BE:
+		return "16 bit unsigned(BE)";
+	case PCM_SAMPLE_SINT16_BE:
+		return "16 bit signed(BE)";
+	case PCM_SAMPLE_UINT24_LE:
+		return "24 bit unsigned(LE)";
+	case PCM_SAMPLE_SINT24_LE:
+		return "24 bit signed(LE)";
+	case PCM_SAMPLE_UINT24_BE:
+		return "24 bit unsigned(BE)";
+	case PCM_SAMPLE_SINT24_BE:
+		return "24 bit signed(BE)";
+	case PCM_SAMPLE_UINT24_32_LE:
+		return "24 bit(4byte aligned) unsigned(LE)";
+	case PCM_SAMPLE_UINT24_32_BE:
+		return "24 bit(4byte aligned) unsigned(BE)";
+	case PCM_SAMPLE_SINT24_32_LE:
+		return "24 bit(4byte aligned) signed(LE)";
+	case PCM_SAMPLE_SINT24_32_BE:
+		return "24 bit(4byte aligned) signed(BE)";
+	case PCM_SAMPLE_UINT32_LE:
+		return "32 bit unsigned(LE)";
+	case PCM_SAMPLE_UINT32_BE:
+		return "32 bit unsigned(BE)";
+	case PCM_SAMPLE_SINT32_LE:
+		return "32 bit signed(LE)";
+	case PCM_SAMPLE_SINT32_BE:
+		return "32 bit signed(BE)";
+	case PCM_SAMPLE_FLOAT32:
+		return "32 bit float";
+	default:
+		return "Unknown sample format";
+	}
+}
+
+#endif
+
+/**
+ * @}
+ */
+
Index: uspace/lib/pcm/src/format.c
===================================================================
--- uspace/lib/pcm/src/format.c	(revision 5856b627dd9852525f88109f58d955b5b633266d)
+++ uspace/lib/pcm/src/format.c	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 2012 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 audio
+ * @brief HelenOS sound server
+ * @{
+ */
+/** @file
+ */
+
+#include <assert.h>
+#include <byteorder.h>
+#include <errno.h>
+#include <macros.h>
+#include <stdio.h>
+
+#include "format.h"
+
+#define uint8_t_le2host(x) (x)
+#define host2uint8_t_le(x) (x)
+#define uint8_t_be2host(x) (x)
+#define host2uint8_t_be(x) (x)
+
+#define int8_t_le2host(x) (x)
+#define host2int8_t_le(x) (x)
+
+#define int16_t_le2host(x) uint16_t_le2host(x)
+#define host2int16_t_le(x) host2uint16_t_le(x)
+
+#define int32_t_le2host(x) uint32_t_le2host(x)
+#define host2int32_t_le(x) host2uint32_t_le(x)
+
+#define int8_t_be2host(x) (x)
+#define host2int8_t_be(x) (x)
+
+#define int16_t_be2host(x) uint16_t_be2host(x)
+#define host2int16_t_be(x) host2uint16_t_be(x)
+
+#define int32_t_be2host(x) uint32_t_be2host(x)
+#define host2int32_t_be(x) host2uint32_t_be(x)
+
+// TODO float endian?
+#define float_le2host(x) (x)
+#define float_be2host(x) (x)
+
+#define host2float_le(x) (x)
+#define host2float_be(x) (x)
+
+#define from(x, type, endian) (float)(type ## _ ## endian ## 2host(x))
+#define to(x, type, endian) (float)(host2 ## type ## _ ## endian(x))
+
+/** Default linear PCM format */
+const pcm_format_t AUDIO_FORMAT_DEFAULT = {
+	.channels = 2,
+	.sampling_rate = 44100,
+	.sample_format = PCM_SAMPLE_SINT16_LE,
+	};
+
+/** Special ANY PCM format.
+ * This format is used if the real format is no know or important.
+ */
+const pcm_format_t AUDIO_FORMAT_ANY = {
+	.channels = 0,
+	.sampling_rate = 0,
+	.sample_format = 0,
+	};
+
+static float get_normalized_sample(const void *buffer, size_t size,
+    unsigned frame, unsigned channel, const pcm_format_t *f);
+
+/**
+ * Compare PCM format attribtues.
+ * @param a Format description.
+ * @param b Format description.
+ * @return True if a and b describe the same format, false otherwise.
+ */
+bool pcm_format_same(const pcm_format_t *a, const pcm_format_t* b)
+{
+	assert(a);
+	assert(b);
+	return
+	    a->sampling_rate == b->sampling_rate &&
+	    a->channels == b->channels &&
+	    a->sample_format == b->sample_format;
+}
+
+/**
+ * Fill audio buffer with silence in the specified format.
+ * @param dst Destination audio buffer.
+ * @param size Size of the destination audio buffer.
+ * @param f Pointer to the format description.
+ */
+void pcm_format_silence(void *dst, size_t size, const pcm_format_t *f)
+{
+#define SET_NULL(type, endian, nullv) \
+do { \
+	type *buffer = dst; \
+	const size_t sample_count = size / sizeof(type); \
+	for (unsigned i = 0; i < sample_count; ++i) { \
+		buffer[i] = to((type)nullv, type, endian); \
+	} \
+} while (0)
+
+	switch (f->sample_format) {
+	case PCM_SAMPLE_UINT8:
+		SET_NULL(uint8_t, le, INT8_MIN); break;
+	case PCM_SAMPLE_SINT8:
+		SET_NULL(int8_t, le, 0); break;
+	case PCM_SAMPLE_UINT16_LE:
+		SET_NULL(uint16_t, le, INT16_MIN); break;
+	case PCM_SAMPLE_SINT16_LE:
+		SET_NULL(int16_t, le, 0); break;
+	case PCM_SAMPLE_UINT16_BE:
+		SET_NULL(uint16_t, be, INT16_MIN); break;
+	case PCM_SAMPLE_SINT16_BE:
+		SET_NULL(int16_t, be, 0); break;
+	case PCM_SAMPLE_UINT32_LE:
+		SET_NULL(uint32_t, le, INT32_MIN); break;
+	case PCM_SAMPLE_SINT32_LE:
+		SET_NULL(int32_t, le, 0); break;
+	case PCM_SAMPLE_UINT32_BE:
+		SET_NULL(uint32_t, be, INT32_MIN); break;
+	case PCM_SAMPLE_SINT32_BE:
+		SET_NULL(int32_t, le, 0); break;
+	case PCM_SAMPLE_UINT24_32_LE:
+	case PCM_SAMPLE_SINT24_32_LE:
+	case PCM_SAMPLE_UINT24_32_BE:
+	case PCM_SAMPLE_SINT24_32_BE:
+	case PCM_SAMPLE_UINT24_LE:
+	case PCM_SAMPLE_SINT24_LE:
+	case PCM_SAMPLE_UINT24_BE:
+	case PCM_SAMPLE_SINT24_BE:
+	case PCM_SAMPLE_FLOAT32:
+	default: ;
+	}
+#undef SET_NULL
+}
+
+/**
+ * Mix audio data of the same format and size.
+ * @param dst Destination buffer
+ * @param src Source buffer
+ * @param size Size of both the destination and the source buffer
+ * @param f Pointer to the format descriptor.
+ * @return Error code.
+ */
+int pcm_format_mix(void *dst, const void *src, size_t size, const pcm_format_t *f)
+{
+	return pcm_format_convert_and_mix(dst, size, src, size, f, f);
+}
+
+/**
+ * Add and mix audio data.
+ * @param dst Destination audio buffer
+ * @param dst_size Size of the destination buffer
+ * @param src Source audio buffer
+ * @param src_size Size of the source buffer.
+ * @param sf Pointer to the source format descriptor.
+ * @param df Pointer to the destination format descriptor.
+ * @return Error code.
+ *
+ * Buffers must contain entire frames. Destination buffer is always filled.
+ * If there are not enough data in the source buffer silent data is assumed.
+ */
+int pcm_format_convert_and_mix(void *dst, size_t dst_size, const void *src,
+    size_t src_size, const pcm_format_t *sf, const pcm_format_t *df)
+{
+	if (!dst || !src || !sf || !df)
+		return EINVAL;
+	const size_t src_frame_size = pcm_format_frame_size(sf);
+	if ((src_size % src_frame_size) != 0)
+		return EINVAL;
+
+	const size_t dst_frame_size = pcm_format_frame_size(df);
+	if ((dst_size % dst_frame_size) != 0)
+		return EINVAL;
+
+	/* This is so ugly it eats kittens, and puppies, and ducklings,
+	 * and all little fluffy things...
+	 */
+#define LOOP_ADD(type, endian, low, high) \
+do { \
+	const unsigned frame_count = dst_size / dst_frame_size; \
+	for (size_t i = 0; i < frame_count; ++i) { \
+		for (unsigned j = 0; j < df->channels; ++j) { \
+			const float a = \
+			    get_normalized_sample(dst, dst_size, i, j, df);\
+			const float b = \
+			    get_normalized_sample(src, src_size, i, j, sf);\
+			float c = (a + b); \
+			if (c < -1.0) c = -1.0; \
+			if (c > 1.0) c = 1.0; \
+			c += 1.0; \
+			c *= ((float)(type)high - (float)(type)low) / 2; \
+			c += (float)(type)low; \
+			type *dst_buf = dst; \
+			const unsigned pos = i * df->channels  + j; \
+			if (pos < (dst_size / sizeof(type))) \
+				dst_buf[pos] = to((type)c, type, endian); \
+		} \
+	} \
+} while (0)
+
+	switch (df->sample_format) {
+	case PCM_SAMPLE_UINT8:
+		LOOP_ADD(uint8_t, le, UINT8_MIN, UINT8_MAX); break;
+	case PCM_SAMPLE_SINT8:
+		LOOP_ADD(uint8_t, le, INT8_MIN, INT8_MAX); break;
+	case PCM_SAMPLE_UINT16_LE:
+		LOOP_ADD(uint16_t, le, UINT16_MIN, UINT16_MAX); break;
+	case PCM_SAMPLE_SINT16_LE:
+		LOOP_ADD(int16_t, le, INT16_MIN, INT16_MAX); break;
+	case PCM_SAMPLE_UINT16_BE:
+		LOOP_ADD(uint16_t, be, UINT16_MIN, UINT16_MAX); break;
+	case PCM_SAMPLE_SINT16_BE:
+		LOOP_ADD(int16_t, be, INT16_MIN, INT16_MAX); break;
+	case PCM_SAMPLE_UINT24_32_LE:
+	case PCM_SAMPLE_UINT32_LE: // TODO this are not right for 24bit
+		LOOP_ADD(uint32_t, le, UINT32_MIN, UINT32_MAX); break;
+	case PCM_SAMPLE_SINT24_32_LE:
+	case PCM_SAMPLE_SINT32_LE:
+		LOOP_ADD(int32_t, le, INT32_MIN, INT32_MAX); break;
+	case PCM_SAMPLE_UINT24_32_BE:
+	case PCM_SAMPLE_UINT32_BE:
+		LOOP_ADD(uint32_t, be, UINT32_MIN, UINT32_MAX); break;
+	case PCM_SAMPLE_SINT24_32_BE:
+	case PCM_SAMPLE_SINT32_BE:
+		LOOP_ADD(int32_t, be, INT32_MIN, INT32_MAX); break;
+	case PCM_SAMPLE_UINT24_LE:
+	case PCM_SAMPLE_SINT24_LE:
+	case PCM_SAMPLE_UINT24_BE:
+	case PCM_SAMPLE_SINT24_BE:
+	case PCM_SAMPLE_FLOAT32:
+	default:
+		return ENOTSUP;
+	}
+	return EOK;
+#undef LOOP_ADD
+}
+
+/**
+ * Converts all sample formats to float <-1,1>
+ * @param buffer Audio data
+ * @param size Size of the buffer
+ * @param frame Index of the frame to read
+ * @param channel Channel within the frame
+ * @param f Pointer to a format descriptor
+ * @return Normalized sample <-1,1>, 0.0 if the data could not be read
+ */
+static float get_normalized_sample(const void *buffer, size_t size,
+    unsigned frame, unsigned channel, const pcm_format_t *f)
+{
+	assert(f);
+	assert(buffer);
+	if (channel >= f->channels)
+		return 0.0f;
+#define GET(type, endian, low, high) \
+do { \
+	const type *src = buffer; \
+	const size_t sample_count = size / sizeof(type); \
+	const size_t sample_pos = frame * f->channels + channel; \
+	if (sample_pos >= sample_count) {\
+		return 0.0f; \
+	} \
+	float sample = from(src[sample_pos], type, endian); \
+	/* This makes it positive */ \
+	sample -= (float)(type)low; \
+	/* This makes it <0,2> */ \
+	sample /= (((float)(type)high - (float)(type)low) / 2.0f); \
+	return sample - 1.0f; \
+} while (0)
+
+	switch (f->sample_format) {
+	case PCM_SAMPLE_UINT8:
+		GET(uint8_t, le, UINT8_MIN, UINT8_MAX);
+	case PCM_SAMPLE_SINT8:
+		GET(int8_t, le, INT8_MIN, INT8_MAX);
+	case PCM_SAMPLE_UINT16_LE:
+		GET(uint16_t, le, UINT16_MIN, UINT16_MAX);
+	case PCM_SAMPLE_SINT16_LE:
+		GET(int16_t, le, INT16_MIN, INT16_MAX);
+	case PCM_SAMPLE_UINT16_BE:
+		GET(uint16_t, be, UINT16_MIN, UINT16_MAX);
+	case PCM_SAMPLE_SINT16_BE:
+		GET(int16_t, be, INT16_MIN, INT16_MAX);
+	case PCM_SAMPLE_UINT24_32_LE:
+	case PCM_SAMPLE_UINT32_LE:
+		GET(uint32_t, le, UINT32_MIN, UINT32_MAX);
+	case PCM_SAMPLE_SINT24_32_LE:
+	case PCM_SAMPLE_SINT32_LE:
+		GET(int32_t, le, INT32_MIN, INT32_MAX);
+	case PCM_SAMPLE_UINT24_32_BE:
+	case PCM_SAMPLE_UINT32_BE:
+		GET(uint32_t, be, UINT32_MIN, UINT32_MAX);
+	case PCM_SAMPLE_SINT24_32_BE:
+	case PCM_SAMPLE_SINT32_BE:
+		GET(int32_t, le, INT32_MIN, INT32_MAX);
+	case PCM_SAMPLE_UINT24_LE:
+	case PCM_SAMPLE_SINT24_LE:
+	case PCM_SAMPLE_UINT24_BE:
+	case PCM_SAMPLE_SINT24_BE:
+	case PCM_SAMPLE_FLOAT32:
+	default: ;
+	}
+	return 0;
+#undef GET
+}
+/**
+ * @}
+ */
Index: uspace/lib/posix/include/posix/stdio.h
===================================================================
--- uspace/lib/posix/include/posix/stdio.h	(revision 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/posix/include/posix/stdio.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -65,7 +65,4 @@
 
 #define BUFSIZ  4096
-#define SEEK_SET  0
-#define SEEK_CUR  1
-#define SEEK_END  2
 
 typedef struct _IO_FILE FILE;
Index: uspace/lib/posix/include/posix/stdlib.h
===================================================================
--- uspace/lib/posix/include/posix/stdlib.h	(revision 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/posix/include/posix/stdlib.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -46,4 +46,6 @@
 	#define NULL  ((void *) 0)
 #endif
+
+#define RAND_MAX  714025
 
 /* Process Termination */
@@ -122,4 +124,8 @@
 extern int __POSIX_DEF__(mkstemp)(char *tmpl);
 
+/* Pseudo-random number generator */
+extern int __POSIX_DEF__(rand)(void);
+extern void __POSIX_DEF__(srand)(unsigned int seed);
+
 /* Legacy Declarations */
 extern char *__POSIX_DEF__(mktemp)(char *tmpl);
Index: uspace/lib/posix/include/posix/time.h
===================================================================
--- uspace/lib/posix/include/posix/time.h	(revision 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/posix/include/posix/time.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -87,4 +87,7 @@
 extern void __POSIX_DEF__(tzset)(void);
 
+/* Time */
+extern time_t __POSIX_DEF__(time)(time_t *t);
+
 /* Broken-down Time */
 extern struct tm *__POSIX_DEF__(gmtime_r)(const time_t *restrict timer,
Index: uspace/lib/posix/include/posix/unistd.h
===================================================================
--- uspace/lib/posix/include/posix/unistd.h	(revision 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/posix/include/posix/unistd.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -44,4 +44,8 @@
 #include "stddef.h"
 
+#define SEEK_SET  0
+#define SEEK_CUR  1
+#define SEEK_END  2
+
 /* Process Termination */
 #define _exit exit
@@ -77,4 +81,6 @@
 extern ssize_t __POSIX_DEF__(read)(int fildes, void *buf, size_t nbyte);
 extern ssize_t __POSIX_DEF__(write)(int fildes, const void *buf, size_t nbyte);
+extern __POSIX_DEF__(off_t) __POSIX_DEF__(lseek)(int fildes,
+    __POSIX_DEF__(off_t) offset, int whence);
 extern int __POSIX_DEF__(fsync)(int fildes);
 extern int __POSIX_DEF__(ftruncate)(int fildes, __POSIX_DEF__(off_t) length);
Index: uspace/lib/posix/source/stdlib.c
===================================================================
--- uspace/lib/posix/source/stdlib.c	(revision 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/posix/source/stdlib.c	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -394,4 +394,24 @@
 
 /**
+ * Generate a pseudo random integer in the range 0 to RAND_MAX inclusive.
+ *
+ * @return The pseudo random integer.
+ */
+int posix_rand(void)
+{
+	return (int) random();
+}
+
+/**
+ * Initialize a new sequence of pseudo-random integers.
+ *
+ * @param seed The seed of the new sequence.
+ */
+void posix_srand(unsigned int seed)
+{
+	srandom(seed);
+}
+
+/**
  * Creates and opens an unique temporary file from template.
  *
Index: uspace/lib/posix/source/time.c
===================================================================
--- uspace/lib/posix/source/time.c	(revision 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/posix/source/time.c	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -74,4 +74,17 @@
 	posix_daylight = 0;
 	posix_timezone = 0;
+}
+
+/**
+ * Get the time in seconds
+ *
+ * @param t If t is non-NULL, the return value is also stored in the memory
+ *          pointed to by t.
+ * @return  On success, the value of time in seconds since the Epoch
+ *          is returned. On error, (time_t)-1 is returned.
+ */
+time_t posix_time(time_t *t)
+{
+	return time(t);
 }
 
Index: uspace/lib/posix/source/unistd.c
===================================================================
--- uspace/lib/posix/source/unistd.c	(revision 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/posix/source/unistd.c	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -221,4 +221,18 @@
 {
 	return errnify(write, fildes, buf, nbyte);
+}
+
+/**
+ * Reposition read/write file offset
+ *
+ * @param fildes File descriptor of the opened file.
+ * @param offset New offset in the file.
+ * @param whence The position from which the offset argument is specified.
+ * @return Upon successful completion, returns the resulting offset
+ *         as measured in bytes from the beginning of the file, -1 otherwise.
+ */
+posix_off_t posix_lseek(int fildes, posix_off_t offset, int whence)
+{
+	return errnify(lseek, fildes, offset, whence);
 }
 
Index: uspace/lib/softfloat/softfloat.c
===================================================================
--- uspace/lib/softfloat/softfloat.c	(revision 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/softfloat/softfloat.c	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -1265,4 +1265,15 @@
 }
 
+
+float __aeabi_i2f(int i)
+{
+	return __floatsisf(i);
+}
+
+float __aeabi_ui2f(int i)
+{
+	return __floatunsisf(i);
+}
+
 double __aeabi_i2d(int i)
 {
@@ -1280,4 +1291,9 @@
 }
 
+int __aeabi_f2uiz(float a)
+{
+	return __fixunssfsi(a);
+}
+
 int __aeabi_d2iz(double a)
 {
@@ -1288,4 +1304,24 @@
 {
 	return __fixunsdfsi(a);
+}
+
+int __aeabi_fcmpge(float a, float b)
+{
+	return __gesf2(a, b);
+}
+
+int __aeabi_fcmpgt(float a, float b)
+{
+	return __gtsf2(a, b);
+}
+
+int __aeabi_fcmplt(float a, float b)
+{
+	return __ltsf2(a, b);
+}
+
+int __aeabi_fcmpeq(float a, float b)
+{
+	return __eqsf2(a, b);
 }
 
Index: uspace/lib/softfloat/softfloat.h
===================================================================
--- uspace/lib/softfloat/softfloat.h	(revision 0ee999d5050a78c577615a7e2cf559ca2561e44d)
+++ uspace/lib/softfloat/softfloat.h	(revision 5856b627dd9852525f88109f58d955b5b633266d)
@@ -204,4 +204,6 @@
 
 /* ARM EABI */
+extern float __aeabi_i2f(int);
+extern float __aeabi_ui2f(int);
 extern double __aeabi_i2d(int);
 extern double __aeabi_ui2d(unsigned int);
@@ -209,5 +211,11 @@
 
 extern int __aeabi_f2iz(float);
+extern int __aeabi_f2uiz(float);
 extern int __aeabi_d2iz(double);
+
+extern int __aeabi_fcmpge(float, float);
+extern int __aeabi_fcmpgt(float, float);
+extern int __aeabi_fcmplt(float, float);
+extern int __aeabi_fcmpeq(float, float);
 
 extern int __aeabi_dcmpge(double, double);
