Index: uspace/srv/audio/hound/Makefile
===================================================================
--- uspace/srv/audio/hound/Makefile	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
+++ uspace/srv/audio/hound/Makefile	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
@@ -0,0 +1,53 @@
+#
+# 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 = ../../..
+BINARY = hound
+
+EXTRA_CFLAGS = \
+	-DNAME="\"hound\"" \
+	-I$(LIBDRV_PREFIX)/include \
+	-I$(LIBHOUND_PREFIX)/include \
+	-I$(LIBPCM_PREFIX)/include
+
+LIBS = \
+	$(LIBDRV_PREFIX)/libdrv.a \
+	$(LIBHOUND_PREFIX)/libhound.a \
+	$(LIBPCM_PREFIX)/libpcm.a
+
+SOURCES = \
+	audio_client.c \
+	audio_device.c \
+	audio_sink.c \
+	audio_source.c \
+	hound.c \
+	main.c
+
+
+include $(USPACE_PREFIX)/Makefile.common
+
Index: uspace/srv/audio/hound/audio_client.c
===================================================================
--- uspace/srv/audio/hound/audio_client.c	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
+++ uspace/srv/audio/hound/audio_client.c	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
@@ -0,0 +1,147 @@
+/*
+ * 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 <errno.h>
+#include <stdlib.h>
+#include <str.h>
+
+#include "audio_client.h"
+#include "log.h"
+
+static void init_common(audio_client_t *client, const char *name,
+    const pcm_format_t *f, async_sess_t *sess)
+{
+	link_initialize(&client->link);
+	client->name = str_dup(name);
+	client->format = *f;
+	client->sess = sess;
+	client->exch = NULL;
+	client->is_playback = false;
+	client->is_recording = false;
+}
+
+static int client_sink_connection_change(audio_sink_t *sink, bool new);
+static int client_source_connection_change(audio_source_t *source);
+static int client_source_update_data(audio_source_t *source, size_t size);
+
+
+audio_client_t *audio_client_get_playback(
+    const char *name, const pcm_format_t *f, async_sess_t *sess)
+{
+	audio_client_t *client = malloc(sizeof(audio_client_t));
+	if (!client)
+		return NULL;
+	init_common(client, name, f, sess);
+	audio_source_init(&client->source, name, client,
+	    client_source_connection_change, client_source_update_data, f);
+	client->is_playback = true;
+	return client;
+}
+
+audio_client_t *audio_client_get_recording(
+    const char *name, const pcm_format_t *f, async_sess_t *sess)
+{
+	audio_client_t *client = malloc(sizeof(audio_client_t));
+	if (!client)
+		return NULL;
+	init_common(client, name, f, sess);
+	audio_sink_init(&client->sink, name, client,
+	    client_sink_connection_change, NULL, f);
+	client->is_recording = true;
+	return client;
+}
+
+void audio_client_destroy(audio_client_t *client)
+{
+	if (!client)
+		return;
+	if (client->is_recording) {
+		/* There is a fibril running */
+		client->is_recording = false;
+		return;
+	}
+	async_exchange_end(client->exch);
+	async_hangup(client->sess);
+	free(client->name);
+	if (client->is_playback) {
+		audio_source_fini(&client->source);
+	} else { /* was recording */
+		audio_sink_fini(&client->sink);
+	}
+	free(client);
+}
+
+static int client_sink_connection_change(audio_sink_t *sink, bool new)
+{
+	//TODO create fibril
+	return ENOTSUP;
+}
+
+static int client_source_connection_change(audio_source_t *source)
+{
+	assert(source);
+	audio_client_t *client = source->private_data;
+	if (source->connected_sink) {
+		client->exch = async_exchange_begin(client->sess);
+		return client->exch ? EOK : ENOMEM;
+	}
+	async_exchange_end(client->exch);
+	client->exch = NULL;
+	return EOK;
+}
+
+static int client_source_update_data(audio_source_t *source, size_t size)
+{
+	assert(source);
+	audio_client_t *client = source->private_data;
+	void *buffer = malloc(size);
+	if (!buffer)
+		return ENOMEM;
+	assert(client->exch);
+	const int ret = async_data_read_start(client->exch, buffer, size);
+	if (ret != EOK) {
+		log_debug("Failed to read data from client");
+		free(buffer);
+		return ret;
+	}
+	void *old_buffer = source->available_data.base;
+	source->available_data.position = buffer;
+	source->available_data.base = buffer;
+	source->available_data.size = size;
+	free(old_buffer);
+
+	return EOK;
+}
Index: uspace/srv/audio/hound/audio_client.h
===================================================================
--- uspace/srv/audio/hound/audio_client.h	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
+++ uspace/srv/audio/hound/audio_client.h	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
@@ -0,0 +1,69 @@
+/*
+ * 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 AUDIO_CLIENT_H_
+#define AUDIO_CLIENT_H_
+
+#include <adt/list.h>
+#include <async.h>
+#include <pcm/format.h>
+
+#include "audio_source.h"
+#include "audio_sink.h"
+
+typedef struct {
+	link_t link;
+	const char *name;
+	audio_source_t source;
+	audio_sink_t sink;
+	pcm_format_t format;
+	async_sess_t *sess;
+	async_exch_t *exch;
+	bool is_playback;
+	bool is_recording;
+} audio_client_t;
+
+static inline audio_client_t * audio_client_list_instance(link_t *l)
+{
+	return list_get_instance(l, audio_client_t, link);
+}
+
+audio_client_t *audio_client_get_playback(
+    const char *name, const pcm_format_t *f, async_sess_t *sess);
+audio_client_t *audio_client_get_recording(
+    const char *name, const pcm_format_t *f, async_sess_t *sess);
+void audio_client_destroy(audio_client_t *client);
+
+#endif
Index: uspace/srv/audio/hound/audio_device.c
===================================================================
--- uspace/srv/audio/hound/audio_device.c	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
+++ uspace/srv/audio/hound/audio_device.c	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
@@ -0,0 +1,271 @@
+/*
+ * 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 <async.h>
+#include <errno.h>
+#include <loc.h>
+#include <str.h>
+#include <str_error.h>
+
+
+#include "audio_device.h"
+#include "log.h"
+
+#define BUFFER_PARTS 2
+
+static int device_sink_connection_callback(audio_sink_t *sink, bool new);
+static int device_source_connection_callback(audio_source_t *source);
+static void device_event_callback(ipc_callid_t iid, ipc_call_t *icall, void *arg);
+static int device_check_format(audio_sink_t* sink);
+static int get_buffer(audio_device_t *dev);
+static int release_buffer(audio_device_t *dev);
+
+
+int audio_device_init(audio_device_t *dev, service_id_t id, const char *name)
+{
+	assert(dev);
+	link_initialize(&dev->link);
+	dev->id = id;
+	dev->name = str_dup(name);
+	dev->sess = audio_pcm_open_service(id);
+	if (!dev->sess) {
+		log_debug("Failed to connect to device \"%s\"", name);
+		return ENOMEM;
+	}
+
+	audio_sink_init(&dev->sink, name, dev, device_sink_connection_callback,
+	    device_check_format, &AUDIO_FORMAT_ANY);
+	audio_source_init(&dev->source, name, dev,
+	    device_source_connection_callback, NULL, &AUDIO_FORMAT_ANY);
+
+	/* Init buffer members */
+	fibril_mutex_initialize(&dev->buffer.guard);
+	fibril_condvar_initialize(&dev->buffer.wc);
+	dev->buffer.base = NULL;
+	dev->buffer.position = NULL;
+	dev->buffer.size = 0;
+
+	log_verbose("Initialized device (%p) '%s' with id %u.",
+	    dev, dev->name, dev->id);
+
+	return EOK;
+}
+void audio_device_fini(audio_device_t *dev)
+{
+	//TODO implement;
+}
+
+static int device_sink_connection_callback(audio_sink_t* sink, bool new)
+{
+	assert(sink);
+	audio_device_t *dev = sink->private_data;
+	if (new && list_count(&sink->sources) == 1) {
+		log_verbose("First connection on device sink '%s'", sink->name);
+
+		int ret = get_buffer(dev);
+		if (ret != EOK) {
+			log_error("Failed to get device buffer: %s",
+			    str_error(ret));
+			return ret;
+		}
+		audio_pcm_register_event_callback(dev->sess,
+		    device_event_callback, dev);
+
+		/* Fill the buffer first */
+		audio_sink_mix_inputs(&dev->sink,
+		    dev->buffer.base, dev->buffer.size);
+
+		const unsigned frames = dev->buffer.size /
+		    (BUFFER_PARTS * pcm_format_frame_size(&dev->sink.format));
+		ret = audio_pcm_start_playback_fragment(dev->sess, frames,
+		    dev->sink.format.channels, dev->sink.format.sampling_rate,
+		    dev->sink.format.sample_format);
+		if (ret != EOK) {
+			log_error("Failed to start playback: %s",
+			    str_error(ret));
+			release_buffer(dev);
+			return ret;
+		}
+	}
+	if (list_count(&sink->sources) == 0) {
+		assert(!new);
+		log_verbose("No connections on device sink '%s'", sink->name);
+		int ret = audio_pcm_stop_playback(dev->sess);
+		if (ret != EOK) {
+			log_error("Failed to start playback: %s",
+			    str_error(ret));
+			return ret;
+		}
+		dev->sink.format = AUDIO_FORMAT_ANY;
+		ret = release_buffer(dev);
+		if (ret != EOK) {
+			log_error("Failed to release buffer: %s",
+			    str_error(ret));
+			return ret;
+		}
+	}
+	return EOK;
+}
+
+static int device_source_connection_callback(audio_source_t *source)
+{
+	assert(source);
+	audio_device_t *dev = source->private_data;
+	if (source->connected_sink) {
+		int ret = get_buffer(dev);
+		if (ret != EOK) {
+			log_error("Failed to get device buffer: %s",
+			    str_error(ret));
+			return ret;
+		}
+		const unsigned frames = dev->buffer.size /
+		    (BUFFER_PARTS * pcm_format_frame_size(&dev->sink.format));
+		ret = audio_pcm_start_capture_fragment(dev->sess, frames,
+		    dev->sink.format.channels, dev->sink.format.sampling_rate,
+		    dev->sink.format.sample_format);
+		if (ret != EOK) {
+			log_error("Failed to start recording: %s",
+			    str_error(ret));
+			release_buffer(dev);
+			return ret;
+		}
+	} else { /* Disconnected */
+		int ret = audio_pcm_stop_capture(dev->sess);
+		if (ret != EOK) {
+			log_error("Failed to start recording: %s",
+			    str_error(ret));
+			return ret;
+		}
+		source->format = AUDIO_FORMAT_ANY;
+		ret = release_buffer(dev);
+		if (ret != EOK) {
+			log_error("Failed to release buffer: %s",
+			    str_error(ret));
+			return ret;
+		}
+		audio_pcm_unregister_event_callback(dev->sess);
+	}
+
+	return EOK;
+}
+
+static void device_event_callback(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	/* Answer initial request */
+	async_answer_0(iid, EOK);
+	audio_device_t *dev = arg;
+	assert(dev);
+	while (1) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		async_answer_0(callid, EOK);
+		switch(IPC_GET_IMETHOD(call)) {
+		case PCM_EVENT_FRAMES_PLAYED: {
+			//TODO add underrun protection.
+			if (dev->buffer.position) {
+				dev->buffer.position +=
+				    (dev->buffer.size / BUFFER_PARTS);
+			}
+			if ((!dev->buffer.position) ||
+			    (dev->buffer.position >=
+			        (dev->buffer.base + dev->buffer.size)))
+			{
+				dev->buffer.position = dev->buffer.base;
+			}
+			audio_sink_mix_inputs(&dev->sink, dev->buffer.position,
+			    dev->buffer.size / BUFFER_PARTS);
+			break;
+		}
+		case PCM_EVENT_PLAYBACK_TERMINATED:
+			log_verbose("Playback terminated!");
+			return;
+		case PCM_EVENT_FRAMES_CAPTURED:
+			//TODO implement
+			break;
+		case PCM_EVENT_CAPTURE_TERMINATED:
+			log_verbose("Recording terminated!");
+			return;
+		}
+
+	}
+}
+static int device_check_format(audio_sink_t* sink)
+{
+	assert(sink);
+	audio_device_t *dev = sink->private_data;
+	assert(dev);
+	log_verbose("Checking format on sink %s", sink->name);
+	return audio_pcm_test_format(dev->sess, &sink->format.channels,
+	    &sink->format.sampling_rate, &sink->format.sample_format);
+}
+
+static int get_buffer(audio_device_t *dev)
+{
+	assert(dev);
+	if (!dev->sess) {
+		log_debug("No connection to device");
+		return EIO;
+	}
+	if (dev->buffer.base) {
+		log_debug("We already have a buffer");
+		return EBUSY;
+	}
+
+	dev->buffer.size = 0;
+
+	return audio_pcm_get_buffer(dev->sess, &dev->buffer.base,
+	    &dev->buffer.size);
+}
+
+static int release_buffer(audio_device_t *dev)
+{
+	assert(dev);
+	assert(dev->buffer.base);
+
+	const int ret = audio_pcm_release_buffer(dev->sess);
+	if (ret == EOK) {
+		dev->buffer.base = NULL;
+		dev->buffer.size = 0;
+		dev->buffer.position = NULL;
+	} else {
+		log_debug("Failed to release buffer: %s", str_error(ret));
+	}
+	return ret;
+}
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/audio_device.h
===================================================================
--- uspace/srv/audio/hound/audio_device.h	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
+++ uspace/srv/audio/hound/audio_device.h	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
@@ -0,0 +1,92 @@
+/*
+ * 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 AUDIO_DEVICE_H_
+#define AUDIO_DEVICE_H_
+
+#include <adt/list.h>
+#include <async.h>
+#include <fibril_synch.h>
+#include <errno.h>
+#include <ipc/loc.h>
+#include <audio_pcm_iface.h>
+
+#include "audio_source.h"
+#include "audio_sink.h"
+
+typedef struct {
+	link_t link;
+	service_id_t id;
+	audio_pcm_sess_t *sess;
+	char *name;
+	struct {
+		fibril_mutex_t guard;
+		fibril_condvar_t wc;
+		void *base;
+		size_t size;
+		void *position;
+	} buffer;
+	audio_source_t source;
+	audio_sink_t sink;
+} audio_device_t;
+
+static inline audio_device_t * audio_device_list_instance(link_t *l)
+{
+	return list_get_instance(l, audio_device_t, link);
+};
+
+int audio_device_init(audio_device_t *dev, service_id_t id, const char *name);
+void audio_device_fini(audio_device_t *dev);
+static inline audio_source_t * audio_device_get_source(audio_device_t *dev)
+{
+	assert(dev);
+	return &dev->source;
+}
+
+static inline audio_sink_t * audio_device_get_sink(audio_device_t *dev)
+{
+	assert(dev);
+	return &dev->sink;
+}
+
+int audio_device_recorded_data(audio_device_t *dev, void **base, size_t *size);
+int audio_device_available_buffer(audio_device_t *dev, void **base, size_t *size);
+
+#endif
+
+/**
+ * @}
+ */
+
Index: uspace/srv/audio/hound/audio_sink.c
===================================================================
--- uspace/srv/audio/hound/audio_sink.c	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
+++ uspace/srv/audio/hound/audio_sink.c	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
@@ -0,0 +1,181 @@
+/*
+ * 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 <errno.h>
+#include <stdlib.h>
+#include <str.h>
+#include <str_error.h>
+
+#include "audio_sink.h"
+#include "log.h"
+
+
+int audio_sink_init(audio_sink_t *sink, const char *name,
+    void *private_data, int (*connection_change)(audio_sink_t *, bool),
+    int (*check_format)(audio_sink_t *sink), const pcm_format_t *f)
+{
+	assert(sink);
+	if (!name) {
+		log_debug("Incorrect parameters.");
+		return EINVAL;
+	}
+	link_initialize(&sink->link);
+	list_initialize(&sink->sources);
+	sink->name = str_dup(name);
+	sink->private_data = private_data;
+	sink->format = *f;
+	sink->connection_change = connection_change;
+	sink->check_format = check_format;
+	log_verbose("Initialized sink (%p) '%s'", sink, sink->name);
+	return EOK;
+}
+
+void audio_sink_fini(audio_sink_t *sink)
+{
+	assert(sink);
+	assert(!sink->private_data);
+	free(sink->name);
+	sink->name = NULL;
+}
+
+int audio_sink_add_source(audio_sink_t *sink, audio_source_t *source)
+{
+	assert(sink);
+	assert(source);
+	assert_link_not_used(&source->link);
+	list_append(&source->link, &sink->sources);
+
+	const pcm_format_t old_format = sink->format;
+
+	/* The first source for me */
+	if (list_count(&sink->sources) == 1) {
+		/* Set audio format according to the first source */
+		if (pcm_format_is_any(&sink->format)) {
+			int ret = audio_sink_set_format(sink, &source->format);
+			if (ret != EOK)
+				return ret;
+		}
+	}
+
+	audio_source_connected(source, sink);
+
+	if (sink->connection_change) {
+		log_verbose("Calling connection change");
+		const int ret = sink->connection_change(sink, true);
+		if (ret != EOK) {
+			log_debug("Connection hook failed.");
+			audio_source_connected(source, NULL);
+			list_remove(&source->link);
+			sink->format = old_format;
+			return ret;
+		}
+	}
+	log_verbose("Connected source '%s' to sink '%s'",
+	    source->name, sink->name);
+
+	return EOK;
+}
+
+int audio_sink_set_format(audio_sink_t *sink, const pcm_format_t *format)
+{
+	assert(sink);
+	assert(format);
+	if (!pcm_format_is_any(&sink->format)) {
+		log_debug("Sink %s already has a format", sink->name);
+		return EEXISTS;
+	}
+	const pcm_format_t old_format;
+
+	if (pcm_format_is_any(format)) {
+		log_verbose("Setting DEFAULT format for sink %s", sink->name);
+		sink->format = AUDIO_FORMAT_DEFAULT;
+	} else {
+		sink->format = *format;
+	}
+	if (sink->check_format) {
+		const int ret = sink->check_format(sink);
+		if (ret != EOK && ret != ELIMIT) {
+			log_debug("Format check failed on sink %s", sink->name);
+			sink->format = old_format;
+			return ret;
+		}
+	}
+	log_verbose("Set format for sink %s: %u channel(s), %uHz, %s",
+	    sink->name, format->channels, format->sampling_rate,
+	    pcm_sample_format_str(format->sample_format));
+	return EOK;
+}
+
+int audio_sink_remove_source(audio_sink_t *sink, audio_source_t *source)
+{
+	assert(sink);
+	assert(source);
+	assert(list_member(&source->link, &sink->sources));
+	list_remove(&source->link);
+	if (sink->connection_change) {
+		const int ret = sink->connection_change(sink, false);
+		if (ret != EOK) {
+			log_debug("Connected hook failed.");
+			list_append(&source->link, &sink->sources);
+			return ret;
+		}
+	}
+	audio_source_connected(source, NULL);
+	return EOK;
+}
+
+
+void audio_sink_mix_inputs(audio_sink_t *sink, void* dest, size_t size)
+{
+	assert(sink);
+	assert(dest);
+
+	pcm_format_silence(dest, size, &sink->format);
+	list_foreach(sink->sources, it) {
+		audio_source_t *source = audio_source_list_instance(it);
+		const int ret =
+		    audio_source_add_self(source, dest, size, &sink->format);
+		if (ret != EOK) {
+			log_warning("Failed to mix source %s: %s",
+			    source->name, str_error(ret));
+		}
+	}
+}
+
+
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/audio_sink.h
===================================================================
--- uspace/srv/audio/hound/audio_sink.h	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
+++ uspace/srv/audio/hound/audio_sink.h	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
@@ -0,0 +1,80 @@
+/*
+ * 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 AUDIO_SINK_H_
+#define AUDIO_SINK_H_
+
+#include <adt/list.h>
+#include <async.h>
+#include <stdbool.h>
+#include <fibril.h>
+#include <pcm/format.h>
+
+#include "audio_source.h"
+
+typedef struct audio_sink audio_sink_t;
+
+struct audio_sink {
+	link_t link;
+	list_t sources;
+	const char *name;
+	pcm_format_t format;
+	void *private_data;
+	int (*connection_change)(audio_sink_t *, bool);
+	int (*check_format)(audio_sink_t *);
+};
+
+static inline audio_sink_t * audio_sink_list_instance(link_t *l)
+{
+	return list_get_instance(l, audio_sink_t, link);
+}
+
+int audio_sink_init(audio_sink_t *sink, const char *name,
+    void *private_data, int (*connection_change)(audio_sink_t *, bool),
+    int (*check_format)(audio_sink_t *), const pcm_format_t *f);
+void audio_sink_fini(audio_sink_t *sink);
+
+int audio_sink_set_format(audio_sink_t *sink, const pcm_format_t *format);
+int audio_sink_add_source(audio_sink_t *sink, audio_source_t *source);
+int audio_sink_remove_source(audio_sink_t *sink, audio_source_t *source);
+void audio_sink_mix_inputs(audio_sink_t *sink, void* dest, size_t size);
+
+
+#endif
+
+/**
+ * @}
+ */
+
Index: uspace/srv/audio/hound/audio_source.c
===================================================================
--- uspace/srv/audio/hound/audio_source.c	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
+++ uspace/srv/audio/hound/audio_source.c	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
@@ -0,0 +1,153 @@
+/*
+ * 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 <errno.h>
+#include <macros.h>
+#include <stdlib.h>
+#include <str.h>
+#include <str_error.h>
+
+#include "audio_source.h"
+#include "audio_sink.h"
+#include "log.h"
+
+
+int audio_source_init(audio_source_t *source, const char *name, void *data,
+    int (*connection_change)(audio_source_t *),
+    int (*update_available_data)(audio_source_t *, size_t),
+    const pcm_format_t *f)
+{
+	assert(source);
+	if (!name || !f) {
+		log_debug("Incorrect parameters.");
+		return EINVAL;
+	}
+	link_initialize(&source->link);
+	source->name = str_dup(name);
+	source->private_data = data;
+	source->connection_change = connection_change;
+	source->update_available_data = update_available_data;
+	source->connected_sink = NULL;
+	source->format = *f;
+	source->available_data.base = NULL;
+	source->available_data.position = NULL;
+	source->available_data.size = 0;
+	log_verbose("Initialized source (%p) '%s'", source, source->name);
+	return EOK;
+}
+
+void audio_source_fini(audio_source_t *source)
+{
+	assert(source);
+	assert(source->connected_sink == NULL);
+	free(source->name);
+	source->name = NULL;
+}
+
+int audio_source_connected(audio_source_t *source, struct audio_sink *sink)
+{
+	assert(source);
+	audio_sink_t *old_sink = source->connected_sink;
+	const pcm_format_t old_format = source->format;
+
+	source->connected_sink = sink;
+	if (pcm_format_is_any(&source->format)) {
+		assert(sink);
+		assert(!pcm_format_is_any(&sink->format));
+		source->format = sink->format;
+	}
+	if (source->connection_change) {
+		const int ret = source->connection_change(source);
+		if (ret != EOK) {
+			source->format = old_format;
+			source->connected_sink = old_sink;
+			return ret;
+		}
+	}
+	return EOK;
+}
+
+int audio_source_add_self(audio_source_t *source, void *buffer, size_t size,
+    const pcm_format_t *f)
+{
+	assert(source);
+	if (!buffer) {
+		log_debug("Non-existent buffer");
+		return EBADMEM;
+	}
+	if (!f || size == 0) {
+		log_debug("No format or zero size");
+	}
+	if (size % (pcm_sample_format_size(f->sample_format) * f->channels)) {
+		log_debug("Buffer does not fit integer number of frames");
+		return EINVAL;
+	}
+	if (source->format.sampling_rate != f->sampling_rate) {
+		log_debug("Resampling is not supported, yet");
+		return ENOTSUP;
+	}
+	const size_t src_frame_size = pcm_format_frame_size(&source->format);
+	const size_t dst_frames = size / pcm_format_frame_size(f);
+
+	if (source->available_data.position == NULL ||
+	    source->available_data.size == 0) {
+		int ret = EOVERFLOW; /* In fact this is underflow... */
+		if (source->update_available_data)
+			ret = source->update_available_data(source,
+			    dst_frames * src_frame_size);
+		if (ret != EOK) {
+			log_debug("No data to add to %p(%zu)", buffer, size);
+			return ret;
+		}
+	}
+
+	const int ret = pcm_format_convert_and_mix(buffer, size,
+	       source->available_data.position, source->available_data.size,
+	       &source->format, f);
+	if (ret != EOK) {
+		log_debug("Mixing failed %p <= %p, frames: %zu",
+		    buffer, source->available_data.position, dst_frames);
+		return ret;
+	}
+
+	source->available_data.position += (dst_frames * src_frame_size);
+	source->available_data.size -= (dst_frames * src_frame_size);
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/audio_source.h
===================================================================
--- uspace/srv/audio/hound/audio_source.h	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
+++ uspace/srv/audio/hound/audio_source.h	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
@@ -0,0 +1,82 @@
+/*
+ * 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 AUDIO_SOURCE_H_
+#define AUDIO_SOURCE_H_
+
+#include <adt/list.h>
+#include <pcm/format.h>
+
+struct audio_sink;
+typedef struct audio_source audio_source_t;
+
+struct audio_source {
+	link_t link;
+	const char *name;
+	pcm_format_t format;
+	void *private_data;
+	int (*connection_change)(audio_source_t *source);
+	int (*update_available_data)(audio_source_t *source, size_t size);
+	struct audio_sink *connected_sink;
+	struct {
+		void *position;
+		void *base;
+		size_t size;
+	} available_data;
+};
+
+static inline audio_source_t * audio_source_list_instance(link_t *l)
+{
+	return list_get_instance(l, audio_source_t, link);
+}
+
+int audio_source_init(audio_source_t *source, const char *name, void *data,
+    int (*connection_change)(audio_source_t *),
+    int (*update_available_data)(audio_source_t *, size_t),
+    const pcm_format_t *f);
+void audio_source_fini(audio_source_t *source);
+int audio_source_connected(audio_source_t *source, struct audio_sink *sink);
+int audio_source_add_self(audio_source_t *source, void *buffer, size_t size,
+    const pcm_format_t *f);
+static inline const pcm_format_t *audio_source_format(const audio_source_t *s)
+{
+	assert(s);
+	return &s->format;
+}
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/hound.c
===================================================================
--- uspace/srv/audio/hound/hound.c	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
+++ uspace/srv/audio/hound/hound.c	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
@@ -0,0 +1,308 @@
+/*
+ * 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 <stdlib.h>
+
+#include "hound.h"
+#include "audio_client.h"
+#include "audio_device.h"
+#include "audio_sink.h"
+#include "audio_source.h"
+#include "log.h"
+#include "errno.h"
+#include "str_error.h"
+
+#define FIND_BY_NAME(type) \
+do { \
+	assert(list); \
+	assert(name); \
+	list_foreach(*list, it) { \
+		audio_ ## type ## _t *dev = \
+		    audio_ ## type ## _list_instance(it); \
+		if (str_cmp(name, dev->name) == 0) { \
+			log_debug("%s with name '%s' is in the list", \
+			    #type, name); \
+			return dev; \
+		} \
+	} \
+	return NULL; \
+} while (0)
+
+static audio_device_t * find_device_by_name(list_t *list, const char *name)
+{
+	FIND_BY_NAME(device);
+}
+static audio_source_t * find_source_by_name(list_t *list, const char *name)
+{
+	FIND_BY_NAME(source);
+}
+static audio_sink_t * find_sink_by_name(list_t *list, const char *name)
+{
+	FIND_BY_NAME(sink);
+}
+static int hound_disconnect_internal(hound_t *hound, const char* source_name, const char* sink_name);
+
+int hound_init(hound_t *hound)
+{
+	assert(hound);
+	fibril_mutex_initialize(&hound->list_guard);
+	list_initialize(&hound->devices);
+	list_initialize(&hound->sources);
+	list_initialize(&hound->sinks);
+	return EOK;
+}
+
+int hound_add_device(hound_t *hound, service_id_t id, const char *name)
+{
+	log_verbose("Adding device \"%s\", service: %zu", name, id);
+
+	assert(hound);
+	if (!name || !id) {
+		log_debug("Incorrect parameters.");
+		return EINVAL;
+	}
+
+	list_foreach(hound->devices, it) {
+		audio_device_t *dev = audio_device_list_instance(it);
+		if (dev->id == id) {
+			log_debug("Device with id %zu is already present", id);
+			return EEXISTS;
+		}
+	}
+
+	audio_device_t *dev = find_device_by_name(&hound->devices, name);
+	if (dev) {
+		log_debug("Device with name %s is already present", name);
+		return EEXISTS;
+	}
+
+	dev = malloc(sizeof(audio_device_t));
+	if (!dev) {
+		log_debug("Failed to malloc device structure.");
+		return ENOMEM;
+	}
+
+	const int ret = audio_device_init(dev, id, name);
+	if (ret != EOK) {
+		log_debug("Failed to initialize new audio device: %s",
+			str_error(ret));
+		free(dev);
+		return ret;
+	}
+
+	list_append(&dev->link, &hound->devices);
+	log_info("Added new device: '%s'", dev->name);
+
+	audio_source_t *source = audio_device_get_source(dev);
+	if (source) {
+		const int ret = hound_add_source(hound, source);
+		if (ret != EOK) {
+			log_debug("Failed to add device source: %s",
+			    str_error(ret));
+			audio_device_fini(dev);
+			return ret;
+		}
+		log_verbose("Added source: '%s'.", source->name);
+	}
+
+	audio_sink_t *sink = audio_device_get_sink(dev);
+	if (sink) {
+		const int ret = hound_add_sink(hound, sink);
+		if (ret != EOK) {
+			log_debug("Failed to add device sink: %s",
+			    str_error(ret));
+			audio_device_fini(dev);
+			return ret;
+		}
+		log_verbose("Added sink: '%s'.", sink->name);
+	}
+
+	if (!source && !sink)
+		log_warning("Neither sink nor source on device '%s'.", name);
+
+	return ret;
+}
+
+int hound_add_source(hound_t *hound, audio_source_t *source)
+{
+	assert(hound);
+	if (!source || !source->name || str_cmp(source->name, "default") == 0) {
+		log_debug("Invalid source specified.");
+		return EINVAL;
+	}
+	fibril_mutex_lock(&hound->list_guard);
+	if (find_source_by_name(&hound->sources, source->name)) {
+		log_debug("Source by that name already exists");
+		fibril_mutex_unlock(&hound->list_guard);
+		return EEXISTS;
+	}
+	list_foreach(hound->sinks, it) {
+		audio_sink_t *sink = audio_sink_list_instance(it);
+		if (find_source_by_name(&sink->sources, source->name)) {
+			log_debug("Source by that name already exists");
+			fibril_mutex_unlock(&hound->list_guard);
+			return EEXISTS;
+		}
+	}
+	list_append(&source->link, &hound->sources);
+	fibril_mutex_unlock(&hound->list_guard);
+	return EOK;
+}
+
+int hound_add_sink(hound_t *hound, audio_sink_t *sink)
+{
+	assert(hound);
+	if (!sink || !sink->name || str_cmp(sink->name, "default") == 0) {
+		log_debug("Invalid source specified.");
+		return EINVAL;
+	}
+	fibril_mutex_lock(&hound->list_guard);
+	if (find_sink_by_name(&hound->sinks, sink->name)) {
+		log_debug("Sink by that name already exists");
+		fibril_mutex_unlock(&hound->list_guard);
+		return EEXISTS;
+	}
+	list_append(&sink->link, &hound->sinks);
+	fibril_mutex_unlock(&hound->list_guard);
+	return EOK;
+}
+
+int hound_remove_source(hound_t *hound, audio_source_t *source)
+{
+	assert(hound);
+	if (!source)
+		return EINVAL;
+	log_verbose("Removing source '%s'.", source->name);
+	fibril_mutex_lock(&hound->list_guard);
+	if (!list_member(&source->link, &hound->sources)) {
+		assert(source->connected_sink);
+		hound_disconnect_internal(hound, source->name,
+		    source->connected_sink->name);
+	}
+	list_remove(&source->link);
+	fibril_mutex_unlock(&hound->list_guard);
+	return EOK;
+}
+
+int hound_remove_sink(hound_t *hound, audio_sink_t *sink)
+{
+	assert(hound);
+	if (!sink)
+		return EINVAL;
+	log_verbose("Removing sink '%s'.", sink->name);
+	fibril_mutex_lock(&hound->list_guard);
+
+	if (!list_empty(&sink->sources)) {
+		// TODO disconnect instead
+		fibril_mutex_unlock(&hound->list_guard);
+		return EBUSY;
+	}
+	list_remove(&sink->link);
+	fibril_mutex_unlock(&hound->list_guard);
+	return EOK;
+}
+
+int hound_connect(hound_t *hound, const char* source_name, const char* sink_name)
+{
+	assert(hound);
+	log_verbose("Connecting '%s' to '%s'.", source_name, sink_name);
+	fibril_mutex_lock(&hound->list_guard);
+
+	audio_source_t *source =
+	    audio_source_list_instance(list_first(&hound->sources));
+	if (str_cmp(source_name, "default") != 0)
+	    source = find_source_by_name(&hound->sources, source_name);
+
+	audio_sink_t *sink =
+	    audio_sink_list_instance(list_first(&hound->sinks));
+	if (str_cmp(sink_name, "default") != 0)
+	    sink = find_sink_by_name(&hound->sinks, sink_name);
+
+	if (!source || !sink) {
+		fibril_mutex_unlock(&hound->list_guard);
+		log_debug("Source (%p), or sink (%p) not found", source, sink);
+		return ENOENT;
+	}
+	list_remove(&source->link);
+	const int ret = audio_sink_add_source(sink, source);
+	if (ret != EOK) {
+		log_debug("Failed add source to sink list: %s", str_error(ret));
+		list_append(&source->link, &hound->sources);
+	}
+	fibril_mutex_unlock(&hound->list_guard);
+	return EOK;
+}
+
+int hound_disconnect(hound_t *hound, const char* source_name, const char* sink_name)
+{
+	assert(hound);
+	fibril_mutex_lock(&hound->list_guard);
+	const int ret = hound_disconnect_internal(hound, source_name, sink_name);
+	fibril_mutex_unlock(&hound->list_guard);
+	return ret;
+}
+
+static int hound_disconnect_internal(hound_t *hound, const char* source_name, const char* sink_name)
+{
+	assert(hound);
+	assert(fibril_mutex_is_locked(&hound->list_guard));
+	log_verbose("Disconnecting '%s' to '%s'.", source_name, sink_name);
+
+	audio_sink_t *sink =
+	    audio_sink_list_instance(list_first(&hound->sinks));
+	if (str_cmp(sink_name, "default") != 0)
+	    sink = find_sink_by_name(&hound->sinks, sink_name);
+
+	audio_source_t *source =
+	    audio_source_list_instance(list_first(&hound->sources));
+	if (str_cmp(source_name, "default") != 0)
+	    source = sink ? find_source_by_name(&sink->sources, source_name) : NULL;
+	if (!source || !sink) {
+		log_debug("Source (%p), or sink (%p) not found", source, sink);
+		return ENOENT;
+	}
+	const int ret = audio_sink_remove_source(sink, source);
+	if (ret != EOK) {
+		log_debug("Failed remove source to sink list: %s", str_error(ret));
+	} else {
+		list_append(&source->link, &hound->sources);
+	}
+	return EOK;
+}
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/hound.h
===================================================================
--- uspace/srv/audio/hound/hound.h	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
+++ uspace/srv/audio/hound/hound.h	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
@@ -0,0 +1,70 @@
+/*
+ * 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 HOUND_H_
+#define HOUND_H_
+
+#include <async.h>
+#include <adt/list.h>
+#include <ipc/loc.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <pcm/format.h>
+
+#include "audio_source.h"
+#include "audio_sink.h"
+
+
+typedef struct {
+	fibril_mutex_t list_guard;
+	list_t devices;
+	list_t sources;
+	list_t sinks;
+} hound_t;
+
+int hound_init(hound_t *hound);
+int hound_add_device(hound_t *hound, service_id_t id, const char* name);
+int hound_add_source(hound_t *hound, audio_source_t *source);
+int hound_add_sink(hound_t *hound, audio_sink_t *sink);
+int hound_remove_source(hound_t *hound, audio_source_t *source);
+int hound_remove_sink(hound_t *hound, audio_sink_t *sink);
+int hound_connect(hound_t *hound, const char* source_name, const char* sink_name);
+int hound_disconnect(hound_t *hound, const char* source_name, const char* sink_name);
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/log.h
===================================================================
--- uspace/srv/audio/hound/log.h	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
+++ uspace/srv/audio/hound/log.h	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
@@ -0,0 +1,56 @@
+/*
+ * 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 LOG_H_
+#define LOG_H_
+
+#ifndef NAME
+#define NAME "NONAME"
+#endif
+
+#include <stdio.h>
+
+#define log_fatal(msg, ...) printf(NAME ": Fatal: " msg "\n", ##__VA_ARGS__);
+#define log_error(msg, ...) printf(NAME ": Error: " msg "\n", ##__VA_ARGS__);
+#define log_warning(msg, ...) printf(NAME ": Warn: " msg "\n", ##__VA_ARGS__);
+#define log_info(msg, ...) printf(NAME ": Info: " msg "\n", ##__VA_ARGS__);
+#define log_debug(msg, ...) printf("%s: Debug: %s: " msg "\n", NAME, __FUNCTION__, ##__VA_ARGS__);
+#define log_verbose(msg, ...) printf("%s: %s: " msg "\n", NAME, __FUNCTION__, ##__VA_ARGS__);
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/main.c
===================================================================
--- uspace/srv/audio/hound/main.c	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
+++ uspace/srv/audio/hound/main.c	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
@@ -0,0 +1,260 @@
+/*
+ * 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 <async.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <str_error.h>
+#include <hound/server.h>
+
+
+#include "hound.h"
+
+#define NAMESPACE "audio"
+#define NAME "hound"
+#define CATEGORY "audio-pcm"
+
+#include "audio_client.h"
+#include "log.h"
+
+static hound_t hound;
+
+static int device_callback(service_id_t id, const char *name)
+{
+	return hound_add_device(&hound, id, name);
+}
+
+static void scan_for_devices(void)
+{
+	hound_server_devices_iterate(device_callback);
+}
+
+static void client_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	async_answer_0(iid, EOK);
+
+	LIST_INITIALIZE(local_playback);
+	LIST_INITIALIZE(local_recording);
+	pcm_format_t format = {0};
+	const char *name = NULL;
+	async_sess_t *sess = NULL;
+
+	while (1) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		switch (IPC_GET_IMETHOD(call)) {
+		case HOUND_REGISTER_PLAYBACK: {
+			hound_server_get_register_params(&name, &sess,
+			    &format.channels, &format.sampling_rate,
+			    &format.sample_format);
+			audio_client_t *client =
+			    audio_client_get_playback(name, &format, sess);
+			free(name);
+			if (!client) {
+				log_error("Failed to create playback client");
+				async_answer_0(callid, ENOMEM);
+				break;
+			}
+			int ret = hound_add_source(&hound, &client->source);
+			if (ret != EOK){
+				audio_client_destroy(client);
+				log_error("Failed to add audio source: %s",
+				    str_error(ret));
+				async_answer_0(callid, ret);
+				break;
+			}
+			log_info("Added audio client %p '%s'",
+			    client, client->name);
+			async_answer_0(callid, EOK);
+			list_append(&client->link, &local_playback);
+			break;
+		}
+		case HOUND_REGISTER_RECORDING: {
+			hound_server_get_register_params(&name, &sess,
+			    &format.channels, &format.sampling_rate,
+			    &format.sample_format);
+			audio_client_t *client =
+			    audio_client_get_recording(name, &format, sess);
+			free(name);
+			if (!client) {
+				log_error("Failed to create recording client");
+				async_answer_0(callid, ENOMEM);
+				break;
+			}
+			int ret = hound_add_sink(&hound, &client->sink);
+			if (ret != EOK){
+				audio_client_destroy(client);
+				log_error("Failed to add audio sink: %s",
+				    str_error(ret));
+				async_answer_0(callid, ret);
+				break;
+			}
+			async_answer_0(callid, EOK);
+			list_append(&client->link, &local_recording);
+			break;
+		}
+		case HOUND_UNREGISTER_PLAYBACK: {
+			const char *name = NULL;
+			hound_server_get_unregister_params(&name);
+			int ret = ENOENT;
+			list_foreach(local_playback, it) {
+				audio_client_t *client =
+				    audio_client_list_instance(it);
+				if (str_cmp(client->name, name) == 0) {
+					ret = hound_remove_source(&hound,
+					    &client->source);
+					if (ret == EOK) {
+						list_remove(&client->link);
+						audio_client_destroy(client);
+					}
+					break;
+				}
+			}
+			free(name);
+			async_answer_0(callid, ret);
+			break;
+		}
+		case HOUND_UNREGISTER_RECORDING: {
+			const char *name = NULL;
+			hound_server_get_unregister_params(&name);
+			int ret = ENOENT;
+			list_foreach(local_recording, it) {
+				audio_client_t *client =
+				    audio_client_list_instance(it);
+				if (str_cmp(client->name, name) == 0) {
+					ret = hound_remove_sink(&hound,
+					    &client->sink);
+					if (ret == EOK) {
+						list_remove(&client->link);
+						audio_client_destroy(client);
+					}
+					break;
+				}
+			}
+			free(name);
+			async_answer_0(callid, ret);
+			break;
+		}
+		case HOUND_CONNECT: {
+			const char *source = NULL, *sink = NULL;
+			hound_server_get_connection_params(&source, &sink);
+			const int ret = hound_connect(&hound, source, sink);
+			if (ret != EOK)
+				log_error("Failed to connect '%s' to '%s': %s",
+				    source, sink, str_error(ret));
+			free(source);
+			free(sink);
+			async_answer_0(callid, ret);
+			break;
+		}
+		case HOUND_DISCONNECT: {
+			const char *source = NULL, *sink = NULL;
+			hound_server_get_connection_params(&source, &sink);
+			const int ret = hound_disconnect(&hound, source, sink);
+			if (ret != EOK)
+				log_error("Failed to disconnect '%s' from '%s'"
+				    ": %s", source, sink, str_error(ret));
+			free(source);
+			free(sink);
+			async_answer_0(callid, ret);
+			break;
+		}
+		default:
+			log_debug("Got unknown method %u",
+			    IPC_GET_IMETHOD(call));
+			async_answer_0(callid, ENOTSUP);
+			break;
+		case 0:
+			while(!list_empty(&local_recording)) {
+				audio_client_t *client =
+				    audio_client_list_instance(
+				        list_first(&local_recording));
+				list_remove(&client->link);
+				hound_remove_sink(&hound, &client->sink);
+				audio_client_destroy(client);
+			}
+			while(!list_empty(&local_playback)) {
+				audio_client_t *client =
+				    audio_client_list_instance(
+				        list_first(&local_playback));
+				list_remove(&client->link);
+				hound_remove_source(&hound, &client->source);
+				audio_client_destroy(client);
+			}
+			return;
+		}
+	}
+}
+
+int main(int argc, char **argv)
+{
+	printf("%s: HelenOS sound service\n", NAME);
+
+	int ret = hound_init(&hound);
+	if (ret != EOK) {
+		log_fatal("Failed to initialize hound structure: %s",
+		    str_error(ret));
+		return -ret;
+	}
+
+	async_set_client_connection(client_connection);
+
+	service_id_t id = 0;
+	ret = hound_server_register(NAME, &id);
+	if (ret != EOK) {
+		log_fatal("Failed to register server: %s", str_error(ret));
+		return -ret;
+	}
+
+	ret = hound_server_set_device_change_callback(scan_for_devices);
+	if (ret != EOK) {
+		log_fatal("Failed to register for device changes: %s",
+		    str_error(ret));
+		hound_server_unregister(id);
+		return -ret;
+	}
+	log_info("Running with service id %u", id);
+
+	scan_for_devices();
+
+	async_manager();
+	return 0;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/hid/compositor/compositor.c
===================================================================
--- uspace/srv/hid/compositor/compositor.c	(revision f597bc442c30ebe583db3c7ae73298e745aa081c)
+++ uspace/srv/hid/compositor/compositor.c	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
@@ -2092,6 +2092,8 @@
 	/* Establish input bidirectional connection. */
 	rc = input_connect(input_svc);
