Index: uspace/app/drec/Makefile
===================================================================
--- uspace/app/drec/Makefile	(revision e1b7e3616660c5647a45f22df7fe7b6e2c575500)
+++ uspace/app/drec/Makefile	(revision e1b7e3616660c5647a45f22df7fe7b6e2c575500)
@@ -0,0 +1,42 @@
+#
+# 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 = drec
+
+LIBS = \
+	$(LIBDRV_PREFIX)/libdrv.a
+
+EXTRA_CFLAGS = \
+	-I$(LIBDRV_PREFIX)/include
+
+SOURCES = \
+	drec.c \
+	wave.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/drec/drec.c
===================================================================
--- uspace/app/drec/drec.c	(revision e1b7e3616660c5647a45f22df7fe7b6e2c575500)
+++ uspace/app/drec/drec.c	(revision e1b7e3616660c5647a45f22df7fe7b6e2c575500)
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup dplay
+ * @{
+ */
+/**
+ * @file PCM playback audio devices
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <str_error.h>
+#include <str.h>
+#include <devman.h>
+#include <audio_pcm_buffer_iface.h>
+#include <fibril_synch.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <macros.h>
+
+#include "wave.h"
+
+#define DEFAULT_DEVICE "/hw/pci0/00:01.0/sb16/dsp"
+#define SUBBUFFERS 2
+
+const unsigned sampling_rate = 44100, sample_size = 16, channels = 2;
+bool sign = true;
+
+typedef struct {
+	struct {
+		void *base;
+		size_t size;
+		unsigned id;
+		void* position;
+	} buffer;
+	FILE* file;
+	async_exch_t *device;
+} record_t;
+
+static void record_initialize(record_t *rec, async_exch_t *exch)
+{
+	assert(exch);
+	assert(rec);
+	rec->buffer.id = 0;
+	rec->buffer.base = NULL;
+	rec->buffer.size = 0;
+	rec->buffer.position = NULL;
+	rec->file = NULL;
+	rec->device = exch;
+}
+
+
+static void device_event_callback(ipc_callid_t iid, ipc_call_t *icall, void* arg)
+{
+	async_answer_0(iid, EOK);
+	record_t *rec = arg;
+	const size_t buffer_part = rec->buffer.size / SUBBUFFERS;
+	while (1) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		if (IPC_GET_IMETHOD(call) != IPC_FIRST_USER_METHOD) {
+			printf("Unknown event.\n");
+			break;
+		}
+		const size_t bytes = fwrite(rec->buffer.position,
+		   sizeof(uint8_t), buffer_part, rec->file);
+		printf("%zu ", bytes);
+		rec->buffer.position += buffer_part;
+
+		if (rec->buffer.position >= (rec->buffer.base + rec->buffer.size))
+			rec->buffer.position = rec->buffer.base;
+		async_answer_0(callid, EOK);
+	}
+}
+
+
+static void record(record_t *rec, unsigned sampling_rate, unsigned sample_size,
+    unsigned channels, bool sign)
+{
+	assert(rec);
+	assert(rec->device);
+	rec->buffer.position = rec->buffer.base;
+	printf("Recording: %dHz, %d-bit %ssigned samples, %d channel(s).\n",
+	    sampling_rate, sample_size, sign ? "": "un", channels);
+	int ret = audio_pcm_buffer_start_record(rec->device, rec->buffer.id,
+	    SUBBUFFERS, sampling_rate, sample_size, channels, sign);
+	if (ret != EOK) {
+		printf("Failed to start recording: %s.\n", str_error(ret));
+		return;
+	}
+
+	getchar();
+	printf("\n");
+	audio_pcm_buffer_stop_record(rec->device, rec->buffer.id);
+}
+
+int main(int argc, char *argv[])
+{
+	const char *device = DEFAULT_DEVICE;
+	const char *file;
+	switch (argc) {
+	case 2:
+		file = argv[1];
+		break;
+	case 3:
+		device = argv[1];
+		file = argv[2];
+		break;
+	default:
+		printf("Usage: %s [device] file.\n", argv[0]);
+		return 1;
+	}
+
+	devman_handle_t pcm_handle;
+	int ret = devman_fun_get_handle(device, &pcm_handle, 0);
+	if (ret != EOK) {
+		printf("Failed to get device(%s) handle: %s.\n",
+		    device, str_error(ret));
+		return 1;
+	}
+
+	async_sess_t *session = devman_device_connect(
+	    EXCHANGE_SERIALIZE, pcm_handle, IPC_FLAG_BLOCKING);
+	if (!session) {
+		printf("Failed to connect to device.\n");
+		return 1;
+	}
+
+	async_exch_t *exch = async_exchange_begin(session);
+	if (!exch) {
+		ret = EPARTY;
+		printf("Failed to start session exchange.\n");
+		goto close_session;
+	}
+	const char* info = NULL;
+	ret = audio_pcm_buffer_get_info_str(exch, &info);
+	if (ret != EOK) {
+		printf("Failed to get PCM info.\n");
+		goto close_session;
+	}
+	printf("Playing on %s.\n", info);
+	free(info);
+
+	record_t rec;
+	record_initialize(&rec, exch);
+
+	ret = audio_pcm_buffer_get_buffer(rec.device, &rec.buffer.base,
+	    &rec.buffer.size, &rec.buffer.id, device_event_callback, &rec);
+	if (ret != EOK) {
+		printf("Failed to get PCM buffer: %s.\n", str_error(ret));
+		goto close_session;
+	}
+	printf("Buffer (%u): %p %zu.\n", rec.buffer.id, rec.buffer.base,
+	    rec.buffer.size);
+	uintptr_t ptr = 0;
+	as_get_physical_mapping(rec.buffer.base, &ptr);
+	printf("buffer mapped at %x.\n", ptr);
+
+	rec.file = fopen(file, "wb");
+	if (rec.file == NULL) {
+		ret = ENOENT;
+		printf("Failed to open %s.\n", file);
+		goto cleanup;
+	}
+	wave_header_t header = {
+		.chunk_id = CHUNK_ID,
+		.format = FORMAT_STR,
+		.subchunk1_id = SUBCHUNK1_ID,
+		.subchunk1_size = PCM_SUBCHUNK1_SIZE,
+		.audio_format = FORMAT_LINEAR_PCM,
+		.channels = channels,
+		.sampling_rate = sampling_rate,
+		.sample_size = sample_size,
+		.byte_rate = (sampling_rate / 8) * channels,
+		.block_align = (sampling_rate / 8) * channels,
+		.subchunk2_id = SUBCHUNK2_ID,
+	};
+	fwrite(&header, sizeof(header), 1, rec.file);
+	record(&rec, sampling_rate, sample_size, channels, sign);
+	fclose(rec.file);
+
+cleanup:
+	munmap(rec.buffer.base, rec.buffer.size);
+	audio_pcm_buffer_release_buffer(exch, rec.buffer.id);
+close_session:
+	async_exchange_end(exch);
+	async_hangup(session);
+	return ret == EOK ? 0 : 1;
+}
+/**
+ * @}
+ */
Index: uspace/app/drec/wave.c
===================================================================
--- uspace/app/drec/wave.c	(revision e1b7e3616660c5647a45f22df7fe7b6e2c575500)
+++ uspace/app/drec/wave.c	(revision e1b7e3616660c5647a45f22df7fe7b6e2c575500)
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/** @addtogroup dplay
+ * @{
+ */
+/**
+ * @file PCM playback audio devices
+ */
+
+#include <byteorder.h>
+#include <str.h>
+#include <errno.h>
+
+#include "wave.h"
+
+int wav_parse_header(void *file, const void **data, size_t *data_size,
+    unsigned *sampling_rate, unsigned *sample_size, unsigned *channels,
+    bool *sign, const char **error)
+{
+	if (!file) {
+		if (error)
+			*error = "file not present";
+		return EINVAL;
+	}
+
+	const wave_header_t *header = file;
+	if (str_lcmp(header->chunk_id, CHUNK_ID, 4) != 0) {
+		if (error)
+			*error = "invalid chunk id";
+		return EINVAL;
+	}
+
+	if (str_lcmp(header->format, FORMAT_STR, 4) != 0) {
+		if (error)
+			*error = "invalid format string";
+		return EINVAL;
+	}
+
+	if (str_lcmp(header->subchunk1_id, SUBCHUNK1_ID, 4) != 0) {
+		if (error)
+			*error = "invalid subchunk1 id";
+		return EINVAL;
+	}
+
+	if (uint16_t_le2host(header->subchunk1_size) != PCM_SUBCHUNK1_SIZE) {
+		if (error)
+			*error = "invalid subchunk1 size";
+		return EINVAL;
+	}
+
+	if (uint16_t_le2host(header->audio_format) != FORMAT_LINEAR_PCM) {
+		if (error)
+			*error = "unknown format";
+		return ENOTSUP;
+	}
+
+	if (str_lcmp(header->subchunk2_id, SUBCHUNK2_ID, 4) != 0) {
+		if (error)
+			*error = "invalid subchunk2 id";
+		return EINVAL;
+	}
+
+	if (data)
+		*data = header->data;
+	if (data_size)
+		*data_size = uint32_t_le2host(header->subchunk2_size);
+
+	if (sampling_rate)
+		*sampling_rate = uint32_t_le2host(header->sampling_rate);
+	if (sample_size)
+		*sample_size = uint32_t_le2host(header->sample_size);
+	if (channels)
+		*channels = uint16_t_le2host(header->channels);
+	if (sign)
+		*sign = uint32_t_le2host(header->sample_size) == 16
+		    ? true : false;
+	if (error)
+		*error = "no error";
+
+	return EOK;
+}
+/**
+ * @}
+ */
Index: uspace/app/drec/wave.h
===================================================================
--- uspace/app/drec/wave.h	(revision e1b7e3616660c5647a45f22df7fe7b6e2c575500)
+++ uspace/app/drec/wave.h	(revision e1b7e3616660c5647a45f22df7fe7b6e2c575500)
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/** @addtogroup dplay
+ * @{
+ */
+/** @file
+ * @brief .wav file format.
+ */
+#ifndef WAVE_H
+#define WAVE_H
+
+#include <stdint.h>
+#include <bool.h>
+
+/** Wave file header format.
+ *
+ * https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
+ * @note: 8-bit samples are stored as unsigned bytes,
+ * 16-bit samples are stored as signed integers.
+ * @note: The default byte ordering assumed for WAVE data files is
+ * little-endian. Files written using the big-endian byte ordering scheme have
+ * the identifier RIFX instead of RIFF.
+ */
+typedef struct wave_header {
+	/** Should be 'R', 'I', 'F', 'F'. */
+	char chunk_id[4];
+#define CHUNK_ID "RIFF"
+
+	/** Total size minus the first 8 bytes */
+	uint32_t chunk_size;
+	/** Should be 'W', 'A', 'V', 'E'. */
+	char format[4];
+#define FORMAT_STR "WAVE"
+
+	/** Should be 'f', 'm', 't', ' '. */
+	char subchunk1_id[4];
+#define SUBCHUNK1_ID "fmt "
+
+	/** Size of the ret of this subchunk. 16 for PCM file. */
+	uint32_t subchunk1_size;
+#define PCM_SUBCHUNK1_SIZE 16
+	/** Format. 1 for Linear PCM */
+	uint16_t audio_format;
+#define FORMAT_LINEAR_PCM 1
+	/** Number of channels. */
+	uint16_t channels;
+	/** Sampling rate. */
+	uint32_t sampling_rate;
+	/** Byte rate. */
+	uint32_t byte_rate;
+	/** Block align. Bytes in one block (samples for all channels). */
+	uint16_t block_align;
+	/** Bits per sample (one channel). */
+	uint16_t sample_size;
+
+	/** Should be 'd', 'a', 't', 'a'. */
+	char subchunk2_id[4];
+#define SUBCHUNK2_ID "data"
+	/** Audio data size. */
+	uint32_t subchunk2_size;
+	/** Audio data. */
+	uint8_t data[];
+
+} wave_header_t;
+
+int wav_parse_header(void *, const void**, size_t *, unsigned *, unsigned *,
+    unsigned *, bool *, const char **error_str);
+
+#endif
+/**
+ * @}
+ */