-	if (rc != EOK)
+	if (rc != EOK) {
+		printf("%s: Failed to connect to input service.\n", NAME);
 		return rc;
+	}
 
 	/* Create viewports and connect them to visualizers. */
@@ -2099,4 +2101,5 @@
 	rc = loc_category_get_id("visualizer", &cat_id, IPC_FLAG_BLOCKING);
 	if (rc != EOK) {
+		printf("%s: Failed to get visualizer category.\n", NAME);
 		input_disconnect();
 		return -1;
@@ -2107,4 +2110,5 @@
 	rc = loc_category_get_svcs(cat_id, &svcs, &svcs_cnt);
 	if (rc != EOK || svcs_cnt == 0) {
+		printf("%s: Failed to get visualizer category services.\n", NAME);
 		input_disconnect();
 		return -1;
@@ -2123,4 +2127,5 @@
 	
 	if (list_empty(&viewport_list)) {
+		printf("%s: Failed to get view ports.\n", NAME);
 		input_disconnect();
 		return -1;
Index: uspace/srv/locsrv/locsrv.c
===================================================================
--- uspace/srv/locsrv/locsrv.c	(revision f597bc442c30ebe583db3c7ae73298e745aa081c)
+++ uspace/srv/locsrv/locsrv.c	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
@@ -1377,4 +1377,7 @@
 	categ_dir_add_cat(&cdir, cat);
 	
+	cat = category_new("audio-pcm");
+	categ_dir_add_cat(&cdir, cat);
+	
 	return true;
 }
Index: uspace/srv/net/tcp/tcp_header.h
===================================================================
--- uspace/srv/net/tcp/tcp_header.h	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
+++ uspace/srv/net/tcp/tcp_header.h	(revision 9e1800cd0d11714b8a9dd6d7e56a5e435315d57d)
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2009 Lukas Mejdrech
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+
+/** @file
+ * TCP header definition.
+ * Based on the RFC 793.
+ */
+
+#ifndef NET_TCP_HEADER_H_
+#define NET_TCP_HEADER_H_
+
+#include <sys/types.h>
+
+/** TCP header size in bytes. */
+#define TCP_HEADER_SIZE				sizeof(tcp_header_t)
+
+/** Returns the actual TCP header length in bytes.
+ * @param[in] header The TCP packet header.
+ */
+#define TCP_HEADER_LENGTH(header)		(GET_TCP_HEADER_LENGTH(header) * 4U)
+
+/** Returns the TCP header length.
+ * @param[in] length The TCP header length in bytes.
+ */
+#define TCP_COMPUTE_HEADER_LENGTH(length)	((uint8_t) ((length) / 4U))
+
+/** Type definition of the transmission datagram header.
+ * @see tcp_header
+ */
+typedef struct tcp_header tcp_header_t;
+
+/** Type definition of the transmission datagram header option.
+ * @see tcp_option
+ */
+typedef struct tcp_option tcp_option_t;
+
+/** Type definition of the Maximum segment size TCP option. */
+typedef struct tcp_max_segment_size_option tcp_max_segment_size_option_t;
+
+/** Transmission datagram header. */
+struct tcp_header {
+	uint16_t source_port;
+	uint16_t destination_port;
+	uint32_t sequence_number;
+	uint32_t acknowledgement_number;
+
+	uint8_t hlr; /* header length, reserved1 */
+
+#define GET_TCP_HEADER_LENGTH(header) \
+	(((header)->hlr & 0xf0) >> 4)
+#define SET_TCP_HEADER_LENGTH(header, length) \
+	((header)->hlr = \
+	 ((length & 0x0f) << 4) | ((header)->hlr & 0x0f))
+
+#define GET_TCP_HEADER_RESERVED1(header) \
+	((header)->hlr & 0x0f)
+#define SET_TCP_HEADER_RESERVED1(header, reserved1) \
+	((header)->hlr = \
+	 (reserved1 & 0x0f) | ((header)->hlr & 0xf0))
+
+	/* reserved2, urgent, acknowledge, push, reset, synchronize, finalize */
+	uint8_t ruaprsf;  
+
+#define GET_TCP_HEADER_RESERVED2(header) \
+	(((header)->ruaprsf & 0xc0) >> 6)
+#define SET_TCP_HEADER_RESERVED2(header, reserved2) \
+	((header)->ruaprsf = \
+	 ((reserved2 & 0x03) << 6) | ((header)->ruaprsf & 0x3f))
+
+#define GET_TCP_HEADER_URGENT(header) \
+	(((header)->ruaprsf & 0x20) >> 5)
+#define SET_TCP_HEADER_URGENT(header, urgent) \
+	((header)->ruaprsf = \
+	 ((urgent & 0x01) << 5) | ((header)->ruaprsf & 0xdf))
+
+#define GET_TCP_HEADER_ACKNOWLEDGE(header) \
+	(((header)->ruaprsf & 0x10) >> 4)
+#define SET_TCP_HEADER_ACKNOWLEDGE(header, acknowledge) \
+	((header)->ruaprsf = \
+	 ((acknowledge & 0x01) << 4) | ((header)->ruaprsf & 0xef))
+
+#define GET_TCP_HEADER_PUSH(header) \
+	(((header)->ruaprsf & 0x08) >> 3)
+#define SET_TCP_HEADER_PUSH(header, push) \
+	((header)->ruaprsf = \
+	 ((push & 0x01) << 3) | ((header)->ruaprsf & 0xf7))
+
+#define GET_TCP_HEADER_RESET(header) \
+	(((header)->ruaprsf & 0x04) >> 2)
+#define SET_TCP_HEADER_RESET(header, reset) \
+	((header)->ruaprsf = \
+	 ((reset & 0x01) << 2) | ((header)->ruaprsf & 0xfb))
+
+#define GET_TCP_HEADER_SYNCHRONIZE(header) \
+	(((header)->ruaprsf & 0x02) >> 1)
+#define SET_TCP_HEADER_SYNCHRONIZE(header, synchronize) \
+	((header)->ruaprsf = \
+	 ((synchronize & 0x01) << 1) | ((header)->ruaprsf & 0xfd))
+
+#define GET_TCP_HEADER_FINALIZE(header) \
+	((header)->ruaprsf & 0x01)
+#define SET_TCP_HEADER_FINALIZE(header, finalize) \
+	((header)->ruaprsf = \
+	 (finalize & 0x01) | ((header)->ruaprsf & 0xfe))
+
+	uint16_t window;
+	uint16_t checksum;
+	uint16_t urgent_pointer;
+} __attribute__ ((packed));
+
+/** Transmission datagram header option. */
+struct tcp_option {
+	/** Option type. */
+	uint8_t type;
+	/** Option length. */
+	uint8_t length;
+};
+
+/** Maximum segment size TCP option. */
+struct tcp_max_segment_size_option {
+	/** TCP option.
+	 * @see TCPOPT_MAX_SEGMENT_SIZE
+	 * @see TCPOPT_MAX_SEGMENT_SIZE_LENGTH
+	 */
+	tcp_option_t option;
+	
+	/** Maximum segment size in bytes. */
+	uint16_t max_segment_size;
+} __attribute__ ((packed));
+
+#endif
+
+/** @}
+ */
